views:

825

answers:

2

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:

alt text

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

+1  A: 

I found this regarding the Roles and Users parameters for the [Authorize] attribute:

http://www.robertschultz.org/2009/07/29/multiple-roles-with-authorize-attribute-using-enums-in-asp-net-mvc/

Based on the code in the custom Authorize attribute, it looks to me like the name is probably case-sensitive. Have you tried:

[Authorize(Roles="Admin")]

You could also try using the custom code in that article so you can get away from using strings.

Dave Swersky
no, it's not that because in database it is also inserted as "admin"
ile
+1  A: 

I suspect it's trying to call one of the methods you have yet to implement in SARoleProvider. I would look at the method RoleExists first. Put a break point on each of the methods in SARoleProvider to see which one is being called. Then you'll know which method(s) you'll need to work with.

37Stars
Thanks, I'll try it tomorrow morning! I don't have a habit to use breakpoints, but in this example it's a good idea!
ile
altoguh, I don't think problem is in RoleExists because in tutorial I followed role manager works and the same method implementations are used... but I'll see tomorrow
ile
I found the problem thanks to debugging. The problem was in Controller in method LogOn. There is this line: FormsAuthentication.SetAuthCookie(userFullName, false); and it inserted wrong parameter to cookie, instead of userFullName there is suppose to be userName
ile
Awesome, glad you got it resolved.
37Stars

related questions