Rikk / recaptcha

Automatically exported from code.google.com/p/recaptcha
0 stars 0 forks source link

ASP.Net: Validation (undesirably) firing on every postback #56

Closed GoogleCodeExporter closed 9 years ago

GoogleCodeExporter commented 9 years ago
The captcha validation appears to fire on every page postback. This is not 
desirable, as even buttons with the "CausesValidation" property set to 
"false" are causing the catcha control to validate.

As an aside, it would be nice to see the control support ValidationGroups.

Original issue reported on code.google.com by mr.leigh...@googlemail.com on 17 Sep 2009 at 9:18

GoogleCodeExporter commented 9 years ago
Hmmm... I've downloaded the source for this and, in your test site, everything 
appears 
to behave as expected. It must be something specific to my project. I'll 
investigate 
further.

I'd still like to see ValidationGroups implemented though.

Original comment by mr.leigh...@googlemail.com on 17 Sep 2009 at 9:39

GoogleCodeExporter commented 9 years ago
After a little further investigation, I can recreate the problem in your test 
site:

1. Add a validation summary control to the page.
2. Add another button, and set its "CausesValidation" property to false.
3. Run the site and click on the new button

The ErrorMessage text appears in the validation summary control!

Original comment by mr.leigh...@googlemail.com on 17 Sep 2009 at 9:45

GoogleCodeExporter commented 9 years ago
I've fixed the ValidationSummary issue. How can I submit it for approval to be 
included?

Original comment by mr.leigh...@googlemail.com on 17 Sep 2009 at 12:27

GoogleCodeExporter commented 9 years ago
I submitted the fix for this some time ago. As the control does not appear to 
be 
receiving updates, and I have already had a number of requests for my fix, I 
though 
I'd post it here:

// Copyright (c) 2007 Adrian Godong, Ben Maurer, Mike Hatalski
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

