Implementing Spring Security AccessDecisionManager by example - spring-boot

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.

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.

Spring Security - how to provide additional parameter to UserDetailsService

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.

Spring Security Unit Test - MockMvc perform test with custom user

I'm setting up unit tests for my Spring MVC Controllers and I am attempting to leverage the Spring MVC test framework. For each endpoint in my controller, I want to ensure that only users with designated authorities have access. My problem is that I use a custom User implementation and get class cast exceptions when using the mockMvc tooling around this.
For each request, I expect it to look something like this:
mockMvc.perform(MockMvcRequestBuilders.get(path).with(user("user").authorities(authorities)))
.andExpect(status().isOk())
.andExpect(authenticated().withUsername("user"));
I would like to somehow tweak the above statement to specify my custom user principal. See Spring's user method below. My first thought was I would override the class UserRequestPostProcessor and tweak it so that it's using my custom user type instead of the standard Spring Security user, but this class is declared as final and cannot be subclassed. Is there any support for overriding the default behavior and using a custom user type??
public static UserRequestPostProcessor user(String username) {
return new UserRequestPostProcessor(username);
}
From what I have seen, I am a candidate for annotating my tests with #WithSecurityContext so that I can set my custom user principal in the Authentication. My concern here is that I would be limited to testing one user for each method and this doesn't scale for what I am trying to do.
How can I test requests made by multiple custom users?
I learned of two ways to do this:
I could create a class that implements UserDetails and extends my
custom user principal class. I could then pass this as a parameter
to the user method.
I can scratch the user method altogether, and pass in my
Authentication already set with my custom user principal.
I went with the latter.
protected void performTest(HttpMethod method, String path, Object[] pathVariables, UserRoleEnum role,
boolean expectAllowed) throws Exception {
mockMvc.perform(buildRequest(method, path, pathVariables).with(authentication(createAuthentication(role))))
.andExpect(expectAllowed ? status().isNotFound() : status().isForbidden())
.andExpect(authenticated().withUsername("user"));
}
note - the buildRequest and createAuthentication methods are helper methods that I created, whereas all the other methods are provided by Spring. The helper methods return MockHttpServletRequestBuilder and Authentication respectively.

Does Spring Security's RunAsManagerImpl work?

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.

AuthenticationEntryPoint dependent on required roles

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.

Resources