package fr.openwide.core.jpa.security.service;
import java.util.Collection;
import java.util.Set;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.acls.domain.PermissionFactory;
import org.springframework.security.acls.model.Permission;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.util.StringUtils;
import com.google.common.collect.Sets;
import fr.openwide.core.jpa.security.business.authority.model.Authority;
import fr.openwide.core.jpa.security.business.authority.service.IAuthorityService;
import fr.openwide.core.jpa.security.business.person.model.IGroupedUser;
import fr.openwide.core.jpa.security.business.person.model.IUserGroup;
import fr.openwide.core.jpa.security.business.person.service.IGenericUserService;
import fr.openwide.core.jpa.security.hierarchy.IPermissionHierarchy;
import fr.openwide.core.jpa.security.model.CoreUserDetails;
public class CoreJpaUserDetailsServiceImpl implements UserDetailsService {
private static final String EMPTY_PASSWORD_HASH = "* NO PASSWORD *";
@Autowired
private IGenericUserService<?> userService;
@Autowired
private RoleHierarchy roleHierarchy;
@Autowired
private IPermissionHierarchy permissionHierarchy;
@Autowired
private PermissionFactory permissionFactory;
@Autowired
private IAuthorityService authorityService;
private AuthenticationUserNameComparison authenticationUserNameComparison = AuthenticationUserNameComparison.CASE_SENSITIVE;
public void setAuthenticationUserNameComparison(AuthenticationUserNameComparison authenticationUserNameComparison) {
this.authenticationUserNameComparison = authenticationUserNameComparison;
}
@Override
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException, DataAccessException {
IGroupedUser<?> user = getUserByUsername(userName);
if (user == null) {
throw new UsernameNotFoundException("CoreJpaUserDetailsServiceImpl: User not found: " + userName);
}
if (!user.isActive()) {
throw new DisabledException("User is disabled");
}
Pair<Set<GrantedAuthority>, Set<Permission>> authoritiesAndPermissions = getAuthoritiesAndPermissions(user);
Collection<? extends GrantedAuthority> expandedGrantedAuthorities = roleHierarchy.getReachableGrantedAuthorities(authoritiesAndPermissions.getLeft());
addCustomPermissionsFromAuthorities(expandedGrantedAuthorities, authoritiesAndPermissions.getRight());
addPermissionsFromAuthorities(expandedGrantedAuthorities, authoritiesAndPermissions.getRight());
Collection<Permission> expandedReachablePermissions = permissionHierarchy.getReachablePermissions(authoritiesAndPermissions.getRight());
// In any case, we can't pass an empty password hash to the CoreUserDetails
CoreUserDetails userDetails = new CoreUserDetails(user.getUserName(),
StringUtils.hasText(user.getPasswordHash()) ? user.getPasswordHash() : EMPTY_PASSWORD_HASH,
user.isActive(), true, true, true,
expandedGrantedAuthorities, expandedReachablePermissions);
return userDetails;
}
protected IGroupedUser<?> getUserByUsername(String userName) {
IGroupedUser<?> user;
if (AuthenticationUserNameComparison.CASE_INSENSITIVE.equals(authenticationUserNameComparison)) {
user = userService.getByUserNameCaseInsensitive(userName);
} else {
user = userService.getByUserName(userName);
}
return user;
}
protected void addCustomPermissionsFromAuthorities(Collection<? extends GrantedAuthority> expandedGrantedAuthorities, Set<Permission> permissions) {
for (GrantedAuthority grantedAuthority : expandedGrantedAuthorities) {
Authority authority = authorityService.getByName(grantedAuthority.getAuthority());
if (authority == null) {
continue;
}
addPermissions(permissions, authority.getCustomPermissionNames());
}
}
/**
* Override this in order to define some permissions based on the (expanded) granted authorities.
*/
protected void addPermissionsFromAuthorities(Collection<? extends GrantedAuthority> expandedGrantedAuthorities, Set<Permission> permissions) {
// Does nothing by default
}
protected Pair<Set<GrantedAuthority>, Set<Permission>> getAuthoritiesAndPermissions(IGroupedUser<?> user) {
Set<GrantedAuthority> grantedAuthorities = Sets.newHashSet();
Set<Permission> permissions = Sets.newHashSet();
addAuthorities(grantedAuthorities, user.getAuthorities());
permissions.addAll(user.getPermissions());
for (IUserGroup personGroup : user.getGroups()) {
addAuthorities(grantedAuthorities, personGroup.getAuthorities());
permissions.addAll(personGroup.getPermissions());
}
return new ImmutablePair<Set<GrantedAuthority>, Set<Permission>>(grantedAuthorities, permissions);
}
protected void addAuthorities(Set<GrantedAuthority> grantedAuthorities, Set<Authority> authorities) {
for (Authority authority : authorities) {
grantedAuthorities.add(new SimpleGrantedAuthority(authority.getName()));
}
}
protected void addPermissions(Set<Permission> permissions, Collection<String> permissionNames) {
for (String permissionName : permissionNames) {
permissions.add(permissionFactory.buildFromName(permissionName));
}
}
}