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