/**
*
*/
package it.geosolutions.geostore.services.rest.security;
import it.geosolutions.geostore.core.model.User;
import it.geosolutions.geostore.core.model.UserGroup;
import it.geosolutions.geostore.core.model.enums.GroupReservedNames;
import it.geosolutions.geostore.core.model.enums.Role;
import it.geosolutions.geostore.core.security.UserMapper;
import it.geosolutions.geostore.services.UserGroupService;
import it.geosolutions.geostore.services.UserService;
import it.geosolutions.geostore.services.exception.BadRequestServiceEx;
import it.geosolutions.geostore.services.exception.NotFoundServiceEx;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.ldap.authentication.LdapAuthenticationProvider;
import org.springframework.security.ldap.authentication.LdapAuthenticator;
import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator;
import org.springframework.security.ldap.userdetails.LdapUserDetails;
/**
* @author alessio.fabiani
*
*/
public class UserLdapAuthenticationProvider extends LdapAuthenticationProvider {
private final static Logger LOGGER = Logger.getLogger(UserLdapAuthenticationProvider.class);
@Autowired
UserService userService;
@Autowired
UserGroupService userGroupService;
private UserMapper userMapper;
/**
* Message shown if the user credentials are wrong. TODO: Localize it
*/
private static final String UNAUTHORIZED_MSG = "Bad credentials";
/**
* Message shown if the user it's not found. TODO: Localize it
*/
public static final String USER_NOT_FOUND_MSG = "User not found. Please check your credentials";
public static final String USER_NOT_ENABLED = "The user present but not enabled";
public UserLdapAuthenticationProvider(LdapAuthenticator authenticator,
LdapAuthoritiesPopulator authoritiesPopulator) {
super(authenticator, authoritiesPopulator);
}
public void setUserService(UserService userService) {
this.userService = userService;
}
public void setUserGroupService(UserGroupService userGroupService) {
this.userGroupService = userGroupService;
}
public void setUserMapper(UserMapper userMapper) {
this.userMapper = userMapper;
}
@Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
try {
authentication = super.authenticate(authentication);
} catch (Exception e) {
LOGGER.log(Level.ERROR, e.getMessage(), e);
throw new BadCredentialsException(UNAUTHORIZED_MSG);
}
LdapUserDetails ldapUser = null;
if (authentication.isAuthenticated()) {
Collection<GrantedAuthority> authorities = null;
ldapUser = (LdapUserDetails) authentication.getPrincipal();
if (!(ldapUser.isAccountNonExpired() && ldapUser.isAccountNonLocked()
&& ldapUser.isCredentialsNonExpired() && ldapUser.isEnabled())) {
throw new DisabledException(USER_NOT_FOUND_MSG);
}
authorities = ldapUser.getAuthorities();
String pw = (String) authentication.getCredentials();
String us = ldapUser.getUsername();
// We use the credentials for all the session in the GeoStore client
User user = null;
try {
user = userService.get(us);
LOGGER.info("US: " + us);// + " PW: " + PwEncoder.encode(pw) + " -- " + user.getPassword());
if (!user.isEnabled()) {
throw new DisabledException(USER_NOT_FOUND_MSG);
}
} catch (Exception e) {
LOGGER.info(USER_NOT_FOUND_MSG);
user = null;
}
if (user != null) {
// check that ROLE and GROUPS match with the LDAP ones
try {
Set<UserGroup> groups = new HashSet<UserGroup>();
Role role = extractUserRoleAndGroups(user.getRole(), authorities, groups);
user.setRole(role);
user.setGroups(checkReservedGroups(groups));
if (userService != null)
userService.update(user);
Authentication a = prepareAuthentication(pw, user, role);
return a;
} catch (BadRequestServiceEx e) {
LOGGER.log(Level.ERROR, e.getMessage(), e);
throw new UsernameNotFoundException(USER_NOT_FOUND_MSG);
} catch (NotFoundServiceEx e) {
LOGGER.log(Level.ERROR, e.getMessage(), e);
throw new UsernameNotFoundException(USER_NOT_FOUND_MSG);
}
} else {
// check that ROLE and GROUPS match with the LDAP ones
try {
user = new User();
user.setName(us);
user.setNewPassword(null);
user.setEnabled(true);
Set<UserGroup> groups = new HashSet<UserGroup>();
Role role = extractUserRoleAndGroups(null, authorities, groups);
user.setRole(role);
user.setGroups(checkReservedGroups(groups));
if(userMapper != null) {
userMapper.mapUser(ldapUser, user);
}
if (userService != null)
userService.insert(user);
Authentication a = prepareAuthentication(pw, user, role);
return a;
} catch (BadRequestServiceEx e) {
LOGGER.log(Level.ERROR, e.getMessage(), e);
throw new UsernameNotFoundException(USER_NOT_FOUND_MSG);
} catch (NotFoundServiceEx e) {
LOGGER.log(Level.ERROR, e.getMessage(), e);
throw new UsernameNotFoundException(USER_NOT_FOUND_MSG);
}
}
} else {
throw new BadCredentialsException(UNAUTHORIZED_MSG);
}
}
/**
* @param pw
* @param user
* @param role
* @return
*/
protected Authentication prepareAuthentication(String pw, User user, Role role) {
List<GrantedAuthority> grantedAuthorities = new ArrayList<GrantedAuthority>();
grantedAuthorities.add(new GrantedAuthorityImpl("ROLE_" + role));
Authentication a = new UsernamePasswordAuthenticationToken(user, pw, grantedAuthorities);
// a.setAuthenticated(true);
return a;
}
/**
* @param role2
* @param authorities
* @param groups
* @return
* @throws BadRequestServiceEx
*/
protected Role extractUserRoleAndGroups(Role userRole,
Collection<GrantedAuthority> authorities, Set<UserGroup> groups)
throws BadRequestServiceEx {
Role role = (userRole != null ? userRole : Role.USER);
for (GrantedAuthority a : authorities) {
if (a.getAuthority().startsWith("ROLE_")) {
if (a.getAuthority().toUpperCase().endsWith("ADMIN")
&& (role == Role.GUEST || role == Role.USER)) {
role = Role.ADMIN;
} else if (a.getAuthority().toUpperCase().endsWith("USER") && role == Role.GUEST) {
role = Role.USER;
}
} else {
groups.add(synchronizeGroup(a));
}
}
return role;
}
public void synchronizeGroups() throws BadRequestServiceEx {
if(getAuthoritiesPopulator() instanceof GroupsRolesService) {
GroupsRolesService groupsService = (GroupsRolesService) getAuthoritiesPopulator();
for(GrantedAuthority authority : groupsService.getAllGroups()) {
synchronizeGroup(authority);
}
}
}
private UserGroup synchronizeGroup(GrantedAuthority a)
throws BadRequestServiceEx {
UserGroup group = new UserGroup();
group.setGroupName(a.getAuthority());
if (userGroupService != null) {
UserGroup userGroup = userGroupService.get(group.getGroupName());
if (userGroup == null) {
LOGGER.log(Level.INFO, "Creating new group from LDAP: " + group.getGroupName());
long groupId = userGroupService.insert(group);
userGroup = userGroupService.get(groupId);
}
return userGroup;
} else {
return group;
}
}
/**
* Utility method to remove Reserved group (for example EVERYONE) from a group list
*
* @param groups
* @return
*/
private Set<UserGroup> checkReservedGroups(Set<UserGroup> groups){
List<UserGroup> reserved = new ArrayList<UserGroup>();
for(UserGroup ug : groups){
if(!GroupReservedNames.isAllowedName(ug.getGroupName())){
reserved.add(ug);
}
}
for(UserGroup ug : reserved){
groups.remove(ug);
}
return groups;
}
}