This post is one of a series of posts about the Advantage Providers for ASP.NET other posts in this series are listed below.
- Advantage and the Provider Model
- Advantage Membership Provider
- Advantage Role Provider
- Advantage SiteMap Provider
- Implementing the Advantage ASP.NET Providers
A Role Provider is another part of the ASP.NET Provider Model and is used in conjunction with a Membership Provider. Creating a custom Role Provider is much easier than creating a Membership Provider because it has far fewer properties and methods. This article will discuss some of the implementation details of an Advantage Role Provider. These providers are used with an ASP.NET web site which is configured to use forms authentication.
The Advantage Role Provider inherits the RoleProvider base class which will allow it to be used by the ASP.NET Login Controls. A Role Provider has only three public properties; ApplicationName, Description and Name. Each is set when configuring the provider in the Web.config file. It is important that the Membership and Role providers use the same application name since this is used to identify users and roles (see this post for more information on the data structure)
The public methods of the Role Provider include creating, deleting, finding and getting roles. The most commonly used function is IsUserInRole which is used to determine if a user has permissions to a particular folder on the site. Adding roles is done with CreateRole and users are assigned roles using AddUsersToRoles.
The CreateRole function adds a new record into the Roles table. It uses the ApplicationID which is set in the web.config file so several applications can use the same roles table. The method has two business rules; a check for commas (,) which are not allowed and a check to ensure the role name is unique for the given application.
1: public override void CreateRole(string rolename)
2: {
3: if (rolename.Contains(","))
4: {
5: throw new ArgumentException("Role names cannot contain commas.");
6: }
7:
8: if (RoleExists(rolename))
9: {
10: throw new ProviderException("Role name already exists.");
11: }
12:
13: AdsConnection conn = new AdsConnection(connectionString);
14: AdsCommand cmd = new AdsCommand("INSERT INTO Roles " +
15: " (Rolename, ApplicationID) " +
16: " Values(:Rolename, :ApplicationID)", conn);
17:
18: cmd.Parameters.Add("Rolename", System.Data.DbType.String).Value = rolename;
19: cmd.Parameters.Add("ApplicationID", System.Data.DbType.String).Value = pApplicationID;
20:
21: try
22: {
23: conn.Open();
24:
25: cmd.ExecuteNonQuery();
26: }
27: catch (AdsException e)
28: {
29: if (WriteExceptionsToEventLog)
30: {
31: WriteToEventLog(e, "CreateRole");
32: }
33: else
34: {
35: throw e;
36: }
37: }
38: finally
39: {
40: conn.Close();
41: }
42: }
Once roles are created users can be added to the roles using the AddUsersToRoles method. This method uses string arrays which allow multiple users to be added to multiple roles using a single call. The method inserts records into the UsersInRoles table which provides the many-to-many relationship between the Users and Roles tables. Since the method can insert multiple records into the table a transaction is used to ensure an all or none situation. Just like the CreateUser method some business rules must be applied before inserting the records. A check to verify that the specified user(s) and role(s) exist is mad prior to inserting any records into the UsersInRoles table. Finally the role names and user names must be converted to their respective IDs which are unique since the same user name or role name may be used by another application.
1: public override void AddUsersToRoles(string[] usernames, string[] rolenames)
2: {
3: string[] roleIDs;
4: string tmpRoles = "";
5: string[] userIDs;
6: string tmpUsers = "";
7:
8: // Build a string array of the roleIDs
9: foreach (string rolename in rolenames)
10: {
11: if (!RoleExists(rolename))
12: {
13: throw new ProviderException("Role name not found.");
14: }
15: else
16: {
17: tmpRoles += GetRoleID(rolename) + ",";
18: }
19: }
20:
21: foreach (string username in usernames)
22: {
23: if (username.Contains(","))
24: {
25: throw new ArgumentException("User names cannot contain commas.");
26: }
27: else
28: {
29: tmpUsers += GetUserID(username) + ",";
30: }
31:
32: foreach (string rolename in rolenames)
33: {
34: if (IsUserInRole(username, rolename))
35: {
36: throw new ProviderException("User is already in role.");
37: }
38: }
39: }
40:
41: // Remove trailing comma and setup the arrays of IDs
42: tmpRoles = tmpRoles.Substring(0, tmpRoles.Length - 1);
43: roleIDs = tmpRoles.Split(',');
44: tmpUsers = tmpUsers.Substring(0, tmpUsers.Length - 1);
45: userIDs = tmpUsers.Split(',');
46:
47:
48: AdsConnection conn = new AdsConnection(connectionString);
49: AdsCommand cmd = new AdsCommand("INSERT INTO UsersInRoles " +
50: " (UserID, RoleID) Values(:UserID, :RoleID)", conn);
51:
52: AdsParameter userParm = cmd.Parameters.Add("UserID", System.Data.DbType.String);
53: AdsParameter roleParm = cmd.Parameters.Add("RoleID", System.Data.DbType.String);
54:
55: AdsTransaction tran = null;
56:
57: try
58: {
59: conn.Open();
60: tran = conn.BeginTransaction();
61: cmd.Transaction = tran;
62:
63: foreach (string username in userIDs)
64: {
65: foreach (string rolename in roleIDs)
66: {
67: userParm.Value = username;
68: roleParm.Value = rolename;
69: cmd.ExecuteNonQuery();
70: }
71: }
72:
73: tran.Commit();
74: }
75: catch (AdsException e)
76: {
77: try
78: {
79: tran.Rollback();
80: }
81: catch { }
82:
83:
84: if (WriteExceptionsToEventLog)
85: {
86: WriteToEventLog(e, "AddUsersToRoles");
87: }
88: else
89: {
90: throw e;
91: }
92: }
93: finally
94: {
95: conn.Close();
96: }
97: }
The IsUserInRole method takes a username and role name and searches the UsersInRoles table for a match. Just like with the AddUsersToRoles the username and role name must be converted to the unique IDs prior to performing the search. This is done with the GetUserID and GetRoleID helper functions.
1: public override bool IsUserInRole(string username, string rolename)
2: {
3: bool userIsInRole = false;
4:
5: AdsConnection conn = new AdsConnection(connectionString);
6: AdsCommand cmd = new AdsCommand("SELECT COUNT(*) FROM UsersInRoles " +
7: " WHERE UserID = :UserID AND RoleID = :RoleID", conn);
8:
9: cmd.Parameters.Add("UserID", System.Data.DbType.String).Value = GetUserID(username);
10: cmd.Parameters.Add("RoleID", System.Data.DbType.String).Value = GetRoleID(rolename);
11:
12: try
13: {
14: conn.Open();
15:
16: int numRecs = (int)cmd.ExecuteScalar();
17:
18: if (numRecs > 0)
19: {
20: userIsInRole = true;
21: }
22: }
23: catch (AdsException e)
24: {
25: if (WriteExceptionsToEventLog)
26: {
27: WriteToEventLog(e, "IsUserInRole");
28: }
29: else
30: {
31: throw e;
32: }
33: }
34: finally
35: {
36: conn.Close();
37: }
38:
39: return userIsInRole;
40: }
You can download the provider from CodeCentral on the DevZone under the WebApps category. You can also use this direct link to the Advantage ASP.NET Providers project. I'll be discussing the SiteMap Provider next.