Access profiles using attributes - c#

On my website, I have different pages with different levels of access permissions. Usually, I use something along:
if(!user.IsAdministrator)
return RedirectToAction("AccessDenied", "Security");
But I realized this was tieing me to the security levels that I'd prebuilt; what I in fact want is a fully customizable access scheme.
One solution I thought about was to set some attributes in the actions I wanted to give restricted access, then retrieve where were they placed. I'm somewhat unexperienced with Attributes (custom ones, at least), and although I know how to list all the actions being marked, I'm still struggling to conceive a way to check for the right access permission and denying the access.
Any light on the subject? I'm also interested to know if there are any standard practices for dealing with this issue.

Typically in ASP.NET MVC Authorize attribute is what can be used for such purpose.
You can derive from it and override AuthorizeCore method to satisfy your needs. Then you can mark MVC actions or whole MVC controllers with this attribute. Or what is even better you can configure it as a global filter, so it will automatically be automatically enabled for all controllers. Then actions you don't want to be secured can be marked with AllowAnonymous attribute: http://blogs.msdn.com/b/rickandy/archive/2012/03/23/securing-your-asp-net-mvc-4-app-and-the-new-allowanonymous-attribute.aspx
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
if (filters != null)
{
filters.Add(new CustomAuthorizeAttribute());
}
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public sealed class CustomAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
//your code here
}
Here is SO post that already discusses the attribute
ASP.NET MVC 4 Custom Authorize Attribute with Permission Codes (without roles)
You can find more articles on this topic on the internet.

Related

How can I secure Controllers in a Razor Class Library

I have developed some Admin functionality (EF logic, Controller, and Razor UI for Audit Logs actually) that I've packaged into a Razor Class Library (RCL) and created a NuGet package. I want this functionality available to users of the package, but I want to allow them to control the access security. I would usually decorate the Controller with an Authorize Attribute, something like:
[Area("MyAuditLogPackage")]
[Authorize(Roles = "Admin")]
public class AuditLogController : Controller
...
But I don't want to presume the client's security policy and Audit Logs are sensitive data.
They could derive their own controller from mine, but the original Route would still be in their default Area Mappings.
How can I give full control of this over to the package clients?
Rather than authorizing by Role, you could require that people using your code create custom security policies that are defined on startup. This would result in something like
[Area("MyAuditLogPackage")]
[Authorize(Policy= "AuditControllerPolicy")]
public class AuditLogController : Controller
...
The policy approach is extremely flexible so the policy might be a requirement that a user be in role Admin. It could also require other claims be present in the token, including custom claims. Check out Policy-based Authorization in Asp.Net Core.
This approach gives a user of your NuGet package complete flexibility, but many might find it burdensome. You might want to canvas a few to get their opinion first.
You can create a extension method to dynamically secure your Razor Class Library routes.
Definition:
internal static class IEndpointConventionBuilderExtensions
{
public static TBuilder AddAuthorization<TBuilder>(this TBuilder builder, AuthorizeAttribute? metadata = null)
where TBuilder : IEndpointConventionBuilder
{
if(metadata != null)
{
builder.WithMetadata(metadata);
}
return builder;
}
}
Usage:
app.MapControllerRoute(
name: "MasterData",
pattern: "{culture}/{area:exists}/{controller=Log}/{action=Index}/{dictionaryName?}/")
.AddAuthorization(new AuthorizeAttribute("MasterData"));

Authorization in ASP.net5

I am trying to see if there is something "out of the box" in ASP.net5 for authorization for my application needs. I am using a group/permission based approach for authorization. Using Identity3 I am using Role as Group and then I have created permissions from this. Each permission has a resource that it links to and 1 or more values, like:
Resource = Page, Permissions = Add, Update, View, Delete
Another complication is that the groups have dynamic names, and dynamic permissions!!
I have started to read about authorization in ASP.net5 and it seems that I have found something called Policies, which sound good. It seems to force you to use Claims, which is possible if I use a ClaimsTransformer to get all my permissions and add them as claims from the Db. But am I right in thinking that I would have to create a policy for each Permission, on each resource? That seems like a lot of setup.
Is there anything that I do not know about is already built in ASP.net5 that I could use? Like an attribute like this
[Authorize("Page", "Delete")]
Which I could add to the PageController Delete method.
If I have to use some sort of service and DI that into the controller to implement this, then that would be fine as well.
There is a ClaimsPrincipalPermissionAttribute that can fit to your requirements.
Or you can implement your own AuthorizeAttribute.
I use AspNet.Security.OpenIdConnect.Server for authorization. But you can also have a look at OpenIddict
In any case you can add the Authorize attribute to any method you want like this
[Authorize(Roles = "Administrator,SimpleUser,AnOtherRole")]
public void MyMethod() {}
Resource based authorization might fulfill your needs, but I am a little confused with the page being the resource, rather than what the page acts upon.
Taking your Page/Delete combination, I would imagine that rather than the resource being Page, your Page Delete action takes a parameter, indicating the page that is to be deleted? (If this is not the case then this approach isn't going to work of course)
In this case you'd do something like
[Authorize]
public class PageController : Controller
{
IAuthorizationService _authorizationService;
public PageController(IAuthorizationService authorizationService)
{
_authorizationService = authorizationService;
}
public Delete(int pageId)
{
var page = pageRepo.GetPage(pageId);
if (await authorizationService.AuthorizeAsync(User, page, Operations.Delete))
{
return View(page);
}
else
{
return new ChallengeResult();
}
}
}
In order to enable this you're write a handler based on page and an Operations requirement (or any old requirement, but a parameterized operations requirement means you can write a single handler and branch accordingly).
We tried very hard to move away from putting data in the attribute, and move it into requirements, because data in attributes is, to be frank, a maintenance nightmare.
One other thing to note; as handlers are resolved through DI you could inject your user to permissions resolver into the handler, which would avoid using claims transformation.
ASP.NET provides authentication mechanism out of the box which is easy to use, example:
public class HomeController : Controller
{
[Authorize]
public ActionResult Index()
{
ViewBag.Message = "This can be viewed only by authenticated users only";
return View();
}
[Authorize(Roles="admin")]
public ActionResult AdminIndex()
{
ViewBag.Message = "This can be viewed only by users in Admin role only";
return View();
}
}
Check this tutorial
Or if you want more sophisticated mechanism you can implement your own memberhsip provider based on the ASP.NET Membership Provider

Blocking anonymous access by default in ASP .NET 5

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 to perform fine grained access control in an asp.net MVC application.

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.

Validating user rights when invoking controller method in ASP.NET MVC

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

Categories

Resources