CustomAuthorizeAttribute - HttpActionContext instead of AuthorizationContext - c#

I'm attempting to create a custom authorize attribute in MVC4/Razor and am having an issue with the "AllowAnnoymous" attribute running under the custom authorize attribute (it seems to ignore it). That's all fine and dandy, as I found a solution (see below) to that by checking if the controller or action contain an allow anonymous attribute and then allow pass through if so.
However, I'm seeing that when I create the "AuthorizeAttribute" class and attempt to implement "OnAuthorization" override, it sets the object handlers to a type of "AuthorizationContext" but in the below example and many others i've found on here, it seems the "AuthorizationContext" should not be used - instead it should be "HttpActionContext". Though I tried to replace it with "HttpActionContext" and the override then fails saying there is no suitable method. Any ideas on what I'm missing/doing wrong?
Example Found Here (By Jammer)
private static bool SkipAuthorization(HttpActionContext actionContext)
{
Contract.Assert(actionContext != null);
return actionContext.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().Any()
|| actionContext.ControllerContext.ControllerDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().Any();
}
public override void OnAuthorization(HttpActionContext actionContext)
{
base.OnAuthorization(actionContext);
}
My Code
private override void OnAuthorization(AuthorizationContext filterContext) // Not sure how to change this to HttpActionContext
{
if (filterContext == null) throw new ArugmentException("filterContext");
if (!AllowAnnonymous(new HttpActionContext()))
{
throw new HttpResponseException(HttpStatusCode.UnAuthorized);
}
else
{
base.OnAuthorization(filterContext);
}
}

Any ideas on what I'm missing/doing wrong?
First of all, you are looking at an example that uses the Web API System.Web.Http.AuthorizeAttribute instead of the MVC System.Web.Mvc.AuthorizeAttribute. MVC and Web API are separate frameworks and neither will recognize the other's attributes. This is also the reason why you have a different context type in your AuthorizeAttribute.
Secondly, the reason your custom AuthorizeAttribute doesn't recognize AllowAnonymousAttribute is because you are overriding the logic that does that check in OnAuthorization (along with other important logic that deals with output caching). If you instead override AuthorizeCore and return true/false, then you will not skip this important logic.
If you need to change where the user is redirected, you can override HandleUnauthorizedRequest, which only executes when authorization fails.
Finally, if you need to access the ActionDescriptor to scan for your own attributes, it is passed into OnAuthorization via AuthorizationContext.ActionDescriptor. Unfortunately, it is not passed into AuthorizeCore automatically, but you can work around this by setting it to HttpContext.Items in OnAuthorization as in this example.

Related

Startup Class IsInRole .net core

I'm working inside of ConfigureServices of my Startup class. I want to run IsInRole() which I understand to be this method in order to populate an Action:
However, running:
System.Security.Principal.IPrincipal.IsInRole("BRV_Projects_Edit");
Is clearly not successful as this refers to an interface, not an instance implementing that interface?
My Question is, within the context of ConfigureService, where I am defining a custom attribute how can I access (inject?) the Principal of the current user.
Per the feedback above User.InRole is not accessible at this point in the pipeline but is accessible via the creation of an ActionFilterAttribute.
The ActionFilterAttribute makes available OnActionExecuting, Get User Name on Action Filter extremely similar to this post except with changes relevant to .net core
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.Controller is Controller controller)
{
controller.ViewBag.CanEdit = filterContext.HttpContext.User.IsInRole("group1");

how can I validate this invalid property in web api?

I have a user search request object which looks like this:
UserSearchRequest
{
FirstName:"John",
LastName:"Smith",
Expand:["groups","devices"]
}
Expand is an optional parameter. I have validation which checks that the provided Expand parameters are within thin expected set of parameters. The problem is that if a client submits a request like this:
{
FirstName:"John",
LastName:"Smith",
Expand:1
}
By default Web API 2 will pass this request to the controller method with an Expand value of null. So the user won't know that he submitted a bad request and the controller won't be able to identify it as a bad request b/c null is a valid value for this property. Is there a way to override this default behavior?
Action filters are triggered before controller executes its logic. I will give a general idea of what you need to do to get you on the right track.
First requirement for ActionFilter is to create your own filter class by extending ActionFilterAttribute class.
The following is a sample for this
public class ValidateCustomModel : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
//Write your logic to check that all attributes are not null
}
}
Now onto the second step. This step will register your filter in WebApiConfig class so that the application will know that it has to pass requests to this filter wherever the attribute is used
config.Filters.Add(new ValidateModelAttribute());
Now the third step is to call the custom class as an attribute on the controller method that is being executed when user makes a request.
[ValidateModel]
Hope this helps you to customise it for your own logic. Happy coding.

