I have a Problem that i cannot solve after researching alot.
I have a Keyloak with Clients(Application) and Roles that secure the Application.
Inside my Application i check with .hasRole()-Method if the Role of the User or other Application matches with the defined Role. Everything works excepted.
The Problem is i want to combine Roles and check them in the Application.
To access my Application the user should have the role 'read' AND 'write'.
In Spring the hasRole()-Method checks only one Role at a Time.
The hasAnyRole()-Method checks if one of the Roles matches.
Is there any Method like say hasAllRoles? Which checks if all the Roles match?
One request is to solve that Problem only with Configuration but the implemented Method in the Application is hasRole() so i except that there is no possible way of solving this with only configuration on Keycloak or Application.properties inside the Application
The easiest solution is probably to find a way to explain to the person who decided "to solve that Problem only with Configuration" that using #PreAuthorize meta-data is a better solution :
controllers code is not "polluted" with access-control: annotation are not inside methods body and are evaluated only if enabled in spring security (which makes no difference with access-control in conf)
access-control rules are close to access-point definition, which makes it much more readable (easy to grasp what is applied to a specific route and HTTP verb)
SpEL is much more powerful than config request matchers. You can even define your own DSL to write expressions like #PreAuthorize("is(#username) or isNice() or onBehalfOf(#username).can('greet')") (this taken from the most advanced of my tutorials).
access-control can decorate any method of any #Component (including #Service and #Repository) and not only to public methods of #Controller decorated with #RequestMapping or one of its declination.
With SpEL, you can do what you need (read and write) plus define rules based on the accessed resource itself (who created it, what it is linked to,...)
To give a try:
add #EnableMethodSecurity to your security conf class
only define with request matchers what is accessible to anonymous and what requires authentication
add #PreAuthorize("hasRole('read') and hasRole('write')") just above your controller method
In addition to what #ch4mp explained, I'd like to offer a few other principles to keep in mind.
First, your question. You can do hasAllRoles in two ways. The first is with AuthorizationManagers.allOf and the second is with SpEL:
http.authorizeHttpRequests((authorize) -> authorize
.requestMatchers("/endpoint").access(
allOf(hasAuthority("read"), hasAuthority("write"))
)
)
and
#PreAuthorize("hasAuthority('read') and hasAuthority('write')")
Read on for some additional recommendations relative to your comment:
The Problem is i want to combine Roles and check them in the Application.
A Bean
A nice way to extract authorization logic into a component is to reference an authorization bean in your expression.
For example, you can do:
#Component("authz")
public final class MyAuthorizationDecider {
public boolean check(MethodSecurityExpressionOperations operations) {
// ... place authorization logic here
}
}
And then you can do:
#PreAuthorize("#authz.check(#root)")
(If I'm not mistaken, you can still use #ch4mp's library with this approach, simply calling the library's DSL from a Java method instead of within a SpEL expression.)
Hierarchies
It's also the case that some permissions imply others. It may be the case for you that message:write implies message:read. In such a case, your expressions can be simplified by codifying this relationship in a RoleHierarchy instance.
At Login Time
At times, it can be helpful to map authorities at login time. For example, the role of USER might translate into message:read and ADMIN into message:read and message:write. It might go the other way as well. If the client granted message:read and message:write, perhaps this translates into a single permission of message:redact.
If you perform this translation at login time, it can allow for fewer computations at request time and a single location to reason about authorities being granted.
For example, instead of doing
#PreAuthorize("hasAuthority('message:read') and hasAuthority('message:write')")
or
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers("/message/redact").access(
allOf(hasAuthority("message:read"), hasAuthority("message:write"))
)
)
you'd do:
#PreAuthorize("hasAuthority('message:redact')")
or
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers("/message/redact").hasAuthority("message:redact")
)
Since you are using Keycloak, in this case, you'd consider a custom JwtGrantedAuthoritiesConverter if you are a Resource Server or GrantedAuthoritiesMapper if you are a Client to map the granted authorities from the Jwt to authorities that map to what you are doing in your app.
Related
To give you a background, we have an application with lots of REST services. Now for these services there are security permission entries. Now we could have an AccessVoter that intercepts all the request to check whether the user has permission to a resource but this is inefficient as we have to load thousands of rows from database and check the incoming request with the pattern saved in database. The pattern that I mentioned is exactly the same as the pattern that the developer put in their REST method, for example:
#PostMapping(value = "/accounts/{id}/advisers/{adviserId}")
Or
#PostMapping(value = "/accounts/{id}/advisers/address/{adrId}")
You can see how complex it can be and going through the records to find a match is expensive while Spring has already done the mapping and it can find the associated method very nicely.
We could create an aspect to check the permissions before the method invocation but at that time is too late as there are validation layers that happens before
I was thinking if we could have an access voter and then ask Spring to give us the method that it's going to invoke so that we can read the annotation, in above example #PostMapping and then find the pattern (say e.g. /accounts/{id}/advisers/{adviserId}) so we can only query that pattern from database, then this will be very efficient and less complex in our code.
The question is whether it's possible to get this information from Spring Boot or not?
Is there any other way than using Spring's AccessVoter to do the job? For example when we configure ResourceServerConfigurationAdaptor we have this code:
http.authorizeRequests()
.antMatchers(oAuth2AuthenticationSettings.getProtectedUrlPattern().split(","))
.authenticated().accessDecisionManager(accessDecisionManager);
As you can see accessDecisionManager is the one that returns AccessVoter but we could also set .authenticated().expressionHandler(...) which I don't know what it is used for. Can it be used for our purposes?
We don't want to use #PreAuthorize as it means we have to go and annotate all of our REST services.
I have a need to provide an optional additional parameter to my implementation of Spring Security's UserDetailsService.
As a simple example, let's say I need to pass a Pet ID for the pet that belongs to the User.
Not all Users have pets, but if they do, my Person class that implements UserDetails has a Pet that needs to be retrieved upon login.
Here's what I tried thus far:
How to pass an additional parameter with spring security login page
The problem with the accepted answer is it feels wrong/leaky for UserDetailsService implementation to couple in awareness of HttpRequest and HttpSession.
Other approaches I've looked at involve custom AuthenticationDetails and AuthenticationDetailsSource, but this feels wrong also, since the UserDetailsService seems like it should be responsible for a single thing (loading User Details), and the Pet would be part of those User Details.
I know I can get one of these approaches to work, but they don't feel clean. Has anyone come up with a good approach on solving this problem? Not looking for anyone to write code for me, just looking for a general approach.
Thank you very much!
I think there maybe a misunderstanding as to the purpose of UserDetails. Looking at the Javadoc UserDetails is about how the user relates to the security apparatus of your application/system. As the method summary shows; getPassword, getUsername, getAuthorities, etc, essentially who is this person (authC) and what can they do (authZ). The loading of a user's pet information would be more of a business concern and should be handled in a separate service, PetService.doesUserHavePet(User) PerService.loadPet(User), not within your implementation of UseDetailsService which should be loading an users info from your organizations directory service and/or user store. I think the reason why you are finding every approach less then clean is you are trying to incorporate two concepts/responsibilities into a single class.
Is this a bug in Spring Security's RunAsManagerImpl, or are my expectations wrong?
My understanding of the (limited) documentation, is that with a RunAsManagerImpl defined in my config if I call doFoo() in the following:
#Secured({"ROLE_FOO", "RUN_AS_BAR"})
public void doFoo() {
doBar();
}
#Secured("ROLE_BAR")
public void doBar() {
// ...
}
then, provided the current Authentication has the role "FOO", doBar() will execute successfully.
But it doesn't, Spring throws an AccessDeniedException. However, changing doBar()'s annotation to:
#Secured("ROLE_RUN_AS_BAR")
works successfully.
Upon examination of the source code, the reason is fairly clear - if it encounters an attribute that starts with "RUN_AS_", it creates:
GrantedAuthority extraAuthority = new SimpleGrantedAuthority(getRolePrefix() + attribute.getAttribute());
where, by default:
private String rolePrefix = "ROLE_";
So the authority that is applied is "ROLE_RUN_AS_BAR", which doesn't seem right at all. Is this a bug that I should raise, or have I misunderstood the intended use of this functionality?
It's the expected behavior, as described in the documentation:
The created GrantedAuthorityImpls will be prefixed with a special
prefix indicating that it is a role (default prefix value is ROLE_),
and then the remainder of the RUN_AS_ keyword. For example, RUN_AS_FOO
will result in the creation of a granted authority of ROLE_RUN_AS_FOO.
The purpose of such basic implementation is not to impersonate a user, but to acquire a "technical role". For example, some part of your code should require a technical role of "database manager". No user has this role but I can be acquired programmatically.
Of course, you can bypass this code by just updating the Authentication in SecurityContextHolder, but having a central implementation point to "upgrade" an Authentication object can be more secure when used by a jvm securitymanager.
However, the RunAsManager is a really simple interface, in order to be easily reimplemented: If the default behavior doesn't match what you need, you only have one method to reimplement.
Alternate titles
How do I get the mapped Method from an HttpServletRequest?
How to apply WebSecurityConfig based on #Secured annotations rather than paths?
Problem
I have a Spring MVC server using #Secured annotations to specify the required roles for each controller method - they do not map easily onto path patterns.
Certain roles are granted via specific authentication methods (e.g. x509, Basic realm A, Basic realm B, Bearer token).
When the caller is not authenticated, the WWW-Authenticate header should not suggest things that do not grant the required roles for the method.
Where I am
I thought the easiest way to do this was to have the HttpSecurity configured with all possible authentication methods and to permitAll(), delegating all the checks to the method security. However, I can only define one AuthenticationEntryPoint for the chain when this fails.
Thus I need to implement an AuthenticationEntryPoint whose behaviour depends on the roles required, but I have been unable to find a way to get that information within the commence method - there appear to be no methods or attributes detailing the mapped Method (from which I could inspect the annotations) or the required roles (in either the request object or the InsufficientAuthenticationException).
For the same reasons, a DelegatingAuthenticationEntryPoint won't work, as I can't get at these things in a RequestMatcher either.
Is there a bean floating around that will let me easily get hold of this information?
Am I even on the right track to solving the problem?
How do I get the mapped Method from an HttpServletRequest
Method method = ((HandlerMethod) ((ApplicationContext) request
.getAttribute(DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE))
.getBean("requestMappingHandlerMapping", HandlerMapping.class)
.getHandler(request)
.getHandler())
.getMethod();
There's also a getMethodAnnotation(Class) on HandlerMethod to skip a step.
From there you can get the details of the #Secured annotations and find out what the required roles are.
Currently my app looks at router parameter and logged in user (Principal.Identity) to authorize access to certain resources (e.g: Add student to your class [identity + class id]). However, If I'm not wrong, breeze js support just one bulk save. It seems to be that I will have to open each and every data and run through the validation/authorization. That is fine,
but what I may lose is nice separation of cross cutting concern out side my business logic (as a message handler) (finding what roles user has on the class) and nice Authroize annotation feature (just say what roles are needed). So do I have to trade off or is there better programming model which Breeze JS might suggest?
Update:
My question is more on how to separate the authorization (find assigned roles in message handler + verify if required roles are present by adding authorize attribute to controller methods) logic from business or data access logic. Without breeze, I will inspect the incoming message and its route parameter to fetch all its roles then in my put/post/delete methods I would annotate with required roles. I cannot use this technique with breeze (its not breeze's limitation, its trade off when you go for bulk save). So wanted to know if there is any programming model or design pattern already used by breeze guys. There is something on breeze's samples which is overriding context and using repository pattern, will follow that for now.
Breeze can have as many 'save' endpoints as you want. For example, a hypothetical server implementation might be
[BreezeController]
public class MyController : ApiController {
[HttpPost]
[Authorize(...)]
public SaveResult SaveCustomersAndOrders(JObject saveBundle) {
// CheckCustomersAndOrders would be a custom method that validates your data
ContextProvider.BeforeSaveEntitiesDelegate = CheckCustomerAndOrders;
return ContextProvider.SaveChanges(saveBundle);
}
[HttpPost]
[Authorize]
public SaveResult SaveSuppliersAndProducts(JObject saveBundle) {
...
}
You would call these endpoints like this
var so = new SaveOptions({ resourceName: "SaveWithFreight2", tag: "freight update" });
myEntityManager.saveChanges(customerAndOrderEntities, {
resourceName: "SaveCustomersAndOrder" }
).then(...)
or
myEntityManager.saveChanges(supplierAndProductEntities, {
resourceName: "SaveSuppliersAndProducts" }
).then(...)
Authorization is mediated via the [Authorize] attribute on each of the [HttpPost] methods. You can read more about the [Authorize] attribute here:
http://sixgun.wordpress.com/2012/02/29/asp-net-web-api-basic-authentication/
The proper way to do this IMHO is to separate the endpoint authorization and the database actions authorization.
First, create an entity that manages the grands per controller/method and role. For each method you have a value allowed - not allowed for the specific role. You create a special attribute (subclass of Authorize) that you apply to your controllers (breeze or plain web api) that reads the data and decides whether the specific endpoint can be called for the user/role. Otherwise it throws the Unauthorized exception.
On the breeze side (client) you extend the default adapter settings with a method that adds the authentication headers from identity that you received at login, something like this :
var origAjaxCtor = breeze.config.getAdapterInstance('ajax');
$.extend(true, origAjaxCtor.defaultSettings, Security.getAuthenticationHeaders());
On the server, add a second entity that manages the authorization for the CRUD operations. You need a table like (EntityName, AllowInsert, AllowUpdate, AllowDelete). Add a BeforeSave event on the Context Manager or on the ORM (EF or something else) that loops all entities and applies the policy specified on the table above.
This way you have a clear separation of the endpoint logic from the backend CRUD logic.
In all cases the authorization logic should first be implemented server side and if needed should be pushed to the clients.
The way breeze is implemented and with the above design you should not need more than 1 save endpoint.
Hope it helps.
However, If I'm not wrong, breeze js support just one bulk save.
That is entirely wrong. You have free reign to create your own save methods. Read the docs, it's all there.