ASP.NET 5 Handling Permissions with Authorize Attribute - asp.net

I am struggling with the new ASP.NET 5 Authorization:
I would like to define a permission to each Action, and configure in the Database, what user / group does have which permission.
Unfortunately, I don't see a way to handle this with the new Authorize Attribute / Policies.
(Side information: I am planning to have about 100 permissions, 1000 users, 50 groups; in the past, I solved this with a custom Authorize Attribute, and the "HandleUnauthorizedRequest" method)

Abstract your permissions out to an IPermissionsProvider, then implement it, add it to DI and inject it into the handler for your requirements.
That way inside Handle you can call out to your database with the user, and the action details from the Context parameter (you will need to cast it to Microsoft.AspNet.Mvc.Filters.AuthorizationContext to get at the request and routing details)

Related

Authorize by controller ,action and userRole in configure file ,not set the Authorize attribute on every action method ;.net core mvc

my means how to write one authorization filter like mvc4/mvc4,
like this scenario:
I have many controller and many actions ,at once I have two roles; some time ,we need add new user role, and need to change those actions to three roles;
If the filter can authorize by the configure ,I only modify the configure file;
otherwise, I need change the Authorize attribute on every actions;
I think it's terrible.
I have read the document about on this article here . But can't find solution. So who can help me to resolve it.
thank !

Authorization by Role/Group in ASP.NET using Auth0

