I want to create a single custom authorization attribute to be added to controller actions that require authenticated users.
My first shot at this, so I may have it all wrong
Got 3 Roles which corresponds to 3 Areas in my application (Client app, so I'll just call them RoleOne, RoleTwo, RoleThree and AreaOne, AreaTwo, AreaThree).
Each area have similiar (to a point) views and actions, like Sign Up, Area specific home, etc.
In the attribute I want to determine the current area, controller and action. Then I just add code like:
If not logged in, and in AreaOne, go to AreaOne Signup (etc)
If logged in as RoleOne, and AreaOne Home is requested, go there, but if AreaTwo Home is requested ,go to AreaTwo Signup
So in my attribute class I need to determine the current area, controller and action, and see what kind of user we have logged in (if any)
Using ActionExecutingContext it seems I can determine the contoller and action method names, however I cannot find the Area name, and altered the attribute to be called like this:
public string Area { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
HttpContext ctx = HttpContext.Current;
if(ctx.Request.IsAuthenticated)
{
var user = ctx.User.Identity.Name;
if(ctx.User.IsInRole(Area))
{
// all fine....just get out?
} else {
// redirect to the signup action for this area
}
} else {
// redirect user to signup action for this area
}
base.OnActionExecuting(filterContext);
}
Made the above code a bit simpler (and yes, it does the job for now).
Note I compare the current user name with the area name, as I'm using a mocked role provider for now. (will change to IsInRole() later)
public string Area { get; set; }
// If the user is not logged in with a role matching the area's name, redirect to the signup for that area
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (HttpContext.Current.User.Identity.Name != Area.ToString())
HttpContext.Current.Response.Redirect("~/" + Area + "/Signup/");
base.OnActionExecuting(filterContext);
}
Please note - authorization or other security decisions must never
be made based on the current area, e.g. examining the current route to determine which area the current request belongs to. The only supported way of accomplishing this is to put an [Authorize] or other similar filter
which doesn't depend on the current area on a base controller, then making sure that each controller within an area subclasses that base controller.
Your first code sample is vulnerable because it tries to get information (like the area name) from the route servicing the request. This makes the route itself responsible for the security decision, which is a Very Bad Thing since Routing can be bypassed.
Your second code sample should be fine because you've statically included the name as part of the attribute, so what you're doing is really no different than [Authorize] with a custom redirect URL.
Subtle but important difference. :)
Marked as answer by ricka6 on Mar 09, 2010 09:08 PM
Lucky for me, this is just a temporary solution (I'm "only" working on views and routes at this stage). (Converting the static html prototype/design to views, leaving the logic for either later or other team members)
If you know about some usefull links on this topic, please share!
The best and safest thing for you to do would be to subclass AuthorizeAttribute and override only the HandleUnauthorizedRequest method. Then you could attribute your methods like so:
Don't override any other method as the default logic should suffice in your case. (The default logic handles things like the IsInRole() check, suppressing output caching, etc.) Then from the HandleUnauthorizedAttribute method, set filterContext.Result
= new RedirectResult("url_to_redirect_to").
krokonoster
Contributor
4291 Points
1352 Posts
Bit lost on first custom Authorization Attribute
Mar 02, 2010 04:57 AM|LINK
I want to create a single custom authorization attribute to be added to controller actions that require authenticated users.
My first shot at this, so I may have it all wrong
Got 3 Roles which corresponds to 3 Areas in my application (Client app, so I'll just call them RoleOne, RoleTwo, RoleThree and AreaOne, AreaTwo, AreaThree).
Each area have similiar (to a point) views and actions, like Sign Up, Area specific home, etc.
In the attribute I want to determine the current area, controller and action. Then I just add code like:
So in my attribute class I need to determine the current area, controller and action, and see what kind of user we have logged in (if any)
Using ActionExecutingContext it seems I can determine the contoller and action method names, however I cannot find the Area name, and altered the attribute to be called like this:
public string Area { get; set; } public override void OnActionExecuting(ActionExecutingContext filterContext) { HttpContext ctx = HttpContext.Current; if(ctx.Request.IsAuthenticated) { var user = ctx.User.Identity.Name; if(ctx.User.IsInRole(Area)) { // all fine....just get out? } else { // redirect to the signup action for this area } } else { // redirect user to signup action for this area } base.OnActionExecuting(filterContext); }krokonoster
Contributor
4291 Points
1352 Posts
Re: Bit lost on first custom Authorization Attribute
Mar 02, 2010 05:33 AM|LINK
Made the above code a bit simpler (and yes, it does the job for now).
Note I compare the current user name with the area name, as I'm using a mocked role provider for now. (will change to IsInRole() later)
public string Area { get; set; } // If the user is not logged in with a role matching the area's name, redirect to the signup for that area public override void OnActionExecuting(ActionExecutingContext filterContext) { if (HttpContext.Current.User.Identity.Name != Area.ToString()) HttpContext.Current.Response.Redirect("~/" + Area + "/Signup/"); base.OnActionExecuting(filterContext); }levib
Star
7702 Points
1099 Posts
Microsoft
Re: Bit lost on first custom Authorization Attribute
Mar 02, 2010 07:24 AM|LINK
Please note - authorization or other security decisions must never be made based on the current area, e.g. examining the current route to determine which area the current request belongs to. The only supported way of accomplishing this is to put an [Authorize] or other similar filter which doesn't depend on the current area on a base controller, then making sure that each controller within an area subclasses that base controller.
Your first code sample is vulnerable because it tries to get information (like the area name) from the route servicing the request. This makes the route itself responsible for the security decision, which is a Very Bad Thing since Routing can be bypassed.
Your second code sample should be fine because you've statically included the name as part of the attribute, so what you're doing is really no different than [Authorize] with a custom redirect URL.
Subtle but important difference. :)
krokonoster
Contributor
4291 Points
1352 Posts
Re: Bit lost on first custom Authorization Attribute
Mar 02, 2010 08:01 AM|LINK
Thanks for the answer Levi.
Lucky for me, this is just a temporary solution (I'm "only" working on views and routes at this stage). (Converting the static html prototype/design to views, leaving the logic for either later or other team members)
If you know about some usefull links on this topic, please share!
Thanks again. :-)
levib
Star
7702 Points
1099 Posts
Microsoft
Re: Bit lost on first custom Authorization Attribute
Mar 02, 2010 04:30 PM|LINK
The best and safest thing for you to do would be to subclass AuthorizeAttribute and override only the HandleUnauthorizedRequest method. Then you could attribute your methods like so:
[MyCustomAuthorization(Roles = "some_role[, some_other_role[, ...]]")]
Don't override any other method as the default logic should suffice in your case. (The default logic handles things like the IsInRole() check, suppressing output caching, etc.) Then from the HandleUnauthorizedAttribute method, set filterContext.Result = new RedirectResult("url_to_redirect_to").
Good luck. :)