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.
Related
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.
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
}
My team and I are starting up a new website project in ASP .NET 5 and I'm trying to set up the basis of our user authentication and authorization policy.
So far, I've managed to use the [Authorize] and [AllowAnonymous] attributes to selectively define an authorization policy controllers or actions. The one thing I'm still struggling to achieve is defining a default authorization policy.
Bascially, I'd like every controller and action to behave as if they had an [Authorize] attribute by default, so that only actions specifically tagged as [AllowAnonymous] can be accessed by an anonymous user. Otherwise, we expect that, at some point, someone will forget to add an [Authorize] attribute to their controller and introduce vulnerabilities into the webapp.
It is my understanding that what I'm trying to do could be achieved in previous versions of ASP .NET by adding the following statement in FilterConfig.cs:
filters.Add(new AuthorizeAttribute());
... except that FilterConfig.cs no longer exists in MVC 6. According to How to register a global filter with mvc 6, asp.net 5 I can now access the global filters list using:
services.ConfigureMvc(options =>
{
options.Filters.Add(new YouGlobalActionFilter());
}
... tried it, looks fine, but now it's the AuthorizeAttribute filter that I can't seem to find.
For experimenting purposes I've tried to handcraft an equivalent to the AuthorizeAttribute filter and came up with the following:
public class LoginFilter: AuthorizeFilter
{
public LoginFilter(): base(new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build())
{
}
public override Task OnAuthorizationAsync(Microsoft.AspNet.Mvc.AuthorizationContext context)
{
if(!context.HttpContext.User.Identity.IsAuthenticated && context.ActionDescriptor is ControllerActionDescriptor)
{
var action = context.ActionDescriptor as ControllerActionDescriptor;
if(!AcceptAnonymous(action.ControllerTypeInfo) && !AcceptAnonymous(action.MethodInfo))
{
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
}
}
return base.OnAuthorizationAsync(context);
}
private static bool AcceptAnonymous(ICustomAttributeProvider o)
{
return o.IsDefined(typeof(AllowAnonymousAttribute), true);
}
}
This kinda works... I can add it to the global filters list, and it does reject queries coming from unauthenticated users unless the query is resolved to an action tagged [AllowsAnonymous].
However...
the AuthorizationPolicyBuilder thingy is ugly and misleading: it does not serve any purpose and is apparently ignored during the whole processing. The only reason I added it is that AuthorizeFilter requires an AuthorizationPolicy in its constructor. I guess, but haven't tried yet, that directly implementing IAsyncAuthorizationFilter would solve this particular issue
nothing in this code is specific to my webapp and the functionality was apparently provided in previous versions of the framework, so I'm willing to bet that there already is (or there will soon be) a component doing exactly the same thing, and I'd rather use a standard component from the framework than handcraft my own.
So, long story short, where has the AuthorizeAttribute filter gone? Or is there any functional equivalent I can use to make rejection of anonymous users the default behavior?
You can use Microsoft.AspNet.Mvc.AuthorizeFilter.
using Microsoft.AspNet.Mvc;
using Microsoft.AspNet.Authorization;
services.ConfigureMvc(options =>
{
options.Filters.Add(new AuthorizeFilter(new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build()));
});
If you need custom authorization requirements see this answer for more information.
How is everyone else performing fine grained access control in an MVC app? i.e. a user may be related to multiple objects and have different access requirements to each object. Can this be achieved using asp.net identity claims / roles? or do I have to role out my own?
Is there any design pattern I can follow if I need to roll out my own?
No doubt there are plenty of ways to do this, but asp.net-mvc leads itself to extensibility of the AuthorizeAttribute, eg:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public sealed class ActionPermissionAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
// Override OnAuthorization, not AuthorizeCore as AuthorizeCore will force user login prompt rather than inform the user of the issue.
var controller = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;
var action = filterContext.ActionDescriptor.ActionName;
bool authorised = ... // Check permissions here
if (!authorised)
throw new UnauthorizedAccessException("You are not authorised to perform this action.");
base.OnAuthorization(filterContext);
}
}
this can be applied to the controller (or as base controller) so doesn't need to be on every single action.
The actual check permissions can be as simple or complicated as you want - eg store controller + action + active directory group in a database will allow the permissions to be changed dynamically.
I'm working on a project where users can log in and create as many number of "work projects" as they like, which are tied to their account Id. We're using OWIN and ASP.NET Identity 2.1.
All the MVC controller actions that respond to HTTP POST requests require the WorkProjectId to be passed in as a HTTP header. The logged in user should only ever be able to interact with WorkProjects that are associated with their login. This presents an important security consideration: is it best practice to interrogate what WorkProjectId are associated with the currently logged in user at the time the controller action is invoked, perhaps by using a custom attribute?
E.g.
[EnsureUserIsAllowedToDoAnythingToThisWPID]
public async Task UpdateWorkProjectTitle(ViewModel vm) {
...
}
Because the user can create as many WorkProjects as they see fit, I don't think I can do this with Claims based security. As far as I understand, if WorkProjectIds were somehow stored as Claims, if they were modified it would necessitate logging the user in and out whenever that happened ... which is obviously not acceptable.
So, to achieve what I need, is it "wrong" to store the Ids the logged in user has access to in session state? I've been burned very badly in the past on other projects with session state abuse (read: far too much data being serialised into session state) bringing the web servers to their knees due. I'd prefer to avoid it if there are equally simple approaches.
Thanks
Why not just add/remove claims for current user? On controller side via UserManager.AddClaim by pasting in logged-in-user id and desired Claim object (i.e. id of workProject?). As far as I know, storing user data (i.e. allowed WorkProjectIds) in cookies is preferable. And your custom authorize attribute will check if requested WorkProject is allowed for current user:
[AttributeUsageAttribute(AttributeTargets.Class | AttributeTargets.Method,
Inherited = true, AllowMultiple = true)]
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
private string _url; // path to action, also you can get it from request
private Operations _operation; // user requested action (CRUD? or administer, execute, etc.)
// example of usage as attribute [CustomAuthAttrib("some string", Operations.Create)]
public CustomAuthorizeAttribute(string url, Operations operation)
{
_url = url;
_operation = operation;
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
// any httpContext.Request... operations
return base.AuthorizeCore(httpContext);
}
}
Here is my some raw listing, currently I'm facing somewhat similar problem. And, to access claims here probably you will need some extension methods that came within OWIN/Katana and/or ASP.NET Identity framework