How to verify user role before executing action?

I'm working on a project in which some users can be in the role AdminReader. Those users can see everything, but will not be able to save/edit any data.
I know I can do it this way:
public JsonResult ChangeStatus(int? id)
{
// AdminReader validation
if (base.User.isAdminReader)
{
return Json(new
{
Message = "You don't have privileges to alter data.",
Success = false,
}, JsonRequestBehavior.AllowGet);
}
// Function code
But I don't want to insert the above code inside all project functions.
I thought I could decorate my methods like we use [HttpGet]. I've also read this SO post.
Then I dropped the idea.
But then I found about Exception Handler Attribute and a logging action filter.
Is it possible to somehow combine the public void OnActionExecuting(ActionExecutingContext filterContext) with my AdminReader validation?
I don't know if it is the right way to go about my problem. Also, I'm not sure it could work really. What's the best practice in this situation?
Any suggestion is welcome, thanks in advance.
There are many ways to do this.
Yes, it's true that attributes are just metadata. However, the MVC framework has code in it that recognizes certain metadata and performs actions on it. Examples include the two attributes you mentioned (ActionFilters and ExceptionFilters), there's also AuthorizationFilters, which may be what you actually want.
AuthorizationFilters run before ActionFilters, near the start of the MVC pipeline, which allows them to block access before the page actually renders. But, if you don't need that, you can just use this point to do specific things before the page renders.
However, having said that, you are still going to need to have code on each page that controls what the user can and can't do based on their role. There is no magic way around that. Whenever you want to control what a user can do on a page based on access, you need code that does that in each section where control is required.
It's not clear from your example what you are trying to do, since the return value from a page is typically the HTML to render, but it looks like you want to return some kind of status message. I don't see how that can be replicated to all pages, since the pages themselves need to render.
I'm not entirely sure I understood your question, so sorry if this is off: but if you wanted to perform your AdminReader logic, you could write your own custom attribute like below:
public class AccessDeniedAuthorizeAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
if (filterContext.Result is HttpUnauthorizedResult)
{
// Perform your unauthorized action here.
}
}
}
And then throw the attribute on any method where it applies (or you could throw it on the entire Controller class, if it applied to everything). Like so:
// The RoleSettings is a class of constants I defined that just contain strings
[AccessDeniedAuthorize(Roles = RoleSettings.AdminRole]
[HttpPost]
public ActionResult MyEditMethod()
{
// Perform actions if they are in the AdminRole
// If not authorized, it will do whatever you defined above in the
// AccessDeniedAuthorizeAttribute
}

Getting Filter.OnAuthorization to run before Controller.OnAuthorization

I have an existing site which has a base controller class for all of its controllers which overrides the implementation of OnAuthorization. In the simplest cut down form:
protected override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
//Do Stuff
}
This all works fine and well and does what it wants at the right time. I now want to add a new global authorization that will run before all other authorisation attributes. For test purposes this attribute looks like this:
public class TestFilterAttribute : FilterAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
var text = "debug point";
return;
}
}
And is added to the Global Filters like this:
filters.Add(new TestFilterAttribute());
My problem is that the OnAuthorization of the controller always seems to run before my filter's one. Is there any way that I can change this? I've tried playing with the order property that you can set when adding it to the global filter collection but that doesn't seem to help.
I could probably move the logic of the Controller's OnAuthorization into a new filter attribute when order would probably be usable but I'd rather avoid major code restructuring if there is an easier way to do it.
I've been searching for information on the Controller.OnAuthorization method but the best I have found is http://msdn.microsoft.com/en-us/library/gg416513(v=vs.98).aspx which only talks about filters. I had assumed that they would work the same way on Controllers but they seem to be getting treated specially, in particular not respecting the order (not even int.MinValue gets in first so its not just that the controller has a very low order by default).
So any suggestions on how to get an auth filter to run as the very first thing?
The final solution used was to refactor the code. I took all of the methods that can be run on a filter attribute (eg OnAuthorization, OnActionExecuting, etc.) and moved them onto attributes named for the classes they came from. So the OnAuthorization from BasicController became the same method on BasicControllerAttribute.
These attributes were then applied to the controllers and the attributes are then inherited by all of the classes that subclass from BasicController which essentially maintains the same functionality.
However the attribute can have its Order set that allows you to play around with the running order however you want.
My takeaway from this was to never override those methods on the controller and to always use attributes. :)
I had a related problem with the Controller.OnAuthorization and the AuthorizationFilter.OnAuthorization methods order of execution.
The short answer is: you can't override that Controller.OnAuthorization
runs prior to other filters.
Detailed answer on the question 'why is it so?' is in the code from
ASP.NET MVC sources below.
There is also nice blog post on filter ordering.
Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
FilterProviders.cs
namespace System.Web.Mvc
{
public static class FilterProviders
{
static FilterProviders()
{
Providers = new FilterProviderCollection();
Providers.Add(GlobalFilters.Filters);
Providers.Add(new FilterAttributeFilterProvider());
Providers.Add(new ControllerInstanceFilterProvider());
}
public static FilterProviderCollection Providers { get; private set; }
}
}
ControllerInstanceFilterProvider.cs
using System.Collections.Generic;
namespace System.Web.Mvc
{
public class ControllerInstanceFilterProvider : IFilterProvider
{
public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
if (controllerContext.Controller != null)
{
// Use FilterScope.First and Order of Int32.MinValue to ensure controller instance methods always run first
yield return new Filter(controllerContext.Controller, FilterScope.First, Int32.MinValue);
}
}
}
}

