0

We have a .Net Core 2.2 WebApi that uses both Windows and Anonymous Authentication. We want to restrict access to controllers/methods Based on a users AD group. We do not want to have to change the [Authorize(Roles="XXX")] group when we go from Development to Staging to Production. Our solutions is to somehow bind the authorized role to the appsettings and than allow our DevOps pipeline transform as we move between environments.

We tried the following implementation: We added the keys to our appsettings.json

    "Permissions": {
    "Group1": {
        "Admin": "Admin-Dev",
        "User": "User-Dev"
    },
    "Group2": {
        "Admin": "Admin-Dev2",
        "User": "User-Dev2"
    }}

We than created a class to read the section:

 public class AuthorizationConfiguration
{
    public string Group1 { get; set; }
    public string Group2 { get; set; }


}
public class AuthorizationConfigurationSection
{
    public string Admin { get; set; }
    public string User { get; set; }
}

and set it in the startup.cs

 services.Configure<AuthorizationConfiguration>(Configuration.GetSection("Permissions"));

Now my issue is my AuthorizationAttribute needs access to IConfig when i add the authorization attribute to my controller.

    public class Authorization : AuthorizeAttribute, IAuthorizationFilter
{
    private readonly AuthorizationConfiguration _permissonNames;

    public Authorization(IOptions<AuthorizationConfiguration> permissonNames)
    {
        _permissonNames = permissonNames.Value;
    }

    public string[] Permissions { get; set; } //Permission string to get from controller

    public void OnAuthorization(AuthorizationFilterContext context)
    {

        if (Permissions == null || Permissions.Length ==0)
        {   
            context.Result = new UnauthorizedResult();
            return;
        }

        if(Permissions.Any(perm => IsInRole(perm)))
        {
            return;
        }

        context.Result = new UnauthorizedResult();
        return;
    }

    private bool IsInRole(string perm)
    {
        var groupName = GetPropertyValue(_permissonNames, perm);
        return true;
    }
    private string GetPropertyValue(object o, string path)
    {
        var propertyNames = path.Split('.');
        var value = o.GetType().GetProperty(propertyNames[0]).GetValue(o, null);

        if (propertyNames.Length == 1 || value == null)
            return value.ToString();
        else
        {
            return GetPropertyValue(value, path.Replace(propertyNames[0] + ".", ""));
        }
    }
}

I cannot use the attribute without getting the error "There is no argument given that corresponds to the required formal paramater 'permissionNames' of 'Authorization.Authorization(IOptions)'

 [Authorize]
[Authorization(Permissions = new string[] {AuthorizationKeys.Admin,AuthorizationKeys.Group1,
    AuthorizationKeys.Group2})]
[Route("api/[controller]")]
[ApiController]
scarson
  • 67
  • 1
  • 4

1 Answers1

0

There are a lot of issues in your codes . Firstly , you should modify the AuthorizationConfiguration to get the correctly AuthorizationConfigurationSection values from appsettings.json :

public class AuthorizationConfiguration
{

    public AuthorizationConfigurationSection Group1 { get; set; }
    public AuthorizationConfigurationSection Group2 { get; set; }


}
public class AuthorizationConfigurationSection
{
    public string Admin { get; set; }
    public string User { get; set; }
}

Then modify the Authorization class to accept string[] which pass from your attribute on action :

public Authorization(string[] values)
{
    Permissions = values;
}

public Authorization() : base()
{
}

To read the AuthorizationConfiguration you can resolve it through the HttpContext.RequestServices within the filter AuthorizationFilterContext :

public void OnAuthorization(AuthorizationFilterContext context)
{
    var  _permissonNames = context.HttpContext.RequestServices.GetService<IOptions<AuthorizationConfiguration>>().Value;

    if (Permissions == null || Permissions.Length == 0)
    {
        context.Result = new UnauthorizedResult();
        return;
    }

    if (Permissions.Any(perm => IsInRole(perm, _permissonNames)))
    {
        return;
    }

    context.Result = new UnauthorizedResult();
    return;
}

Also modify your IsInRole method :

private bool IsInRole(string perm , AuthorizationConfiguration _permissonNames)
{

    var groupName = GetPropertyValue(_permissonNames, perm);
    return true;
}
Nan Yu
  • 26,101
  • 9
  • 68
  • 148
  • Thanks for the help. My only issue is now the Identity is coming across as a ClaimsPrincipal not a WindowsPrincipal, therfore User.IsInRole is always false. – scarson Mar 16 '20 at 13:25
  • see [this](https://stackoverflow.com/a/53533020/5751404) , use IClaimsTransformation to add custom claims to ClaimsPrincipal – Nan Yu Mar 16 '20 at 13:35