/* * 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 privileges and * limitations under the License. */ package org.apache.ambari.server.controller.internal; import java.util.Collection; import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.concurrent.TimeUnit; import org.apache.ambari.server.controller.spi.NoSuchParentResourceException; import org.apache.ambari.server.controller.spi.NoSuchResourceException; import org.apache.ambari.server.controller.spi.Predicate; import org.apache.ambari.server.controller.spi.Request; import org.apache.ambari.server.controller.spi.Resource; import org.apache.ambari.server.controller.spi.SystemException; import org.apache.ambari.server.controller.spi.UnsupportedPropertyException; import org.apache.ambari.server.orm.dao.ClusterDAO; import org.apache.ambari.server.orm.dao.GroupDAO; import org.apache.ambari.server.orm.dao.UserDAO; import org.apache.ambari.server.orm.dao.ViewInstanceDAO; import org.apache.ambari.server.orm.entities.ClusterEntity; import org.apache.ambari.server.orm.entities.GroupEntity; import org.apache.ambari.server.orm.entities.PrincipalEntity; import org.apache.ambari.server.orm.entities.PrincipalTypeEntity; import org.apache.ambari.server.orm.entities.PrivilegeEntity; import org.apache.ambari.server.orm.entities.UserEntity; import org.apache.ambari.server.orm.entities.ViewEntity; import org.apache.ambari.server.orm.entities.ViewInstanceEntity; import org.apache.ambari.server.security.authorization.AuthorizationException; import org.apache.ambari.server.security.authorization.AuthorizationHelper; import org.apache.ambari.server.security.authorization.ResourceType; import org.apache.ambari.server.security.authorization.RoleAuthorization; import org.apache.ambari.server.security.authorization.UserType; import org.apache.ambari.server.security.authorization.Users; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; /** * Resource provider for user privilege resources. */ public class UserPrivilegeResourceProvider extends ReadOnlyResourceProvider { protected static final String PRIVILEGE_PRIVILEGE_ID_PROPERTY_ID = PrivilegeResourceProvider.PRIVILEGE_ID_PROPERTY_ID; protected static final String PRIVILEGE_PERMISSION_NAME_PROPERTY_ID = PrivilegeResourceProvider.PERMISSION_NAME_PROPERTY_ID; protected static final String PRIVILEGE_PERMISSION_LABEL_PROPERTY_ID = PrivilegeResourceProvider.PERMISSION_LABEL_PROPERTY_ID; protected static final String PRIVILEGE_PRINCIPAL_NAME_PROPERTY_ID = PrivilegeResourceProvider.PRINCIPAL_NAME_PROPERTY_ID; protected static final String PRIVILEGE_PRINCIPAL_TYPE_PROPERTY_ID = PrivilegeResourceProvider.PRINCIPAL_TYPE_PROPERTY_ID; protected static final String PRIVILEGE_VIEW_NAME_PROPERTY_ID = ViewPrivilegeResourceProvider.PRIVILEGE_VIEW_NAME_PROPERTY_ID; protected static final String PRIVILEGE_VIEW_VERSION_PROPERTY_ID = ViewPrivilegeResourceProvider.PRIVILEGE_VIEW_VERSION_PROPERTY_ID; protected static final String PRIVILEGE_INSTANCE_NAME_PROPERTY_ID = ViewPrivilegeResourceProvider.PRIVILEGE_INSTANCE_NAME_PROPERTY_ID; protected static final String PRIVILEGE_CLUSTER_NAME_PROPERTY_ID = ClusterPrivilegeResourceProvider.PRIVILEGE_CLUSTER_NAME_PROPERTY_ID; protected static final String PRIVILEGE_TYPE_PROPERTY_ID = AmbariPrivilegeResourceProvider.PRIVILEGE_TYPE_PROPERTY_ID; protected static final String PRIVILEGE_USER_NAME_PROPERTY_ID = "PrivilegeInfo/user_name"; /** * Data access object used to obtain user entities. */ protected static UserDAO userDAO; /** * Data access object used to obtain cluster entities. */ protected static ClusterDAO clusterDAO; /** * Data access object used to obtain group entities. */ protected static GroupDAO groupDAO; /** * Data access object used to obtain view instance entities. */ protected static ViewInstanceDAO viewInstanceDAO; /** * Helper to obtain privilege data for requested users */ private static Users users; /** * The property ids for a privilege resource. */ private static Set<String> propertyIds = new HashSet<>(); static { propertyIds.add(PRIVILEGE_PRIVILEGE_ID_PROPERTY_ID); propertyIds.add(PRIVILEGE_PERMISSION_NAME_PROPERTY_ID); propertyIds.add(PRIVILEGE_PERMISSION_LABEL_PROPERTY_ID); propertyIds.add(PRIVILEGE_PRINCIPAL_NAME_PROPERTY_ID); propertyIds.add(PRIVILEGE_PRINCIPAL_TYPE_PROPERTY_ID); propertyIds.add(PRIVILEGE_VIEW_NAME_PROPERTY_ID); propertyIds.add(PRIVILEGE_VIEW_VERSION_PROPERTY_ID); propertyIds.add(PRIVILEGE_INSTANCE_NAME_PROPERTY_ID); propertyIds.add(PRIVILEGE_CLUSTER_NAME_PROPERTY_ID); propertyIds.add(PRIVILEGE_TYPE_PROPERTY_ID); propertyIds.add(PRIVILEGE_USER_NAME_PROPERTY_ID); } /** * Static initialization. * @param userDAO the user data access object * @param clusterDAO the cluster data access object * @param groupDAO the group data access object * @param viewInstanceDAO the view instance data access object * @param users the Users helper object */ public static void init(UserDAO userDAO, ClusterDAO clusterDAO, GroupDAO groupDAO, ViewInstanceDAO viewInstanceDAO, Users users) { UserPrivilegeResourceProvider.userDAO = userDAO; UserPrivilegeResourceProvider.clusterDAO = clusterDAO; UserPrivilegeResourceProvider.groupDAO = groupDAO; UserPrivilegeResourceProvider.viewInstanceDAO = viewInstanceDAO; UserPrivilegeResourceProvider.users = users; } @SuppressWarnings("serial") private static Set<String> pkPropertyIds = new HashSet<String>() { { add(PRIVILEGE_PRIVILEGE_ID_PROPERTY_ID); } }; /** * The key property ids for a privilege resource. */ private static Map<Resource.Type, String> keyPropertyIds = new HashMap<>(); static { keyPropertyIds.put(Resource.Type.User, PRIVILEGE_USER_NAME_PROPERTY_ID); keyPropertyIds.put(Resource.Type.UserPrivilege, PRIVILEGE_PRIVILEGE_ID_PROPERTY_ID); } private ThreadLocal<LoadingCache<Long, ClusterEntity>> clusterCache = new ThreadLocal<LoadingCache<Long, ClusterEntity>>(){ @Override protected LoadingCache<Long, ClusterEntity> initialValue() { CacheLoader<Long, ClusterEntity> loader = new CacheLoader<Long, ClusterEntity>() { @Override public ClusterEntity load(Long key) throws Exception { return clusterDAO.findByResourceId(key); } }; return CacheBuilder.newBuilder().expireAfterWrite(20, TimeUnit.SECONDS).build(loader); } }; private ThreadLocal<LoadingCache<Long, ViewInstanceEntity>> viewInstanceCache = new ThreadLocal<LoadingCache<Long, ViewInstanceEntity>>(){ @Override protected LoadingCache<Long, ViewInstanceEntity> initialValue() { CacheLoader<Long, ViewInstanceEntity> loader = new CacheLoader<Long, ViewInstanceEntity>() { @Override public ViewInstanceEntity load(Long key) throws Exception { return viewInstanceDAO.findByResourceId(key); } }; return CacheBuilder.newBuilder().expireAfterWrite(20, TimeUnit.SECONDS).build(loader); } }; private ThreadLocal<LoadingCache<String, UserEntity>> usersCache = new ThreadLocal<LoadingCache<String, UserEntity>>(){ @Override protected LoadingCache<String, UserEntity> initialValue() { CacheLoader<String, UserEntity> loader = new CacheLoader<String, UserEntity>() { @Override public UserEntity load(String key) throws Exception { //fallback mechanism, mostly for unit tests UserEntity userEntity = userDAO.findLocalUserByName(key); if (userEntity == null) { userEntity = userDAO.findLdapUserByName(key); } if (userEntity == null) { userEntity = userDAO.findUserByNameAndType(key, UserType.JWT); } return userEntity; } }; return CacheBuilder.newBuilder() .expireAfterWrite(20, TimeUnit.SECONDS) .build(loader); } }; private ThreadLocal<LoadingCache<PrincipalEntity, GroupEntity>> groupsCache = new ThreadLocal<LoadingCache<PrincipalEntity, GroupEntity>>(){ @Override protected LoadingCache<PrincipalEntity, GroupEntity> initialValue() { CacheLoader<PrincipalEntity, GroupEntity> loader = new CacheLoader<PrincipalEntity, GroupEntity>() { @Override public GroupEntity load(PrincipalEntity key) throws Exception { return groupDAO.findGroupByPrincipal(key); } }; return CacheBuilder.newBuilder() .expireAfterWrite(20, TimeUnit.SECONDS) .build(loader); } }; private GroupEntity getCachedGroupByPrincipal(PrincipalEntity principalEntity) { GroupEntity entity = groupsCache.get().getIfPresent(principalEntity); if (entity == null) { for (GroupEntity groupEntity : groupDAO.findAll()) { groupsCache.get().put(groupEntity.getPrincipal(), groupEntity); } entity = groupsCache.get().getUnchecked(principalEntity); } return entity; } /** * Constructor. */ public UserPrivilegeResourceProvider() { super(propertyIds, keyPropertyIds, null); EnumSet<RoleAuthorization> requiredAuthorizations = EnumSet.of(RoleAuthorization.AMBARI_ASSIGN_ROLES); setRequiredCreateAuthorizations(requiredAuthorizations); setRequiredDeleteAuthorizations(requiredAuthorizations); setRequiredGetAuthorizations(requiredAuthorizations); setRequiredUpdateAuthorizations(requiredAuthorizations); } // ----- PrivilegeResourceProvider ----------------------------------------- @Override protected Set<String> getPKPropertyIds() { return pkPropertyIds; } @Override public Set<Resource> getResources(Request request, Predicate predicate) throws SystemException, UnsupportedPropertyException, NoSuchResourceException, NoSuchParentResourceException { final Set<Resource> resources = new HashSet<>(); final Set<String> requestedIds = getRequestPropertyIds(request, predicate); boolean isUserAdministrator = AuthorizationHelper.isAuthorized(ResourceType.AMBARI, null, RoleAuthorization.AMBARI_MANAGE_USERS); for (Map<String, Object> propertyMap : getPropertyMaps(predicate)) { final String userName = (String) propertyMap.get(PRIVILEGE_USER_NAME_PROPERTY_ID); // Ensure that the authenticated user has authorization to get this information if (!isUserAdministrator && !AuthorizationHelper.getAuthenticatedName().equalsIgnoreCase(userName)) { throw new AuthorizationException(); } if (userName != null) { UserEntity userEntity = usersCache.get().getIfPresent(userName); if (userEntity == null) { //temporary tradeoff, add ~200ms for single user call, but start saving time for 100+ subsequent calls //usual case for management page is to populate subresources for all users Map<String, UserEntity> userNames = new TreeMap<>(); for (UserEntity entity : userDAO.findAll()) { UserEntity existing = userNames.get(entity.getUserName()); if (existing == null || entity.getUserType() == UserType.LOCAL || existing.getUserType() == UserType.JWT) { userNames.put(entity.getUserName(), entity); } } usersCache.get().putAll(userNames); userEntity = usersCache.get().getUnchecked(userName); } if (userEntity == null) { userEntity = userDAO.findUserByNameAndType(userName, UserType.PAM); } if (userEntity == null) { throw new SystemException("User " + userName + " was not found"); } final Collection<PrivilegeEntity> privileges = users.getUserPrivileges(userEntity); for (PrivilegeEntity privilegeEntity : privileges) { resources.add(toResource(privilegeEntity, userName, requestedIds)); } } } return resources; } /** * Translate the found data into a Resource * * @param privilegeEntity the privilege data * @param userName the username * @param requestedIds the relevant request ids * @return a resource */ protected Resource toResource(PrivilegeEntity privilegeEntity, Object userName, Set<String> requestedIds){ final ResourceImpl resource = new ResourceImpl(Resource.Type.UserPrivilege); setResourceProperty(resource, PRIVILEGE_USER_NAME_PROPERTY_ID, userName, requestedIds); setResourceProperty(resource, PRIVILEGE_PRIVILEGE_ID_PROPERTY_ID, privilegeEntity.getId(), requestedIds); setResourceProperty(resource, PRIVILEGE_PERMISSION_NAME_PROPERTY_ID, privilegeEntity.getPermission().getPermissionName(), requestedIds); setResourceProperty(resource, PRIVILEGE_PERMISSION_LABEL_PROPERTY_ID, privilegeEntity.getPermission().getPermissionLabel(), requestedIds); setResourceProperty(resource, PRIVILEGE_PRINCIPAL_TYPE_PROPERTY_ID, privilegeEntity.getPrincipal().getPrincipalType().getName(), requestedIds); final String principalTypeName = privilegeEntity.getPrincipal().getPrincipalType().getName(); if (principalTypeName.equals(PrincipalTypeEntity.USER_PRINCIPAL_TYPE_NAME)) { final UserEntity user = userDAO.findUserByPrincipal(privilegeEntity.getPrincipal()); setResourceProperty(resource, PRIVILEGE_PRINCIPAL_NAME_PROPERTY_ID, user.getUserName(), requestedIds); } else if (principalTypeName.equals(PrincipalTypeEntity.GROUP_PRINCIPAL_TYPE_NAME)) { final GroupEntity groupEntity = getCachedGroupByPrincipal(privilegeEntity.getPrincipal()); setResourceProperty(resource, PRIVILEGE_PRINCIPAL_NAME_PROPERTY_ID, groupEntity.getGroupName(), requestedIds); } String typeName = privilegeEntity.getResource().getResourceType().getName(); ResourceType resourceType = ResourceType.translate(typeName); if(resourceType != null) { switch (resourceType) { case AMBARI: // there is nothing special to add for this case break; case CLUSTER: final ClusterEntity clusterEntity = clusterCache.get().getUnchecked(privilegeEntity.getResource().getId()); setResourceProperty(resource, PRIVILEGE_CLUSTER_NAME_PROPERTY_ID, clusterEntity.getClusterName(), requestedIds); break; case VIEW: final ViewInstanceEntity viewInstanceEntity = viewInstanceCache.get().getUnchecked(privilegeEntity.getResource().getId()); final ViewEntity viewEntity = viewInstanceEntity.getViewEntity(); setResourceProperty(resource, PRIVILEGE_VIEW_NAME_PROPERTY_ID, viewEntity.getCommonName(), requestedIds); setResourceProperty(resource, PRIVILEGE_VIEW_VERSION_PROPERTY_ID, viewEntity.getVersion(), requestedIds); setResourceProperty(resource, PRIVILEGE_INSTANCE_NAME_PROPERTY_ID, viewInstanceEntity.getName(), requestedIds); break; } setResourceProperty(resource, PRIVILEGE_TYPE_PROPERTY_ID, resourceType.name(), requestedIds); } return resource; } }