package alien4cloud.security; import java.io.IOException; import java.util.Map; import java.util.Set; import javax.annotation.Resource; import org.elasticsearch.index.query.FilterBuilder; import org.elasticsearch.index.query.FilterBuilders; import org.springframework.context.event.EventListener; import org.springframework.stereotype.Service; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import alien4cloud.dao.IGenericSearchDAO; import alien4cloud.dao.model.GetMultipleDataResult; import alien4cloud.exception.NotFoundException; import alien4cloud.security.event.GroupDeletedEvent; import alien4cloud.security.event.UserDeletedEvent; import alien4cloud.utils.TypeScanner; /** * Handle groups roles CRUD on any resource implementing {@link ISecuredResource} * * @author mourouvi * */ @Service public class ResourceRoleService { @Resource(name = "alien-es-dao") private IGenericSearchDAO alienDAO; /** * Add a role to a specific user on a secured resource * * @param resource on which to add user role * @param username for who we want to add a role * @param role to add to a specific user for this resource */ public void addUserRole(ISecuredResource resource, String username, String role) { validateResource(resource); Map<String, Set<String>> userRolesMap = resource.getUserRoles(); if (userRolesMap == null) { userRolesMap = Maps.newHashMap(); resource.setUserRoles(userRolesMap); } // Perform some verification and format the role role = formatRole(resource, role); Set<String> userRoles = userRolesMap.get(username); if (userRoles == null) { userRoles = Sets.newHashSet(); userRolesMap.put(username, userRoles); } if (userRoles.add(role)) { // Only save resource if the role to be added does not exist alienDAO.save(resource); } } /** * Remove a role to a specific user on a resource * * @param resource on which to remove user role * @param username for who we want to remove a role * @param role to remove to a specific user for this resource */ public void removeUserRole(ISecuredResource resource, String username, String role) { validateResource(resource); Map<String, Set<String>> userRolesMap = resource.getUserRoles(); if (userRolesMap != null) { Set<String> userRoles = userRolesMap.get(username); if (userRoles != null) { // Perform some verification and format the role role = formatRole(resource, role); if (userRoles.remove(role)) { if (userRoles.isEmpty()) { // If an user does not have any more role, we remove it from the resource if (userRolesMap.remove(username) != null) { if (userRolesMap.isEmpty()) { resource.setUserRoles(null); } } } // Only save resource if we could really remove a role alienDAO.save(resource); } } } } /** * Add a role for a group on a resource * * @param resource on which to add group role * @param groupId on which we want to add a role * @param role to add to a specific group for this resource */ public void addGroupRole(ISecuredResource resource, String groupId, String role) { validateResource(resource); Map<String, Set<String>> groupRolesMap = resource.getGroupRoles(); if (groupRolesMap == null) { groupRolesMap = Maps.newHashMap(); resource.setGroupRoles(groupRolesMap); } Set<String> groupRoles = groupRolesMap.get(groupId); if (groupRoles == null) { groupRoles = Sets.newHashSet(); groupRolesMap.put(groupId, groupRoles); } if (groupRoles.add(role)) { // Only save group if the role to be added does not exist alienDAO.save(resource); } } /** * Remove a role for a group on a resource * * @param resource on which to remove group role * @param groupId on which we want to add a role * @param role to add to a specific group for this resource */ public void removeGroupRole(ISecuredResource resource, String groupId, String role) { validateResource(resource); Map<String, Set<String>> groupRolesMap = resource.getGroupRoles(); if (groupRolesMap != null) { Set<String> groupRoles = groupRolesMap.get(groupId); if (groupRoles != null) { // Perform some verification and format the role role = formatRole(resource, role); if (groupRoles.remove(role)) { if (groupRoles.isEmpty()) { // If a group do not have any more role, we remove it from the resource if (groupRolesMap.remove(groupId) != null) { if (groupRolesMap.isEmpty()) { resource.setGroupRoles(null); } } } // Only save resource if we could really remove a role alienDAO.save(resource); } } } } /** * Clean the role string * * @param resource The resource for which to format a role string. * @param role The role string to check and format. * @return The formatted and checked role string. An exception is thrown in case the role is not a valid role for the given resource. */ @SuppressWarnings({ "rawtypes", "unchecked" }) private String formatRole(ISecuredResource resource, String role) { if (role == null || role.toString().trim().isEmpty()) { throw new NotFoundException("Resource Role [" + role + "] is empty"); } String goodRoleToAdd = role.toString().toUpperCase(); try { Class enumClass = resource.roleEnum(); Enum.valueOf(enumClass, goodRoleToAdd); } catch (IllegalArgumentException e) { throw new NotFoundException("Resource role [" + role + "] cannot be found", e); } return goodRoleToAdd; } private void validateResource(ISecuredResource resource) { if (resource == null) { throw new NotFoundException("The target resource on which we want to update group/user role cannot be found"); } } /** * Delete a groupRoles entry (groupId) in all ISecuredResource object * * @param groupId group id to remove in groupRoles * @throws ClassNotFoundException * @throws IOException */ private void deleteGroupRoles(String groupId) throws ClassNotFoundException, IOException { FilterBuilder resourceFilter = FilterBuilders.nestedFilter("groupRoles", FilterBuilders.termFilter("groupRoles.key", groupId)); deleteRoles(resourceFilter, groupId, new DeleteRoleVisitor() { @Override public void deleteRoleOfOwner(Object[] securedResources, String owner) { deleteRoleOfGroup(securedResources, owner); } }); } /** * Delete a userRoles entry (userId) in all ISecuredResource object * * @param userId user id (username) to remove in userRoles * @throws ClassNotFoundException * @throws IOException */ private void deleteUserRoles(String userId) throws ClassNotFoundException, IOException { FilterBuilder resourceFilter = FilterBuilders.nestedFilter("userRoles", FilterBuilders.termFilter("userRoles.key", userId)); deleteRoles(resourceFilter, userId, new DeleteRoleVisitor() { @Override public void deleteRoleOfOwner(Object[] securedResources, String owner) { deleteRoleOfUser(securedResources, owner); } }); } private void deleteRoles(FilterBuilder appFilter, String ownerId, DeleteRoleVisitor deleteRoleVisitor) throws IOException, ClassNotFoundException { int from = 0; long totalResult; Set<String> indices = Sets.newHashSet(); Set<Class<?>> classes = TypeScanner.scanTypes("alien4cloud.model", ISecuredResource.class); for (Class<?> clazz : classes) { indices.add(alienDAO.getIndexForType(clazz)); } do { GetMultipleDataResult<Object> result = alienDAO.search(indices.toArray(new String[indices.size()]), classes.toArray(new Class<?>[classes.size()]), null, null, appFilter, null, from, 20); deleteRoleVisitor.deleteRoleOfOwner(result.getData(), ownerId); from += result.getData().length; totalResult = result.getTotalResults(); } while (from < totalResult); } private void deleteRoleOfGroup(Object[] securedResources, String groupId) { for (Object securedResource : securedResources) { // Only remove in class implementing ISecuredResource ISecuredResource resource = (ISecuredResource) securedResource; if (resource.getGroupRoles().remove(groupId) != null) { if (resource.getGroupRoles().isEmpty()) { resource.setGroupRoles(null); } alienDAO.save(resource); } } } private void deleteRoleOfUser(Object[] securedResources, String userId) { for (Object securedResource : securedResources) { // Only remove in class implementing ISecuredResource ISecuredResource resource = (ISecuredResource) securedResource; if (resource.getUserRoles().remove(userId) != null) { if (resource.getUserRoles().isEmpty()) { resource.setUserRoles(null); } alienDAO.save(resource); } } } private static interface DeleteRoleVisitor { void deleteRoleOfOwner(Object[] securedResources, String owner); } @EventListener public void userDeletedEventListener(UserDeletedEvent event) throws IOException, ClassNotFoundException { deleteUserRoles(event.getUser().getUsername()); } @EventListener public void groupDeletedEventListener(GroupDeletedEvent event) throws IOException, ClassNotFoundException { deleteGroupRoles(event.getGroup().getId()); } }