Creating a Read-Only Membership Provider for phpBB 2.0 Users

I recently posted a blog that was titled Creating a Read-Only Snitz Membership Provider, where I re-used the code from my How to use the Sample Read-Only XML Membership and Role Providers with IIS 7.0 walkthrough on the learn.iis.net web site to write a membership provider for web sites that use the Snitz Forums application. After I finished writing that blog post, I started thinking about the web sites where I use the phpBB 2.0 application, which leads us to today's blog post.

Following in the footsteps of my Snitz blog and XML walkthrough, today's blog post will show how to set up a simple, read-only provider for phpBB 2.0, although for this blog I tested a web site that was specifically using the phpBB 2.0.22 version. (Before anyone sends me email about it - I know that phpBB 3.0 is has been released and it's way cooler, but I still have web sites that are running the 2.0 phpBB forums.)

As I did with the Snitz provider, I chose to create a single namespace ("ReadOnlyPhpBBProvider") that contained two classes: one for the membership provider ("PhpBBMembershipProvider") and the other for the role provider ("PhpBBRoleProvider"); and once again I added an additional class ("PhpBBUtils") with a couple of helper methods.

Here are some of the additional implementation details:

  • Unlike the Snitz forums, the phpBB forums have groups, so I used those for my roles implementation.
  • My SQL queries are hard-coded to use the default "phpbb_" prefix; this could easily be modified.
  • I use the "user_regdate" and "user_session_time" fields in the "phpp_users" table to populate the membership CreationDate, LastLoginDate, and LastActivityDate values. It would be possible to retrieve the last post for a user from the phpbb_posts table and compute the LastActivityDate, but I'm not really using that value so I chose to avoid the hassle.

Step 1: Creating the Project

In this section you will create a project in Visual Studio for the membership/role provider.

  1. Open Microsoft Visual Studio 2008.
  2. Click the File menu, then New, then Project.
  3. In the New Project dialog:
    • Choose Visual C# as the project type.
    • Choose Class Library as the template.
    • Type ReadOnlyPhpBBProvider as the name of the project.
    • Uncheck the Create directory for solution box.
    • Click OK.
  4. Add a reference path to the System.Configuration library:
    • In the solution explorer, right-click the ReadOnlyPhpBBProvider project, then Add Reference...
    • Click the .NET tab.
    • Select "System.Configuration" in the list of assemblies.
    • Click OK.
  5. Add a reference path to the System.Web library:
    • In the solution explorer, right-click the ReadOnlyPhpBBProvider project, then Add Reference...
    • Click the .NET tab.
    • Select "System.Web" in the list of assemblies.
    • Click OK.
  6. Add a strong name key to the project:
    • In the solution explorer, right-click the ReadOnlyPhpBBProvider project, then Properties.
    • Click the Signing tab.
    • Check the Sign the assembly check box.
    • Choose <New...> from the strong key name drop-down box.
    • Enter ReadOnlyPhpBBProviderKey for the key file name.
    • If desired, enter a password for the key file; otherwise, uncheck the Protect my key file with a password box.
    • Click OK.
  7. (Optional) Add a custom build event to automatically register the DLL in the GAC:
    • In the solution explorer, right-click the ReadOnlyPhpBBProvider project, then Properties.
    • Click the Build Events tab.
    • Enter the following in the Post-build event command line box:
      call "%VS90COMNTOOLS%\vsvars32.bat">null
      gacutil.exe /if "$(TargetPath)"
  8. Save the solution.

Step 2: Add the provider classes for the project

