How do roles and rights translate to Spring Security? - spring

In my applications I usually create three tables for access management. Roles, Rights and an association table that maps between Roles and Rights.
I am trying to translate this approach to Spring security and after reading [this article][1] I thought I was on the right track. I created a custom AuthenticationProvider and implemented the authenticate() method like so:
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
UserProfile profile = userProfileService.findByEmail(authentication.getPrincipal().toString());
if(profile == null){
throw new UsernameNotFoundException(String.format("Invalid credentials", authentication.getPrincipal()));
}
String suppliedPasswordHash = DigestUtils.shaHex(authentication.getCredentials().toString());
if(!profile.getPasswordHash().equals(suppliedPasswordHash)){
throw new BadCredentialsException("Invalid credentials");
}
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(profile, null, profile.getAuthorities());
return token;
}
The profile.getAuthorities() method creates a list of Rights (rights are wrapped in my own implementation of GrantedAuthority). So, the UsernamePasswordAuthenticationToken object is created with this list. This is the UserProfile.getGrantedAuthorities() method that takes care of this:
public Collection<? extends GrantedAuthority> getAuthorities() {
Set<ProduxAuthority> authorities = new HashSet<ProduxAuthority>();
for (Role role : roles) {
for (Right right : role.getRights()) {
ProduxAuthority produxAuthority = new ProduxAuthority(right.getName());
authorities.add(produxAuthority);
}
}
return authorities;
}
My question is whether this is a correct approach. I am getting the impression that I should stuff roles into GrantedAuthorities instead of rights, but I would like to use rights to secure methods and urls, because it gives me more fine grained control over authorization. How would I accomplish this? And what is the difference between a ROLE and a PERMISSION in Spring? Do permissions map to rights and could I use hasPermission() to secure stuff bases on rights instead of roles?

I am going to answer my own question again:
Spring doesn't know rights and permissions that are used by the hasPermission method apply only to the relatively complex Domain Object Security/ACL in Spring security. Spring's simple security knows just roles and roles or more generic "permissions" (in the general sense of the word, not to be confused with permissions in Domain Object Security/ACL) like IS_AUTHENTICATED_ANONYMOUSLY are handed to Spring in the third constructor parameter of the Authentication object's.
I summerized everything on my own website and created an example implementation that stuffs rights into roles and in that way still manages to be pretty flexible.
http://en.tekstenuitleg.net/blog/spring-security-with-roles-and-rights

Related

Spring boot auth server client/ resource server: OAuth 2.1 Authorization Code Grant programatic simulation? Password grant no longer exists

Spring Authorization Server OAuth 2.1.
How can i programatically simulate the authorization_code grant?
Since all grants except for authorization_code and client_credentials have been dropped this has become quite a headache.
The scenario calls for a #Scheduled job to login as a specific user where the client credentials are encoded properties within the server performing the login.
The user roles are important when executing downstream resources and is considered a regular user of the registered Client.
Using the Password grant was perfect for this scenario in OAuth 2.0.
Before i start hacking our Spring Auth server and implement a Password grant for registered resources or maybe overloading the client_credentials for user_credentialed payloads.
Quite a pain if you ask me, so please enlighten me? Are there any patterns for implementing this that i have not yet discovered?
While I'm curious what specific use case you have that needs to perform tasks as a particular user (as opposed to a single confidential client), it should still be possible with customization.
maybe overloading the client_credentials for user_credentialed payloads
This approach makes the most sense to me as a way to adapt supported flows in OAuth 2.1 to emulate a deprecated flow like the resource owner password grant. You can use a variation of this github gist, extending it with your user's authorities if needed. One possible solution might look like the following:
#Component
public final class DaoRegisteredClientRepository implements RegisteredClientRepository {
private final RegisteredClient registeredClient;
private final UserDetailsService userDetailsService;
public DaoRegisteredClientRepository(RegisteredClient registeredClient, UserDetailsService userDetailsService) {
this.registeredClient = registeredClient;
this.userDetailsService = userDetailsService;
}
#Override
public void save(RegisteredClient registeredClient) {
throw new UnsupportedOperationException();
}
#Override
public RegisteredClient findById(String id) {
return this.registeredClient.getId().equals(id) ? this.registeredClient : null;
}
#Override
public RegisteredClient findByClientId(String clientId) {
UserDetails userDetails;
try {
userDetails = this.userDetailsService.loadUserByUsername(clientId);
} catch (UsernameNotFoundException ignored) {
return null;
}
return RegisteredClient.from(this.registeredClient)
.clientId(userDetails.getUsername())
.clientSecret(userDetails.getPassword())
.clientSettings(ClientSettings.builder().setting("user.authorities", userDetails.getAuthorities()).build())
.build();
}
}
This uses a single client registration, but makes use of a UserDetailsService to resolve a subject representing your user's username and a secret which is actually the user's password. You would then need to provide an #Bean of type OAuth2TokenCustomizer<JwtEncodingContext> to access the user.authorities setting and add those authorities to the resulting access token (JWT) using whatever claim your resource server expects them in.
Alternatively, you could just override the scopes parameter of the returned RegisteredClient if desired.
I had the similar problem and ended up creating a password grant emulation servlet filter. Please refer to my example:
https://github.com/andreygrigoriev/spring_authorization_server_password_grant