using System;
using System.ComponentModel;
using System.Configuration;
using System.Text;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace Recaptcha
{
    /// <summary>
    /// This class encapsulates reCAPTCHA UI and logic into an ASP.NET server 
control.
    /// </summary>
    [ToolboxData("<{0}:RecaptchaControl runat=\"server\" />")]
    [Designer(typeof(Recaptcha.Design.RecaptchaControlDesigner))]
    public class RecaptchaControl : WebControl, IValidator
    {
        #region Private Fields

        private const string RECAPTCHA_CHALLENGE_FIELD = "recaptcha_challenge_field";
        private const string RECAPTCHA_RESPONSE_FIELD = "recaptcha_response_field";

        private const string RECAPTCHA_SECURE_HOST = "https://api-
secure.recaptcha.net";
        private const string RECAPTCHA_HOST = "http://api.recaptcha.net";

        private RecaptchaResponse recaptchaResponse;

        private string publicKey;
        private string privateKey;
        private string theme;
        private string customThemeWidget;
        private string errorMessage;
        private bool skipRecaptcha;
        private bool allowMultipleInstances;
        private bool overrideSecureMode;

        private bool isValid;

        #endregion

        #region Public Properties

        [Category("Settings")]
        [Description("The public key from admin.recaptcha.net. Can also be set using 
RecaptchaPublicKey in AppSettings.")]
        public string PublicKey
        {
            get { return this.publicKey; }
            set { this.publicKey = value; }
        }

        [Category("Settings")]
        [Description("The private key from admin.recaptcha.net. Can also be set using 
RecaptchaPrivateKey in AppSettings.")]
        public string PrivateKey
        {
            get { return this.privateKey; }
            set { this.privateKey = value; }
        }

        [Category("Appearence")]
        [DefaultValue("red")]
        [Description("The theme for the reCAPTCHA control. Currently supported values 
are red, blackglass, white, and clean")]
        public string Theme
        {
            get { return this.theme; }
            set { this.theme = value; }
        }

        [Category("Appearence")]
        [DefaultValue(null)]
        [Description("When using custom theming, this is a div element which contains 
the widget. ")]
        public string CustomThemeWidget
        {
            get { return this.customThemeWidget; }
            set { this.customThemeWidget = value; }
        }

        [Category("Settings")]
        [DefaultValue(false)]
        [Description("Set this to true to stop reCAPTCHA validation. Useful for 
testing platform. Can also be set using RecaptchaSkipValidation in 
AppSettings.")]
        public bool SkipRecaptcha
        {
            get { return this.skipRecaptcha; }
            set { this.skipRecaptcha = value; }
        }

        [Category("Settings")]
        [DefaultValue(false)]
        [Description("Set this to true to enable multiple reCAPTCHA on a single page. 
There may be complication between controls when this is enabled.")]
        public bool AllowMultipleInstances
        {
            get { return this.allowMultipleInstances; }
            set { this.allowMultipleInstances = value; }
        }

        [Category("Settings")]
        [DefaultValue(false)]
        [Description("Set this to true to override reCAPTCHA usage of Secure API.")]
        public bool OverrideSecureMode
        {
            get { return this.overrideSecureMode; }
            set { this.overrideSecureMode = value; }
        }

        #endregion

        /// <summary>
        /// Initializes a new instance of the <see cref="RecaptchaControl"/> class.
        /// </summary>
        public RecaptchaControl()
        {
            isValid = true;

            this.publicKey = ConfigurationManager.AppSettings["RecaptchaPublicKey"];
            this.privateKey = 
ConfigurationManager.AppSettings["RecaptchaPrivateKey"];
            if 
(!bool.TryParse(ConfigurationManager.AppSettings["RecaptchaSkipValidation"], 
out 
this.skipRecaptcha))
            {
                this.skipRecaptcha = false;
            }
        }

        #region Overriden Methods

        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);

            if (string.IsNullOrEmpty(this.PublicKey) || 
string.IsNullOrEmpty(this.PrivateKey))
            {
                throw new ApplicationException("reCAPTCHA needs to be configured with 
a public & private key.");
            }

            if (this.allowMultipleInstances || !this.CheckIfRecaptchaExists())
            {
                Page.Validators.Add(this);
            }
        }

        /// <summary>
        /// Iterates through the Page.Validators property and look for registered 
instance of <see cref="RecaptchaControl"/>.
        /// </summary>
        /// <returns>True if an instance is found, False otherwise.</returns>
        private bool CheckIfRecaptchaExists()
        {
            foreach (var validator in Page.Validators)
            {
                if (validator is RecaptchaControl)
                {
                    return true;
                }
            }

            return false;
        }

        protected override void Render(HtmlTextWriter writer)
        {
            if (this.skipRecaptcha)
            {
                writer.WriteLine("reCAPTCHA validation is skipped. Set SkipRecaptcha 
property to false to enable validation.");
            }
            else
            {
                this.RenderContents(writer);
            }
        }

        protected override void RenderContents(HtmlTextWriter output)
        {
            // <script> setting
            output.AddAttribute(HtmlTextWriterAttribute.Type, "text/javascript");
            output.RenderBeginTag(HtmlTextWriterTag.Script);
            output.Indent++;
            output.WriteLine("var RecaptchaOptions = {");
            output.Indent++;
            output.WriteLine("theme : '{0}',", this.theme ?? string.Empty);
            if (null != customThemeWidget)
                output.WriteLine("custom_theme_widget : '{0}',", customThemeWidget);
            output.WriteLine("tabindex : {0}", TabIndex);
            output.Indent--;
            output.WriteLine("};");
            output.Indent--;
            output.RenderEndTag();

            // <script> display
            output.AddAttribute(HtmlTextWriterAttribute.Type, "text/javascript");
            output.AddAttribute(HtmlTextWriterAttribute.Src, 
this.GenerateChallengeUrl(false), false);
            output.RenderBeginTag(HtmlTextWriterTag.Script);
            output.RenderEndTag();

            // <noscript> display
            output.RenderBeginTag(HtmlTextWriterTag.Noscript);
            output.Indent++;
            output.AddAttribute(HtmlTextWriterAttribute.Src, 
this.GenerateChallengeUrl(true), false);
            output.AddAttribute(HtmlTextWriterAttribute.Width, "500");
            output.AddAttribute(HtmlTextWriterAttribute.Height, "300");
            output.AddAttribute("frameborder", "0");
            output.RenderBeginTag(HtmlTextWriterTag.Iframe);
            output.RenderEndTag();
            output.WriteBreak(); // modified to make XHTML-compliant. Patch by 
xitch13@gmail.com.
            output.AddAttribute(HtmlTextWriterAttribute.Name, 
"recaptcha_challenge_field");
            output.AddAttribute(HtmlTextWriterAttribute.Rows, "3");
            output.AddAttribute(HtmlTextWriterAttribute.Cols, "40");
            output.RenderBeginTag(HtmlTextWriterTag.Textarea);
            output.RenderEndTag();
            output.AddAttribute(HtmlTextWriterAttribute.Name, 
"recaptcha_response_field");
            output.AddAttribute(HtmlTextWriterAttribute.Value, "manual_challenge");
            output.AddAttribute(HtmlTextWriterAttribute.Type, "hidden");
            output.RenderBeginTag(HtmlTextWriterTag.Input);
            output.RenderEndTag();
            output.Indent--;
            output.RenderEndTag();
        }

        #endregion

        #region IValidator Members

        [LocalizableAttribute(true)]
        [DefaultValue("The verification words are incorrect.")]
        public string ErrorMessage
        {
            get
            {
                if (this.errorMessage != null)
                {
                    return this.errorMessage;
                }

                return "The verification words are incorrect.";
            }

            set
            {
                this.errorMessage = value;
            }
        }

        [Browsable(false)]
        public bool IsValid
        {
            get
            {
                return isValid;
            }
            set
            {
                throw new NotImplementedException("This setter is not implemented.");
            }
        }

        public void Validate()
        {
            if (Page.IsPostBack && Visible && Enabled && !this.skipRecaptcha)
            {
                if (this.recaptchaResponse == null)
                {
                    this.ValidateCaptcha();
                }

                isValid = this.recaptchaResponse != null && 
this.recaptchaResponse.IsValid;
            }
        }

        /// <summary>
        /// Perform validation of reCAPTCHA.
        /// </summary>
        public void ValidateCaptcha()
        {
            if (this.skipRecaptcha)
            {
                this.recaptchaResponse = RecaptchaResponse.Valid;
            }

            if (this.recaptchaResponse == null)
            {
                if (Visible && Enabled)
                {
                    RecaptchaValidator validator = new RecaptchaValidator();
                    validator.PrivateKey = this.PrivateKey;
                    validator.RemoteIP = Page.Request.UserHostAddress;
                    validator.Challenge = 
Context.Request.Form[RECAPTCHA_CHALLENGE_FIELD];
                    validator.Response = 
Context.Request.Form[RECAPTCHA_RESPONSE_FIELD];

                    try
                    {
                        this.recaptchaResponse = validator.Validate();
                    }
                    catch (ArgumentNullException ex)
                    {
                        this.recaptchaResponse = null;
                        this.errorMessage = ex.Message;
                    }
                }
            }
        }

        #endregion

        /// <summary>
        /// This function generates challenge URL.
        /// </summary>
        private string GenerateChallengeUrl(bool noScript)
        {
            StringBuilder urlBuilder = new StringBuilder();
            urlBuilder.Append(Context.Request.IsSecureConnection || 
this.overrideSecureMode ? RECAPTCHA_SECURE_HOST : RECAPTCHA_HOST);
            urlBuilder.Append(noScript ? "/noscript?" : "/challenge?");
            urlBuilder.AppendFormat("k={0}", this.PublicKey);
            if (this.recaptchaResponse != null && this.recaptchaResponse.ErrorCode != 
string.Empty)
            {
                urlBuilder.AppendFormat("&error={0}", 
this.recaptchaResponse.ErrorCode);
            }

            return urlBuilder.ToString();
        }
    }
}

Original comment by lbowers....@gmail.com on 14 Jan 2010 at 11:55

GoogleCodeExporter commented 9 years ago

Original comment by adrian.g...@gmail.com on 1 Dec 2010 at 10:47

GoogleCodeExporter commented 9 years ago

Original comment by adrian.g...@gmail.com on 30 Mar 2012 at 6:16