In this second step you will create the classes for the membership and role providers. Once again, the code for these classes is largely based on the Membership Providers and Role Providers topics on MSDN.

  1. Open the Class1.cs file if it is not already open.
  2. Remove all of the existing code from the class.
  3. Paste the following sample code into the editor:
    /* ======================================== */
    //
    // ReadOnlyPhpBBProvider
    //
    // A read-only membership and role provider for phpBB forums.
    //
    /* ======================================== */
    
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Collections.Specialized;
    using System.Configuration;
    using System.Configuration.Provider;
    using System.Data.Odbc;
    using System.Linq;
    using System.Security.Cryptography;
    using System.Text;
    using System.Web.Security;
    
    namespace ReadOnlyPhpBBProvider
    {
      /* ======================================== */
      //
      // PhpBBMembershipProvider
      //
      /* ======================================== */
    
      public class PhpBBMembershipProvider : MembershipProvider
      {
        private Dictionary<string, MembershipUser> _Users;
        private string _connectionStringName;
        private string _connectionString;
        private PhpBBUtils _phpbbUtils;
    
        /* ---------------------------------------- */
        // MembershipProvider Properties
        /* ---------------------------------------- */
    
        public override string ApplicationName
        {
          get { throw new NotSupportedException(); }
          set { throw new NotSupportedException(); }
        }
    
        /* ---------------------------------------- */
    
        public override bool EnablePasswordRetrieval
        {
          get { return false; }
        }
    
        /* ---------------------------------------- */
    
        public override bool EnablePasswordReset
        {
          get { return false; }
        }
    
        /* ---------------------------------------- */
    
        public override int MaxInvalidPasswordAttempts
        {
          get { throw new NotSupportedException(); }
        }
    
        /* ---------------------------------------- */
    
        public override int MinRequiredNonAlphanumericCharacters
        {
          get { throw new NotSupportedException(); }
        }
    
        /* ---------------------------------------- */
    
        public override int MinRequiredPasswordLength
        {
          get { throw new NotSupportedException(); }
        }
    
        /* ---------------------------------------- */
    
        public override int PasswordAttemptWindow
        {
          get { throw new NotSupportedException(); }
        }
    
        /* ---------------------------------------- */
    
        public override MembershipPasswordFormat PasswordFormat
        {
          get { throw new NotSupportedException(); }
        }
    
        /* ---------------------------------------- */
    
        public override string PasswordStrengthRegularExpression
        {
          get { throw new NotSupportedException(); }
        }
    
        /* ---------------------------------------- */
    
        public override bool RequiresQuestionAndAnswer
        {
          get { throw new NotSupportedException(); }
        }
    
        /* ---------------------------------------- */
    
        public override bool RequiresUniqueEmail
        {
          get { throw new NotSupportedException(); }
        }
    
        /* ---------------------------------------- */
        // MembershipProvider Methods
        /* ---------------------------------------- */
    
        public override void Initialize(
          string name, NameValueCollection config)
        {
          if (config == null)
            throw new ArgumentNullException("config");
          if (String.IsNullOrEmpty(name))
            name = "ReadOnlyPhpBBMembershipProvider";
          if (string.IsNullOrEmpty(config["description"]))
          {
            config.Remove("description");
            config.Add("description", "Read-only phpBB membership provider");
          }
          base.Initialize(name, config);
          _connectionStringName = config["connectionStringName"];
          if (String.IsNullOrEmpty(_connectionStringName))
          {
            throw new ProviderException("No connection string was specified.\n");
          }
          _connectionString = ConfigurationManager.ConnectionStrings[
            _connectionStringName].ConnectionString;
          _phpbbUtils = new PhpBBUtils();
        }
    
        /* ---------------------------------------- */
    
        public override bool ValidateUser(
          string username, string password)
        {
          if (String.IsNullOrEmpty(username) ||
            String.IsNullOrEmpty(password))
            return false;
          try
          {
            ReadMembershipDataStore();
            MembershipUser user;
            if (_Users.TryGetValue(username, out user))
            {
              if ((user.Comment == _phpbbUtils.PasswordHash(password))
                && (user.IsLockedOut == false)
                && (user.IsApproved == true))
              {
                return true;
              }
            }
            return false;
          }
          catch (Exception)
          {
            return false;
          }
        }
    
        /* ---------------------------------------- */
    
        public override MembershipUser GetUser(
          string username, bool userIsOnline)
        {
          if (String.IsNullOrEmpty(username)) return null;
          ReadMembershipDataStore();
          try
          {
            MembershipUser user;
            if (_Users.TryGetValue(username, out user)) return user;
          }
          catch (Exception ex)
          {
            throw new ProviderException("Error: " + ex.Message);
          }
          return null;
        }
    
        /* ---------------------------------------- */
    
        public override MembershipUserCollection GetAllUsers(
          int pageIndex, int pageSize, out int totalRecords)
        {
          ReadMembershipDataStore();
          MembershipUserCollection users = new MembershipUserCollection();
          if ((pageIndex >= 0) && (pageSize >= 1))
          {
            try
            {
              foreach (KeyValuePair<string, MembershipUser> pair
                in _Users.Skip(pageIndex * pageSize).Take(pageSize))
              {
                users.Add(pair.Value);
              }
            }
            catch (Exception ex)
            {
              throw new ProviderException("Error: " + ex.Message);
            }
          }
          totalRecords = _Users.Count;
          return users;
        }
    
        /* ---------------------------------------- */
    
        public override int GetNumberOfUsersOnline()
        {
          throw new NotSupportedException();
        }
    
        /* ---------------------------------------- */
    
        public override bool ChangePassword(
          string username, string oldPassword, string newPassword)
        {
          throw new NotSupportedException();
        }
    
        /* ---------------------------------------- */
    
        public override bool ChangePasswordQuestionAndAnswer(
          string username, string password,
          string newPasswordQuestion, string newPasswordAnswer)
        {
          throw new NotSupportedException();
        }
    
        /* ---------------------------------------- */
    
        public override MembershipUser CreateUser(
          string username, string password, string email,
          string passwordQuestion, string passwordAnswer,
          bool isApproved, object providerUserKey,
          out MembershipCreateStatus status)
        {
          throw new NotSupportedException();
        }
    
        /* ---------------------------------------- */
    
        public override bool DeleteUser(
          string username, bool deleteAllRelatedData)
        {
          throw new NotSupportedException();
        }
    
        /* ---------------------------------------- */
    
        public override MembershipUserCollection FindUsersByEmail(
          string emailToMatch, int pageIndex,
          int pageSize, out int totalRecords)
        {
          throw new NotSupportedException();
        }
    
        /* ---------------------------------------- */
        public override MembershipUserCollection FindUsersByName(
          string usernameToMatch, int pageIndex,
          int pageSize, out int totalRecords)
        {
          throw new NotSupportedException();
        }
    
        /* ---------------------------------------- */
    
        public override string GetPassword(
          string username, string answer)
        {
          throw new NotSupportedException();
        }
    
        /* ---------------------------------------- */
    
        public override MembershipUser GetUser(
          object providerUserKey, bool userIsOnline)
        {
          throw new NotSupportedException();
        }
    
        /* ---------------------------------------- */
    
        public override string GetUserNameByEmail(string email)
        {
          throw new NotSupportedException();
        }
    
        /* ---------------------------------------- */
    
        public override string ResetPassword(
          string username, string answer)
        {
          throw new NotSupportedException();
        }
    
        /* ---------------------------------------- */
    
        public override bool UnlockUser(string userName)
        {
          throw new NotSupportedException();
        }
    
        /* ---------------------------------------- */
    
        public override void UpdateUser(MembershipUser user)
        {
          throw new NotSupportedException();
        }
    
        /* ---------------------------------------- */
        // MembershipProvider helper method
        /* ---------------------------------------- */
    
        public void ReadMembershipDataStore()
        {
          lock (this)
          {
            if (_Users == null)
            {
              try
              {
                _Users = new Dictionary<string, MembershipUser>(
                  16, StringComparer.InvariantCultureIgnoreCase);
                string queryString = "SELECT * FROM phpbb_users WHERE [user_id]>0";
                using (OdbcConnection connection =
                  new OdbcConnection(_connectionString))
                {
                  OdbcCommand command =
                    new OdbcCommand(queryString, connection);
                  connection.Open();
                  OdbcDataReader reader =
                    command.ExecuteReader();
                  while (reader.Read())
                  {
                    string sUserName = reader["username"].ToString();
                    string sEmail = reader["user_email"].ToString();
                    string sPassword = reader["user_password"].ToString();
                    DateTime dCreationDate =
                      _phpbbUtils.ConvertDate(reader["user_regdate"].ToString());
                    DateTime dLastLoginDate =
                      _phpbbUtils.ConvertDate(reader["user_session_time"].ToString());
                    if (dLastLoginDate == new DateTime(1970, 1, 1))
                    {
                      dLastLoginDate = dCreationDate;
                    }
                    DateTime dLastActivityDate =
                      _phpbbUtils.ConvertDate(reader["user_session_time"].ToString());
                    if (dLastActivityDate == new DateTime(1970, 1, 1))
                    {
                      dLastActivityDate = dLastLoginDate;
                    }
                    Int32 status = Convert.ToInt32(reader["user_active"].ToString());
                    bool approved = (status == 0) ? false : true;
                    bool locked = (status == 0) ? true : false;
                    MembershipUser user = new MembershipUser(
                      Name,              // Provider name
                      sUserName,         // UserName
                      null,              // ProviderUserKey
                      sEmail,            // Email
                      String.Empty,      // PasswordQuestion
                      sPassword,         // Comment
                      approved,          // IsApproved
                      locked,            // IsLockedOut
                      dCreationDate,     // CreationDate
                      dLastLoginDate,    // LastLoginDate
                      dLastActivityDate, // LastActivityDate
                      dCreationDate,     // LastPasswordChangedDate
                      dCreationDate      // LastLockoutDate
                     );
                    _Users.Add(user.UserName, user);
                  }
                  reader.Close();
                }
              }
              catch (Exception ex)
              {
                throw new ProviderException("Error: " + ex.Message);
              }
            }
          }
        }
      }
    
      /* ======================================== */
      //
      // PhpBBRoleProvider
      //
      /* ======================================== */
    
      public class PhpBBRoleProvider : RoleProvider
      {
        private string _connectionStringName;
        private string _connectionString;
        private PhpBBUtils _phpbbUtils;
        private Dictionary<string, string[]> _UsersAndRoles =
          new Dictionary<string, string[]>(
            16, StringComparer.InvariantCultureIgnoreCase);
        private Dictionary<string, string[]> _RolesAndUsers =
          new Dictionary<string, string[]>(
            16, StringComparer.InvariantCultureIgnoreCase);
    
        /* ---------------------------------------- */
        // RoleProvider properties
        /* ---------------------------------------- */
    
        public override string ApplicationName
        {
          get { throw new NotSupportedException(); }
          set { throw new NotSupportedException(); }
        }
    
        /* ---------------------------------------- */
        // RoleProvider methods
        /* ---------------------------------------- */
    
        public override void Initialize(
          string name, NameValueCollection config)
        {
          if (config == null)
            throw new ArgumentNullException("config");
          if (String.IsNullOrEmpty(name))
            name = "ReadOnlyPhpBBRoleProvider";
          if (String.IsNullOrEmpty(config["description"]))
          {
            config.Remove("description");
            config.Add("description", "Read-only phpBB role provider");
          }
          base.Initialize(name, config);
          _connectionStringName = config["connectionStringName"];
          if (String.IsNullOrEmpty(_connectionStringName))
          {
            throw new ProviderException(
              "No connection string was specified.\n");
          }
          _connectionString = ConfigurationManager.ConnectionStrings
            [_connectionStringName].ConnectionString;
          _phpbbUtils = new PhpBBUtils();
          ReadRoleDataStore();
        }
    
        /* ---------------------------------------- */
    
        public override bool IsUserInRole(
          string username, string roleName)
        {
          if (username == null || roleName == null)
            throw new ArgumentNullException();
          if (username == String.Empty || roleName == String.Empty)
            throw new ArgumentException();
          if (!_UsersAndRoles.ContainsKey(username))
            throw new ProviderException("Invalid user name");
          if (!_RolesAndUsers.ContainsKey(roleName))
            throw new ProviderException("Invalid role name");
          string[] roles = _UsersAndRoles[username];
          foreach (string role in roles)
          {
            if (String.Compare(role, roleName, true) == 0)
              return true;
          }
          return false;
        }
    
        /* ---------------------------------------- */
    
        public override string[] GetRolesForUser(string username)
        {
          if (username == null)
            throw new ArgumentNullException();
          if (username == String.Empty)
            throw new ArgumentException();
          string[] roles;
          if (!_UsersAndRoles.TryGetValue(username, out roles))
            throw new ProviderException("Invalid user name");
          return roles;
        }
    
        /* ---------------------------------------- */
    
        public override string[] GetUsersInRole(string roleName)
        {
          if (roleName == null)
            throw new ArgumentNullException();
          if (roleName == string.Empty)
            throw new ArgumentException();
          string[] users;
          if (!_RolesAndUsers.TryGetValue(roleName, out users))
            throw new ProviderException("Invalid role name");
          return users;
        }
    
        /* ---------------------------------------- */
    
        public override string[] GetAllRoles()
        {
          int i = 0;
          string[] roles = new string[_RolesAndUsers.Count];
          foreach (KeyValuePair<string, string[]> pair in _RolesAndUsers)
            roles[i++] = pair.Key;
          return roles;
        }
    
        /* ---------------------------------------- */
    
        public override bool RoleExists(string roleName)
        {
          if (roleName == null)
            throw new ArgumentNullException();
          if (roleName == String.Empty)
            throw new ArgumentException();
          return _RolesAndUsers.ContainsKey(roleName);
        }
    
        /* ---------------------------------------- */
    
        public override void CreateRole(string roleName)
        {
          throw new NotSupportedException();
        }
    
        /* ---------------------------------------- */
    
        public override bool DeleteRole(
          string roleName, bool throwOnPopulatedRole)
        {
          throw new NotSupportedException();
        }
    
        /* ---------------------------------------- */
    
        public override void AddUsersToRoles(
          string[] usernames, string[] roleNames)
        {
          throw new NotSupportedException();
        }
    
        /* ---------------------------------------- */
    
        public override string[] FindUsersInRole(
          string roleName, string usernameToMatch)
        {
          throw new NotSupportedException();
        }
    
        /* ---------------------------------------- */
    
        public override void RemoveUsersFromRoles(
          string[] usernames, string[] roleNames)
        {
          throw new NotSupportedException();
        }
    
        /* ---------------------------------------- */
        // RoleProvider helper method
        /* ---------------------------------------- */
    
        private void ReadRoleDataStore()
        {
          lock (this)
          {
            try
            {
              string userQueryString = "SELECT * FROM phpbb_users WHERE [user_id]>0";
              using (OdbcConnection connection =
                new OdbcConnection(_connectionString))
              {
                connection.Open();
                OdbcCommand userCommand =
                  new OdbcCommand(userQueryString, connection);            
                OdbcDataReader userReader =
                  userCommand.ExecuteReader();
                while (userReader.Read())
                {
                  string user =
                    userReader["username"].ToString();
                  string userid =
                    userReader["user_id"].ToString();
    
                  string groupQueryString =
                    "SELECT phpbb_groups.group_name " +
                    " FROM phpbb_user_group INNER JOIN phpbb_groups " +
                    " ON phpbb_user_group.group_id = phpbb_groups.group_id " +
                    " WHERE (((phpbb_user_group.user_pending)=0) " +
                    " AND ((phpbb_user_group.user_id)=" + userid + ") " +
                    " AND ((phpbb_groups.group_single_user)=0));";
    
                  OdbcCommand groupCommand =
                    new OdbcCommand(groupQueryString, connection);
                  OdbcDataReader groupReader =
                    groupCommand.ExecuteReader();
    
                  if (groupReader.HasRows == false)
                  {
                    _UsersAndRoles.Add(user, new string[0]);
                  }
                  else
                  {
                    ArrayList roleList = new ArrayList();
                    while (groupReader.Read())
                    {
                      roleList.Add(groupReader["group_name"].ToString());
                    }
                    string[] roles =
                      (string[])roleList.ToArray(typeof(string));
                    _UsersAndRoles.Add(user, roles);
                    foreach (string role in roles)
                    {
                      string[] users1;
                      if (_RolesAndUsers.TryGetValue(role, out users1))
                      {
                        string[] users2 = new string[users1.Length + 1];
                        users1.CopyTo(users2, 0);
                        users2[users1.Length] = user;
                        _RolesAndUsers.Remove(role);
                        _RolesAndUsers.Add(role, users2);
                      }
                      else
                        _RolesAndUsers.Add(role, new string[] { user });
                    }
                  }
                  groupReader.Close();
                }
                userReader.Close();
              }
            }
            catch (Exception ex)
            {
              throw new ProviderException("Error: " + ex.Message);
            }
          }
        }
      }
    
      /* ======================================== */
      //
      // PhpBBUtils
      //
      /* ======================================== */
    
      internal class PhpBBUtils
      {
        /* ---------------------------------------- */
    
        internal string PasswordHash(string password)
        {
          try
          {
            MD5 md5 = MD5.Create();
            byte[] byteArray =
              md5.ComputeHash(Encoding.ASCII.GetBytes(password));
            StringBuilder stringBuilder =
              new StringBuilder(byteArray.Length * 2);
            foreach (byte byteMember in byteArray)
            {
              stringBuilder.AppendFormat("{0:x2}", byteMember);
            }
            return stringBuilder.ToString();
          }
          catch (Exception ex)
          {
            throw new ProviderException("Error: " + ex.Message);
          }
        }
    
        /* ---------------------------------------- */
    
        internal DateTime ConvertDate(string offset)
        {
          DateTime dateTime = new DateTime(1970, 1, 1);
          if (!(String.IsNullOrEmpty(offset)))
          {
            try
            {
              dateTime = dateTime.AddSeconds(Convert.ToDouble(offset));
            }
            catch (Exception ex)
            {
              throw new ProviderException("Error: " + ex.Message);
            }
          }
          return dateTime;
        }
      }
    }
  4. Save and compile the project.

