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;
}
Related
I have a case where certain endpoints on a controller should return 401 if the username that the user authenticated with is the same username as the user being operated on.
I have been thinking of the best way to do this. Currently, I have an authentication facade (https://www.baeldung.com/get-user-in-spring-security) where I check in the body of each controller method if the user should have access to operate on the item it is asking to.
IE. A user should only be able to delete their account:
User u = service.findOne(id);
if (u != null) {
// user can only delete their own account
User authenticated = authenticationFacade.getAuthUser();
RestPreconditions.checkRequestState(authenticated.getId() == u.getId());
}
Another case is where a user needs to operate on something of a different data type that they have access to work on.
IE
Post p = service.findOne(id);
if (p != null) {
// user can only delete their own posts
User authenticated = authenticationFacade.getAuthUser();
RestPreconditions.checkRequestState(authenticated.getId() == p.getUser().getId());
}
I am here to ask if this is the best way. As demonstrated above, some of the checks require operating through different objects and making database calls to get the data to determine if the user should have access.
I considered a role-based implementation and was wondering if anyone could provide some insight into how I would do that and if it is cleaner than the method above.
The reason I ask is that I also want to allow people with the ROLE_ADMIN role to be able to do all operations but I would need to transform the current checks to or || with the current checks and that seems messy. But simply preauthorizing just role admin would still fail with the facade without the or
Check about #PreAuthorize / #PostAuthorize which allows you to use SpEL to secure a method call in a declarative way.
One of the very nice thing is that you can even uses SpEL to refer a spring bean method , which means you can do something like below.
First, defining a bean to encapsulate all the security related checking.Suppose all entities implements some sort of interface (e.g BaseEntity) which can get the owner of that entity :
#Service
public class SecurityService{
public boolean isAllowAccessedByCurrentUser(BaseEntity entity) {
User authenticated = authenticationFacade.getAuthUser();
return authenticated.getId() == entity.getOwnerId();
}
}
To use it to apply the security checking :
#Service
public class UserService {
//"returnObject" is the built-in variable referring to the return object
#PostAuthorize ("#securityService.isAllowAccessedByCurrentUser(returnObject)")
public User findOne(Integer id){
}
}
#Service
public class PostService {
//"returnObject" is the built-in variable refer to the return object of the method
#PostAuthorize ("#securityService.isAllowAccessedByCurrentUser(returnObject)")
public Post findOne(Integer id){
}
}
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.
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
I am using spring mvc3 to build a user-manager system.
This system conain the following models:
Department
User
And the Departments have the hierarchic structure,for example:
Dep1
SubDep1
SubDep2
Sub_sub_dep1
xxxx
One can add/update/delete departments/users if he is authenciationed,but he can only do these operation within his department and the sub-departmens.
For example,there are three departments(with there users):
Dep01(user1:{id:1}}
Dep0101(user2:{id:2}
Dep0102(user3:{id:3}
Dep010201(user4:{id:4}
So user1 can do the /add/upate/delete all the users(user1,user2,user3,user4)
While user3 can only do the operation for user(user3,user4).
I can control that the user3 can not see the user1 and user2 in the department/list page.
But how about if he enter the url like this:
department/update/1
This has to be avoided since the user1(whose id is 1) does not belong to Dep0102 or Dep010201.
How to controll this?
One option is to create a custom Spring Security PermissionEvaluator and implement your custom checks in the hasPermission(Authentication authentication, Object targetDomainObject, Object permission) method.
The signature of the method to protect ends up looking like this:
#PreAuthorize("hasRole('ROLE_USER') and hasPermission(#_dept, 'deptAndSubs')")
public String methodToProtect(String _dept)throws Exception {
<custom code>;
}
The first argument to the hasPermission expression is the department that the user wants to modify and the second is the permission. For us the deptAndSubs permission indicates that the user can execute the method only if the department being modified is equal to the users assigned department or any of the sub departments of that department (other permissions are 'deptOnly' and 'subsOnly').
In our application we have a custom Spring Security UserDetails object that includes the user department code so we can get the logged in user's department directly from the Authentication object that Spring passes into the method. Here's what the custom evaluator finally ends up looking like:
public class CustomPermissionEvaluator implements PermissionEvaluator {
#Override
public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {
AppUser appUser = (AppUser)authentication.getPrincipal();
if(permission instanceof String){
if("deptAndSubs".equals(permission)){
return isTargetDeptInUserDeptTree((String)targetDomainObject, appUser.getDeptCode());
}else if(.... other permission checks){}
}
return false;
}
The method isTargetDeptInUserDeptTree is custom code to extract the user's department tree and verify that the target department is in it.
Finally you have to set up your xml configuration:
<global-method-security pre-post-annotations="enabled" >
<expression-handler ref="expressionHandler"/>
</global-method-security>
<beans:bean id="expressionHandler" class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
<beans:property name="permissionEvaluator" ref="customPermissionEvaluator"/>
</beans:bean>
<beans:bean id="customPermissionEvaluator" class="....CustomPermissionEvaluator"/>
Good luck!
I'm currently looking into the spring-security framework - great stuff so far, pretty impressed.
However, I haven't found out where or how to define a inheritance of permissions.
e.g. I want the ROLE_ADMIN to have at least the same rights as the ROLE_USER. I defined three intercep-urls for spring:
<intercept-url pattern="/auth/login.do" access="permitAll"/>
<intercept-url pattern="/voting/*" access="hasRole('ROLE_USER')"/>
<intercept-url pattern="/admin/*" access="hasRole('ROLE_ADMIN')"/>
When trying to access any site nesting from /voting/, while being logged in as a ROLE_ADMIN user, I am being denied. Am I missing something here? I know, I could define several roles for the /voting/* branch, but if I imagine that I might have 10 different user roles in one of my real-life usecases, I can imagine the .xml file to get really messy, really fast.
Can I configure the inheritance of roles somewhere?
cheers
EDIT:
Thanks to the great community and their input, I came up with a working solution - it may be good style or not - it works :D
I defined an enum which reflects the inheriting spring-sec roles:
public enum UserRoles {
ROLE_USER(new String[]{"ROLE_USER"}),
ROLE_ADMIN(new String[]{"ROLE_USER", "ROLE_ADMIN"});
private final String[] roles;
private UserRoles(String[] roles) {
this.roles = roles;
}
public String[] getRoles() {
return roles;
}
}
I then implemented my own UserDetailsService:
Within the methode
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException { ... }
where it comes to adding granted authorities to a UserDetail, I get the corresponding enum value and append all the roles defined by this enum value:
List<GrantedAuthority> authList = new ArrayList<GrantedAuthority>(2);
for (String role : UserRoles.ROLE_ADMIN.getRoles()) {
authList.add(new GrantedAuthorityImpl(role));
}
UserDetails user = null;
try {
//user = new User(username, md5.hashPassword(username), true, true, true, true, authList);
} catch (NoSuchAlgorithmException ex) {
logger.error(ex.getMessage(), ex);
}
My domain object which is persisted, contains a #Enumerated field with a UserRole - in a real environment, this field is loaded from the DB and the corresponding Roles are picked from that enum.
thanks again for the input - love this community ^^
Check out RoleHierarchy and RoleHierarchyImpl and this question.
As far as I know, Spring Security does not support the concept of Roles and Privileges. In Spring security are only Roles sometimes called Authority -- Moreover: In Spring Security are Roles/Authorities that what in a Roles and Privileges System is called Privileges.
So if you want to build a System of Roles and Privileges, then you need to do it by your one by building your own Spring Security AuthenticationManager, and tread the Spring Security Roles/Authorities like Privileges.
#See This Blog: Spring Security customization (Part 1 – Customizing UserDetails or extending GrantedAuthority) -- It is written for Spring Security 2.0 and shows how to implement what I am talking about. It also stayes that RoleHierarchy has some drawbacks, but this article is about 2.0, may the drawbacks are gone in 3.0