1
votes

I am developing a MVC 5 application with custom role provider, but it seems that the Authorize Attribute never call my customer role provider. My code is as below:

namespace SIMSPortal.Models
{
public class MyCustomRoleProvider:RoleProvider
{
public override string[] GetRolesForUser(string username)
    {
        //throw new NotImplementedException();
        using(var usersContext = new SchoolPortalEntities())
        {
            var user = usersContext.login_details.SingleOrDefault(u => u.user_id == username);
if(user == null)
                return new string[] { };
            return user.UserInRoles == null ? new string[] { } :
              user.UserInRoles.Select(u => u.Role).Select(u => u.RoleName).ToArray();

        }          
    }
  }
}    

my config file:

<add key="PreserveLoginUrl" value="true" />
<add key="enableSimpleMembership" value="false" />
<add key="autoFormsAuthentication" value="false" />
</appSettings>
<system.web>
<authentication mode="Forms">
  <forms loginUrl="~/Login/UserLogin" timeout="2880" />
</authentication>
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5" />
<roleManager defaultProvider="CustomRoleProvider" enabled="true"  cacheRolesInCookie="false">
  <providers>
    <clear />
    <add name="CustomRoleProvider" type="SIMSPortal.Models.MyCustomRoleProvider" />
  </providers>
</roleManager>

However, my custom role provider is in Model folder, and i am using EF DB first approach. I am able to call my custom role provider method with following code within my controller:

String[] roles = Roles.GetRolesForUser(userId);

However any controller where [Authorize] attribute is being used, users are always redirect to the login page, even when user login and role are both valid.

How can I make my Authorize Attribute, call my Custom Role Provider?

2
Why would this ever be true u.user_id == username ?? surely it should be u.username == username or u.user_id == userIdCallum Linington
Could you post your SchoolPortalEntities implementation and login_details type's implementation?Callum Linington
@CallumLinington The codes are longer than the required length, and i dont know how to edit my questionUwakPeter

2 Answers

0
votes

You must set the current principal to RolePrincipal. You can do that globally on an AuthenticationFilter and register it on global.asax. See those two links for some sample: RolePrincipal / IAuthenticationFilter

-1
votes

I added the following code in Web.Config (under authentication tag) to solve the same problem:

<authorization>
  <deny users="?" />
</authorization>

The following is the login controller I use (working):

// POST: /ClientAccount/Login
[AllowAnonymous]
[HttpPost]
public ActionResult Login(LoginViewModel model, string returnUrl)
{
        if (ModelState.IsValid)
        {
            //try to pick the password from database
        ClientAccountDAO userManager = ClientAccountDAO.Instance;
            string password = userManager.GetUserPassword(model.Password);

            if (string.IsNullOrEmpty(password))
            {
             //the password wars not found in database

                ModelState.AddModelError("", "The user login or password provided is incorrect.");

            }

            if (model.Password == password)
            {
                //password is in database, do login FormsAuthentication.SetAuthCookie(model.Username, false);
                System.Web.HttpContext.Current.Session["name"] = model.Username;
                System.Web.HttpContext.Current.Session["nameSurname"] = clientDao.getName(model.Username, model.Password);
                if (Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1 && returnUrl.StartsWith("/")
                    && !returnUrl.StartsWith("//") && !returnUrl.StartsWith("/\\"))
                {

                    return Redirect(returnUrl);
                }
                else
                {
                    return RedirectToAction("Docs", "ClientAccount");
                }
            }
            else
            {

                ModelState.AddModelError("", "Username or password not correct");
            }
        }
        ViewBag.Successfull = false;
        // If we got this far, something failed, redisplay form
        return View("Login", model);
    }

In addiction, this link has been usefull for me: https://msdn.microsoft.com/en-us/library/ff647070.aspx

Edit

I have added some comments to clarify some parts of the code. The method GetUserPassword tries to pick up the given password from db, while GetName picks up the Name of the user with given credentials.

Edit

I added the code of GetUserPassword:

  public string GetUserPassword(string userLogIn)
       {
           var user = db.users.Where(m => m.pwd == userLogIn && m.attivo == true);

           if (user.ToList().Count > 0)
               return user.First().pwd;
           else
               return string.Empty;
       }

and getName:

 public string getName(string username, string password)
         {
             var user = db.users.Where(m => m.username == username && m.pwd == password);
             if (user.ToList().Count <= 0)
                 return "";
             else {
                 var client= db.clients.Where(m => m.credenziali == user.First().id);
                 return (client.First().name + client.First().surname);

             }

I used these method only for testing purpose.