Step 3: Add the provider to IIS

In this third step you will determine the assembly information for the membership and role provider, and then add that information to the list of trusted providers for IIS.

  1. Determine the assembly information for the provider:
    • In Windows Explorer, open your "%WinDir%\assembly" path.
    • Right-click the ReadOnlyPhpBBProvider assembly and click Properties.
    • Copy the Culture value; for example: Neutral.
    • Copy the Version number; for example: 1.0.0.0.
    • Copy the Public Key Token value; for example: f0e1d2c3b4a59687.
    • Click Cancel.
  2. Add the provider to the list of trusted providers for IIS:
    • Open the Administration.config file for editing. (Note: This file is located in your "%WinDir%\System32\Inetsrv\Config" folder.)
    • Add the providers with the assembly properties from the previous steps to the <trustedProviders> section using the following syntax:

      <add type="ReadOnlyPhpBBProvider.PhpBBMembershipProvider, ReadOnlyPhpBBProvider, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f0e1d2c3b4a59687" />

      <add type="ReadOnlyPhpBBProvider.PhpBBRoleProvider, ReadOnlyPhpBBProvider, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f0e1d2c3b4a59687" />
       

    • Save and close the the Administration.config file.

Step 4: Configure your site for Forms Authentication using the phpBB provider

In this fourth step you will configure your Web site to use forms authentication with the membership and role providers by manually creating a Web.config file for your Web site that sets the requisite properties for forms authentication/authorization, and adding a Login.aspx page to the Web site that will process forms authentication requests.

