To create a custom membership provider I followed these instructions:
http://stackoverflow.com/questions/2771094/asp-net-mvc2-custom-membership
and these:
http://mattwrock.com/post/2009/10/14/Implementing-custom-Membership-Provider-and-Role-Provider-for-Authinticating-ASPNET-MVC-Applications.aspx
So far, I've managed to implement custom membership provider and that part works fine. RoleManager still needs some modifications...
Project structure:
SAMembershipProvider.cs:
public class SAMembershipProvider : MembershipProvider
{
#region - Properties -
private int NewPasswordLength { get; set; }
private string ConnectionString { get; set; }
public bool enablePasswordReset { get; set; }
public bool enablePasswordRetrieval { get; set; }
public bool requiresQuestionAndAnswer { get; set; }
public bool requiresUniqueEmail { get; set; }
public int maxInvalidPasswordAttempts { get; set; }
public int passwordAttemptWindow { get; set; }
public MembershipPasswordFormat passwordFormat { get; set; }
public int minRequiredNonAlphanumericCharacters { get; set; }
public int minRequiredPasswordLength { get; set; }
public string passwordStrengthRegularExpression { get; set; }
public override string ApplicationName { get; set; }
public override bool EnablePasswordRetrieval
{
get { return enablePasswordRetrieval; }
}
public override bool EnablePasswordReset
{
get { return enablePasswordReset; }
}
public override bool RequiresQuestionAndAnswer
{
get { return requiresQuestionAndAnswer; }
}
public override int MaxInvalidPasswordAttempts
{
get { return maxInvalidPasswordAttempts; }
}
public override int PasswordAttemptWindow
{
get { return passwordAttemptWindow; }
}
public override bool RequiresUniqueEmail
{
get { return requiresUniqueEmail; }
}
public override MembershipPasswordFormat PasswordFormat
{
get { return passwordFormat; }
}
public override int MinRequiredPasswordLength
{
get { return minRequiredPasswordLength; }
}
public override int MinRequiredNonAlphanumericCharacters
{
get { return minRequiredNonAlphanumericCharacters; }
}
public override string PasswordStrengthRegularExpression
{
get { return passwordStrengthRegularExpression; }
}
#endregion
#region - Methods -
public override void Initialize(string name, NameValueCollection config)
{
throw new NotImplementedException();
}
public override bool ChangePassword(string username, string oldPassword, string newPassword)
{
throw new NotImplementedException();
}
public override bool ChangePasswordQuestionAndAnswer(string username, string
password, string newPasswordQuestion, string newPasswordAnswer)
{
throw new NotImplementedException();
}
public override MembershipUser CreateUser(string username, string
password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status)
{
throw new NotImplementedException();
}
public override bool DeleteUser(string username, bool deleteAllRelatedData)
{
throw new NotImplementedException();
}
public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords)
{
throw new NotImplementedException();
}
public override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords)
{
throw new NotImplementedException();
}
public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords)
{
throw new NotImplementedException();
}
public override int GetNumberOfUsersOnline()
{
throw new NotImplementedException();
}
public override string GetPassword(string username, string answer)
{
throw new NotImplementedException();
}
public override MembershipUser GetUser(object providerUserKey, bool userIsOnline)
{
throw new NotImplementedException();
}
public override MembershipUser GetUser(string username, bool userIsOnline)
{
throw new NotImplementedException();
}
public override string GetUserNameByEmail(string email)
{
throw new NotImplementedException();
}
protected override void OnValidatingPassword(ValidatePasswordEventArgs e)
{
base.OnValidatingPassword(e);
}
public override string ResetPassword(string username, string answer)
{
throw new NotImplementedException();
}
public override bool UnlockUser(string userName)
{
throw new NotImplementedException();
}
public override void UpdateUser(MembershipUser user)
{
throw new NotImplementedException();
}
public override bool ValidateUser(string username, string password)
{
AccountRepository accountRepository = new AccountRepository();
var user = accountRepository.GetUser(username);
if (string.IsNullOrEmpty(password.Trim())) return false;
if (user == null) return false;
//string
hash = EncryptPassword(password);
var email = user.Email;
var pass = user.Password;
if (user == null) return false;
if (pass == password)
{
//User = user;
return true;
}
return false;
}
#endregion
protected string EncryptPassword(string password)
{
//we use codepage 1252 because that is what sql server uses
byte[] pwdBytes = Encoding.GetEncoding(1252).GetBytes(password);
byte[] hashBytes = System.Security.Cryptography.MD5.Create().ComputeHash(pwdBytes);
return Encoding.GetEncoding(1252).GetString(hashBytes);
}
}
SARoleProvider.cs
public class SARoleProvider : RoleProvider
{
AccountRepository accountRepository = new AccountRepository();
public override bool IsUserInRole(string username, string roleName)
{
return true;
}
public override string ApplicationName
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
public override void AddUsersToRoles(string[] usernames, string[] roleNames)
{
throw new NotImplementedException();
}
public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames)
{
throw new NotImplementedException();
}
public override void CreateRole(string roleName)
{
throw new NotImplementedException();
}
public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)
{
throw new NotImplementedException();
}
public override bool RoleExists(string roleName)
{
throw new NotImplementedException();
}
public override string[] GetRolesForUser(string username)
{
int rolesCount = 0;
IQueryable<RoleViewModel> rolesNames;
try
{
// get roles for this user from DB...
rolesNames = accountRepository.GetRolesForUser(username);
rolesCount = rolesNames.Count();
}
catch (Exception ex)
{
throw ex;
}
string[] roles = new string[rolesCount];
int counter = 0;
foreach (var item in rolesNames)
{
roles[counter] = item.RoleName.ToString();
counter++;
}
return roles;
}
public override string[] GetUsersInRole(string roleName)
{
throw new NotImplementedException();
}
public override string[] FindUsersInRole(string roleName, string usernameToMatch)
{
throw new NotImplementedException();
}
public override string[] GetAllRoles()
{
throw new NotImplementedException();
}
}
AccountRepository.cs
public class RoleViewModel
{
public string RoleName { get; set; }
}
public class AccountRepository
{
private DB db = new DB();
public User GetUser(string email)
{
return db.Users.SingleOrDefault(d => d.Email == email);
}
public IQueryable<RoleViewModel> GetRolesForUser(string email)
{
var result = (
from role in db.Roles
join user in db.Users on role.RoleID equals user.RoleID
where user.Email == email
select new RoleViewModel
{
RoleName = role.Name
});
return result;
}
}
webconfig
<membership defaultProvider="SAMembershipProvider" userIsOnlineTimeWindow="15">
<providers>
<clear/>
<add
name="SAMembershipProvider"
type="SA_Contacts.Membership.SAMembershipProvider, SA_Contacts"
connectionStringName ="ShinyAntConnectionString"
/>
</providers>
</membership>
<roleManager defaultProvider="SARoleProvider" enabled="true" cacheRolesInCookie="true">
<providers>
<clear/>
<add
name="SARoleProvider"
type="SA_Contacts.Membership.SARoleProvider"
connectionStringName ="ShinyAntConnectionString"
/>
</providers>
</roleManager>
AccountController.cs:
public class AccountController : Controller
{
SAMembershipProvider provider = new SAMembershipProvider();
AccountRepository accountRepository = new AccountRepository();
public AccountController()
{
}
public ActionResult LogOn()
{
return View();
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult LogOn(string userName, string
password, string returnUrl)
{
if (!ValidateLogOn(userName, password))
{
return View();
}
var user = accountRepository.GetUser(userName);
var userFullName = user.FirstName + " " + user.LastName;
FormsAuthentication.SetAuthCookie(userFullName, false);
if (!String.IsNullOrEmpty(returnUrl) && returnUrl != "/")
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
public ActionResult LogOff()
{
FormsAuthentication.SignOut();
return RedirectToAction("Index", "Home");
}
private bool ValidateLogOn(string userName, string password)
{
if (String.IsNullOrEmpty(userName))
{
ModelState.AddModelError("username", "You must specify a username.");
}
if (String.IsNullOrEmpty(password))
{
ModelState.AddModelError("
password", "You must specify a
password.");
}
if (!provider.ValidateUser(userName, password))
{
ModelState.AddModelError("_FORM", "The username or
password provided is incorrect.");
}
return ModelState.IsValid;
}
}
In some testing controller I have following:
[Authorize]
public class ContactsController : Controller
{
SAMembershipProvider saMembershipProvider = new SAMembershipProvider();
SARoleProvider saRoleProvider = new SARoleProvider();
//
// GET: /Contact/
public ActionResult Index()
{
string[] roleNames = Roles.GetRolesForUser("
[email protected]");
// Outputs admin
ViewData["r1"] = roleNames[0].ToString();
// Outputs True
// I'm not even sure if this method is the same as the one below
ViewData["r2"] = Roles.IsUserInRole("
[email protected]", roleNames[0].ToString());
// Outputs True
ViewData["r3"] = saRoleProvider.IsUserInRole("
[email protected]", "admin");
return View();
}
If I use attribute [Authorize] then everything works ok, but if I use [Authorize(Roles="admin")] then user is always rejected, like he is not in role.
Any idea of what could be wrong here?
Thanks in advance,
Ile