Thanks in advance for your help in this matter!
I was hoping someone could help me figure out how to authorize API access by Group assigned in the Auth0 Authorization extension.
I currently am using the [Authorize] attribute in the web api perfectly - it allows an api call if they have signed in successfully and blocks it if not.
However, if I try [Authorize(Roles = "myGroupName")] authorization fails. Same occurs if I add it to the users app_metadata manually in the Users dashboard on the Auth0 website instead of assigning through the extension.
My project is set up by following the Angular Quick Start and Asp.Net Quick Start. My webapiconfig where I validate the token server side is:
class WebApiConfig
{
public static void Register(HttpConfiguration configuration)
{
var clientID = WebConfigurationManager.AppSettings["auth0:ClientId"];
var clientSecret = WebConfigurationManager.AppSettings["auth0:ClientSecret"];
configuration.MessageHandlers.Add(new JsonWebTokenValidationHandler()
{
Audience = clientID,
SymmetricKey = clientSecret
});
configuration.Routes.MapHttpRoute("API Default", "api/{controller}/{id}",
new { id = RouteParameter.Optional });
}
}
The Auth0 Authorization extension currently supports authorization decisions through the concept of groups. You can create a group, assign users to that group and that configure an application to only be accessible to user within a specific group. All of this would be handled automatically and any user outside of the application expected groups would be denied complete access.
Your use case is a bit different, but valid nonetheless. You want the groups configured with the extension to be sent along the generated token so that the application itself makes authorization decisions based on those values.
In order for the groups configured within the extension to be sent along in the token, the first thing you need to do is request them. For this, you need to include the groups scope when performing the authentication requests.
Add the user's group membership to the outgoing token (which can be requested via the OpenID groups scope);
(emphasis is mine, source: Authorization Extension Docs, section Rule Behavior)
If you request a token using that scope and then decode it in jwt.io, you would get something similar to this (the actual groups would vary by user):
{
"groups": [
"GROUP-1",
"GROUP-2"
],
"iss": "https://[tenant].auth0.com/"
}
Now, for the validation of this information on the ASP .NET API side. Assuming the sample you're using is this one (ASP.NET Web API), the group information contained within the token would be mapped to the following claims:
Type: groups | Value: GROUP-1
Type: groups | Value: GROUP-2
This happens because of the logic that exists in the JsonWebToken class which handles arrays coming from the JWT payload by creating per-value claim that share the same type.
The final part is making sure the AuthorizeAttribute checks these claims of type groups instead of trying to lookup role claims. You should be able to accomplish this, by changing the RoleClaimType constant in the JsonWebToken class to have the value "groups" instead of "http://schemas.microsoft.com/ws/2008/06/identity/claims/role".
Like you certrainly know, the Authorize attribute works using what is in the principal: something that inherits IPrincipal.
In web api, it is even more specific; it is something that inherits ClaimsPrincipal (this implements himself IPrincipal).
As you certainly know already, a claim is like a key-value pair.
The ClaimsPrincipal contains a serie of key-value pairs that are directly taken from the authentication token. This authentication token is issued by the authentication server most of time as JWT (Json Web Token). Most of time as well, the authentication server is using OAuth, like is your case.
If the user group, that you expect to be the role in your application doesn't work by using the out-of-the-box Authorize attribute, it's because it is not mapped correctly: Auhtorize checks the claim with claim type: http://schemas.microsoft.com/ws/2008/06/identity/claims/role (the "claim type" is the "key" of the key-value pair). That means that if you want your Authorize to work, this claim must be valued with the group.
You can do several things to have a clean authorization in your application.
Make a custom Authorize attribute. This Authorize attribute would check the role using a different claim type. The claim type that refers to the user group depends on your authentication server. If you don't find what claim type is used for groups in the doc of your authentication server, run your application in debug, and check every claim that is contained in the property User of your controller. You will certainly find what the claim type you are interested in.
Change the setup of your authorization server by redefining the mapping between user information and claims of the token that is produced (in your case, map groups of the user to the claim that has the type http://schemas.microsoft.com/ws/2008/06/identity/claims/role). Generally, this can be setup per client application or even globally. For example this is the way that must be done if you use an ADFS authentication, AzureAD or WSO2 authentication server (http://wso2.com/products/identity-server/)
Add an owin middleware to modify the current principal. It will change the current principal by copying the value of the claim that contains groups into the claim type http://schemas.microsoft.com/ws/2008/06/identity/claims/role. This middleware must be inserted in the flow after the authentication middleware
I have no rights to comment so I'm going to inquire from here. Why are you doing this
[Authorize(Roles = "myGroupName")]
as far as I remember when I was implementing group based authorization I was still typing
[Authorize(Roles = "myRoleName")]
Not other way around.

Handle cookie in Spring MVC

I have an Spring MVC application which using the classic three layer: controller service and dao.
And the related models in the application contains User Department Project.
One user will belong to a department, and there may be a lot of projects belong to a certain department, and the departments are organized as a tree like structure, for example:
dep1
dep1-1
dep1-1-1
dep1-1-2
...
...
...
Now I have a controller to list the projects:
class ProjectController{
private ProjectService projectService;
#RequestMapping("/list")
public String list(#RequestParameter("depId") String depId){
projectService.list(depId);
return "list";
}
}
ProjectServiceImpl implements ProjectService{
ProjectDao projectDao;
public List<Department> list(String depId){
}
}
It seems that this is rather simple, however we have two problems:
1 The result filter.
According to the configuration, the department of the current user maybe(or not) be under consideration during the query operation, for example, when the parameter depId is dep1-1-1, and the current user belongs to dep1-1-2, then we should return null.
As said, this feature maybe closed at all at some situation.
2 The authentication.
The user authentication and management is served in another application which will deployed at the same domain with my application, you can think they are two different folder inside the /tomcat/webapps. We use cookie to share the user information:save a token for a user.
Which means for every request, I will have to get the token of the current user(if they have login) from the cookie, and then call the service provided by the other application to get the information like department and etc.
So where to do the department check, in controller or service? I am not sure if inject the HttpRequest to service is a good idea or not.
Also Since there are too many controllers and services related to this kind of operation, I want to avoid the duplicate codes everywhere.
Is there any better choices?
It may be overkill, but what you are asking for make me think to spring-security :
authentication : spring security could do it directly, but you can also implement a custom PreauthenticatedAuthenticationFilter that would use the cookie to get the user info and populates a Spring Security Authentication token. This part is easy and highly configurable.
result filter : extract from the spring security reference manual :
Spring Security supports filtering of collections and arrays and this can now be achieved using expressions. This is most commonly performed on the return value of a method. For example:
#PreAuthorize("hasRole('ROLE_USER')")
#PostFilter("hasPermission(filterObject, 'read') or hasPermission(filterObject, 'admin')")
public List<Contact> getAll();
When using the #PostFilter annotation, Spring Security iterates through the returned collection and removes any elements for which the supplied expression is false
All this is implemented through Spring AOP so it is easy to add those annotations on service methods.
Spring Security integrates nicely in a Spring MVC application. The only problem is that a full configuration including domain object security is not really trivial ...

Does Role Provider cache per request?

My MVC application makes use of a User's Role in multiple places during individual page requests. My question is whether the default SqlRoleProvider caches the current User's Roles for the lifetime of a page-request?
For example, I make use of Roles in attributes on Controller methods:
[Authorize(Roles = "Admin")]
and custom code
if (user.IsInRole(MembershipRole.Admin))
{
// Do something
}
else if (user.IsInRole(MembershipRole.Printer))
{
// Do something else
}
If the Role Provider does not cache roles, is the best solution to write a custom Role Provider that inherits from the default one, and override the methods to get the Roles once and cache them for the Request duration? Can this be done in a way that both the Authorize attribute and my own code will make use of the cached roles?
(In case you were wondering, I don't want to use the cacheRolesInCookie web.config option to cache the roles in cookies).
Thanks in advance for any suggestions.
[Edit to include details triggered from Joe's answer]
I decompiled System.Web.Mvc.AuthorizeAttribute and the AuthorizeCore method calls the following method for each role to be checked:
httpContext.User.IsInRole
Then peering into System.Web.Security.RolePrincipal (which is what "User" is above) both the methods below do indeed use a cached copy of the User's roles (or populates the cache if empty):
public string[] GetRoles()
public bool IsInRole(string role)
The cache is stored as a field on User, so its lifetime is for the duration of the request.
The methods find the roles using:
Roles.Providers[this._ProviderName].GetRolesForUser(this.Identity.Name)
so will use whatever role provider you have chosen for the application (default or custom).
If you use a RoleProvider in an ASP.NET or ASP.NET MVC application, then HttpContext.User will reference a RolePrincipal which does cache roles for the lifetime of the request.
However, in a WCF service that uses ASP.NET roles:
<behavior ...>
<serviceAuthorization principalPermissionMode ="UseAspNetRoles"
roleProviderName ="MyRoleProvider" />
</behavior>
this is not true: instead HttpContext.User will reference the internal class System.ServiceModel.Security.RoleProviderPrincipal, which does not cache roles: instead it always calls RoleProvider.IsUserInRole.
The out-of-the-box RoleProviders don't do any caching, so this can result in repeated connections to the underlying data store. It seems like a deficiency to me: it would have been easy to cache the roles on first access.
is the best solution to write a custom Role Provider that inherits from the default one, and override the methods to get the Roles once and cache them for the Request duration?
Not necessary for ASP.NET or ASP.NET MVC, but could be envisaged for WCF. Caching for the Request duration will presumably use HttpContext.Items, so will introduce a dependency on the existence of HttpContext, but that's not necessarily a problem except for making unit testing harder.
Can this be done in a way that both the Authorize attribute and my own code will make use of the cached roles?
If you configure your custom RoleProvider in web.config there's nothing more you need to do so that the Authorize attribute will use it.

ASP.NET Webforms User Authorization with Routing

I have a route
routes.MapPageRoute("clientOrder", "Contract/{contractId}/Orders",
"~/ContractOrders.aspx");
The idea is to authorize user to allow access to a certain set of contracts.
For instance user1 has access to pages Contract/001/Orders and Contract/002/Orders
user2 has access only to Contract/003/Orders, etc.
I'm using Forms Authentication and trying restrict access with
CheckUrlAccessForPrinсipal but it checks only physical access to the page not logical.
I tried to check access in Global.asax in Application_AuthorizeRequest but
Request.RequestContext.RouteData there is allways empty so I don't know the requested contractId. I can parse it manually from HttpRequest object. But it is a very dummy and unraliable solution.
Please advice
I believe that the only way is to add some code to check the contractId at the ContractOrders.aspx page level and if the Id doesn't pass the autorization, you manually redirect somewhere to indicate that the access is not granted.
The built-in mechanism always works at the physical level with route maps, so no matter how your route looks like, the engine always checks the access to the resource the route is mapped to, not the route itself.

Resources