package alien4cloud.security.users;
import static alien4cloud.utils.AlienUtils.safe;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.inject.Inject;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.event.EventListener;
import org.springframework.security.crypto.bcrypt.BCrypt;
import org.springframework.stereotype.Component;
import com.google.common.collect.Sets;
import alien4cloud.dao.model.GetMultipleDataResult;
import alien4cloud.exception.AlreadyExistException;
import alien4cloud.exception.NotFoundException;
import alien4cloud.security.event.GroupDeletedEvent;
import alien4cloud.security.event.UserDeletedEvent;
import alien4cloud.security.groups.IAlienGroupDao;
import alien4cloud.security.model.Group;
import alien4cloud.security.model.Role;
import alien4cloud.security.model.User;
import alien4cloud.security.users.rest.UpdateUserRequest;
import alien4cloud.utils.ReflectionUtil;
@Component
public class UserService {
@Resource
private IAlienUserDao alienUserDao;
@Resource
private IAlienGroupDao alienGroupDao;
@Value("${alien_security.admin.ensure}")
private boolean ensure;
@Value("${alien_security.admin.username}")
private String adminUserName;
@Value("${alien_security.admin.password}")
private String adminPassword;
@Value("${alien_security.admin.email}")
private String email;
@Inject
private ApplicationEventPublisher publisher;
/** Ensure that there is at least one user with admin role. */
@PostConstruct
public void ensureAdminUser() {
if (!ensure) {
return;
}
// we should have one at least one user with role ADMIN
Map<String, String[]> filters = new HashMap<String, String[]>();
filters.put("roles", new String[] { Role.ADMIN.toString() });
GetMultipleDataResult searchResult = alienUserDao.find(filters, 1);
// if no user with admin role can be found, then creates a new one.
if (searchResult != null && searchResult.getTotalResults() == 0) {
adminUserName = (adminUserName == null) ? "admin" : adminUserName;
adminPassword = (adminPassword == null) ? "admin" : adminPassword;
String[] roles = new String[] { Role.ADMIN.toString() };
createUser(adminUserName, email, null, null, roles, adminPassword);
}
}
@SuppressWarnings("rawtypes")
public int countAdminUser() {
Map<String, String[]> filters = new HashMap<String, String[]>();
filters.put("roles", new String[] { Role.ADMIN.toString() });
GetMultipleDataResult searchResult = alienUserDao.find(filters, 1);
return (int) searchResult.getTotalResults();
}
/**
* Create a new internal user and saves it.
*
* @param userName The userName of the new user.
* @param email The email of the new user.
* @param firstName The firstName of the new user.
* @param lastName The lastname of the new user.
* @param roles The roles of the new user.
* @param password The password of the new user (hash only will be saved).
*/
public void createUser(String userName, String email, String firstName, String lastName, String[] roles, String password) {
if (alienUserDao.find(userName) != null) {
throw new AlreadyExistException("A user with the given username already exists.");
}
User user = new User();
user.setUsername(userName);
user.setEmail(email);
user.setFirstName(firstName);
user.setLastName(lastName);
user.setRoles(roles);
user.setPassword(BCrypt.hashpw(password, BCrypt.gensalt()));
user.setInternalDirectory(true);
user.setEnabled(true);
user.setAccountNonExpired(true);
user.setAccountNonLocked(true);
user.setCredentialsNonExpired(true);
alienUserDao.save(user);
}
/**
* Update an user on a specific field name
*
* @param userName name of the user
* @param userUpdateRequest value of the field
*/
public void updateUser(String userName, UpdateUserRequest userUpdateRequest) {
User user = retrieveUser(userName);
ReflectionUtil.mergeObject(userUpdateRequest, user);
if (userUpdateRequest.getPassword() != null) {
user.setPassword(BCrypt.hashpw(userUpdateRequest.getPassword(), BCrypt.gensalt()));
}
alienUserDao.save(user);
}
/**
* retrieve a user if exists
*
* @param username the username of the user to retrieve
* @return the {@link User} if found.
*/
public User retrieveUser(String username) {
User user = alienUserDao.find(username);
if (user == null) {
throw new NotFoundException("User [" + username + "] cannot be found");
}
return user;
}
/**
* Add a group to a user, including all the group roles
*
* @param group The group to process
* @param user The user to process
*/
public void addGroupToUser(Group group, User user) {
Set<String> groupSet = user.getGroups() == null ? new HashSet<String>() : user.getGroups();
groupSet.add(group.getId());
user.setGroups(groupSet);
if (CollectionUtils.isNotEmpty(group.getRoles())) {
Set<String> groupRolesSet = user.getGroupRoles() == null ? new HashSet<String>() : user.getGroupRoles();
groupRolesSet.addAll(group.getRoles());
user.setGroupRoles(groupRolesSet);
}
alienUserDao.save(user);
}
/**
* Add a group to a user, including all the group roles
*
* @param group The group to process
* @param username The username of the user to process
*/
public void addGroupToUser(Group group, String username) {
addGroupToUser(group, retrieveUser(username));
}
/**
* Add a group role to a user
*
* @param role The group role to add
* @param username The username of the user to process
*/
public void addGroupRoleToUser(String username, String role) {
User user = retrieveUser(username);
Set<String> groupRolesSet = user.getGroupRoles() == null ? new HashSet<String>() : user.getGroupRoles();
groupRolesSet.add(Role.getStringFormatedRole(role));
user.setGroupRoles(groupRolesSet);
alienUserDao.save(user);
}
public void saveUser(User user) {
alienUserDao.save(user);
}
/**
*
* Regenerate the group roles in the user.
*
* @param user the user for which to regenerate the group roles
*/
public void updateUserGroupRoles(User user) {
if (CollectionUtils.isEmpty(user.getGroups())) {
user.setGroupRoles(null);
} else {
Set<String> groupRolesSet = Sets.newHashSet();
for (String groupId : user.getGroups()) {
Group group = alienGroupDao.find(groupId);
if (group == null) {
throw new NotFoundException("Group [" + groupId + "] cannot be found");
}
if (CollectionUtils.isNotEmpty(group.getRoles())) {
groupRolesSet.addAll(group.getRoles());
}
}
user.setGroupRoles(groupRolesSet);
}
alienUserDao.save(user);
}
/**
*
* Regenerate the group roles in the user.
*
* @param username the username of the user for which to regenerate the group roles
*/
public void updateUserGroupRoles(String username) {
updateUserGroupRoles(retrieveUser(username));
}
/**
* Remove a group from a user object
*
* @param username the username of the user to process
* @param group the group object to remove
*/
public void removeGroupFromUser(String username, Group group) {
User user = retrieveUser(username);
removeGroupFromUser(user, group);
}
/**
* Remove a group from a user object
*
* @param user the user to process
* @param group the group object to remove
*/
public void removeGroupFromUser(User user, Group group) {
if (CollectionUtils.isEmpty(user.getGroups())) {
return;
}
user.getGroups().remove(group.getId());
if (CollectionUtils.isNotEmpty(group.getRoles())) {
updateUserGroupRoles(user);
} else {
alienUserDao.save(user);
}
}
public boolean isAdmin(String username) {
User user = retrieveUser(username);
if (user.getRoles() == null) {
return false;
}
ArrayList<String> roles = new ArrayList(Arrays.asList(user.getRoles()));
return roles.contains("ADMIN");
}
/**
* Delete a user, and publish an event
*
* @param username The username of the user to delete
*/
public void deleteUser(String username) {
User user = retrieveUser(username);
alienUserDao.delete(username);
publisher.publishEvent(new UserDeletedEvent(this, user));
}
/**
* Listener for group deleted. Removes the group (and related roles) from all users members
*
* @param event
*/
@EventListener
public void groupDeletedEventListener(GroupDeletedEvent event) {
Group group = event.getGroup();
for (String username : safe(group.getUsers())) {
removeGroupFromUser(username, group);
}
}
}