package alien4cloud.security;
import java.util.Map;
import java.util.Set;
import org.elasticsearch.index.query.FilterBuilder;
import org.elasticsearch.index.query.FilterBuilders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.LogoutConfigurer;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.stereotype.Component;
import com.google.common.collect.Sets;
import alien4cloud.Constants;
import alien4cloud.security.groups.IAlienGroupDao;
import alien4cloud.security.model.ApplicationEnvironmentRole;
import alien4cloud.security.model.ApplicationRole;
import alien4cloud.security.model.CloudRole;
import alien4cloud.security.model.Group;
import alien4cloud.security.model.Role;
import alien4cloud.security.model.User;
import alien4cloud.security.spring.Alien4CloudAccessDeniedHandler;
import alien4cloud.security.spring.FailureAuthenticationEntryPoint;
import lombok.extern.slf4j.Slf4j;
/**
* Applications and topologies concerns
*/
@Slf4j
@Component
public final class AuthorizationUtil {
private static IAlienGroupDao alienGroupDao;
private static Alien4CloudAccessDeniedHandler accessDeniedHandler;
@Autowired
public void setAlienGroupDao(IAlienGroupDao alienGroupDao) {
AuthorizationUtil.alienGroupDao = alienGroupDao;
}
@Autowired
public void setAccessDeniedHandler(Alien4CloudAccessDeniedHandler accessDeniedHandler) {
AuthorizationUtil.accessDeniedHandler = accessDeniedHandler;
}
private AuthorizationUtil() {
}
/**
* Check that the user has one of the requested rights for the given application
*
* @param resource any with userRoles and groupRoles maps
* @param expectedRoles any role binded on secured resource
*/
public static void checkAuthorizationForApplication(ISecuredResource resource, IResourceRoles... expectedRoles) {
if (!hasAuthorizationForApplication(resource, expectedRoles)) {
throw new AccessDeniedException("user <" + SecurityContextHolder.getContext().getAuthentication().getName()
+ "> has no authorization to perform the requested operation on this application.");
}
}
/**
* Check that the user has one of the requested rights for the given cloud
*
* @param resource
* @param expectedRoles
*/
public static void checkAuthorizationForLocation(ISecuredResource resource, IResourceRoles... expectedRoles) {
if (!hasAuthorizationForLocation(resource, expectedRoles)) {
throw new AccessDeniedException("user <" + SecurityContextHolder.getContext().getAuthentication().getName()
+ "> has no authorization to perform the requested operation on this cloud.");
}
}
/**
* Check that the user has one of the requested rights for the given application environment
*
* @deprecated use {@link AuthorizationUtil#checkAuthorizationForEnvironment(ISecuredResource, ISecuredResource, IResourceRoles...)} instead
*
* @param resource
* @param expectedRoles
*/
@Deprecated
public static void checkAuthorizationForEnvironment(ISecuredResource resource, IResourceRoles... expectedRoles) {
if (!hasAuthorizationForEnvironment(resource, expectedRoles)) {
throw new AccessDeniedException("user <" + SecurityContextHolder.getContext().getAuthentication().getName()
+ "> has no authorization to perform the requested operation on this cloud.");
}
}
/**
* Check that the user has one of the requested rights for the given application environment
* The APPLICATION_MANAGER and DEPLOYMENT_MANAGER are gods of the environment
*
* @param application
* @param resource
* @param expectedRoles
*/
public static void checkAuthorizationForEnvironment(ISecuredResource application, ISecuredResource resource, IResourceRoles... expectedRoles) {
if (!hasAuthorizationForEnvironment(application, resource, expectedRoles)) {
throw new AccessDeniedException("user <" + SecurityContextHolder.getContext().getAuthentication().getName()
+ "> has no authorization to perform the requested operation on this environment.");
}
}
public static boolean hasAuthorizationForApplication(ISecuredResource resource, IResourceRoles... expectedRoles) {
return hasAuthorization(getCurrentUser(), resource, ApplicationRole.APPLICATION_MANAGER, expectedRoles);
}
public static boolean hasAuthorizationForLocation(ISecuredResource resource, IResourceRoles... expectedRoles) {
return hasAuthorization(getCurrentUser(), resource, CloudRole.CLOUD_DEPLOYER, expectedRoles);
}
/**
* @deprecated use {@link AuthorizationUtil#hasAuthorizationForEnvironment(ISecuredResource, ISecuredResource, IResourceRoles...)} instead
* @param resource
* @param expectedRoles
* @return
*/
@Deprecated
public static boolean hasAuthorizationForEnvironment(ISecuredResource resource, IResourceRoles... expectedRoles) {
return hasAuthorization(getCurrentUser(), resource, ApplicationEnvironmentRole.DEPLOYMENT_MANAGER, expectedRoles);
}
public static boolean hasAuthorizationForEnvironment(ISecuredResource application, ISecuredResource resource, IResourceRoles... expectedRoles) {
return hasAuthorizationForApplication(application)
|| hasAuthorization(getCurrentUser(), resource, ApplicationEnvironmentRole.DEPLOYMENT_MANAGER, expectedRoles);
}
/**
* Check that the current user has the one of the requested role and throw an {@link AccessDeniedException} in case the user has none of the expected roles.
*
* @param expectedRoles The list of roles that are required for the user (must have one of them).
*/
public static void checkHasOneRoleIn(Role... expectedRoles) {
final Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null) {
checkHasOneRoleIn(auth, expectedRoles);
}
}
/**
* Check that the current user has the one of the requested role and throw an {@link AccessDeniedException} in case the user has none of the expected roles.
*
* @param auth authentication information
* @param expectedRoles The list of roles that are required for the user (must have one of them).
*/
public static void checkHasOneRoleIn(Authentication auth, Role... expectedRoles) {
if (hasOneRoleIn(auth, expectedRoles)) {
return;
}
throw new AccessDeniedException("user <" + auth.getName() + "> has no authorization to perform the requested operation.");
}
/**
* True when authorities contains at least one of the expectedRoles or Role.ADMIN
*
* @param expectedRoles The list of expected roles.
* @return true if the user has the requested role or if user is ADMIN.
*/
public static boolean hasOneRoleIn(Role... expectedRoles) {
final Authentication auth = SecurityContextHolder.getContext().getAuthentication();
return hasOneRoleIn(auth, expectedRoles);
}
/**
* True when authorities contains at least one of the expectedRoles or Role.ADMIN
*
* @param auth authentication information
* @param expectedRoles The list of expected roles.
* @return true if the user has the requested role or if user is ADMIN.
*/
public static boolean hasOneRoleIn(Authentication auth, Role... expectedRoles) {
if (auth.getAuthorities().contains(new SimpleGrantedAuthority(Role.ADMIN.toString()))) {
return true;
}
for (GrantedAuthority authority : auth.getAuthorities()) {
for (Role role : expectedRoles) {
if (role.toString().equals(authority.getAuthority())) {
return true;
}
}
}
return false;
}
/**
* Add a filter that check for authorizations on resources
* Takes also in account the ALL_USER group
*/
public static FilterBuilder getResourceAuthorizationFilters() {
final Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth.getAuthorities().contains(new SimpleGrantedAuthority(Role.ADMIN.toString()))) {
return null;
}
FilterBuilder filterBuilder;
User user = (User) auth.getPrincipal();
if (user.getGroups() != null && !user.getGroups().isEmpty()) {
filterBuilder = FilterBuilders.boolFilter()
.should(FilterBuilders.nestedFilter("userRoles", FilterBuilders.termFilter("userRoles.key", auth.getName())))
.should(FilterBuilders.nestedFilter("groupRoles", FilterBuilders.inFilter("groupRoles.key", user.getGroups().toArray())));
} else {
filterBuilder = FilterBuilders.nestedFilter("userRoles", FilterBuilders.termFilter("userRoles.key", auth.getName()));
}
Group group = getAllUsersGroup();
if (group != null) {
String groupId = group.getId();
// add ALL_USERS group as OR filter
filterBuilder = FilterBuilders.orFilter(filterBuilder,
FilterBuilders.nestedFilter("groupRoles", FilterBuilders.inFilter("groupRoles.key", groupId)));
}
return filterBuilder;
}
/**
* Get current logged in user
*
* @return logged in user
*/
public static User getCurrentUser() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null && auth.getPrincipal() instanceof User) {
return (User) auth.getPrincipal();
}
return null;
}
/**
* Return the role that the current user has over the given resource
*
* @param user the user to check for
* @param resource the resource to check for
* @return all roles on the resource if any, empty set otherwise
*/
public static Set<String> getRolesForResource(User user, ISecuredResource resource) {
Set<String> allRoles = Sets.newHashSet();
if (resource.getUserRoles() != null) {
Set<String> userRoles = resource.getUserRoles().get(user.getUsername());
if (userRoles != null && !userRoles.isEmpty()) {
allRoles.addAll(userRoles);
}
}
Set<String> groups = user.getGroups();
if (groups == null) {
groups = Sets.newHashSet();
}
Group allUserGroup = getAllUsersGroup();
if (allUserGroup != null) {
groups.add(allUserGroup.getId());
}
Map<String, Set<String>> groupRolesMap = resource.getGroupRoles();
if (groupRolesMap != null && !groupRolesMap.isEmpty()) {
for (String group : groups) {
Set<String> groupRoles = groupRolesMap.get(group);
if (groupRoles == null || groupRoles.isEmpty()) {
continue;
} else {
allRoles.addAll(groupRoles);
}
}
}
return allRoles;
}
/**
* Return all the roles of an user (Alien 4 Cloud's roles)
*
* @param user
* @return all user's A4C roles
*/
private static Set<String> getRoles(User user) {
Set<String> allRoles = Sets.newHashSet();
String[] userRoles = user.getRoles();
Set<String> groupRoles = user.getGroupRoles();
if (userRoles != null && userRoles.length > 0) {
allRoles.addAll(Sets.newHashSet(userRoles));
}
if (groupRoles != null && !groupRoles.isEmpty()) {
allRoles.addAll(groupRoles);
}
return allRoles;
}
private static boolean hasAtLeastOneRole(Set<String> actualRoles, IResourceRoles adminRole, IResourceRoles... expectedRoles) {
if (actualRoles == null || actualRoles.isEmpty()) {
return false;
}
// Check admin role => true when got adminRole
if (adminRole != null && actualRoles.contains(adminRole.toString())) {
return true;
}
for (IResourceRoles expectedRole : expectedRoles) {
if (actualRoles.contains(expectedRole.toString())) {
return true;
}
}
return false;
}
/**
* Check if user is authorized
*
* @param user the user to check for
* @param resource the resource to check for
* @param resourceAdminRole the role which has the god/admin right on the resource
* @param expectedRoles the role that the user is expected to have in order to have access to the resources
* @throws org.springframework.security.access.AccessDeniedException if user is not authorized
*/
public static void checkAuthorization(User user, ISecuredResource resource, IResourceRoles resourceAdminRole, IResourceRoles... expectedRoles) {
if (!hasAuthorization(user, resource, resourceAdminRole, expectedRoles)) {
throw new AccessDeniedException("user <" + user.getUsername() + "> has no authorization to perform the requested operation on this cloud.");
}
}
/**
* Check if user is authorized
*
* @param user the user to check for
* @param resource the resource to check for
* @param resourceAdminRole the role which has the god/admin right on the resource
* @param expectedRoles the role that the user is expected to have in order to have access to the resources
* @return true if user has access, false otherwise
*/
public static boolean hasAuthorization(User user, ISecuredResource resource, IResourceRoles resourceAdminRole, IResourceRoles... expectedRoles) {
if (resource == null) {
// Trick for topology's template
return true;
}
Set<String> alienRoles = getRoles(user);
// With ADMIN role, all rights
if (alienRoles.contains(Role.ADMIN.toString())) {
return true;
}
Set<String> appRoles = getRolesForResource(user, resource);
return hasAtLeastOneRole(appRoles, resourceAdminRole, expectedRoles);
}
/**
* check if the current user is authorized
*
* @param resource the resource to check for
* @param resourceAdminRole the role which has the god/admin right on the resource
* @param expectedRoles the role that the user is expected to have in order to have access to the resources
* @return true if user has access, false otherwise
*/
public static boolean hasAuthorization(ISecuredResource resource, IResourceRoles resourceAdminRole, IResourceRoles... expectedRoles) {
return hasAuthorization(getCurrentUser(), resource, resourceAdminRole, expectedRoles);
}
/**
* Recover the alien's default all user group
*
* @return
*/
private static Group getAllUsersGroup() {
Group group = alienGroupDao.findByName(Constants.GROUP_NAME_ALL_USERS);
if (group == null) {
log.warn("Default all users group <{}> not found", Constants.GROUP_NAME_ALL_USERS);
return null;
}
return group;
}
/**
* Create an authentication token from an Alien user
*
* @param user the alien user
* @param password the password
* @return the authentication token
*/
public static UsernamePasswordAuthenticationToken createAuthenticationToken(User user, String password) {
Set<SimpleGrantedAuthority> authorities = user.getAuthorities();
Group allUserGroup = getAllUsersGroup();
if (allUserGroup != null) {
Set<String> allUserGroupRoles = allUserGroup.getRoles();
if (allUserGroupRoles != null) {
Set<SimpleGrantedAuthority> mergedAuthorities;
if (authorities != null) {
mergedAuthorities = Sets.newHashSet(authorities);
} else {
mergedAuthorities = Sets.newHashSet();
}
for (String allUserGroupRole : allUserGroupRoles) {
mergedAuthorities.add(new SimpleGrantedAuthority(allUserGroupRole));
}
return new UsernamePasswordAuthenticationToken(user, password, mergedAuthorities);
}
}
return new UsernamePasswordAuthenticationToken(user, password, authorities);
}
/**
* Check if a group has only one specific role on a resource
*
* @param groupId
* @param resource
* @param role
* @return true when the role is found and it is unique
*/
public static boolean hasUniqueGroupRoleOnResource(String groupId, ISecuredResource resource, IResourceRoles role) {
Map<String, Set<String>> allResourceGroupRoles = resource.getGroupRoles();
Set<String> groupRoles = allResourceGroupRoles.get(groupId);
return groupRoles.size() == 1 && groupRoles.contains(role.toString());
}
/**
* Check if a user has only a specific role on a resource
*
* @param user
* @param resource
* @param role
* @return true when the role is found and it is unique
*/
public static boolean hasUniqueUserRoleOnResource(User user, ISecuredResource resource, IResourceRoles role) {
Map<String, Set<String>> allResourceUserRoles = resource.getUserRoles();
Set<String> userRoles = allResourceUserRoles.get(user.getUsername());
return userRoles.size() == 1 && userRoles.contains(role.toString());
}
/**
* Utility method to configure the application endpoint whatever security implementation is defined.
*
* @param httpSecurity The http security object to configure.
* @throws Exception see httpSecurity.authorizeRequests()
*/
public static void configure(HttpSecurity httpSecurity, LogoutSuccessHandler successLogoutHandler) throws Exception {
// authorizations
httpSecurity.authorizeRequests().antMatchers("/*").permitAll();
httpSecurity.authorizeRequests().antMatchers("/static/tosca/**").hasAnyAuthority("ADMIN", "COMPONENTS_MANAGER", "COMPONENTS_BROWSER");
httpSecurity.authorizeRequests().antMatchers("/rest/admin/**").hasAuthority("ADMIN");
// FIXME Secure the editor data
// httpSecurity.authorizeRequests().antMatchers("/static/editor/**").access("hasPermission(#contact, 'admin')");
// previous api version support
httpSecurity.authorizeRequests().antMatchers("/rest/alienEndPoint/**").authenticated();
httpSecurity.authorizeRequests().antMatchers("/rest/audit/**").hasAuthority("ADMIN");
// current api version support
httpSecurity.authorizeRequests().antMatchers("/rest/v1/alienEndPoint/**").authenticated();
httpSecurity.authorizeRequests().antMatchers("/rest/v1/audit/**").hasAuthority("ADMIN");
// login
httpSecurity.formLogin().defaultSuccessUrl("/rest/auth/status").failureUrl("/rest/auth/authenticationfailed").loginProcessingUrl("/login")
.usernameParameter("username").passwordParameter("password").permitAll();
if (successLogoutHandler == null) {
httpSecurity.logout().logoutSuccessUrl("/").deleteCookies("JSESSIONID");
} else {
httpSecurity.getConfigurer(LogoutConfigurer.class).logoutSuccessHandler(successLogoutHandler);
}
// handle non authenticated request
httpSecurity.exceptionHandling().accessDeniedHandler(accessDeniedHandler);
httpSecurity.exceptionHandling().authenticationEntryPoint(new FailureAuthenticationEntryPoint());
httpSecurity.csrf().disable();
}
}