I am trying to create various services, such as:
UserService
UserPermissionService
AddressBookService
Which would access dao's such as:
UserDao
UserPermissionDao
AddressBookDao
CompanyDao
These will use Spring-Hibernate stack and be packaged in a backend jar for multiple webapps. I want the service functionality to be available depending on the permission of the calling user object. Also, Caller (user) object will have permissions of the calling user.
Query: Should I pass Caller to each Service method call and then check its permission? Or is there a better way using 'Spring/AOP' and/or 'Factory Pattern' where the Caller object can be available to the Service methods.
One of the patterns for this case is to store security token in a ThreadLocal and to require corresponding privilege from that token first thing in a service method.
Related
I have an application where single user can work in contexts of multiple companies. We call such a connection (user<->company) a permit. Every one of this permits can have different sets of permissions/roles. We want user to login just once and then he can simply change permits within application without need to enter password again.
Till now we had only one application and kept this whole permission model in our own DB. Unfortunately now we have to support second application which should inherit those permits. I was wondering wether is possible to move that model to keycloak so we don't have to replicate it to every single db and keep it in sync manually.
I have searched keycloak documentation regarding this topic but have found no information att all, which seems quite odd, because I don't think we are the first one working with multiple context application.
So now I'm asking is it possible to configure our model in keycloak and if so, how to do it? Eventually are there different options? I guess that I can provided that model as a claim with json structure but that doesn't feel right to me. I was thinking about custom IDP which could provide such claims based on DB so there no spelling errors and less repetition but I feel there should be a better way.
You could try to write your own Keycloak provider (SPI). There is a built in mechanism that allows you to expose REST endpoint on the Keycloak: https://github.com/keycloak/keycloak/tree/master/examples/providers/domain-extension
That REST could be called with authorized context only for example by passing Access-Token (Authorization header with Bearer value). On the provider level (through implementation of: org.keycloak.services.resource.RealmResourceProviderFactory and org.keycloak.services.resource.RealmResourceProvider) you have access to user's Keycloak session and object UserModel like in the following code:
AuthenticationManager.AuthResult authResult = new AppAuthManager().authenticateBearerToken(keycloakSession, keycloakSession.getContext().getRealm());
UserModel userModel = authResult.getUser();
UserModel class has methods for getting and setting attributes, so some information that indicates the current permit/company ID can be stored there. You can use REST methods exposed on the Keycloak to modify the model within the 'session' (represented by Access-Token).
The Github example shows also how to use another Keycloak provider (ex. built-in JPA provider) from you custom provider's level, so using that approach you could try to connect to the database with your permits/company informations. Of course the datasource representing you database should also be registered as Keycloak datasource.
I have a REST service implemented using Spring MVC (RestControllers) with token based security (using Spring Security). How can i filter resources depending on user identity? Let's say user has some reports. How can I let authorized user by performing a call to /reports to see only his reports?
Obviously i can make userId to be a request parameter or path variable, but something tells me that this is a bad practice.
I assume i can achieve that using Spring Security features, but how exactly could i do that and, more important, where is the most appropriate place to apply such filtering? Should controllers perform calls to services passing user identity or should it be somehow retrieved at repositories level (I use Spring Data JPA)?
Thanks in advance
You have Authentication object whenever a user is successfully logged in.
It contains Object principal Object credentials and Set authorities.
All you need to do is override UserDetailsService to add new parameters for your authenticated user. Add your userId in authentication as shown in blog
Now when you do
SecurityContextHolder.getContext().getAuthentication().getPrincipal()
this will return you the User object of the spring security.
You can get the user id from here and use this in controller to do necessary actions.
I have implemented a REST application with some complicated authorization requirements.
Here's a summary
My customers purchase a proxy device called Collector that enables their home automation control to be centralized. My customers also purchase multiple home automation devices (let's call them HADevices) that report their metrics through the collector to my REST application.
An admin(who is my customer service rep), with role ROLE_ADMIN, should be able to look at any data from any Collector or HADevice. A customer, with role ROLE_USER role, should only be able to view data about the Collector or an HADevice that s/he owns.
The Collector, with role ROLE_COLLECTOR is the only role authorized to insert data i.e. create or update a resource in my REST service. Let's call this url /deviceMetrics (POST). A Collector can insert metrics for any HADevice associated with the customer. HADevices have no role and do not interact with my REST application. A Collector can only insert records to HADevices that have the same customer as the Collector.
I am using spring security 4.0 for authentication and #Secured annotation for authorization. However, I find that my code is cluttered with repetitive permission validations which take up a majority of my logic. The basic insertions and retrievals are pretty straightforward.
I want to use a PermissionEvaluator to centralize Access Control. I have to secure the following urls
GET /collectors/{id}/deviceMetrics - I retrieve the user from the
Principal and verify that the Collector with id={id} in my spring
data repository belongs to the Principal and if not I send a 403
GET/hadevices/{id}/deviceMetrics - I retrieve the user from the
Principal and verify that the HADevice with id={id} in my spring data
repository belongs to the Principal and if not I send a 403.
POST /collectors/{id}/deviceMetrics - I retrieve the Collector uniqueId
from the Principal and make sure that the Collector's id matches the
{id} in the URL
POST /hadevice/{id}/deviceMetrics - I retrieve the
Collector uniqueId from the Principal and the associated Customer. I
also pull the Customer associated with HADevice with id={id} and
compare the two. If they are unequal, I send a 403.
My application is littered with such complex authorization requirements for each REST resource, and I want to use a custom org.springframework.security.access.PermissionEvaluator, specifically by implementing the following method.
boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission)
I'm planning to use a combination of targetType and request.getUrl() to get a specialized Evaluator for each url and resource.
Is there a better way to do this?
Your question is quite broad, but I think you can get away with quite simple logic for most cases.
GET /collectors/{id}/deviceMetrics
Given that you have a DeviceMetrics class with suitable properties, you can annotate your data repository with something like:
#PostAuthorize("hasRole('ROLE_ADMIN') or (hasRole('ROLE_USER') and returnObject.collector.owner = authentication.name)")
public DeviceMetrics getDeviceMetrics(long deviceId);
(This assumes that DeviceMetrics class has a property collector which has a property owner which is the username.)
That doesn't need a PermissionEvaluator at all. Maybe you need one for more complex cases:
POST /collectors/{id}/deviceMetrics
#PreAuthorize("hasRole('ROLE_COLLECTOR') and hasPermission(#collectorId, 'com.example.Collector', 'WRITE')")
public void saveDeviceMetrics(long collectorId, DeviceMetrics deviceMetrics);
You only need one PermissionEvaluator since you get all the information you need as method arguments.
For those who are wondering what my solution looks like, I borrowed from this example.
It's old and it's based on xml configuration which I am not particularly fond of. But the idea is to create a Map and initialize the custom PermissionValidator and to store the authorization logic in the Permission interface implementations.
The biggest pain point was injecting an autowired HashMap of tuples, but that's an implementation detail that reasonably experienced spring users can figure out.
I understand what #Secured DOES but not really sure HOW it does it.
Can someone explain how #Secured grabs the roles? Is it getting it from the authorities-by-username-query? Can I put any String in the params as long as it's in the database?
User's roles are stored in SecurityContext, or to be more specific in Authentication object that is stored in the SecurityContext. When you authenticate, the authentication information is loaded and stored in the security context. The roles can originate from database, depending on your configuration. In your case they are loaded using authorities-by-username-query query.
When the security interceptor processes the authorization (for instance method-level authorization using #Secured annotation) it determines whether the user should be able to access it based on the Authentication stored in the context.
To better understand what happens under the hood, you should look at the Spring Security Filter chain and Architecture section of the reference guide.
SpringSecurity provides and awesome AOP way of securing methods in
Java Application by using #Secured. Spring logically ORs the roles
listed in #Secured annotation. The collection of GrantedAuthorities is obtained from SecurityContextHolder.getContext().getAuthentication().getAuthorities()
AbstractSecurityInterceptor is the abstract class that implements
security interception for secure objects.
Obtain the Authentication object from the SecurityContextHolder.
Determine if the request relates to a secured or public invocation
by looking up the secure object request against the SecurityMetadataSource.
For an invocation that is secured (there is a list of
ConfigAttributes for the secure object invocation):
If either the Authentication.isAuthenticated() returns false, or the alwaysReauthenticate is true, authenticate the request against the configured AuthenticationManager. When authenticated, replace the Authentication object on the SecurityContextHolder with the returned value.
Authorize the request against the configured AccessDecisionManager.
Perform any run-as replacement via the configured RunAsManager.
Pass control back to the concrete subclass, which will actually proceed with executing the object. A InterceptorStatusToken is returned so that after the subclass has finished proceeding with execution of the object, its finally clause can ensure the AbstractSecurityInterceptor is re-called and tidies up correctly using finallyInvocation(InterceptorStatusToken).
The concrete subclass will re-call the AbstractSecurityInterceptor via the afterInvocation (InterceptorStatusToken, Object) method.
If the RunAsManager replaced the Authentication object, return the SecurityContextHolder to the object that existed after the call to AuthenticationManager.
If an AfterInvocationManager is defined, invoke the invocation manager and allow it to replace the object due to be returned to the caller.
Look at the source code for more understanding.
AccessDecisionManager is the interface which is implemented as AffirmativeBased, ConsensusBased or UnanimousBased orchestrates the voters and asks each in turn whether the requesting user should be let through the #Secured annotation or denied.
What are the advantages to get principal as a parameter Principal principal in spring controller and then pass it to service layer over getting principal in the service layer immediately though SecurityContextHolder.getContext().getAuthentication().getPrincipal() ?
What is the best approach to get principal details in service layer without checking getAuthentication() and getPrincipal() objects for null everywhere (something like a custom wrapper)?
Your service API will be more easy to use. You will see dependency on principal directly, so you wan't call some service method by mistake in environment where principal does not exist.
In general less dependencies on SpringSecurity code means less problems in a case of migration to new Spring Security version.
You will be able to reuse your service layer in environment where Spring Security does not exist.
Prepare some wrapper class (for example AuthenticationService). Add getPrincipal() method to it. Implement your checks. Inject AuthenticationService everywhere insted of direct calls to SecurityContextHolder.