Spring security oauth2: create user if not exists, provision user

Here is an excerpt from spring oauth2 tutorial:
How to Add a Local User Database
Many applications need to hold data about their users locally, even if
authentication is delegated to an external provider. We don’t show the
code here, but it is easy to do in two steps.
Choose a backend for your database, and set up some repositories (e.g.
using Spring Data) for a custom User object that suits your needs and
can be populated, fully or partially, from the external
authentication.
Provision a User object for each unique user that logs in by
inspecting the repository in your /user endpoint. If there is already
a user with the identity of the current Principal, it can be updated,
otherwise created.
Hint: add a field in the User object to link to a unique identifier in
the external provider (not the user’s name, but something that is
unique to the account in the external provider).
So, in the user controller we have the following code:
#RequestMapping("/user")
public Map<String, Object> user(Principal user) {
Map<String, Object> map = new HashMap<String, Object>();
// for a facebook the name is facebook id, not an actual name
map.put("name", user.getName());
map.put("roles", AuthorityUtils.authorityListToSet(((Authentication) user)
.getAuthorities()));
return map;
}
Dave Syer(spring maintainer) suggests to:
Downcast the Principal to an Authentication (or possibly
OAuth2Authentication and grab the userAuthentication from that) and
then look in the details property. If your user was authenticated
using a UserInfoTokenServices you will see the Map returned from the
external provider's user info endpoint.
But for me that seems unnatural by two reasons:
Why should we do that in controller call...it smells.
Why should we do all that instanceof and casts..smells as well.
Wouldn't it be better to plugin the OAuth2AuthenticationManager instead: org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationManager
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
// ...
OAuth2Authentication auth = tokenServices.loadAuthentication(token);
// ...
auth.setDetails(authentication.getDetails());
auth.setAuthenticated(true);
// here we could've create our own Authentication object
// to set as Principal with application specific info.
// Note: no casts are required since we know the actual auth type
return auth;
}
What's the best way of creating a user after oauth2 dance completed?

Spring security Preauth: how to pass user roles through customized authentication filter

I'm new to Spring security, and working on a grails app that connect to external authentication and session management service for authentication/authorization. So what I did is create customized authentication filter
class TokenAuthenticationFilter extends AbstractPreAuthenticatedProcessingFilter {
#Override
protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
//in here i call an external service, passing in the a cookie from the request,
//and get username and role information from the service
//not sure what to do with user roles
return username
}
}
Then I looked at super class AbstractPreAuthenticatedProcessingFilter code, in it's doFilter() method, it create an PreAuthenticatedAuthenticationToken (new PreAuthenticatedAuthenticationToken(principal, credentials);) for authentication, but it only takes username and credential, not the role information. Then I tried to inject a different details source J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource into the filter, because looks to me (i might be wrong) this detail source will pass the role information to the authentication object by calling setDetails(). but J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource is reading role information from http request object.
protected Collection<String> getUserRoles(HttpServletRequest request) {
ArrayList<String> j2eeUserRolesList = new ArrayList<String>();
for (String role : j2eeMappableRoles) {
if (request.isUserInRole(role)) {
j2eeUserRolesList.add(role);
}
}
return j2eeUserRolesList;
}
I got confused about the life cycle of authentication. I thought the http request object is getting role information through authentication object in security context, which hasn't been created at this point. I need the role information in order to create the authentication object. Isn't this running in cycle? or am I misunderstanding anything?
I know I can go with another approach to make my app work, just making my own filter to create the authentication object (which takes the role parameter) instead of letting super class (AbstractPreAuthenticatedProcessingFilter) to create the authentication object, but I'm just curious why the first approach is not working. What is J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource trying to do? It calls request.isUserInRole(role), but by who and when is the user role set to http request?
Hopefully I express myself clear enough for someone to understand.

