Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adding custom roles to windows roles in ASP.NET MVC 5

I'm building an intranet app using ASP.NET MVC 5.

My goal is to have the authentication of any user made by the Active Directory (i.e. I'm using the "Windows Authentication"), then add groups to any user inside the application (NOT using domain groups).

I've found some very interesting piece of code here:

http://brockallen.com/2013/01/17/adding-custom-roles-to-windows-roles-in-asp-net-using-claims/

But it's not working in my scenario: when I decorate the controller with [Authorize(Role="AppRole")], I can't be authorized even if the user (using Claims) is associated with the "AppRole" role.

This is my code:

In Global.asax.cs

void Application_PostAuthenticateRequest()
    {
        if (Request.IsAuthenticated)
        {

            string[] roles = Utils.GetRolesForUser(User.Identity.Name);

            var id = ClaimsPrincipal.Current.Identities.First();
            foreach (var role in roles)
            {
                //id.AddClaim(new Claim(ClaimTypes.Role, role.ToString()));
                id.AddClaim(new Claim(ClaimTypes.Role, @"Kairos.mil\Compliance"));
            }


            bool pippo = User.IsInRole("Compliance");

            HttpContext.Current.User = (IPrincipal)id ;

            bool pippo2 = User.IsInRole("Compliance");


        }
    }

The function GetRolesForUser is as follows (and is working fine):

 public static string[] GetRolesForUser(string username)
    {
        dbOrdiniPersonaliEntities db = new dbOrdiniPersonaliEntities();

        string utente = StripDomain(username);

        string[] gruppi = new string[db.vGruppiUtentis.Where(t => t.KairosLogin == utente).Count()];

        int i=0;

        foreach (var gruppo in db.vGruppiUtentis.Where(t => t.KairosLogin == utente))
        {

            gruppi[i]=gruppo.GruppoDes;
            i=i++;
        }
        return gruppi;
    }

And the controller is decorated with the "standard" Authorize clause:

[Authorize(Roles="AppRole")]
    public ActionResult Index(string sortOrder, string currentFilter, string DesSearchString,int? page)
    {
      // my code here
    }

Any idea?

Thanks in advance

UPDATE

Thanks @Leandro I've tried as you suggested the following code

void Application_PostAuthenticateRequest()
    {
        if (Request.IsAuthenticated)
        {

            string[] roles = Utils.GetRolesForUser(User.Identity.Name);

            ClaimsIdentity id = ClaimsPrincipal.Current.Identities.First();
            foreach (var role in roles)
            {
                //id.AddClaim(new Claim(ClaimTypes.Role, role.ToString()));
                id.AddClaim(new Claim(ClaimTypes.Role, @"Kairos.mil\Compliance"));
            }

            bool pippo = User.IsInRole("Compliance");

            SetPrincipal((IPrincipal)id);

            bool pippo2 = User.IsInRole("Compliance");


        }
    }

But I receive a run-time error when the code reaches this point

SetPrincipal((IPrincipal)id);

The error is as follows

Unable to cast object of type 'System.Security.Principal.WindowsIdentity' to type 'System.Security.Principal.IPrincipal'.

Thanks for your help

UPDATE 2 (maybe solved)

Hi Looking deeper into SO, I've found this resource

ASP.NET MVC and Windows Authentication with custom roles

Following the answer of @Xhalent, I modified my code as follows

protected void Application_PostAuthenticateRequest()
    {
        if (Request.IsAuthenticated)
        {
            String[] roles = Utils.GetRolesForUser(User.Identity.Name);

            GenericPrincipal principal = new GenericPrincipal(User.Identity, roles);

            Thread.CurrentPrincipal = HttpContext.Current.User = principal;
        }
    }

It seems now working fine! Any comments? Any drawbacks? Thanks a lot!!

like image 623
Andrea Panzeri Avatar asked May 06 '15 16:05

Andrea Panzeri


1 Answers

Use this method to save the principal, so it sets up also in the thread:

     private void SetPrincipal(IPrincipal principal)
        {
            Thread.CurrentPrincipal = principal;
            if (HttpContext.Current != null)
            {
                HttpContext.Current.User = principal;
            }
        }

Update: Also allow anonymous and test if User.IsInRole is getting something inside the method.

like image 154
Leandro Bardelli Avatar answered Sep 29 '22 00:09

Leandro Bardelli