Spring Security - how to provide additional parameter to UserDetailsService - spring

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.

Related

Keycloak Spring Implementation combining Roles

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.

Implementing Spring Security AccessDecisionManager by example

Please note: someone seems to be serially DVing my questions without explanation. This question is on topic, is not a duplicate, shows research and provides an SSCCE. If you wish to DV or CV it, that's fine, but please provide a comment as to why so I can have a chance to address your concerns...
Spring Boot 2.3.x and Spring Security here.
I have some pretty complicated authorization logic, and so I believe I need to write my own AccessDecisionManager impl and wire it into my WebSecurityConfigurerAdapter impl (if that's wrong or misunderstood in any way, please correct me!).
So then, to implement your own AccessDecisionManager you need to implement 3 methods, one of which is:
public class MyCustomAccessDecisionManager implements AccessDecisionManager {
#Override
public void decide(
Authentication authentication,
Object object,
Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
// TODO
}
}
I have scoured the Google Gods high and low, and for the life of me I cannot find a meaningful, real world example of what the Object object and Collection<ConfigAttribute> configAttributes arguments are, what they are used for, how they are intended on being used, and what some real world (concrete) examples of them will be at runtime.
The Authentication argument is obvious: it is my auth token and will contain the principal, possibly their credential, and a list of GrantedAuthorities (permissions) associated with the principal.
But the other two arguments (object and configAttributes ) are absolute mysteries to me.
Does anybody know what these arguments are, what some real world use cases of them are, and how they are intended to be used?
As JavaDoc for AccessDecisionManager says:
object – the secured object being called
Usually, it's an instance of the MethodInvocation interface and it represents the method for which call security decision should be performed.
configAttributes - the configuration attributes associated with the secured object being invoked
It's a collection of metadata attributes related to the security object (Method). For example, it can contain information about annotations related to this method, such as #PermitAll, #PreAuthorize, #PostFilter, etc.

When To Use #SessionAttribute Over #ModelAttribute

I'm having a hard time figuring out a specific time in which one would use #SessionAttribute over #ModelAttribute.
This question arose because after making a web application I realised that I have got a lot of methods that I passed in Principal principal to. In these methods, I use principal.getName() to get the username of the logged-in user and then retrieve the relevant data from the database using that username. In short, a lot of my methods needed access to the current user data and I resolved this in what I believe to be an inefficient manner.
To rectify this I was going to create a model attribute in a class annotated with #ControllerAdvice, in which I get the principal and get the user data from the database and add it to the model.
E.g model.addAttribute("currentUser", currentUser);
so that in the parameter list of these methods I can have (#ModelAttribute("currentUser") UserAccount currentUser)
saving unnecessary work by getting the principal and then proceeding to get the user from the database.
While I don't know a whole lot about #SessionAttribute, I feel like this sort of data(UserAccount currentUser) is more relevant to the session as opposed to the model. Am I Wrong?
I also heard that #SessionAttribute doesn't make its data available across multiple controllers which in this case I need. Hence why I'm using #ControllerAdvice.
My questions are as follows:
What is the best practice for implementing the above where I need to
repeatedly access the current users data. Maybe I can further increase efficiency by adding a current user bean on login and then use #Autowired so that I wouldn't even need to have currentUser in the parameter list. But I don't know if that's even possible. Is it?
Is it true that the method annotated with #ModelAttribute is called
prior to every #RequestMapping, #GetMapping, #PostMapping etc. call?
and that an object specific to #SessionAttribute remains in the
model for the duration of the session?
Also In what situation should I user #SessionAttribute over
#ModelAttribute?
The #SessionAttrributes annotation is for the use-case where you need to have a model attribute that you need to access over multiple screens. Like doing a checkout for a shopping cart, you would store the Order in the session, screen 1, confirm, screen 2 payment details, screen 3, delivery details, screen 4 OK. After screen 4 you would then call SessionStatus.setComplete() and it will clean that attribute.
That is the use case for #SessionAttributes and should be used in conjunction with #ModelAttribute. It is not intended to be used to store a, for instance, the user in the session for the duration of the HttpSession.
The #SessionAttribute (a different annotation!) is to retrieve an attribute from the HttpSession that was placed there earlier. In your case after authentication, you would place the User in the HttpSession with HttpSession.setAttribute("currentUser", user);. In a controller method, you could use #SessionAttribute("currentUser") User user to retrieve and use it. No need for an #ControllerAdvice or model attribute anymore.
However I would strongly to ditch your custom security implementation and use something like Spring Security instead. That way all that, and more, is already provided out of the box. In a controller method you can then use the #AuthenticationPrincipal annotation to retrieve the current user.

How to find the REST method that matches a request URI in Spring Boot within an interceptor?

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.

Spring Data REST: Infer owner from authenticated request

I have an #Entity that has a field owner:
#Entity
#EntityListeners({TalkListener.class})
public class Talk {
private #ManyToOne(targetEntity = User.class, optional = false) User owner;
// ...
}
Note: I have a listener in place that implementes #PrePersist. From what I have read it is discouraged to lookup request parameters from there (maybe it is also impossible, I didn't go on researching in this direction). There is the section about events in the docs, but it also seems purely entity-related and not taking the context of the request into account.
I would like to infer the owner from the authenticated user that POSTs the request. What is the easiest way to do so? If it is necessary to override the POST a snippet or linked example would be much appreciated.
Update 1:
#NeilMcGuigan suggested using Spring Data JPA's auditing features, which include a #CreatedBy annotation that would clearly solve the original question (and I would accept it as an answer).
Update 2:
A good tutorial about auditing with Spring Data JPA as well as an alternative of getting the principal within entity lifecycle events can be found here.
For educational purpose, what would be another way if I needed to access some value from the request to populate a value in my entity (say my current use case wasn't #CreatedBy but something different)?

Resources