Building a MVC2 Template, Part 12, Finishing the Custom Role Provider
May 24
.Net, Development Asp.Net Mvc, Nehemiah Project 1 Comment
We began writing the custom role provider in part 9 of this series. In today’s post we’ll finish what we started in part 9. This will be nearly all code and no fluff commentary. Ok, maybe a little commentary.
When I initially ran through this exercise I had a separate repository for User, Role, and UserInRole. Which I felt was a bit ridiculous since these three will almost always be used together and I didn’t feel the need to pass a separate repository for every POCO that might be read or modified from our data repository. So I merged all of them into the IProviderRepository interface. These are the kinds of things you run into as build and learn as you go.
Process Recap
I started by modifying specifications in the RoleProviderSpecs.cs file according to the Method and Property descriptions found here, http://www.codeproject.com/KB/aspnet/WSSecurityProvider.aspx. I tried to write at least one specification per statement found in each description.
Sometimes information was missing. Take GetRolesForUser for example. This method returns a string array of all roles for the user. It doesn’t say if the rules are sorted or not. The method and property descriptions for methods in the Membership Provider always indicated lists were sorted by name or email. One would have to assume it doesn’t matter if the results are sorted or not. Since these lists will usually be presented to the user, in my code they will be sorted by role name or user name, depending on the return data.
At each step of the process I would compile the solution and reference Report.html. In the beginning many of the specifications will be listed as not implemented.
To resolved these errors, I started with the easiest methods first, namely RoleExists. I made sure I had a method in my interface named GetRoleByRoleName. Then I added the method and supporting code to the MockProviderRepository. Then compiled the solution and referenced Report.html. When all the specs for a given function produced the correct results I moved on to the next method.
Occasionally I would have to change the code in a specification. Usually it was after I added a new Role for testing a specification. In the specification for GetAllRoles I have a condition that checks to see if the correct number of roles are returned. Whenever I added a new role, I had to increase the comparison value.
I continued the process of adding code to the RoleProvider first. Followed by adding to the repository interface as needed and then adding the supporting code to the mock repository. Refine the code until the specifications passed testing. When necessary I added data to the RoleList, UserList, and/or UserInRoleList variables.
Using this iterative approach I arrived at the code listed below. Will this be our final code? Most likely not. It will have some tweaks. Maybe some major changes.
The Code
Let’s start by creating a couple of class to hold our data. We’ll most likely modify these heavily at a later date. Add a new class file named Role.cs to the Nehemiah.Data project. The code for this class is shown below.
using System;
using System.Collections.Generic;
using System.Text;
namespace Nehemiah.Data.Models
{
public partial class Role
{
public int RoleId { get; set; }
public string ApplicationName { get; set; }
public string RoleName { get; set; }
public Role() { }
public Role(string application, string name)
: this()
{
ApplicationName = application;
RoleName = name;
}
} // End Class
} // End Namespace
Next add a new class file named UserInRole.cs to the Nehemiah.Data project. This class will contain the data association between roles and users. The code for this class is listed below.
using System;
using System.Collections.Generic;
using System.Text;
namespace Nehemiah.Data.Models
{
public partial class UserInRole
{
public Guid UserId { get; set; }
public int RoleId { get; set; }
public UserInRole() { }
public UserInRole(Guid userId, int roleId)
: this()
{
UserId = userId;
RoleId = roleId;
}
} // End Class
} // End Namespace
Since we are adding two new POCOs we need to expand our IProviderRepository interface. The code below adds methods for retrieving Role and UserInRole POCOs.
using System;
using System.Collections;
using System.Collections.Generic;
using Nehemiah.Data.Models;
using System.Linq;
namespace Nehemiah.Data
{
public interface IProviderRepository
{
#region - Role -
bool Add(string applicationName, Role role);
bool DeleteRole(string applicationName, string roleName);
IQueryable<Role> GetRoles(string applicationName);
Role GetRoleByRoleName(string applicationName, string roleName);
Role GetRoleByKey(string applicationName, int roleId);
IList<Role> GetRoleList(string applicationName, int index, int pageSize);
IList<Role> GetRoleListByRoleName(string applicationName, string roleName, int index, int pageSize);
int NumberOfRoles(string applicationName);
bool Save(string applicationName, Role role);
IList<Role> GetRolesForUser(string applicationName, Guid userId);
IList<Role> GetRolesForUser(string applicationName, string username);
#endregion
#region - User -
bool Add(string applicationName, User user);
bool DeleteUser(string applicationName, string username);
IQueryable<User> GetUsers(string applicationName);
User GetUserByUserName(string applicationName, string username);
User GetUserByKey(string applicationName, Guid userId);
User GetUserByEmail(string applicationName, string email);
IList<User> GetUserList(string applicationName, int index, int pageSize);
IList<User> GetUserListByEmail(string applicationName, string email, int index, int pageSize);
IList<User> GetUserListByUserName(string applicationName, string username, int index, int pageSize);
int NumberOfUsers(string applicationName);
int NumberOfUsersOnline(string applicationName, int timeWindow);
bool Save(string applicationName, User user);
IList<User> GetUsersInRole(string applicationName, string rolename);
IList<User> GetUsersInRole(string applicationName, string rolename, string username);
#endregion
#region - UserInRole -
bool AddUserToRole(string applicationName, string userName, string roleName);
IQueryable<UserInRole> GetUserInRole(string applicationName);
int NumberOfUsersInRole(string applicationName, int roleId);
bool DeleteUserInRole(string userName, string roleName);
#endregion
} // End Interface
} // End Namespace
The code below lists the expanded MockProviderRepository.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Nehemiah.Data;
using Nehemiah.Data.Models;
namespace Nehemiah.Specs.Repositories
{
public class MockProviderRepository : IProviderRepository
{
IList<User> UserList;
IList<Role> RoleList;
IList<UserInRole> UserInRoleList;
public MockProviderRepository()
{
UserList = new List<User>
{
new User { ApplicationName = "Nehemiah"
, Approved = true
, Comment = "No comment"
, CreationDate = DateTime.UtcNow
, Email = "me@there.com"
, FailedPasswordAnswerAttemptCount = 0
, FailedPasswordAnswerAttemptStartWindow = null
, FailedPasswordAttemptCount = 0
, FailedPasswordAttemptWindowStart = null
, LastActivityDate = DateTime.UtcNow
, LastLockedOutDate = null
, LastLoginDate = null
, LastPasswordChangedDate = null
, LockedOut = false
, Online = true
, Password = "GoodPassword"
, PasswordAnswer = "Old answer"
, PasswordQuestion = "This is an old question"
, UserId = new Guid("01234567-89AB-CDEF-0123-456789ABCDEF")
, UserName = "UserName"
} ,
new User { ApplicationName = "Nehemiah"
, Approved = true
, Comment = "No comment"
, CreationDate = DateTime.UtcNow
, Email = "me2@there.com"
, FailedPasswordAnswerAttemptCount = 0
, FailedPasswordAnswerAttemptStartWindow = null
, FailedPasswordAttemptCount = 0
, FailedPasswordAttemptWindowStart = null
, LastActivityDate = DateTime.UtcNow
, LastLockedOutDate = null
, LastLoginDate = null
, LastPasswordChangedDate = null
, LockedOut = false
, Online = true
, Password = "GoodPassword"
, PasswordAnswer = "Old answer"
, PasswordQuestion = "This is an old question"
, UserId = new Guid("22222222-2222-2222-2222-222222222222")
, UserName = "UserName2"
} ,
new User { ApplicationName = "Nehemiah"
, Approved = true
, Comment = "No comment"
, CreationDate = DateTime.UtcNow
, Email = "me3@there.com"
, FailedPasswordAnswerAttemptCount = 0
, FailedPasswordAnswerAttemptStartWindow = null
, FailedPasswordAttemptCount = 0
, FailedPasswordAttemptWindowStart = null
, LastActivityDate = null
, LastLockedOutDate = null
, LastLoginDate = null
, LastPasswordChangedDate = null
, LockedOut = false
, Online = false
, Password = "GoodPassword"
, PasswordAnswer = "Old answer"
, PasswordQuestion = "This is an old question"
, UserId = new Guid("33333333-3333-3333-3333-333333333333")
, UserName = "UserName3"
} ,
new User { ApplicationName = "Nehemiah"
, Approved = false
, Comment = ""
, CreationDate = DateTime.UtcNow
, Email = "update@somewhere.com"
, FailedPasswordAnswerAttemptCount = 0
, FailedPasswordAnswerAttemptStartWindow = null
, FailedPasswordAttemptCount = 0
, FailedPasswordAttemptWindowStart = null
, LastActivityDate = null
, LastLockedOutDate = null
, LastLoginDate = null
, LastPasswordChangedDate = null
, LockedOut = true
, Online = false
, Password = "GoodPassword"
, PasswordAnswer = "Old answer"
, PasswordQuestion = "This is an old question"
, UserId = new Guid("44444444-4444-4444-4444-444444444444")
, UserName = "UpdateUser"
} ,
new User { ApplicationName = "Nehemiah"
, Approved = false
, Comment = ""
, CreationDate = DateTime.UtcNow
, Email = "update@somewhere.com"
, FailedPasswordAnswerAttemptCount = 0
, FailedPasswordAnswerAttemptStartWindow = null
, FailedPasswordAttemptCount = 0
, FailedPasswordAttemptWindowStart = null
, LastActivityDate = null
, LastLockedOutDate = null
, LastLoginDate = null
, LastPasswordChangedDate = null
, LockedOut = true
, Online = false
, Password = "GoodPassword"
, PasswordAnswer = "Old answer"
, PasswordQuestion = "This is an old question"
, UserId = new Guid("55555555-5555-5555-5555-555555555555")
, UserName = "LockedOutUser"
} ,
new User { ApplicationName = "Nehemiah"
, Approved = false
, Comment = ""
, CreationDate = DateTime.UtcNow
, Email = "update@somewhere.com"
, FailedPasswordAnswerAttemptCount = 0
, FailedPasswordAnswerAttemptStartWindow = null
, FailedPasswordAttemptCount = 0
, FailedPasswordAttemptWindowStart = null
, LastActivityDate = null
, LastLockedOutDate = null
, LastLoginDate = null
, LastPasswordChangedDate = null
, LockedOut = false
, Online = false
, Password = "GoodPassword"
, PasswordAnswer = "Old answer"
, PasswordQuestion = "This is an old question"
, UserId = new Guid("66666666-6666-6666-6666-666666666666")
, UserName = "NotApprovedUser"
} ,
new User { ApplicationName = "Nehemiah"
, Approved = false
, Comment = ""
, CreationDate = DateTime.UtcNow
, Email = "remove@somewhere.com"
, FailedPasswordAnswerAttemptCount = 0
, FailedPasswordAnswerAttemptStartWindow = null
, FailedPasswordAttemptCount = 0
, FailedPasswordAttemptWindowStart = null
, LastActivityDate = null
, LastLockedOutDate = null
, LastLoginDate = null
, LastPasswordChangedDate = null
, LockedOut = false
, Online = false
, Password = "GoodPassword"
, PasswordAnswer = "Old answer"
, PasswordQuestion = "This is an old question"
, UserId = new Guid("77777777-7777-7777-7777-777777777777")
, UserName = "RemoveUser1"
} ,
new User { ApplicationName = "Nehemiah"
, Approved = false
, Comment = ""
, CreationDate = DateTime.UtcNow
, Email = "remove2@somewhere.com"
, FailedPasswordAnswerAttemptCount = 0
, FailedPasswordAnswerAttemptStartWindow = null
, FailedPasswordAttemptCount = 0
, FailedPasswordAttemptWindowStart = null
, LastActivityDate = null
, LastLockedOutDate = null
, LastLoginDate = null
, LastPasswordChangedDate = null
, LockedOut = false
, Online = false
, Password = "GoodPassword"
, PasswordAnswer = "Old answer"
, PasswordQuestion = "This is an old question"
, UserId = new Guid("88888888-8888-8888-8888-888888888888")
, UserName = "RemoveUser2"
} ,
new User { ApplicationName = "Nehemiah"
, Approved = true
, Comment = null
, CreationDate = DateTime.UtcNow
, Email = "deleteme@delete.com"
, FailedPasswordAnswerAttemptCount = 0
, FailedPasswordAnswerAttemptStartWindow = null
, FailedPasswordAttemptCount = 0
, FailedPasswordAttemptWindowStart = null
, LastActivityDate = null
, LastLockedOutDate = null
, LastLoginDate = null
, LastPasswordChangedDate = null
, LockedOut = false
, Online = false
, Password = "GoodPassword"
, PasswordAnswer = "Old answer"
, PasswordQuestion = "This is an old question"
, UserId = new Guid("11111111-2222-3333-4444-555555555555")
, UserName = "DeleteMe"
}
};
RoleList = new List<Role>
{
new Role { ApplicationName = "Nehemiah"
, RoleName = "Administrator"
, RoleId = 1
} ,
new Role { ApplicationName = "Nehemiah"
, RoleName = "Registered"
, RoleId = 2
} ,
new Role { ApplicationName = "Nehemiah"
, RoleName = "DeleteRole"
, RoleId = 3
} ,
new Role { ApplicationName = "Nehemiah"
, RoleName = "NoUsers"
, RoleId = 4
} ,
new Role { ApplicationName = "Nehemiah"
, RoleName = "NewRole1"
, RoleId = 5
} ,
new Role { ApplicationName = "Nehemiah"
, RoleName = "NewRole2"
, RoleId = 5
} ,
new Role { ApplicationName = "Nehemiah"
, RoleName = "NewRole3"
, RoleId = 6
}
};
UserInRoleList = new List<UserInRole>
{
new UserInRole { RoleId = 1
, UserId = new Guid("01234567-89AB-CDEF-0123-456789ABCDEF")
} ,
new UserInRole { RoleId = 2
, UserId = new Guid("01234567-89AB-CDEF-0123-456789ABCDEF")
} ,
new UserInRole { RoleId = 3
, UserId = new Guid("01234567-89AB-CDEF-0123-456789ABCDEF")
} ,
new UserInRole { RoleId = 2
, UserId = new Guid("22222222-2222-2222-2222-222222222222")
} ,
new UserInRole { RoleId = 2
, UserId = new Guid("33333333-3333-3333-3333-333333333333")
} ,
new UserInRole { RoleId = 2
, UserId = new Guid("44444444-4444-4444-4444-444444444444")
} ,
new UserInRole { RoleId = 2
, UserId = new Guid("55555555-5555-5555-5555-555555555555")
} ,
new UserInRole { RoleId = 3
, UserId = new Guid("55555555-5555-5555-5555-555555555555")
} ,
new UserInRole { RoleId = 1
, UserId = new Guid("77777777-7777-7777-7777-777777777777")
} ,
new UserInRole { RoleId = 2
, UserId = new Guid("77777777-7777-7777-7777-777777777777")
} ,
new UserInRole { RoleId = 1
, UserId = new Guid("88888888-8888-8888-8888-888888888888")
} ,
new UserInRole { RoleId = 2
, UserId = new Guid("88888888-8888-8888-8888-888888888888")
}
};
}
#region - Role -
public bool Add(string applicationName, Role role)
{
RoleList.Add(role);
Role rec = RoleList.Where(r => r.ApplicationName == applicationName && r.RoleName == role.RoleName).SingleOrDefault();
return (rec != null);
}
public bool DeleteRole(string applicationName, string roleName)
{
Role rec = RoleList.Where(u => u.ApplicationName == applicationName && u.RoleName == roleName).SingleOrDefault();
RoleList.Remove(rec);
return true;
}
public IQueryable<Role> GetRoles(string applicationName)
{
return RoleList.Where(u => u.ApplicationName == applicationName).AsQueryable();
}
public Role GetRoleByRoleName(string applicationName, string roleName)
{
return GetRoles(applicationName).Where(u => u.RoleName == roleName).SingleOrDefault();
}
public Role GetRoleByKey(string applicationName, int roleId)
{
return GetRoles(applicationName).Where(u => u.RoleId == roleId).SingleOrDefault();
}
public IList<Role> GetRoleList(string applicationName, int index, int pageSize)
{
return GetRoles(applicationName).OrderBy(u => u.RoleName).ToList();
}
public IList<Role> GetRoleListByRoleName(string applicationName, string roleName, int index, int pageSize)
{
return GetRoles(applicationName).Where(u => u.RoleName.Contains(roleName) || u.RoleName == roleName).OrderBy(u => u.RoleName).ToList();
}
public int NumberOfRoles(string applicationName)
{
return GetRoles(applicationName).Count();
}
public bool Save(string applicationName, Role role)
{
bool success = true;
Role rec = RoleList.Where(u => u.ApplicationName == applicationName && u.RoleId == role.RoleId).SingleOrDefault();
if (rec == null)
{
RoleList.Add(role);
}
else
{
RoleList.Remove(rec);
RoleList.Add(role);
}
return success;
}
#endregion
#region - User -
public bool Add(string applicationName, User user)
{
UserList.Add(user);
User rec = UserList.Where(u => u.ApplicationName == applicationName && u.UserName == user.UserName).SingleOrDefault();
return (rec != null);
}
public bool DeleteUser(string applicationName, string username)
{
User rec = UserList.Where(u => u.ApplicationName == applicationName && u.UserName == username).SingleOrDefault();
UserList.Remove(rec);
return true;
}
public IQueryable<User> GetUsers(string applicationName)
{
return UserList.Where(u => u.ApplicationName == applicationName).AsQueryable();
}
public User GetUserByEmail(string applicationName, string email)
{
return GetUsers(applicationName).Where(u => u.Email == email).SingleOrDefault();
}
public User GetUserByKey(string applicationName, Guid userId)
{
return GetUsers(applicationName).Where(u => u.UserId == userId).SingleOrDefault();
}
public User GetUserByUserName(string applicationName, string username)
{
return GetUsers(applicationName).Where(u => u.UserName == username).SingleOrDefault();
}
public IList<User> GetUserList(string applicationName, int index, int pageSize)
{
return GetUsers(applicationName).OrderBy(u => u.UserName).ToList();
}
public IList<User> GetUserListByEmail(string applicationName, string email, int index, int pageSize)
{
return GetUsers(applicationName).Where(u => u.Email.Contains(email) || u.Email == email).OrderBy(u => u.Email).ToList();
}
public IList<User> GetUserListByUserName(string applicationName, string username, int index, int pageSize)
{
return GetUsers(applicationName).Where(u => u.UserName.Contains(username) || u.UserName == username).OrderBy(u => u.UserName).ToList();
}
public int NumberOfUsers(string applicationName)
{
return GetUsers(applicationName).Count();
}
public int NumberOfUsersOnline(string applicationName, int timeWindow)
{
return GetUsers(applicationName).Where(u => u.LastActivityDate >= DateTime.UtcNow.AddMinutes(-1 * timeWindow)).Count();
}
public bool Save(string applicationName, User user)
{
bool success = true;
User rec = UserList.Where(u => u.ApplicationName == applicationName && u.UserId == user.UserId).SingleOrDefault();
if (rec == null)
{
UserList.Add(user);
}
else
{
UserList.Remove(rec);
UserList.Add(user);
}
return success;
}
public IList<User> GetUsersInRole(string applicationName, string rolename)
{
var list = from r in RoleList
join uir in UserInRoleList on r.RoleId equals uir.RoleId
join u in UserList on uir.UserId equals u.UserId
where r.RoleName == rolename
select u;
return list.ToList();
}
public IList<User> GetUsersInRole(string applicationName, string rolename, string username)
{
var list = from r in RoleList
join uir in UserInRoleList on r.RoleId equals uir.RoleId
join u in UserList on uir.UserId equals u.UserId
where r.RoleName == rolename && u.UserName.Contains(username)
select u;
return list.ToList();
}
public bool DeleteUserInRole(string userName, string roleName)
{
var roles = from r in RoleList
join uir in UserInRoleList on r.RoleId equals uir.RoleId
where r.RoleName == roleName
select uir;
foreach (var role in roles)
{
UserInRoleList.Remove(role);
}
return true;
}
#endregion
#region - UserInRole -
public IQueryable<UserInRole> GetUserInRole(string applicationName)
{
var list = from r in RoleList
join uir in UserInRoleList on r.RoleId equals uir.RoleId
where r.ApplicationName == applicationName
select uir;
return list.AsQueryable();
}
public int NumberOfUsersInRole(string applicationName, int roleId)
{
return GetUserInRole(applicationName).Where(r => r.RoleId == roleId).Count();
}
public IList<Role> GetRolesForUser(string applicationName, Guid userId)
{
var list = from uir in GetUserInRole(applicationName)
join r in RoleList on uir.RoleId equals r.RoleId
where uir.UserId == userId
select r;
return list.ToList();
}
public IList<Role> GetRolesForUser(string applicationName, string username)
{
var list = from uir in GetUserInRole(applicationName)
join r in RoleList on uir.RoleId equals r.RoleId
join u in UserList on uir.UserId equals u.UserId
where u.UserName == username
select r;
return list.ToList();
}
public bool AddUserToRole(string applicationName, string userName, string roleName)
{
Role role;
User user;
// Cannot add a non-existant user
user = GetUserByUserName(applicationName, userName);
if (user == null)
{
return false;
}
// Cannot add a non-existant role
role = GetRoleByRoleName(applicationName, roleName);
if (role == null)
{
return false;
}
UserInRole userInRole = new UserInRole(user.UserId, role.RoleId);
UserInRoleList.Add(userInRole);
return true;
}
#endregion
} // End Class
} // End Namespace
Below is our complete Role Provider specifications (RoleProviderSpecs.cs).
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Web.Security;
using Machine.Specifications;
using Nehemiah.Data;
using Nehemiah.Data.Models;
using Nehemiah.Providers;
using Nehemiah.Specs.Repositories;
using System;
using System.Configuration.Provider;
namespace Nehemiah.Specs.Providers
{
[Subject("Role Provider")]
public class role_provider_initialize_method : RoleProviderContext
{
It should_set_the_application_name = () =>
{
roleProvider.ApplicationName.ShouldEqual(RoleProviderContext.applicationName);
};
}
[Subject("Role Provider")]
public class add_users_to_roles_method : RoleProviderContext
{
It should_add_multiple_users_to_multiple_roles = () =>
{
string[] userNames = new string[3] { RoleProviderContext.existingUser, RoleProviderContext.existingUser2, RoleProviderContext.existingUser3 };
string[] roleNames = new string[3] { RoleProviderContext.newRole1, RoleProviderContext.newRole2, RoleProviderContext.newRole3 };
RoleProviderContext.roleProvider.AddUsersToRoles(userNames, roleNames);
IList<User> users = RoleProviderContext.providerRepository.GetUserListByUserName(RoleProviderContext.applicationName, RoleProviderContext.findUser, 0, 10);
users.Count.ShouldEqual(3);
foreach (User user in users)
{
}
};
It should_throw_a_ProviderException_if_any_of_the_user_names_do_not_exist = () =>
{
bool CorrectType = false;
try
{
string[] userNames = new string[3] { RoleProviderContext.nonExistantUser, RoleProviderContext.existingUser2, RoleProviderContext.existingUser3 };
string[] roleNames = new string[3] { RoleProviderContext.newRole1, RoleProviderContext.newRole2, RoleProviderContext.newRole3 };
RoleProviderContext.roleProvider.AddUsersToRoles(userNames, roleNames);
}
catch (Exception ex)
{
CorrectType = (ex is ProviderException);
ex.Message.ShouldEqual(string.Format("User {0} does not exist.", RoleProviderContext.nonExistantUser));
}
CorrectType.ShouldBeTrue();
};
It should_throw_a_ProviderException_if_any_of_the_role_names_do_not_exist = () =>
{
bool CorrectType = false;
try
{
string[] userNames = new string[3] { RoleProviderContext.existingUser, RoleProviderContext.existingUser2, RoleProviderContext.existingUser3 };
string[] roleNames = new string[3] { RoleProviderContext.nonExistantRole, RoleProviderContext.newRole2, RoleProviderContext.newRole3 };
RoleProviderContext.roleProvider.AddUsersToRoles(userNames, roleNames);
}
catch (Exception ex)
{
CorrectType = (ex is ProviderException);
ex.Message.ShouldEqual(string.Format("Role {0} does not exist.", RoleProviderContext.nonExistantRole));
}
CorrectType.ShouldBeTrue();
};
It should_throw_an_ArgumentNullException_if_any_user_name_is_null = () =>
{
bool CorrectType = false;
try
{
string[] userNames = new string[3] { null, RoleProviderContext.existingUser2, RoleProviderContext.existingUser3 };
string[] roleNames = new string[3] { RoleProviderContext.newRole1, RoleProviderContext.newRole2, RoleProviderContext.newRole3 };
RoleProviderContext.roleProvider.AddUsersToRoles(userNames, roleNames);
}
catch (Exception ex)
{
CorrectType = (ex is ArgumentNullException);
ex.Message.Contains("User name cannot be null.").ShouldBeTrue();
}
CorrectType.ShouldBeTrue();
};
It should_throw_an_ArgumentNullException_if_any_role_name_is_null = () =>
{
bool CorrectType = false;
try
{
string[] userNames = new string[3] { RoleProviderContext.existingUser, RoleProviderContext.existingUser2, RoleProviderContext.existingUser3 };
string[] roleNames = new string[3] { null, RoleProviderContext.newRole2, RoleProviderContext.newRole3 };
RoleProviderContext.roleProvider.AddUsersToRoles(userNames, roleNames);
}
catch (Exception ex)
{
CorrectType = (ex is ArgumentNullException);
ex.Message.Contains("Role name cannot be null.").ShouldBeTrue();
}
CorrectType.ShouldBeTrue();
};
It should_throw_an_ArgumentException_if_any_user_name_is_an_empty_string = () =>
{
bool CorrectType = false;
try
{
string[] userNames = new string[3] { string.Empty, RoleProviderContext.existingUser2, RoleProviderContext.existingUser3 };
string[] roleNames = new string[3] { RoleProviderContext.newRole1, RoleProviderContext.newRole2, RoleProviderContext.newRole3 };
RoleProviderContext.roleProvider.AddUsersToRoles(userNames, roleNames);
}
catch (Exception ex)
{
CorrectType = (ex is ArgumentException);
ex.Message.ShouldEqual("User name cannot be empty.");
}
CorrectType.ShouldBeTrue();
};
It should_throw_an_ArgumentException_if_any_role_name_is_an_empty_string = () =>
{
bool CorrectType = false;
try
{
string[] userNames = new string[3] { RoleProviderContext.existingUser, RoleProviderContext.existingUser2, RoleProviderContext.existingUser3 };
string[] roleNames = new string[3] { string.Empty, RoleProviderContext.newRole2, RoleProviderContext.newRole3 };
RoleProviderContext.roleProvider.AddUsersToRoles(userNames, roleNames);
}
catch (Exception ex)
{
CorrectType = (ex is ArgumentException);
ex.Message.ShouldEqual("Role name cannot be empty.");
}
CorrectType.ShouldBeTrue();
};
}
[Subject("Role Provider")]
public class create_role_method : RoleProviderContext
{
It should_create_a_new_role = () =>
{
RoleProviderContext.roleProvider.CreateRole(RoleProviderContext.addRole);
Role role = RoleProviderContext.providerRepository.GetRoleByRoleName(RoleProviderContext.applicationName, RoleProviderContext.addRole);
role.RoleName.ShouldEqual(RoleProviderContext.addRole);
};
It should_throw_a_ProviderException_if_the_role_already_exists = () =>
{
bool CorrectType = false;
try
{
RoleProviderContext.roleProvider.CreateRole(RoleProviderContext.existingRole);
}
catch (Exception ex)
{
CorrectType = (ex is ProviderException);
ex.Message.ShouldEqual("Role already exists.");
}
CorrectType.ShouldBeTrue();
};
It should_throw_a_ProviderException_if_the_role_name_contains_a_comma = () =>
{
bool CorrectType = false;
try
{
RoleProviderContext.roleProvider.CreateRole(RoleProviderContext.commaRole);
}
catch (Exception ex)
{
CorrectType = (ex is ProviderException);
ex.Message.ShouldEqual("Role name cannot contain a comma.");
}
CorrectType.ShouldBeTrue();
};
It should_throw_a_ProviderException_if_the_name_exceeds_the_maximum_length_allowed_by_the_data_source = () =>
{
bool CorrectType = false;
try
{
RoleProviderContext.roleProvider.CreateRole(RoleProviderContext.exceedingLongRole);
}
catch (Exception ex)
{
CorrectType = (ex is ProviderException);
ex.Message.ShouldEqual("Role name is too long.");
}
CorrectType.ShouldBeTrue();
};
}
[Subject("Role Provider")]
public class delete_role_method : RoleProviderContext
{
static bool Success = false;
static int roleId = -99;
Because of = () =>
{
Role role = RoleProviderContext.providerRepository.GetRoleByRoleName(RoleProviderContext.applicationName, RoleProviderContext.deleteRole);
roleId = role.RoleId;
Success = RoleProviderContext.roleProvider.DeleteRole(RoleProviderContext.deleteRole, false);
};
It should_delete_the_specified_role = () =>
{
Success.ShouldBeTrue();
Role role = RoleProviderContext.providerRepository.GetRoleByRoleName(RoleProviderContext.applicationName, RoleProviderContext.deleteRole);
role.ShouldBeNull();
};
It should_remove_users_from_the_role = () =>
{
int count = RoleProviderContext.providerRepository.NumberOfUsersInRole(RoleProviderContext.applicationName, roleId);
roleId.ShouldNotEqual(-99);
count.ShouldEqual(0);
};
It should_throw_a_ProviderException_if_the_throwOnPopulatedRole_is_true_and_users_are_assigned_to_the_role = () =>
{
bool CorrectType = false;
try
{
RoleProviderContext.roleProvider.DeleteRole(RoleProviderContext.assignedRole, true);
}
catch (Exception ex)
{
CorrectType = (ex is ProviderException);
ex.Message.ShouldEqual("There are users assigned to the role.");
}
CorrectType.ShouldBeTrue();
};
}
[Subject("Role Provider")]
public class find_users_in_role_method : RoleProviderContext
{
It should_find_all_users_in_the_specified_role_listed_alphabetically = () =>
{
int i;
string lastUser = "";
string[] users = RoleProviderContext.roleProvider.FindUsersInRole(RoleProviderContext.findRole, RoleProviderContext.findUser);
users.Length.ShouldEqual(3);
for (i = 0; i < users.Length; i++)
{
lastUser.ShouldBeLessThan(users[i]);
lastUser = users[i];
}
};
It should_return_an_empty_string_array_if_no_matches_are_found = () =>
{
string[] users = RoleProviderContext.roleProvider.FindUsersInRole(RoleProviderContext.findRole, RoleProviderContext.nonExistantUser);
users.Length.ShouldEqual(0);
};
It should_throw_a_ProviderException_if_the_role_does_not_exist = () =>
{
bool CorrectType = false;
try
{
string[] users = RoleProviderContext.roleProvider.FindUsersInRole(RoleProviderContext.nonExistantRole, RoleProviderContext.findUser);
}
catch (Exception ex)
{
CorrectType = (ex is ProviderException);
ex.Message.ShouldEqual(string.Format("Role {0} does not exist.", RoleProviderContext.nonExistantRole));
}
CorrectType.ShouldBeTrue();
};
}
[Subject("Role Provider")]
public class get_all_roles_method : RoleProviderContext
{
It should_return_an_array_of_all_roles = () =>
{
string[] roles = RoleProviderContext.roleProvider.GetAllRoles();
roles.Length.ShouldEqual(7);
};
//It should_return_an_empty_string_array_if_there_are_no_roles = () =>
//{
//};
}
[Subject("Role Provider")]
public class get_roles_for_user_method : RoleProviderContext
{
It should_return_an_array_of_all_roles_for_the_specified_user = () =>
{
string[] roles = RoleProviderContext.roleProvider.GetRolesForUser(RoleProviderContext.existingUser);
roles.Length.ShouldEqual(3);
};
It should_return_an_empty_string_array_if_the_user_is_not_assigned_to_any_roles = () =>
{
string[] roles = RoleProviderContext.roleProvider.GetRolesForUser(RoleProviderContext.unassignedUser);
roles.Length.ShouldEqual(0);
};
It should_throw_a_ProviderException_if_the_user_name_does_not_exist = () =>
{
bool CorrectType = false;
try
{
string[] roles = RoleProviderContext.roleProvider.GetRolesForUser(RoleProviderContext.nonExistantUser);
}
catch (Exception ex)
{
CorrectType = (ex is ProviderException);
ex.Message.ShouldEqual(string.Format("User {0} does not exist.", RoleProviderContext.nonExistantUser));
}
CorrectType.ShouldBeTrue();
};
}
[Subject("Role Provider")]
public class get_users_in_role_method : RoleProviderContext
{
It should_return_an_array_of_all_users_in_the_specified_role = () =>
{
string[] users = RoleProviderContext.roleProvider.GetUsersInRole(RoleProviderContext.assignedRole);
users.Length.ShouldEqual(7);
};
It should_return_an_empty_string_array_if_no_users_are_assigned_to_the_role = () =>
{
string[] users = RoleProviderContext.roleProvider.GetUsersInRole(RoleProviderContext.noUsersRole);
users.Length.ShouldEqual(0);
};
It should_throw_a_ProviderException_if_the_role_does_not_exist = () =>
{
bool CorrectType = false;
try
{
string[] roles = RoleProviderContext.roleProvider.GetUsersInRole(RoleProviderContext.nonExistantRole);
}
catch (Exception ex)
{
CorrectType = (ex is ProviderException);
ex.Message.ShouldEqual(string.Format("Role {0} does not exist.", RoleProviderContext.nonExistantRole));
}
CorrectType.ShouldBeTrue();
};
}
[Subject("Role Provider")]
public class is_user_in_role_method : RoleProviderContext
{
static bool success;
It should_return_true_if_the_user_is_assigned_to_the_role = () =>
{
success = RoleProviderContext.roleProvider.IsUserInRole(RoleProviderContext.existingUser, RoleProviderContext.existingRole);
};
It should_return_false_if_the_user_is_not_assigned_to_the_role = () =>
{
success = RoleProviderContext.roleProvider.IsUserInRole(RoleProviderContext.existingUser, RoleProviderContext.noUsersRole);
};
It should_throw_a_ProviderException_if_the_role_does_not_exist = () =>
{
bool CorrectType = false;
try
{
success = RoleProviderContext.roleProvider.IsUserInRole(RoleProviderContext.existingUser, RoleProviderContext.nonExistantRole);
}
catch (Exception ex)
{
CorrectType = (ex is ProviderException);
ex.Message.ShouldEqual(string.Format("Role {0} does not exist.", RoleProviderContext.nonExistantRole));
}
CorrectType.ShouldBeTrue();
};
It should_throw_a_ProviderException_if_the_user_does_not_exist = () =>
{
bool CorrectType = false;
try
{
success = RoleProviderContext.roleProvider.IsUserInRole(RoleProviderContext.nonExistantUser, RoleProviderContext.assignedRole);
}
catch (Exception ex)
{
CorrectType = (ex is ProviderException);
ex.Message.ShouldEqual(string.Format("User {0} does not exist.", RoleProviderContext.nonExistantUser));
}
CorrectType.ShouldBeTrue();
};
}
[Subject("Role Provider")]
public class remove_users_from_roles_method : RoleProviderContext
{
It should_remove_one_or_more_users_from_the_specified_roles = () =>
{
string[] userNames = new string[] { RoleProviderContext.removeUser1, RoleProviderContext.removeUser2 };
string[] roleNames = new string[] { RoleProviderContext.existingRole, RoleProviderContext.assignedRole };
RoleProviderContext.roleProvider.RemoveUsersFromRoles(userNames, roleNames);
};
It should_throw_a_ProviderException_if_any_user_does_not_exist = () =>
{
bool CorrectType = false;
string[] userNames = new string[] { RoleProviderContext.nonExistantUser, RoleProviderContext.removeUser2 };
string[] roleNames = new string[] { RoleProviderContext.existingRole, RoleProviderContext.assignedRole };
try
{
RoleProviderContext.roleProvider.RemoveUsersFromRoles(userNames, roleNames);
}
catch (Exception ex)
{
CorrectType = (ex is ProviderException);
ex.Message.ShouldEqual(string.Format("User {0} does not exist.", RoleProviderContext.nonExistantUser));
}
CorrectType.ShouldBeTrue();
};
It should_throw_a_ProviderException_if_any_role_does_not_exist = () =>
{
bool CorrectType = false;
string[] userNames = new string[] { RoleProviderContext.removeUser1, RoleProviderContext.removeUser2 };
string[] roleNames = new string[] { RoleProviderContext.nonExistantRole, RoleProviderContext.assignedRole };
try
{
RoleProviderContext.roleProvider.RemoveUsersFromRoles(userNames, roleNames);
}
catch (Exception ex)
{
CorrectType = (ex is ProviderException);
ex.Message.ShouldEqual(string.Format("Role {0} does not exist.", RoleProviderContext.nonExistantRole));
}
CorrectType.ShouldBeTrue();
};
It should_throw_a_ProviderException_if_any_user_does_not_belong_to_the_role = () =>
{
bool CorrectType = false;
string[] userNames = new string[] { RoleProviderContext.removeUser1, RoleProviderContext.removeUser2 };
string[] roleNames = new string[] { RoleProviderContext.noUsersRole, RoleProviderContext.assignedRole };
try
{
RoleProviderContext.roleProvider.RemoveUsersFromRoles(userNames, roleNames);
}
catch (Exception ex)
{
CorrectType = (ex is ProviderException);
ex.Message.ShouldEqual(string.Format("User {0} is not in role {1}.", RoleProviderContext.removeUser1, RoleProviderContext.noUsersRole));
}
CorrectType.ShouldBeTrue();
};
}
[Subject("Role Provider")]
public class role_exists_method : RoleProviderContext
{
static bool success;
It should_return_true_if_the_role_exists = () =>
{
success = RoleProviderContext.roleProvider.RoleExists(RoleProviderContext.existingRole);
success.ShouldBeTrue();
};
It should_return_false_if_the_role_does_not_exist = () =>
{
success = RoleProviderContext.roleProvider.RoleExists(RoleProviderContext.nonExistantRole);
success.ShouldBeFalse();
};
}
public abstract class RoleProviderContext
{
protected static IProviderRepository providerRepository;
// Test Data
protected static string applicationName = "Nehemiah";
protected static string existingUser = "UserName";
protected static string existingUser2 = "UserName2";
protected static string existingUser3 = "UserName3";
protected static string nonExistantUser = "NoUserHere";
protected static string findUser = "UserName";
protected static string unassignedUser = "NotApprovedUser";
protected static string removeUser1 = "RemoveUser1";
protected static string removeUser2 = "RemoveUser2";
protected static string existingRole = "Administrator";
protected static string commaRole = "Role,WithComma";
protected static string exceedingLongRole = "abcdefghijklmnopqrstuvwxyz";
protected static string addRole = "Editor";
protected static string newRole1 = "NewRole1";
protected static string newRole2 = "NewRole2";
protected static string newRole3 = "NewRole3";
protected static string nonExistantRole = "NoRoleHere";
protected static string deleteRole = "DeleteRole";
protected static string assignedRole = "Registered";
protected static string findRole = "Registered";
protected static string noUsersRole = "NoUsers";
protected static string name;
protected static RoleProvider roleProvider;
protected static NameValueCollection config;
Establish context = () =>
{
name = "Nehemiah";
config = new NameValueCollection();
config.Add("applicationName", applicationName);
providerRepository = new MockProviderRepository();
roleProvider = new NehemiahRoleProvider(providerRepository, applicationName);
roleProvider.Initialize(name, config);
};
}
}
And lastly the code for the RoleProvider.cs file.
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration;
using System.Linq;
using System.Text;
using System.Web.Security;
using Nehemiah.Data;
using Nehemiah.Data.Models;
using System.Configuration.Provider;
// Reference URL: http://msdn.microsoft.com/en-us/library/aa479032.aspx
// : http://www.codeproject.com/KB/aspnet/WSSecurityProvider.aspx
// : http://msdn.microsoft.com/en-us/library/317sza4k.aspx
namespace Nehemiah.Providers
{
public class NehemiahRoleProvider : RoleProvider
{
#region - Properties -
private string ConnectionString { get; set; }
private IProviderRepository ProviderRepository { get; set; }
private string providerName { get; set; }
private int maxRoleNameLength = 20;
// The name of the application using the role provider. ApplicationName is used to scope
// role data so that applications can choose whether to share role data with other applications.
// This property can be read and written.
public override string ApplicationName { get; set; }
#endregion
#region - Constructors -
public NehemiahRoleProvider() : base() { }
public NehemiahRoleProvider(IProviderRepository repository, string applicationName)
: this()
{
ProviderRepository = repository;
ApplicationName = applicationName;
}
#endregion
#region - Methods -
public override void Initialize(string name, NameValueCollection config)
{
if (config == null)
throw new ArgumentNullException("config");
providerName = "NehemiahRoleProvider";
ApplicationName = (String.IsNullOrEmpty(name)) ? Name : name;
if (String.IsNullOrEmpty(config["description"]))
{
config.Remove("description");
config.Add("description", "Nehemiah Asp.Net MVC role provider");
}
//Initialize the abstract base class.
base.Initialize(name, config);
// Get our connection string
ConnectionString = ConfigurationManager.AppSettings["Connection"];
}
// Takes, as input, a list of user names and a list of role names and adds the specified users to
// the specified roles.
// AddUsersToRoles throws a ProviderException if any of the user names or role names do not exist.
// If any user name or role name is null (Nothing in Visual Basic), AddUsersToRoles throws an
// ArgumentNullException. If any user name or role name is an empty string, AddUsersToRoles throws
// an ArgumentException.
public override void AddUsersToRoles(string[] userNames, string[] roleNames)
{
// Validate role names
foreach (string role in roleNames)
{
// Cannot be null
if (role == null)
{
throw new ArgumentNullException("Role name cannot be null.");
}
// Cannot be empty
if (role == string.Empty)
{
throw new ArgumentException("Role name cannot be empty.");
}
// Each role must exist, otherwise throw and error
if (RoleExists(role) == false)
{
throw new ProviderException(string.Format("Role {0} does not exist.", role));
}
}
// Validate user names
foreach (string username in userNames)
{
// Cannot be null
if (username == null)
{
throw new ArgumentNullException("User name cannot be null.");
}
// Cannot be empty
if (username == string.Empty)
{
throw new ArgumentException("User name cannot be empty.");
}
// Each user must exist, othewise throw an error
User user = ProviderRepository.GetUserByUserName(ApplicationName, username);
if (user == null)
{
throw new ProviderException(string.Format("User {0} does not exist.", username));
}
// This is not in the role provider description, but it is in the
// example ODBC provider code.
foreach (string role in roleNames)
{
if (IsUserInRole(username, role) == true)
{
throw new ProviderException("User is already in role.");
}
}
}
// Now, add users to the role
foreach (string user in userNames)
{
foreach (string role in roleNames)
{
ProviderRepository.AddUserToRole(ApplicationName, user, role);
}
}
}
// Takes, as input, a role name and creates the specified role.
// CreateRole throws a ProviderException if the role already exists, the role name contains a comma,
// or the role name exceeds the maximum length allowed by the data source.
public override void CreateRole(string roleName)
{
if (RoleExists(roleName) == true)
{
throw new ProviderException("Role already exists.");
}
if (roleName.Contains(',') == true)
{
throw new ProviderException("Role name cannot contain a comma.");
}
if (roleName.Length >= maxRoleNameLength)
{
throw new ProviderException("Role name is too long.");
}
Role role = new Role(ApplicationName, roleName);
ProviderRepository.Save(ApplicationName, role);
}
// Takes, as input, a role name and a Boolean value that indicates whether to throw an exception if there
// are users currently associated with the role, and then deletes the specified role.
// If the throwOnPopulatedRole input parameter is true and the specified role has one or more members,
// DeleteRole throws a ProviderException and does not delete the role. If throwOnPopulatedRole is false,
// DeleteRole deletes the role whether it is empty or not.
//
// When DeleteRole deletes a role and there are users assigned to that role, it also removes users from the role.
public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)
{
bool success = false;
string[] users = GetUsersInRole(roleName);
if (throwOnPopulatedRole == true && users.Length > 0)
{
throw new ProviderException("There are users assigned to the role.");
}
foreach (string user in users)
{
success = ProviderRepository.DeleteUserInRole(user, roleName);
}
success = ProviderRepository.DeleteRole(ApplicationName, roleName);
return success;
}
// Takes, as input, a search pattern and a role name and returns a list of users belonging to the specified role
// whose user names match the pattern. Wildcard syntax is data-source-dependent and may vary from provider to
// provider. User names are returned in alphabetical order.
// If the search finds no matches, FindUsersInRole returns an empty string array (a string array with no elements).
// If the role does not exist, FindUsersInRole throws a ProviderException.
public override string[] FindUsersInRole(string roleName, string usernameToMatch)
{
if (RoleExists(roleName) == false)
{
throw new ProviderException(string.Format("Role {0} does not exist.",roleName));
}
List<string> users = new List<string>();
foreach (User user in ProviderRepository.GetUsersInRole(ApplicationName, roleName, usernameToMatch).OrderBy(u => u.UserName))
{
users.Add(user.UserName);
}
return users.ToArray();
}
// Returns the names of all existing roles. If no roles exist, GetAllRoles returns an empty string array (a string
// array with no elements).
public override string[] GetAllRoles()
{
List<string> roles = new List<string>();
foreach (Role role in ProviderRepository.GetRoles(ApplicationName).OrderBy(r => r.RoleName))
{
roles.Add(role.RoleName);
}
return roles.ToArray();
}
// Takes, as input, a user name and returns the names of the roles to which the user belongs.
// If the user is not assigned to any roles, GetRolesForUser returns an empty string array
// (a string array with no elements). If the user name does not exist, GetRolesForUser throws a
// ProviderException.
public override string[] GetRolesForUser(string username)
{
User user = ProviderRepository.GetUserByUserName(ApplicationName, username);
if (user == null)
{
throw new ProviderException(string.Format("User {0} does not exist.", username));
}
List<string> roles = new List<string>();
foreach (Role role in ProviderRepository.GetRolesForUser(ApplicationName, username).OrderBy(r => r.RoleName))
{
roles.Add(role.RoleName);
}
return roles.ToArray();
}
// Takes, as input, a role name and returns the names of all users assigned to that role.
// If no users are associated with the specified role, GetUserInRole returns an empty string array (a string array with
// no elements). If the role does not exist, GetUsersInRole throws a ProviderException.
public override string[] GetUsersInRole(string roleName)
{
if (RoleExists(roleName) == false)
{
throw new ProviderException(string.Format("Role {0} does not exist.", roleName));
}
List<string> users = new List<string>();
foreach (User user in ProviderRepository.GetUsersInRole(ApplicationName, roleName).OrderBy(u => u.UserName))
{
users.Add(user.UserName);
}
return users.ToArray();
}
// Takes, as input, a user name and a role name and determines whether the specified user
// is associated with the specified role.
// If the user or role does not exist, IsUserInRole throws a ProviderException.
public override bool IsUserInRole(string username, string rolename)
{
User user = ProviderRepository.GetUserByUserName(ApplicationName, username);
if (user == null)
{
throw new ProviderException(String.Format("User {0} does not exist.", username));
}
if (RoleExists(rolename) == false)
{
throw new ProviderException(String.Format("Role {0} does not exist.", rolename));
}
IList<Role> roles = ProviderRepository.GetRolesForUser(ApplicationName, user.UserId);
return (roles.Where(r => r.RoleName.Contains(rolename)).Count() > 0);
}
// Takes, as input, a list of user names and a list of role names and removes the specified users from the specified roles.
// RemoveUsersFromRoles throws a ProviderException if any of the users or roles do not exist, or if any user specified
// in the call does not belong to the role from which he or she is being removed.
public override void RemoveUsersFromRoles(string[] userNames, string[] roleNames)
{
// Check to see if all roles exist
foreach (string role in roleNames)
{
if (RoleExists(role) == false)
{
throw new ProviderException(string.Format("Role {0} does not exist.", role));
}
}
// Check to see if all users exist
foreach (string username in userNames)
{
User user = ProviderRepository.GetUserByUserName(ApplicationName, username);
if (user == null)
{
throw new ProviderException(string.Format("User {0} does not exist.", username));
}
// User must be assigned to each role
foreach (string role in roleNames)
{
if (IsUserInRole(username, role) == false)
{
throw new ProviderException(string.Format("User {0} is not in role {1}.", username, role));
}
}
}
// Ok, we can remove the user from the role
foreach (string user in userNames)
{
foreach (string role in roleNames)
{
ProviderRepository.DeleteUserInRole(user, role);
}
}
}
// Takes, as input, a role name and determines whether the role exists.
public override bool RoleExists(string roleName)
{
Role role = ProviderRepository.GetRoleByRoleName(ApplicationName, roleName);
return (role != null);
}
#endregion
} // End Class
} // End Namespace
Once you’ve add all the code, rebuild the solution and check the Report.html file. It should report 10 concerns, 56 contexts, 157 specifications, 10 not implemented specs. Commit the code to subversion. Open http://localhost:8080 and verify TeamCity built the solution witout any errors.
References
- http://msdn.microsoft.com/en-us/library/aa479032.aspx
- http://msdn.microsoft.com/en-us/library/317sza4k.aspx
- http://www.codeproject.com/KB/aspnet/WSSecurityProvider.aspx
Previous Articles in this Series
- Part 1, Introduction
- Part 2, Version Control
- Part 3, Automated Builds
- Part 4, BDD with MSpec
- Part 5, Writing Specs
- Part 6, Writing Specs Continued
- Part 7, Custom Web Errors
- Part 8, Adding a Custom Membership Provider
- Part 9, Adding a Custom Role Provider
- Part 10, Adding a Custom Profile Provider
- Part 11, Finishing the Custom Membership Provider












