Question

In my UserController.Login action, I am trying to AddUserToRole() for an authenticated user, with the value of one of the user entity's MembershipType.MembershipName navigation property:

Roles.AddUserToRole(thisUser.UserName, thisUser.MembershipType.MembershipName);

I'm not using the bloated auto-generated membership types and simply want to add the text value to the user's role - not a range of roles. User --> one role.

In trying to figure this out, I defined a custom role provider in web.config:

<roleManager enabled="true" defaultProvider="CustomRoleProvider">
  <providers>
    <clear/>
    <add name="CustomRoleProvider" 
       type="MyWebsite.CustomRoleProvider, 
             CustomMembershipEF, Version=1.0.0.0, Culture=neutral" 
       connectionStringName="MyDbContext"
       enablePasswordRetrieval="false" enablePasswordReset="true" 
       requiresQuestionAndAnswer="false" writeExceptionsToEventLog="false" />
  </providers>
</roleManager>   

So calling Roles.AddUserToRole() calls the method in my custom provider, but I'm not sure what to do in that method.

Earlier I simply added enabled="true" to the rolemanager section in web.config, but was getting the following error:

Unable to find the requested .Net Framework Data Provider. It may not be installed.

Here's my custom role provider (although I don't know why I should need this, as I just want to add the role name):

public class CustomRoleProvider : RoleProvider
{
    public override bool IsUserInRole(string username, string roleName)
    {
        using (var db = new MyApp.DAL.MyAppDbContext())
        {
            var user = db.Users.SingleOrDefault(u => u.UserName == username);
            if (user == null)
                return false;

            if (user.MembershipType != null && user.MembershipType.MembershipName == roleName)
            {
                return true;
            }
        }
        return false;
    }

    public override string[] GetRolesForUser(string username)
    {
        using (var db = new MyApp.DAL.MyAppDbContext())
        {
            var user = db.Users.SingleOrDefault(u => u.UserName == username);
            if (user == null)
                return new string[] { };

            return db.MembershipTypes == null ? new string[] { } :
              db.MembershipTypes.Select(u => u.MembershipName).ToArray();
        }
    }

    // -- Snip --

    public override string[] GetAllRoles()
    {
        using (var db = new MyApp.DAL.MyAppDbContext())
        {
            return db.MembershipTypes.Select(r => r.MembershipName).ToArray();
        }
    }

    // -- Snip --

    public override void AddUsersToRoles(string[] usernames, string[] roleNames)
    {
        throw new NotImplementedException();
    }

I'm thinking this should be quite simple to accomplish - simply set the user's role name... assistance appreciated.

-- Update --

I commented out the content of the custom AddUserToRole() method, and the user is able to login. However, in calling an action method, the role check wasn't performed - ie. should have failed since the role name doesn't actually exist:

    [HttpGet]
    [AllowAnonymous]
    [Authorize(Roles="WaitConfirmationxxx")]
    public ActionResult WaitConfirmation()
    {

I also noticed that IsInRole() called the custom GetRolesForUser() method which does a db call in it. Not exactly optimal, and wondering if this is actually how it should be?

                if(User.IsInRole("WaitConfirmation"))
                {
                    // email address has not yet been confirmed
                    return RedirectToAction("WaitConfirmation", "User");
                }

-- Update --

My mistake here was thinking that I needed to use Roles.AddUserToRole() (unless someone comments otherwise).

I also added the cacheRolesInCookie parameter in the roleManager definition in the web.config:

<roleManager enabled="true" defaultProvider="CustomRoleProvider" cacheRolesInCookie="true">

So now when I call if(User.IsInRole("WaitConfirmation")), my custom GetRolesForUser(string username) is called.

The problem now is that none of the Authorization attributes work. Suggestions?

Was it helpful?

Solution

You need to remove the [AllowAnonymous] attribute, change it to this:

[HttpGet]
[Authorize(Roles="WaitConfirmationxxx")]
public ActionResult WaitConfirmation()
{

If your RoleProvider is configured correctly, it should kick into this method when it tries to authorise from the attribute on the above method.

public override string[] GetRolesForUser(string username)
        {
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top