Friday 9 November 2012

How to use Recaptcha?

List of IP Addresses used: http://code.google.com/p/recaptcha/wiki/FirewallsAndRecaptcha

Create a public and private key for your domain: http://www.google.com/recaptcha

Then the below code manages everything you'd need not based on the Session attempts but Application attempts:

public class RecaptchaManager
 {
  #region Local variables

  private static volatile RecaptchaManager _instance;
  private static readonly object SyncRoot = new Object();
  private readonly Byte _maxNumberOfValidFailedAttempts;

  private readonly Byte _durationInMinutes;
  private readonly int _cleanUpMaxSize;
  private readonly string _privateKey;
  private readonly string _publicKey;
  private readonly List _result = new List();

  /// 
  /// Be careful where and how the locks are used to avoid deadlocks or inter locks.
  /// 
  private readonly Object _lockObject = new object();

  #endregion

  #region Constructors and class initialization

  private RecaptchaManager()
  {
   if (!Byte.TryParse(SettingHelper.Instance["Recaptcha.ValidFailedAttempts.MaxNumber"], out _maxNumberOfValidFailedAttempts))
   {
    throw new ApplicationException("Invalid or non-existent config key: Recaptcha.ValidFailedAttempts.MaxNumber");
   }

   if (!Byte.TryParse(SettingHelper.Instance["Recaptcha.ValidFailedAttempts.DurationInMinutes"], out _durationInMinutes))
   {
    throw new ApplicationException("Invalid or non-existent config key: Recaptcha.ValidFailedAttempts.DurationInMinutes");
   }

   if (!int.TryParse(SettingHelper.Instance["Recaptcha.CleanUp.MaxSize"], out _cleanUpMaxSize))
   {
    throw new ApplicationException("Invalid or non-existent config key: Recaptcha.CleanUp.MaxSize");
   }

   // private key
   this._privateKey = SettingHelper.Instance["Recaptcha.PrivateKey"];

   if (string.IsNullOrWhiteSpace(this._privateKey))
   {
    throw new ApplicationException("Invalid or non-existent config key: Recaptcha.PrivateKey");
   }

   this._publicKey = SettingHelper.Instance["Recaptcha.PublicKey"];

   if (string.IsNullOrWhiteSpace(this._publicKey))
   {
    throw new ApplicationException("Invalid or non-existent config key: Recaptcha.PublicKey");
   }
  }

  #endregion

  #region Properties

  public static RecaptchaManager Instance
  {
   get
   {
    if (_instance == null)
    {
     lock (SyncRoot)
     {
      if (_instance == null)
       _instance = new RecaptchaManager();
     }
    }

    return _instance;
   }
  }

  public string PublicKey
  {
   get { return this._publicKey; }
  }

  #endregion

  #region public Methods

  public void AddFailedLoginAttempt()
  {
   // we may want to customize these
   addFailedLoginOrRegistrationAttempt();
  }

  public void AddFailedRegistrationAttempt()
  {
   // we may want to customize these
   addFailedLoginOrRegistrationAttempt();
  }

  public bool ShouldRequireRecaptcha()
  {   
   return getNumberOfFailedAttemptsDuringThePeriod() > this._maxNumberOfValidFailedAttempts;
  }

  public bool IsRecaptchaAvailableOnPage()
  {
   // if this field exists then the Recaptcha is available
   string challengeValue = Utils.GetStringForm("recaptcha_challenge_field", null);
   
   return challengeValue != null;
  }

  public bool IsInputValueVerified()
  {
   string challengeValue = Utils.GetStringForm("recaptcha_challenge_field", null);
   string responseValue = Utils.GetStringForm("recaptcha_response_field", null);
   
   bool result = false;

   try
   {
    var captchaValidtor = new RecaptchaValidator()
    {
     // put it in web.config
     PrivateKey = this._privateKey,

     RemoteIP = HttpContext.Current.Request.UserHostAddress,

     Challenge = challengeValue,

     Response = responseValue
    };

    result = captchaValidtor.Validate().IsValid;
   }
   catch { }

   return result;
  }

  #endregion

  #region Private functions
  
  private void addFailedLoginOrRegistrationAttempt()
  {
   lock (this._lockObject)
   {
    if (this._result.Count > this._cleanUpMaxSize)
    {
     this.removeOldData();
    }

    this._result.Add(DateTime.Now);
   }
  }

  /// 
  /// Removes the old data.
  /// 
  private void removeOldData()
  {
   DateTime startDateTime = getStartDateTime();
   this._result.RemoveAll(item => item < startDateTime);
  }

  private int getNumberOfFailedAttemptsDuringThePeriod()
  {
   DateTime startDateTime = getStartDateTime();

   lock (this._lockObject)
   {
    return this._result.Count(failedAttemp => failedAttemp > startDateTime);
   }
  }

  private DateTime getStartDateTime()
  {
   return DateTime.Now.AddMinutes(-this._durationInMinutes);
  }

  #endregion
 }

No comments: