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); } } }