Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MVC custom roleprovider how to hook it up to HttpContext.Current.User.IsInRole("myrole")

I have an MVC app and I wrote a custom roleprovider for it as shown:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Security;
using VectorCheck.Models;

namespace VectorCheck.Security
{
    public class MyRoleProvider : RoleProvider
    {
        private VectorCheckRepository<User> _repository { get; set; }

        public MyRoleProvider()
        {
            _repository = new VectorCheckRepository<User>();
        }

        public MyRoleProvider(VectorCheckRepository<User> repository)
        {
            _repository = repository;
        }

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

        public override string ApplicationName
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                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 string[] FindUsersInRole(string roleName, string usernameToMatch)
        {
            throw new NotImplementedException();
        }

        public override string[] GetAllRoles()
        {
            throw new NotImplementedException();
        }

        public override string[] GetRolesForUser(string username)
        {
            var user = _repository.GetUser(username);

            return new string[] { user.Role.Name };
        }

        public override string[] GetUsersInRole(string roleName)
        {
            throw new NotImplementedException();
        }

        public override bool IsUserInRole(string username, string roleName)
        {
            var user = _repository.GetUser(username);

            return string.Compare(user.Role.Name, roleName, true) == 0;
        }

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

        public override bool RoleExists(string roleName)
        {
            throw new NotImplementedException();
        }
    }
}

This works really well with restricting access to controllers and actions using:

[Authorize(Roles = "Administrator")]

above the controller or action.

I also want restricted access to some things in the view though using:

HttpContext.Current.User.IsInRole("Administrator")

This method isn't part of my roleprovider though so isn't getting overridden.

Does anyone know how to do it for this method as well?

like image 735
AnonyMouse Avatar asked Nov 01 '11 22:11

AnonyMouse


2 Answers

If you've hooked your RoleProvider as the role provider for the application in web.config, then this should work automatically; the framework will create a RolePrincipal for an authenticated user at the start of the request that will call the GetRolesForUser method on your role provider, passing the name from the IIdentity as the user name.

The framework implementation of RolePrincipal's IsInRole(string role) method is something like this (I've added comments)

public bool IsInRole(string role) 
{ 
    if (_Identity == null)
        throw new ProviderException(SR.GetString(SR.Role_Principal_not_fully_constructed)); 

    if (!_Identity.IsAuthenticated || role == null)
        return false;
    role = role.Trim(); 
    if (!IsRoleListCached) {
        _Roles.Clear(); 

        // here the RoleProvider is used to get the roles for the user
        // and are cached in a collection on the RolePrincipal so that
        // they are only fetched once per request
        string[] roles = Roles.Providers[_ProviderName].GetRolesForUser(Identity.Name); 
        foreach(string roleTemp in roles)
            if (_Roles[roleTemp] == null) 
                _Roles.Add(roleTemp, String.Empty);

        _IsRoleListCached = true;
        _CachedListChanged = true; 
    }
    return _Roles[role] != null; 
} 

Set a breakpoint inside of your RoleProvider GetRolesForUser method to ensure that it is being called correctly and also inspect the IPrincipal (HttpContext.Current.User) to ensure that it is of type RolePrincipal for an authenticated user.

like image 63
Russ Cam Avatar answered Oct 23 '22 16:10

Russ Cam


Sorry I am late to the party here;

For the benefit of other people with the same problem - Russ Cam's answer is spot on to finding the answer.

In my case, my custom roleManager did not have 'enabled="true" and cacheRolesInCookie="true". This seemed to stop the GetRolesForUser being called.

Working Code For the web.config:

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

Really Good Tutorial on this topic at http://www.brianlegg.com/post/2011/05/09/Implementing-your-own-RoleProvider-and-MembershipProvider-in-MVC-3.aspx

like image 5
JsAndDotNet Avatar answered Oct 23 '22 16:10

JsAndDotNet