Spring security hasAnyRole and different flows for different permissions

In my Spring Boot application I have a REST controller with a following method:
#PreAuthorize("hasAnyRole('PERMISSION_UPDATE_OWN_COMMENT', 'PERMISSION_UPDATE_ANY_COMMENT')")
#RequestMapping(value = "/update", method = RequestMethod.POST)
public CommentResponse updateComment(#AuthenticationPrincipal User user, #Valid #RequestBody UpdateCommentRequest commentRequest) {
Comment comment = commentService.updateComment(commentRequest.getCommentId(), commentRequest.getTitle(), commentRequest.getContent(), user);
return new CommentResponse(comment);
}
Only users with PERMISSION_UPDATE_OWN_COMMENT or PERMISSION_UPDATE_ANY_COMMENT are allowed to use this endpoint.
Inside of this method I need to create two different flows - one for users with PERMISSION_UPDATE_OWN_COMMENT and another one for users with PERMISSION_UPDATE_ANY_COMMENTpermissions.
So my question is - what is best practice for Spring security in order to implement these different flows of logic inside of the single method ?
Should I validate inside of the updateComment method that the user has one or another permission and based on this condition implement my logic ?
The easiest way is to do the logic inside updateComment function inside controller. Because, you can easily get the instance of SecurityContextHolderAwareRequestWrapper from the action param to find the role.
But the best practice is to put your logic inside service. This will make your life easier to reuse the logic in another place like RESTFul APIs.
So you may use the below code or something similar to check the role inside the Service.
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
boolean authorized = authorities.contains(new SimpleGrantedAuthority("PERMISSION_UPDATE_OWN_COMMENT"));
(edited with further information)
Complete function which can be used to check the roles
protected boolean roleExist(String role) {
SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
for (GrantedAuthority auth : authentication.getAuthorities()) {
if (role.equals(auth.getAuthority()))
return true;
}
return false;
}

Group and acl on Spring Security

I want to use Spring Security to manage user, group and permissions.
I want to use ACL to secure my domain objects but I can't find a way to assign a group to an acl.
For example:
I've got users and groups. Each group can have the following securities:
- manage forums (can be a role like ROLE_FORUM_MANAGER)
- edit a specific forum (acl on the specific forum).
Moreover, Groups are defined by users which have role ROLE_PERMISSION_MANAGER. BUT all groups defined by this user can only be edited and managed by this user. So group are attached to a user. Exactly, imagine that user creates a google group: this user can manage right permission groups only for the group he has created. And so he can create group to manage specific forum of its own google group.
How can I do it?
I read the spring security docs and the following tutorials (so please don't send me to these links):
http://grzegorzborkowski.blogspot.com/2008/10/spring-security-acl-very-basic-tutorial.html
http://blog.denksoft.com/?page_id=20
Check Spring Security 3.0, you might be able to avoid using ACL at all by using the Spring Expression Language.
For instance, for editing a forum, you would have a method secured like this:
#PreAuthorize("hasRole('ROLE_FORUM_MANAGER') and hasPermission(#forum,'update'))
public void updateForum(Forum forum) {
//some implementation
}
You would then implement the hasPermission method in a custom permission evaluator, like:
public class ForumPermissionEvaluator implements PermissionEvaluator {
public boolean hasPermission(Authentication authentication,
Object domainObject, Object permission) {
//implement
}
public boolean hasPermission(Authentication authentication,
Serializable targetId, String targetType, Object permission) {
//implement
}
}
Finally, wire it up together in the application config:
<beans:bean id="expressionHandler"
class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
<beans:property name="permissionEvaluator" ref="permissionEvaluator"/>
</beans:bean>
<beans:bean id="permissionEvaluator"
class="x.y.z.ForumPermissionEvaluator" />
I would just use your Groups like Roles. I've found the Spring ACL implementation to be pretty unwieldy and for the most part unusable. Just assign users to "groups" (Roles in all actuality) and check them as you would normal role based authorization.
I did something similar 'manually': i.e. I had my own code to determine which instances could be edited/deleted by a specific user and only relied on Spring security to ensure they had the right role to access the functionality and to provide role/authentication information for the current user.
So in my code I determined the current principal (our own User class) and based on that I decided what rights this user had on a specific instance.
public static User getCurrentUser() {
User user = null;
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null) {
Object principal = auth.getPrincipal();
if (principal instanceof User) {
user = (User)principal;
}
}
return user;
}

Resources