Note: This example will authorize phpBB accounts that are members of the user-created "Members" group.

  1. Create a Login.aspx file for your Web site:
    • Paste the following code into a text editor:
      <%@ Page Language="C#" %>
      <%@ Import Namespace="System.ComponentModel" %>
      <html>
      <head runat="server">
      <title>Login Page</title>
      </head>
      <body>
         <form id="form1" runat="server">
            <asp:Login id="Login1" runat="server"
                  BorderStyle="Solid"
                  BackColor="#ffffcc"
                  BorderWidth="1px"
                  BorderColor="#cccc99"
                  Font-Size="10pt"
                  Font-Names="Verdana">
               <TitleTextStyle Font-Bold="True"
                  ForeColor="#ffffff"
                  BackColor="#666666"/>
            </asp:Login>
         </form>
      </body>
      </html>
    • Save the code as "Login.aspx" in the root of your Web site.
  2. Create a Web.config file for your Web site:
    • Paste the following code into a text editor:
      <configuration>
        <!-- Add the connection string for the providers. -->
        <connectionStrings>
          <add name="PhpBBForums"
            connectionString="DRIVER={Microsoft Access Driver (*.mdb)};DBQ=C:\Inetpub\wwwdata\phpbb.mdb" />
        </connectionStrings>
        <system.web>
          <!-- Add the read-only membership provider and set it as the default. -->
          <membership defaultProvider="ReadOnlyPhpBBMembershipProvider">
            <providers>
              <add name="ReadOnlyPhpBBMembershipProvider"
                type="ReadOnlyPhpBBProvider.PhpBBMembershipProvider, ReadOnlyPhpBBProvider, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f0e1d2c3b4a59687"
                description="Read-only PhpBB membership provider"
                connectionStringName="PhpBBForums" />
            </providers>
          </membership>
          <!-- Add the read-only role provider and set it as the default. -->
          <roleManager defaultProvider="ReadOnlyPhpBBRoleProvider" enabled="true">
            <providers>
              <add name="ReadOnlyPhpBBRoleProvider"
                type="ReadOnlyPhpBBProvider.PhpBBRoleProvider, ReadOnlyPhpBBProvider, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f0e1d2c3b4a59687"
                description="Read-only PhpBB role provider"
                connectionStringName="PhpBBForums" />
            </providers>
          </roleManager>
          <!-- Set the authentication mode to forms authentication. -->
          <authentication mode="Forms" />
        </system.web>
        <system.webServer>
          <modules>
            <!-- Set authentication for the application. -->
            <remove name="FormsAuthentication" />
            <add name="FormsAuthentication"
              type="System.Web.Security.FormsAuthenticationModule"
              preCondition="" />
            <remove name="DefaultAuthentication" />
            <add name="DefaultAuthentication"
              type="System.Web.Security.DefaultAuthenticationModule"
              preCondition="" />
            <remove name="RoleManager" />
            <add name="RoleManager"
              type="System.Web.Security.RoleManagerModule"
              preCondition="" />
          </modules>
          <security>
            <!-- Set authorization for the application. -->
            <authorization>
              <remove users="*" roles="" verbs="" />
              <add accessType="Allow" roles="Members" />
            </authorization>
          </security>
        </system.webServer>
      </configuration>
    • Note: Make sure that the PublicKeyToken value contains the correct public key token from the assembly properties that you copied in previous steps, and the connectionString value contains the correct information for your phpBB database.
    • Save the code as "Web.config" in the root of your Web site.

