in Development

Community Server Captcha Control

One of the problems we had in the past at Baleares on .NET was the amount of user registrations done by bots, to avoid it we managed to install a captcha control deployed by Dave Burke on the Join option of Community Server. This captha was created for Community Server 2.1, so after install the new version 2007 we needed to do some changes.

Fortunately having the source code of CS is very easy to understand how to apply the changes to have the control up and running again.

Maybe it’s possible to create a standalone captcha control and plug it to CS, but since in the past we already needed to modify the CreateUser class, I’ve preferred just to add it as part of it and recompile. Let’s see how I did it.

I’ve added to the Components folder of CommunityServerComponents20 project a slightly modified version of the CaptchaService class (now called Captcha), nothing important, just added IDisposable interface, some extra disposing not done and modified to use “using” (I’m a fan of the using statement) for some objects instead of the method Dispose at the end of the method.

Then in the folder Page of the project CommunityServerControls20 I’ve added the source code for JpegImage.cs, again with minor modifications.

After this is when we really need to modify the necessary code to support the Captcha component in our Registration page. The idea we follow is to add a new textbox in order our users can introduce the code displayed in the image of the captcha control. To do it we need to modify the class CreateUserForm that is contained in the folder Forms.

We start adding a new field of type TextBox called “CaptchaCodeTextBox” and the next property.

public string CaptchaCodeTextBoxId
{
    get { return (string)ViewState["CaptchaCodeTextBoxId"] ?? string.Empty; }
    set { ViewState["CaptchaCodeTextBoxId"] = value; }
}

This will allow us retrieving the name of the textbox we will add later on in the ASP.NET page for validation. In the method “AttachChildControls” we get and check that effectively the ASP.NET page contains the required textbox.

CaptchaCodeTextBox = CSControlUtility.Instance().FindControl(
        this, CaptchaCodeTextBoxId) as TextBox;

if (UserNameTextBox == null
    || PasswordTextBox == null
    || EmailAddressTextBox == null
    || CreateButton == null
    || CreateUserCustomValidator == null
    || CaptchaCodeTextBox == null)

    throw new InvalidOperationException("UserNameTextBoxId,
        PasswordTextBoxId, EmailAddressTextBoxId, CreateUserCustomValidatorId,
        CaptchaCodeTextBoxId and CreateButtonId must be valid controls
        to render a CreateUserForm");

At the end of the same method we also create the cookie that will contain the code to be manually introduced by the user.

HttpContext.Current.Response.Cookies[Captcha.CookieName].Value = Captcha.GenerateRandomCode();

The next method to modify is the one called “IsValid”. As you already supposed, here is where we will compare the code introduced by the user with the one we have randomly generated. The code is very simple, take a look:

if (this.CaptchaCodeTextBox.Text !=
    HttpContext.Current.Request.Cookies[Captcha.CookieName].Value)
{
    CreateUserCustomValidator.ErrorMessage =
        CommunityServer.Components.ResourceManager.GetString("CreateNewAccount_InvalidCaptchaCode");

    CreateUserCustomValidator.IsValid = false;

    return false;
}

this.CaptchaCodeTextBox.Text = string.Empty;
HttpContext.Current.Response.Cookies[Captcha.CookieName].Value =
        Captcha.GenerateRandomCode();

Note that in the line 4 we are using a new Resource name that we will need to introduce in the Resources.xml file.
With this we are already done with the source code, now we need to do the last updates to the CreateUser.aspx page.
Open the page and at the beginning of the CSControl:CreateUserForm you will see a list with the id’s used for the required controls, as I said before we need to add the one for the new textbox.

<CSControl:CreateUserForm runat="server"
  AcceptAgreementCheckBoxId="AcceptAgreement"
  AcceptAgreementHyperLinkId="AcceptAgreementLink"
  AllowSitePartnersToContactCheckBoxId="AllowSitePartnersToContact"
  AllowSiteToContactCheckBoxId="AllowSiteToContact"
  CreateButtonId="CreateAccount"
  CreateUserCustomValidatorId="CreateUserCustomValidator"
  EmailAddressTextBoxId="Email"
  PasswordTextBoxId="Password"
  SubFormIds=""
  TimeZoneDropDownListId="Timezone"
  UserNameTextBoxId="Username"
  CaptchaCodeTextBoxId="CaptchaCode">

Then you only need add the next code somewhere in the page. I did it after the “TimeZone” DropDownList. Note again that we have added a new resource (line 3).

<tr>
    <td class="CommonFormFieldName">
        <cscontrol:resourcecontrol runat="server" resourcename="CreateNewAccount_EnterCode" />
    </td>
    <td>
        <div class="CommonFormField">
            <asp:TextBox ID="CaptchaCode" runat="server" MaxLength="128" columns="40" onkeyup="validateForm(this);" />
            <asp:RequiredFieldValidator ID="captchaValidator" runat="server" ControlToValidate="CaptchaCode" Cssclass="validationWarning">
            </asp:RequiredFieldValidator>
        </div>
    </td>
</tr>

<tr>
    <td align="right"></td>
    <td>
        <img src="../JpegImage.aspx" alt="Captcha Image" />
    </td>
</tr>

To finish you only need to add to the page JpegImage.aspx in the root folder, replace the assemblies of the folder bin CommunityServer.Components.dll, CommunityServer.Controls.dll with the ones we modified above and don’t forget to add the two new Resources to the file Resources.xml contained in each language folder you support. Just something like this:

Enter the Code:

Code does not match with the image displayed

That’s all; we are ready to use our new captcha control!!

CaptchaCS2007.zip