/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.ambari.server.security.authorization; import java.util.Collection; import java.util.EnumSet; import java.util.HashSet; import java.util.List; import java.util.Set; import org.apache.ambari.server.orm.dao.PrivilegeDAO; import org.apache.ambari.server.orm.dao.ViewInstanceDAO; import org.apache.ambari.server.orm.entities.PermissionEntity; import org.apache.ambari.server.orm.entities.PrivilegeEntity; import org.apache.ambari.server.orm.entities.ResourceEntity; import org.apache.ambari.server.orm.entities.RoleAuthorizationEntity; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import com.google.common.collect.Lists; import com.google.inject.Inject; import com.google.inject.Provider; import com.google.inject.Singleton; /** * Provides utility methods for authentication functionality */ @Singleton public class AuthorizationHelper { private final static Logger LOG = LoggerFactory.getLogger(AuthorizationHelper.class); @Inject static Provider<PrivilegeDAO> privilegeDAOProvider; @Inject static Provider<ViewInstanceDAO> viewInstanceDAOProvider; /** * Converts collection of RoleEntities to collection of GrantedAuthorities */ public Collection<GrantedAuthority> convertPrivilegesToAuthorities(Collection<PrivilegeEntity> privilegeEntities) { Set<GrantedAuthority> authorities = new HashSet<>(privilegeEntities.size()); for (PrivilegeEntity privilegeEntity : privilegeEntities) { authorities.add(new AmbariGrantedAuthority(privilegeEntity)); } return authorities; } /** * Gets the name of the logged in user. Thread-safe due to use of thread-local. * * @return the name of the logged in user, or <code>null</code> if none set. */ public static String getAuthenticatedName() { return getAuthenticatedName(null); } /** * Gets the name of the logged-in user, if any. Thread-safe due to use of * thread-local. * * @param defaultUsername the value if there is no logged-in user * @return the name of the logged-in user, or the default */ public static String getAuthenticatedName(String defaultUsername) { SecurityContext securityContext = SecurityContextHolder.getContext(); Authentication auth = securityContext.getAuthentication(); return (null == auth) ? defaultUsername : auth.getName(); } /** * Gets the ID of the logged-in user. Thread-safe due to use of * thread-local. * * @return the ID of the logged-in user */ public static int getAuthenticatedId() { SecurityContext securityContext = SecurityContextHolder.getContext(); Authentication authentication = securityContext.getAuthentication(); UserIdAuthentication auth; if (authentication instanceof UserIdAuthentication) { auth = (UserIdAuthentication) authentication; } else { return -1; } return auth.getUserId(); } /** * Determines if the authenticated user (from application's security context) is authorized to * perform an operation on the specific resource by matching the authenticated user's * authorizations with the one indicated. * * @param resourceType a resource type being acted upon * @param resourceId the privilege resource id (or adminresource.id) of the relevant resource * @param requiredAuthorization the required authorization * @return true if authorized; otherwise false * @see #isAuthorized(Authentication, ResourceType, Long, Set) */ public static boolean isAuthorized(ResourceType resourceType, Long resourceId, RoleAuthorization requiredAuthorization) { return isAuthorized(getAuthentication(), resourceType, resourceId, EnumSet.of(requiredAuthorization)); } /** * Determines if the authenticated user (from application's security context) is authorized to * perform an operation on the specific resource by matching the authenticated user's * authorizations with one from the provided set of authorizations. * * @param resourceType a resource type being acted upon * @param resourceId the privilege resource id (or adminresource.id) of the relevant resource * @param requiredAuthorizations a set of requirements for which one match will allow authorization * @return true if authorized; otherwise false * @see #isAuthorized(Authentication, ResourceType, Long, Set) */ public static boolean isAuthorized(ResourceType resourceType, Long resourceId, Set<RoleAuthorization> requiredAuthorizations) { return isAuthorized(getAuthentication(), resourceType, resourceId, requiredAuthorizations); } /** * Determines if the specified authenticated user is authorized to perform an operation on the * specific resource by matching the authenticated user's authorizations with the one indicated. * * @param authentication the authenticated user and associated access privileges * @param resourceType a resource type being acted upon * @param resourceId the privilege resource id (or adminresource.id) of the relevant resource * @param requiredAuthorization the required authorization * @return true if authorized; otherwise false * @see #isAuthorized(Authentication, ResourceType, Long, Set) */ public static boolean isAuthorized(Authentication authentication, ResourceType resourceType, Long resourceId, RoleAuthorization requiredAuthorization) { return isAuthorized(authentication, resourceType, resourceId, EnumSet.of(requiredAuthorization)); } /** * Determines if the specified authenticated user is authorized to perform an operation on the * the specific resource by matching the authenticated user's authorizations with one from the * provided set of authorizations. * <p/> * The specified resource type is a high-level resource such as {@link ResourceType#AMBARI Ambari}, * a {@link ResourceType#CLUSTER cluster}, or a {@link ResourceType#VIEW view}. * <p/> * The specified resource id is the (admin)resource id referenced by a specific resource instance * such as a cluster or view. * * @param authentication the authenticated user and associated access privileges * @param resourceType a resource type being acted upon * @param resourceId the privilege resource id (or adminresource.id) of the relevant resource * @param requiredAuthorizations a set of requirements for which one match will allow authorization * @return true if authorized; otherwise false */ public static boolean isAuthorized(Authentication authentication, ResourceType resourceType, Long resourceId, Set<RoleAuthorization> requiredAuthorizations) { if ((requiredAuthorizations == null) || requiredAuthorizations.isEmpty()) { return true; } else if (authentication == null) { return false; } else { // Iterate through the set of required authorizations to see if at least one match is found. // If the user has at least one authorization that exists in the set of required authorizations, // that user is authorized to perform the operation. for (GrantedAuthority grantedAuthority : authentication.getAuthorities()) { AmbariGrantedAuthority ambariGrantedAuthority = (AmbariGrantedAuthority) grantedAuthority; PrivilegeEntity privilegeEntity = ambariGrantedAuthority.getPrivilegeEntity(); ResourceEntity privilegeResource = privilegeEntity.getResource(); ResourceType privilegeResourceType = ResourceType.translate(privilegeResource.getResourceType().getName()); boolean resourceOK; if (ResourceType.AMBARI == privilegeResourceType) { // This resource type indicates administrative access resourceOK = true; } else if ((resourceType == null) || (resourceType == privilegeResourceType)) { resourceOK = (resourceId == null) || resourceId.equals(privilegeResource.getId()); } else { resourceOK = false; } // The the authority is for the relevant resource, see if one of the authorizations matches // one of the required authorizations... if (resourceOK) { PermissionEntity permission = privilegeEntity.getPermission(); Collection<RoleAuthorizationEntity> userAuthorizations = (permission == null) ? null : permission.getAuthorizations(); if (userAuthorizations != null) { for (RoleAuthorizationEntity userAuthorization : userAuthorizations) { try { if (requiredAuthorizations.contains(RoleAuthorization.translate(userAuthorization.getAuthorizationId()))) { return true; } } catch (IllegalArgumentException e) { LOG.warn("Invalid authorization name, '{}'... ignoring.", userAuthorization.getAuthorizationId()); } } } } } return false; } } /** * Determines if the authenticated user (from application's security context) is authorized to * perform an operation on the the specific resource by matching the authenticated user's * authorizations with one from the provided set of authorizations. * <p/> * If not authorized, an {@link AuthorizationException} will be thrown. * * @param resourceType a resource type being acted upon * @param resourceId the privilege resource id (or adminresource.id) of the relevant resource * @param requiredAuthorizations a set of requirements for which one match will allow authorization * @throws AuthorizationException if authorization is not granted * @see #isAuthorized(ResourceType, Long, Set) */ public static void verifyAuthorization(ResourceType resourceType, Long resourceId, Set<RoleAuthorization> requiredAuthorizations) throws AuthorizationException { if (!isAuthorized(resourceType, resourceId, requiredAuthorizations)) { throw new AuthorizationException(); } } /** * Determines if the specified authenticated user is authorized to perform an operation on the * the specific resource by matching the authenticated user's authorizations with one from the * provided set of authorizations. * <p/> * If not authorized, an {@link AuthorizationException} will be thrown. * * @param authentication the authenticated user and associated access privileges * @param resourceType a resource type being acted upon * @param resourceId the privilege resource id (or adminresource.id) of the relevant resource * @param requiredAuthorizations a set of requirements for which one match will allow authorization * @throws AuthorizationException if authorization is not granted * @see #isAuthorized(Authentication, ResourceType, Long, Set) */ public static void verifyAuthorization(Authentication authentication, ResourceType resourceType, Long resourceId, Set<RoleAuthorization> requiredAuthorizations) throws AuthorizationException { if (!isAuthorized(authentication, resourceType, resourceId, requiredAuthorizations)) { throw new AuthorizationException(); } } /** * Retrieves the authenticated user and authorization details from the application's security context. * * @return the authenticated user and associated access privileges; or null if not available */ public static Authentication getAuthentication() { SecurityContext context = SecurityContextHolder.getContext(); return (context == null) ? null : context.getAuthentication(); } /** * There are cases when users log-in with a login name that is * define in LDAP and which do not correspond to the user name stored * locally in ambari. These external login names act as an alias to * ambari users name. This method stores in the current http session a mapping * of alias user name to local ambari user name to make possible resolving * login alias to ambari user name. * @param ambariUserName ambari user name for which the alias is to be stored in the session * @param loginAlias the alias for the ambari user name. */ public static void addLoginNameAlias(String ambariUserName, String loginAlias) { ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); if (attr != null) { LOG.info("Adding login alias '{}' for user name '{}'", loginAlias, ambariUserName); attr.setAttribute(loginAlias, ambariUserName, RequestAttributes.SCOPE_SESSION); } } /** * Looks up the provided loginAlias in the current http session and return the ambari * user name that the alias is defined for. * @param loginAlias the login alias to resolve to ambari user name * @return the ambari user name if the alias is found otherwise returns the passed in loginAlias */ public static String resolveLoginAliasToUserName(String loginAlias) { ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); if (attr != null && attr.getAttribute(loginAlias, RequestAttributes.SCOPE_SESSION) != null) { return (String)attr.getAttribute(loginAlias, RequestAttributes.SCOPE_SESSION); } return loginAlias; } /** * Retrieve authorization names based on the details of the authenticated user * @param authentication the authenticated user and associated access privileges * @return human readable role authorizations */ public static List<String> getAuthorizationNames(Authentication authentication) { List<String> authorizationNames = Lists.newArrayList(); if (authentication.getAuthorities() != null) { for (GrantedAuthority grantedAuthority : authentication.getAuthorities()) { AmbariGrantedAuthority ambariGrantedAuthority = (AmbariGrantedAuthority) grantedAuthority; PrivilegeEntity privilegeEntity = ambariGrantedAuthority.getPrivilegeEntity(); Collection<RoleAuthorizationEntity> roleAuthorizationEntities = privilegeEntity.getPermission().getAuthorizations(); for (RoleAuthorizationEntity entity : roleAuthorizationEntities) { authorizationNames.add(entity.getAuthorizationName()); } } } return authorizationNames; } }