Additional notes for using the read-only phpBB provider

As mentioned before, all of the user account management features are built-in to the phpBB forums, so I did not add them to my provider. The being said, there are still several features that integrate nicely with IIS. The following screenshot shows the list of users for a phpBB forum in Internet Explorer:

You'll notice that several pieces of information are listed for each user: username, date joined, number of posts, etc. If you open the .NET Users feature for your site, you'll notice that some of the account information is mirrored there, as shown in the following illustration:

Likewise, if you open the .NET Roles feature for your site, you'll notice that the three roles are enumerated and the number of users per role is listed:

As with my Read-Only Snitz Provider, all of the above information in the .NET Users and .NET Roles features is read-only, so any attempt to modify user or role information will return an error that the specified method is not supported:

But unlike my Snitz provider, which is limited to three built-in roles, you can add as many groups as you want to your phpBB forums and use those groups as membership roles.

Summary and parting thoughts

So now you have a simple read-only membership and role provider for the phpBB 2.0 forums. As previously mentioned - this is not a full-featured provider because I only needed it to fulfill a specific need for forms authentication. If you want to be a little adventurous, you could easily expand this provider to:

  • Work with the phpBB 3.0 application.
  • Perform some of the additional provider tasks like adding and removing users or assigning users to roles.

Have fun. ;-]

No Comments