I have a custom AuthorizeAttribute like this
public class DevMode : AuthorizationFilterAttribute
{
public override void OnAuthorization(HttpActionContext actionContext)
{
if (myConditionToAuthorize)
{
// how to allow [Authorize] ?
}
}
}
The problem is that it is used along with [Authorize] tag like this:
[Authorize, DevMode]
public class UserController : ApiController { ... }
I need to allow [Authorize] == true inside [DevMode]
Or it is better to put them all together inside a unique authorize class? But then I dont know tho to check Authorize data.
Or it is better to put them all together inside a unique authorize class?
Oh yes, that would indeed be better. You could simply derive from the AuthorizeAttribute and call the base method:
public class DevModeAttribute : AuthorizeAttribute
{
protected override bool IsAuthorized(HttpActionContext actionContext)
{
var authorize = base.IsAuthorized(actionContext);
if (!authorized)
{
// the user is not authorized, no need to go any further
return false;
}
// now apply your custom authorization logic here and return true or false
...
}
}
and then:
[DevMode]
public class UserController : ApiController { ... }
I used this to add a custom IsAdmin (based on claims) using this method
public class IsAdminAttribute : AuthorizeAttribute
{
protected override bool IsAuthorized(HttpActionContext actionContext)
{
IPrincipal principal = actionContext.RequestContext.Principal;
return principal.IsAdmin();
}
}
it kind of answers my own last comment so hope it helps someone else please note the .IsAdmin is an extension method on the IPrincipal that checks claims.
Related
In our MVC solution we have two custom implementations of AuthorizeAttribute - one is named BasicHttpAuthorizeAttribute and has been used in production for years, and the other RoleAuthorizeAttribute, which was recently added.
When creating RoleAuthorizeAttribute I simply copied BasicHttpAuthorizeAttribute and modified some of the already-overridden methods.
Both attributes serve the purpose of authenticating the user, and the RoleAuthorizeAttribute of verifying that the user has the required role.
However, RoleAuthorizeAttribute never authenticates the user. It is simply not being called and instead our MVC controllers throw an exception when a non-logged-in user reaches the controller action and the code requests the context user.
Below is the outline for this custom AuthorizeAttribute. If I put breakpoints on all of those methods I find that none of them are hit when a request is made.
Can anyone explain why this class is not being used to authenticate users? Why is it that an unauthenticated user is not being redirected to the login page, but if I swap RoleAuthorize for BasicHttpAuthorize or simply the base Authorize then they are redirected?
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
public class RoleAuthorizeAttribute : System.Web.Http.AuthorizeAttribute
{
/// <summary>
/// Gets or sets the <see cref="Role"/> enumerations required for authorization.
/// </summary>
public Role[] RequiredRoles
{
get {...}
set {...}
}
public bool RequireSsl { get; set; };
public bool RequireAuthentication { get; set; }
public RoleAuthorizeAttribute(params Role[] requiredRoles)
{
// ...
}
public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext actionContext)
{
// ...
}
protected override void HandleUnauthorizedRequest(System.Web.Http.Controllers.HttpActionContext actionContext)
{
// ...
}
private bool Authenticate(System.Web.Http.Controllers.HttpActionContext actionContext)
{
// ...
}
public static bool TryGetPrincipal(string authHeader, out IPrincipal principal)
{
// ...
}
public static bool TryGetAuthCookie(out IPrincipal principal)
{
// ...
}
private static string[] ParseAuthHeader(string authHeader)
{
// ...
}
private static bool TryGetPrincipal(string username, string password, out IPrincipal principal)
{
// ...
}
}
And here is an example of its usage:
namespace MyProject.Areas.Customer.Controllers
{
[RoleAuthorize(Role.Customer, Role.CompanyAdmin)]
public partial class OrderController : MyCustomController
{
private static readonly ILog Log = LogManager.GetLogger(typeof (OrderController));
public ActionResult Index(int id)
{
// ...
}
}
}
We use Basic Authentication so there's a header set thus:
I've seen older questions asking about the same problem, but in those cases, they also override an AuthorizeCore method which no longer seems to be present on the AuthorizeAttribute class.
I figured out myself why this was happening.
There are two AuthorizeAttributes - one in the System.Web.Http namespace and the other in System.Web.Mvc. I wasn't aware of this and was trying to build a one-size-fits-all attribute, so my attribute was working for WebAPI requests but not for MVC controller requests.
The difference in these two attributes is in the OnAuthorize method where they each take a different context argument.
Once I had built two separate attributes (which are almost identical), each deriving from a different AuthorizeAttribute, everything worked as expected.
As I am working on Asp.Net core Authorization part, I needed a new property in AuthorizeAttribute which I want to utilize as a extra permission value. So, I have extended the AuthorizeAttribute in my own custom Authorize attribute. See below:
public class RoleAuthorizeAttribute : Microsoft.AspNetCore.Authorization.AuthorizeAttribute
{
public string Permission { get; private set; }
public RoleAuthorizeAttribute(string policy, string permission) : base(policy)
{
this.Permission = permission;
}
}
Then, I've created an AuthorizationHandler to check for the requirement as below:
public class RolePermissionAccessRequirement : AuthorizationHandler<RolePermissionDb>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, RolePermissionDb requirement)
{
// check here..
context.Succeed(requirement);
return Task.FromResult(0);
}
}
All respective service collection mapping I have already done, just omitted here.
Now, I want my attribute to use like this on controller action level:
[RoleAuthorize("DefaultPolicy", "CustomPermission")]
public IActionResult List()
{
}
Would anybody suggest me how would I access the permission property value given on the top of Action method in the handler RolePermissionAccessRequirement ??
I want to perform some sort of access rule based on custom permission value given in the Authorize attribute on top of Action method.
Thanks in advance!
To parametrize a custom Authorize attribute, create an authorization filter implementing IAsyncAuthorizationFilter. Then wrap the filter in a TypeFilterAttribute-derived attribute. This attribute can accept parameters and pass it to the authorization filter's constructor.
Usage example:
[AuthorizePermission(Permission.Foo, Permission.Bar)]
public IActionResult Index()
{
return View();
}
Implementation:
public class AuthorizePermissionAttribute : TypeFilterAttribute
{
public AuthorizePermissionAttribute(params Permission[] permissions)
: base(typeof(PermissionFilter))
{
Arguments = new[] { new PermissionRequirement(permissions) };
Order = Int32.MinValue;
}
}
public class PermissionFilter : Attribute, IAsyncAuthorizationFilter
{
private readonly IAuthorizationService _authService;
private readonly PermissionRequirement _requirement;
public PermissionFilter(
IAuthorizationService authService,
PermissionRequirement requirement)
{
//you can inject dependencies via DI
_authService = authService;
//the requirement contains permissions you set in attribute above
//for example: Permission.Foo, Permission.Bar
_requirement = requirement;
}
public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
{
bool ok = await _authService.AuthorizeAsync(
context.HttpContext.User, null, _requirement);
if (!ok) context.Result = new ChallengeResult();
}
}
In addition, register a PermissionHandler in DI to handle PermissionRequirement with permission list:
public class PermissionHandler : AuthorizationHandler<PermissionRequirement>
Look at this this GitHub project for a complete example.
I'm building the below filter:
public class TestflowFilter : FilterAttribute, IActionFilter
{
public void OnActionExecuted(ActionExecutedContext filterContext)
{
var profileId = int.Parse(ClaimsPrincipal.Current.GetClaimValue("UserId"));
var appId = int.Parse(filterContext.RouteData.Values["id"].ToString());
if (profileId != 0 && appId != 0)
{
if (CheckIfValid(profileId, appId))
{
// redirect
filterContext.Result = // url to go to
}
}
}
public void OnActionExecuting(ActionExecutingContext filterContext)
{
}
}
I actually only need OnActionExecuted, but since IActionFilter is an interface I have to implement them both. Is it ok to leave OnActionExecuting blank if I don't need anything to happen, or do I need to call a base version that MVC always runs?
Also in the OnActionExecuted method if the CheckIfValid is true I redirect the user, but if not I don't do anything. Is that ok or do I need to set some property on the filterContext instead.
I actually only need OnActionExecuted, but since IActionFilter is an interface I have to implement them both. Is it ok to leave OnActionExecuting blank if I don't need anything to happen, or do I need to call a base version that MVC always runs?
Leaving the method body empty is perfectly acceptable in this case. Looks good!
Also In the OnActionExecuted method if the CheckIfValid is true I redirect the user, but if not I don't do anything, is that ok or do I need to set some property on the filterContext instead.
Your filter is fine. MVC does offer a different abstract base class called ActionFilterAttribute, which implements these interfaces for you to override as needed. There's a nice overview that you can read about here. If you derive from that class, your filter attribute code could be simplified a little bit:
public class TestflowFilter : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
var profileId = int.Parse(ClaimsPrincipal.Current.GetClaimValue("UserId"));
var appId = int.Parse(filterContext.RouteData.Values["id"].ToString());
if (profileId != 0 && appId != 0)
{
if (CheckIfValid(profileId, appId))
{
// redirect
filterContext.Result = // url to go to
}
}
}
}
public class CustomAuthorizeAttribute : AuthorizationFilterAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
return true;// if my current user is authorised
}
}
Above is my CustomAuthorizeAttribute Class
and
[CustomAuthorize] // both [CustomAuthorize] and [CustomAuthorizeAttribute ] I tried
public class ProfileController : ApiController
{
//My Code..
}
When I'm calling
http://localhost:1142/api/Profile
It is not firing CustomAuthorizeAttribute
More over My FilterConfig class is look like below
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new CustomAuthorizeAttribute());
}
}
Please help if I miss something.
Looks like you are using an MVC filter instead of a Web API filter. It can be detected in the sample because it uses HttpContextBase. Instead use the filter from the System.Web.Http.Filters namespace.
You need to override OnAuthorization or OnAuthorizationAsync on the Web API filter.
You don't need to register a global filter and decorate your controller with it. Registering it will make it run for all controllers.
Web API filter code:
https://github.com/aspnetwebstack/aspnetwebstack/blob/master/src/System.Web.Http/Filters/AuthorizationFilterAttribute.cs
YOur custom attribute should inherit from System.Web.Http.Filters.AuthorizationFilterAttribute
and it should look like this
using System.Web.Http.Controllers;
using System.Web.Http.Filters;
public class CustomAuthorizeAttribute : System.Web.Http.Filters.AuthorizationFilterAttribute
{
public override bool AllowMultiple
{
get { return false; }
}
public override void OnAuthorization(HttpActionContext actionContext)
{
//Perform your logic here
base.OnAuthorization(actionContext);
}
}
Try with this.
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
protected override bool IsAuthorized(System.Web.Http.Controllers.HttpActionContext actionContext)
{
return true;
}
}
To add onto the other answers that have you inherit from System.Web.Http.Filters.AuthorizationFilterAttribute, I put this into my OnAuthorization method to make sure the user was logged in:
if (!actionContext.RequestContext.Principal.Identity.IsAuthenticated)
{
// or whatever sort you want to do to end the execution of the request
throw new HttpException(403, "Forbidden");
}
In MVC3 is there a way to make a role (SuperAdmin) that is ALWAYS authorized even if not explicitly listed in the Roles list?
For example with this markup...
[Authorize(Roles="Accounting")]
Even though I'm not in the Accounting role, as a SuperAdmin is there a way to be Authorized for this Action?
I'd highly recommend reading Securing your ASP.NET MVC 3 Application.
First, create your AnonymousAttribute:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method,
AllowMultiple = false,
Inherited = true)]
public sealed class AllowAnonymousAttribute : Attribute
{
}
Second, create your GlobalAuthorize attribute:
public sealed class GlobalAuthorize : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
bool bypassAuthorization =
filterContext.ActionDescriptor
.IsDefined(typeof(AllowAnonymousAttribute),
true)
|| filterContext.ActionDescriptor
.ControllerDescriptor
.IsDefined(typeof(AllowAnonymousAttribute),
true)
|| (filterContext.RequestContext
.HttpContext
.User != null
&& filterContext.RequestContext
.HttpContext
.User
.IsInRole("SuperAdmin"));
if (!bypassAuthorization)
{
base.OnAuthorization(filterContext);
}
}
}
Third, register GlobalAuthorize in your Global Filters (global.asax):
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new GlobalAuthorize());
}
Now all controllers require the user to be logged in to access. Controllers OR controller Methods can be allowed Anonymous access with the AllowAnonymous attribute. Additionally, all methods are allowed by users in the SuperAdmin role.
You can create your customized AuthorizeAttribute where in the AuthorizeCore method you can implement the extra logic.
A simple example without proper error handling:
public class AuthorizeSuperAdminAttribute : AuthorizeAttribute
{
protected virtual bool AuthorizeCore(HttpContextBase httpContext)
{
IPrincipal user = httpContext.User;
if (user.Identity.IsAuthenticated && user.IsInRole("SuperAdmin"))
return true;
return base.AuthorizeCore(httpContext);
}
}
Then you can use it normally on your actions:
[AuthorizeSuperAdmin(Roles="Accounting")]
public ActionResult MyAction()
{
}