0

We have an application that have three levels of roles. Depending on which role a user is in determines what they have access to. The users are in activedomain groups. The client has asked that test groups be created for test and the original groups are to be used in production. We're using web.config transforms to distinguish the groups in each environment. Previous to this the AuthorizedAttribute class was set up for a save method as follows:

[AuthorizeUsers(Roles = "Group A, Administration")]
    public SomeInformation Post([FromBody]SomeInformation infodata)
    {
        return _manager.SaveInfo(infodata);
    } 

The Group A and Administration are production. In test there is now Group A - Test and Administration - Test.

Is there a way to pull from the web.config file and assign it to the Roles property?

We tried the following:

private string Env = Settings.Default.GroupA + ", " + Settings.Default.Administration
 [AuthorizeUsers(Roles = Env)]

But it gacked. The AuthorizeUsers class inherits from AuthorizeAttribute. Any ideas?

Ivan Gritsenko
  • 4,166
  • 2
  • 20
  • 34
user2073183
  • 141
  • 1
  • 3
  • 13
  • Test using different settings from production? Then it is not a valid test! But even then, why can't you just put `[AuthorizeUsers(Roles = "Group A,Group A - Test, Administration, Administration - Test")]`? Attributes are compiled into the DLL metadata and cannot be changed at runtime, but if you really insist on making it configuration driven, you could use a custom `AuthorizeAttribute` registered as a global filter that scans for custom attributes as shown [here](http://stackoverflow.com/q/32235660/#32254851) - long way to go for such little effect. – NightOwl888 Mar 22 '16 at 03:27
  • Because some of the users are not in higher level groups in production but need to test new functionality in test. Security would have to place them in the applicable user groups to test therefore allowing them access to the same areas in production. And we don't want to keep having to change the code to accommodate the different environments. We thought we could use web.config transforms but that hasn't been the case. – user2073183 Mar 22 '16 at 14:31

1 Answers1

0

You need to make a custom authorize attribute that is read by a custom authorization filter. If you register the filter globally, it will run at the right time and recognize when your controllers and action methods are decorated with the custom authorize attribute.

ConfigAuthorizeAttribute

using System;

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false)]
public class ConfigAuthorizeAttribute : Attribute
{
    public string RolesConfigurationKey { get; set; }
    public string UsersConfigurationKey { get; set; }
}

ConfigAuthorizationFilter

using System;
using System.ComponentModel;
using System.Configuration;
using System.Linq;
using System.Security.Principal;
using System.Web;
using System.Web.Mvc;

public class ConfigAuthorizationFilter : AuthorizeAttribute
{
    // Hide users and roles, since we aren't using them here.
    [Obsolete("Not applicable in this class.", true)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
    new public string Roles { get; set; }

    [Obsolete("Not applicable in this class.", true)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
    new public string Users { get; set; }

    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        // Pass the current action descriptor to the AuthorizeCore
        // method on the same thread by using HttpContext.Items
        filterContext.HttpContext.Items["ActionDescriptor"] = filterContext.ActionDescriptor;
        base.OnAuthorization(filterContext);
    }

    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        if (httpContext == null)
        {
            throw new ArgumentNullException("httpContext");
        }

        var actionDescriptor = httpContext.Items["ActionDescriptor"] as ActionDescriptor;
        if (actionDescriptor != null)
        {
            var authorizeAttribute = this.GetConfigAuthorizeAttribute(actionDescriptor);

            // If the authorization attribute exists
            if (authorizeAttribute != null)
            {
                // Ensure the user is logged in
                IPrincipal user = httpContext.User;
                if (!user.Identity.IsAuthenticated)
                {
                    return false;
                }

                // Lookup the Web.Config settings
                string users = ConfigurationManager.AppSettings[authorizeAttribute.UsersConfigurationKey];
                string roles = ConfigurationManager.AppSettings[authorizeAttribute.RolesConfigurationKey];

                string[] usersSplit = SplitString(users);
                string[] rolesSplit = SplitString(roles);

                // Run the authorization based on the configuration values
                if (usersSplit.Length > 0 && !usersSplit.Contains(user.Identity.Name, StringComparer.OrdinalIgnoreCase))
                {
                    return false;
                }

                if (rolesSplit.Length > 0 && !rolesSplit.Any(user.IsInRole))
                {
                    return false;
                }

                return true;
            }
        }

        return true;
    }

    private ConfigAuthorizeAttribute GetConfigAuthorizeAttribute(ActionDescriptor actionDescriptor)
    {
        ConfigAuthorizeAttribute result = null;

        // Check if the attribute exists on the action method
        result = (ConfigAuthorizeAttribute)actionDescriptor
            .GetCustomAttributes(attributeType: typeof(ConfigAuthorizeAttribute), inherit: true)
            .SingleOrDefault();

        if (result != null)
        {
            return result;
        }

        // Check if the attribute exists on the controller
        result = (ConfigAuthorizeAttribute)actionDescriptor
            .ControllerDescriptor
            .GetCustomAttributes(attributeType: typeof(ConfigAuthorizeAttribute), inherit: true)
            .SingleOrDefault();

        return result;
    }

    internal static string[] SplitString(string original)
    {
        if (string.IsNullOrEmpty(original))
        {
            return new string[0];
        }
        return (from piece in original.Split(new char[] { ',' })
                let trimmed = piece.Trim()
                where !string.IsNullOrEmpty(trimmed)
                select trimmed).ToArray<string>();
    }
}

Usage

First, register your global filter.

public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new ConfigAuthorizationFilter());
        filters.Add(new HandleErrorAttribute());
    }
}

Then, decorate your controllers and actions with the ConfigAuthorizeAttribute, specifying where to lookup the users and roles in the configuration file.

[ConfigAuthorize(RolesConfigurationKey = "authorization:groups:home-about")]
public ActionResult About()
{
    ViewBag.Message = "Your app description page.";

    return View();
}

Then, add the configuration file settings to appSettings in web.config.

<appSettings>
    <add key="authorization:groups:home-about" value="Group A, Administration"/>
</appSettings>
NightOwl888
  • 55,572
  • 24
  • 139
  • 212