Reputation: 5683
Setting an action to be allowed by only a specific user or role is easy using the [Authorize] attribute. E.g.
[Authorize(Roles = "Administrator")]
public ActionResult Index()
{
...
However, I ran into a problem when I wanted the inverse. Is there a way using MVC framework features to allow all authenticated users except those specified by name or role?
The desired usage would be something akin to:
[DoNotAuthorize(Roles = "RestrictedUser")]
public ActionResult Index()
{
...
Upvotes: 3
Views: 4469
Reputation: 1
One way to authorize specific users and assign it to different controllers or actions: Make a simple static class and create const variables.
public static class RolesText
{
public const string MISDeveloper = "MIS Developer";
public const string SuperAdmin = "Super Admin";
public const string Admin = "Admin";
public const string DistrictMnE = "District M&E";
public const string PSUEngineer = "PSU Engineer";
public const string Guest = "Guest";
public const string All_Users = MISDeveloper + "," + SuperAdmin + "," + Admin + "," + DistrictMnE + "," + PSUEngineer + "," + Guest;
public const string Without_PSUEng_DistMnE = MISDeveloper + "," + SuperAdmin + "," + Admin + "," + Guest;
public const string Without_PSUEng_Guest = MISDeveloper + "," + SuperAdmin + "," + Admin + "," + DistrictMnE;
public const string Without_DistMnE_Guest = MISDeveloper + "," + SuperAdmin + "," + Admin + "," + PSUEngineer;
}
Then call these values like below.
[Authorize(Roles = RolesText.Without_PSUEng_Guest)]
public class NeedBasedSMsController : Controller
{
private AUPDBEntities db = new AUPDBEntities();
}
If want to assign different users then call it as below.
[Authorize(Roles = RolesText.Admin + "," + RolesText.Guest)]
public class MyController : Controller
{
// YOUR CONTROLLER CLASS
}
Upvotes: 0
Reputation: 11
The following code simply negates the authorization granted to the specified user/role, effectively becoming an authorization to everyone but the specified user/role.
Use it just like you would use the AuthorizeAttribute class.
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
public class DoNotAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext) => !base.AuthorizeCore(httpContext);
}
Upvotes: 1
Reputation: 127
Old question, but for those who will need it - very simply you can handle it using Policy:
Just add the class:
public class CustomRoleRequirement : AuthorizationHandler<CustomRoleRequirement>, IAuthorizationRequirement
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, CustomRoleRequirement requirement)
{
var roles = new[] { "YourExcludedRole1", "YourExcludedRole2", "YourExcludedRole3"};
var userIsInRole = roles.Any(role => context.User.IsInRole(role));
if (userIsInRole)
{
context.Fail();
return Task.FromResult(false);
}
context.Succeed(requirement);
return Task.FromResult(true);
}
}
Then add to startup.cs
public void ConfigureServices(IServiceCollection services)
{
...
services.AddAuthorization(options => {
options.AddPolicy("ExcludeRoles", policy => policy.AddRequirements(new CustomRoleRequirement()));
});
...
}
And then you can simply use [Authorize(Policy = "ExcludeRoles")]
and all the roles will be authorized excluded "YourExcludedRole1", "YourExcludedRole2", "YourExcludedRole3"
Upvotes: 1
Reputation: 26406
You could just list accepted roles and leave the exception out
[Authorize(Roles = "Administrator, Admin, SuperUser")]
public ActionResult Index()
{
}
Or create a special AuthorizeAttribute with that logic
Upvotes: 1
Reputation: 5683
One fairly simple solution is to derive from the AuthorizeAttribute class and override its AuthorizeCore method, swapping its true/false logic.
/// <summary>
/// Authorizes any authenticated user *except* those who match the provided Users or Roles.
/// </summary>
public class DoNotAuthorizeAttribute : AuthorizeAttribute
{
/// <summary>
/// This is effectively a copy of the MVC source for AuthorizeCore with true/false logic swapped.
/// </summary>
/// <param name="httpContext">The HTTP context, which encapsulates all HTTP-specific information about an individual HTTP request.</param>
/// <returns>true if the user is authorized; otherwise, false.</returns>
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (httpContext == null)
{
throw new ArgumentNullException("httpContext");
}
IPrincipal user = httpContext.User;
if (!user.Identity.IsAuthenticated)
{
return false;
}
string[] usersSplit = SplitString(Users);
if ((usersSplit.Length > 0) && usersSplit.Contains<string>(user.Identity.Name, StringComparer.OrdinalIgnoreCase))
{
return false;
}
string[] rolesSplit = SplitString(Roles);
if ((rolesSplit.Length > 0) && rolesSplit.Any<string>(new Func<string, bool>(user.IsInRole)))
{
return false;
}
return true;
}
/// <summary>
/// This is a direct copy of the MVC source for the internal SplitString method.
/// </summary>
/// <param name="original">The original string to split.</param>
/// <returns>An array of strings.</returns>
internal static string[] SplitString(string original)
{
if (string.IsNullOrWhiteSpace(original))
{
return new string[0];
}
return (from piece in original.Split(new[] { ',' })
let trimmed = piece.Trim()
where !string.IsNullOrEmpty(trimmed)
select trimmed).ToArray<string>();
}
}
Upvotes: 6