Custom membership provider + custom CodeAccessSecurityAttribute

I've been tasked to create a custommembership provider for our methods for our MVC 4.0 project.
Based on a attribute ([Authorize]?) it has to spot whether the attempted user is allowed to use the method or not.
Currently i've got this:
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
public class TestAuthorize : CodeAccessSecurityAttribute
{
public TestAuthorize(SecurityAction action)
: base(action)
{
}
public override IPermission CreatePermission()
{
throw new NotImplementedException();
}
}
When I add
return new PrincipalPermission(null,null,true) I expect the permission to be valid and the user has access to the method.
When I add
return new PrincipalPermission(null.null,false) I expect the permission to be invalid and the user will be denied access to the method.
However, It only stops from continuing when I use a throw new SecurityException("You are denied access") which also forces the MVC application to stop unless this exception is handled at client side (using a try catch).
Is it possible for us to handle this exception in our MVC project?
as an example of what we wish to have done by use of attributes:
[TestAuthorize(SecurityAction.Demand)]
public List<string> GetList()
{
//if access, return new List();
//if no access, return null;
}
Pretty sure you want to be inheriting from AuthorizeAttribute here, not CodeAccessSecurityAttribute. In your attribute, you override AuthorizeCore and simply return true if the user should be allowed to continue, and false if they're not authorised to do whatever it is that method does. A false result will trigger a HTTP-401 Unauthorised response, which ASP.NET automatically handles by redirecting the user to the login page so they can log in as someone with the right access, although you can change this behaviour if you wish.
In fact, you might not even need to create your own attribute; if you're using the existing ASP.NET MVC mempership provider, or you can get whatever you're using to play nice with it, then the existing AuthorizeAttribute will work for you.

Categories

Resources