Repository: ambari Updated Branches: refs/heads/branch-2.4 065c2a8cf -> 6ffa3f8fa
AMBARI-16247. Authorizations given to role-based principals must be dereferenced upon user login (rlevas) Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/6ffa3f8f Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/6ffa3f8f Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/6ffa3f8f Branch: refs/heads/branch-2.4 Commit: 6ffa3f8fa86b258e09cf83c91b7dbe650c3ea41e Parents: 065c2a8 Author: Robert Levas <rle...@hortonworks.com> Authored: Wed Jun 8 11:53:04 2016 -0400 Committer: Robert Levas <rle...@hortonworks.com> Committed: Wed Jun 8 11:53:04 2016 -0400 ---------------------------------------------------------------------- .../server/security/authorization/Users.java | 46 +++++- .../security/authorization/UsersTest.java | 145 +++++++++++++++++++ 2 files changed, 188 insertions(+), 3 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/6ffa3f8f/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/Users.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/Users.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/Users.java index 545095d..f1abb90 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/Users.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/Users.java @@ -44,7 +44,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.crypto.password.PasswordEncoder; @@ -447,7 +446,7 @@ public class Users { /** * Grants AMBARI.ADMINISTRATOR privilege to provided user. * - * @param user user + * @param userId user id */ public synchronized void grantAdminPrivilege(Integer userId) { final UserEntity user = userDAO.findByPK(userId); @@ -466,7 +465,7 @@ public class Users { /** * Revokes AMBARI.ADMINISTRATOR privilege from provided user. * - * @param user user + * @param userId user id */ public synchronized void revokeAdminPrivilege(Integer userId) { final UserEntity user = userDAO.findByPK(userId); @@ -711,6 +710,23 @@ public class Users { entityManagerProvider.get().getEntityManagerFactory().getCache().evictAll(); } + /** + * Gets the explicit and implicit authorities for the given user. + * <p> + * The explicit authorities are the authorities that have be explicitly set by assigning roles to + * a user. For example the Cluster Operator role on a given cluster gives that the ability to + * start and stop services in that cluster, among other privileges for that particular cluster. + * <p> + * The implicit authorities are the authorities that have been given to the roles themselves which + * in turn are granted to the users that have been assigned those roles. For example if the + * Cluster User role for a given cluster has been given View User access on a specified File View + * instance, then all users who have the Cluster User role for that cluster will implicitly be + * granted View User access on that File View instance. + * + * @param userName the username for the relevant user + * @param userType the user type for the relevant user + * @return the users collection of implicit and explicit granted authorities + */ public Collection<AmbariGrantedAuthority> getUserAuthorities(String userName, UserType userType) { UserEntity userEntity = userDAO.findUserByNameAndType(userName, userType); if (userEntity == null) { @@ -730,12 +746,36 @@ public class Users { List<PrivilegeEntity> privilegeEntities = privilegeDAO.findAllByPrincipal(principalEntities); + // A list of principals representing roles/permissions. This collection of roles will be used to + // find additional authorizations inherited by the authenticated user based on the assigned roles. + // For example a File View instance may be set to be accessible to all authenticated user with + // the Cluster User role. + List<PrincipalEntity> rolePrincipals = new ArrayList<PrincipalEntity>(); + Set<AmbariGrantedAuthority> authorities = new HashSet<>(privilegeEntities.size()); for (PrivilegeEntity privilegeEntity : privilegeEntities) { + // Add the principal representing the role associated with this PrivilegeEntity to the collection + // of roles for the authenticated user. + PrincipalEntity rolePrincipal = privilegeEntity.getPermission().getPrincipal(); + if(rolePrincipal != null) { + rolePrincipals.add(rolePrincipal); + } + authorities.add(new AmbariGrantedAuthority(privilegeEntity)); } + // If the collections of assigned roles is not empty find the inherited authorizations that are + // give to the roles and add them to the collection of (Granted) authorities for the user. + if(!rolePrincipals.isEmpty()) { + // For each "role" see if any privileges have been granted... + List<PrivilegeEntity> rolePrivilegeEntities = privilegeDAO.findAllByPrincipal(rolePrincipals); + + for (PrivilegeEntity privilegeEntity : rolePrivilegeEntities) { + authorities.add(new AmbariGrantedAuthority(privilegeEntity)); + } + } + return authorities; } http://git-wip-us.apache.org/repos/asf/ambari/blob/6ffa3f8f/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/UsersTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/UsersTest.java b/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/UsersTest.java new file mode 100644 index 0000000..f059abc --- /dev/null +++ b/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/UsersTest.java @@ -0,0 +1,145 @@ +/* + * 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 com.google.inject.AbstractModule; +import com.google.inject.Guice; +import com.google.inject.Injector; +import junit.framework.Assert; +import org.apache.ambari.server.orm.DBAccessor; +import org.apache.ambari.server.orm.dao.MemberDAO; +import org.apache.ambari.server.orm.dao.PrivilegeDAO; +import org.apache.ambari.server.orm.dao.UserDAO; +import org.apache.ambari.server.orm.entities.GroupEntity; +import org.apache.ambari.server.orm.entities.MemberEntity; +import org.apache.ambari.server.orm.entities.PermissionEntity; +import org.apache.ambari.server.orm.entities.PrincipalEntity; +import org.apache.ambari.server.orm.entities.PrivilegeEntity; +import org.apache.ambari.server.orm.entities.UserEntity; +import org.apache.ambari.server.state.stack.OsFamily; +import org.easymock.Capture; +import org.easymock.CaptureType; +import org.easymock.EasyMockSupport; +import org.junit.Test; +import org.springframework.security.crypto.password.PasswordEncoder; + +import javax.persistence.EntityManager; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import static org.easymock.EasyMock.capture; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.newCapture; + +public class UsersTest extends EasyMockSupport { + @Test + public void testGetUserAuthorities() throws Exception { + Injector injector = getInjector(); + + PrincipalEntity userPrincipalEntity = createMock(PrincipalEntity.class); + + UserEntity userEntity = createMock(UserEntity.class); + expect(userEntity.getPrincipal()).andReturn(userPrincipalEntity).times(1); + + UserDAO userDAO = injector.getInstance(UserDAO.class); + expect(userDAO.findUserByNameAndType("user1", UserType.LOCAL)).andReturn(userEntity).times(1); + + PrincipalEntity groupPrincipalEntity = createMock(PrincipalEntity.class); + + GroupEntity groupEntity = createMock(GroupEntity.class); + expect(groupEntity.getPrincipal()).andReturn(groupPrincipalEntity).times(1); + + MemberEntity memberEntity = createMock(MemberEntity.class); + expect(memberEntity.getGroup()).andReturn(groupEntity).times(1); + + MemberDAO memberDAO = injector.getInstance(MemberDAO.class); + expect(memberDAO.findAllMembersByUser(userEntity)).andReturn(Collections.singletonList(memberEntity)).times(1); + + PrincipalEntity clusterUserPrivilegePermissionPrincipalEntity = createMock(PrincipalEntity.class); + + PermissionEntity clusterUserPrivilegePermissionEntity = createMock(PermissionEntity.class); + expect(clusterUserPrivilegePermissionEntity.getPrincipal()).andReturn(clusterUserPrivilegePermissionPrincipalEntity).times(1); + + PrivilegeEntity clusterUserPrivilegeEntity = createMock(PrivilegeEntity.class); + expect(clusterUserPrivilegeEntity.getPermission()).andReturn(clusterUserPrivilegePermissionEntity).times(1); + + PrincipalEntity clusterOperatorPrivilegePermissionPrincipalEntity = createMock(PrincipalEntity.class); + + PermissionEntity clusterOperatorPrivilegePermissionEntity = createMock(PermissionEntity.class); + expect(clusterOperatorPrivilegePermissionEntity.getPrincipal()).andReturn(clusterOperatorPrivilegePermissionPrincipalEntity).times(1); + + PrivilegeEntity clusterOperatorPrivilegeEntity = createMock(PrivilegeEntity.class); + expect(clusterOperatorPrivilegeEntity.getPermission()).andReturn(clusterOperatorPrivilegePermissionEntity).times(1); + + List<PrivilegeEntity> privilegeEntities = new ArrayList<PrivilegeEntity>(); + privilegeEntities.add(clusterUserPrivilegeEntity); + privilegeEntities.add(clusterOperatorPrivilegeEntity); + + PrivilegeEntity clusterUserViewUserPrivilegeEntity = createMock(PrivilegeEntity.class); + + List<PrivilegeEntity> rolePrivilegeEntities = new ArrayList<PrivilegeEntity>(); + rolePrivilegeEntities.add(clusterUserViewUserPrivilegeEntity); + + Capture<? extends List<PrincipalEntity>> principalEntitiesCapture = newCapture(); + Capture<? extends List<PrincipalEntity>> rolePrincipalEntitiesCapture = newCapture(); + + PrivilegeDAO privilegeDAO = injector.getInstance(PrivilegeDAO.class); + expect(privilegeDAO.findAllByPrincipal(capture(principalEntitiesCapture))).andReturn(privilegeEntities).times(1); + expect(privilegeDAO.findAllByPrincipal(capture(rolePrincipalEntitiesCapture))).andReturn(rolePrivilegeEntities).times(1); + + replayAll(); + + Users user = injector.getInstance(Users.class); + Collection<AmbariGrantedAuthority> authorities = user.getUserAuthorities("user1", UserType.LOCAL); + + verifyAll(); + + Assert.assertEquals(2, principalEntitiesCapture.getValue().size()); + Assert.assertTrue(principalEntitiesCapture.getValue().contains(userPrincipalEntity)); + Assert.assertTrue(principalEntitiesCapture.getValue().contains(groupPrincipalEntity)); + + Assert.assertEquals(2, rolePrincipalEntitiesCapture.getValue().size()); + Assert.assertTrue(rolePrincipalEntitiesCapture.getValue().contains(clusterUserPrivilegePermissionPrincipalEntity)); + Assert.assertTrue(rolePrincipalEntitiesCapture.getValue().contains(clusterOperatorPrivilegePermissionPrincipalEntity)); + + + Assert.assertEquals(3, authorities.size()); + Assert.assertTrue(authorities.contains(new AmbariGrantedAuthority(clusterUserPrivilegeEntity))); + Assert.assertTrue(authorities.contains(new AmbariGrantedAuthority(clusterOperatorPrivilegeEntity))); + Assert.assertTrue(authorities.contains(new AmbariGrantedAuthority(clusterUserViewUserPrivilegeEntity))); + } + + private Injector getInjector() { + return Guice.createInjector(new AbstractModule() { + @Override + protected void configure() { + bind(EntityManager.class).toInstance(createMock(EntityManager.class)); + bind(DBAccessor.class).toInstance(createMock(DBAccessor.class)); + bind(OsFamily.class).toInstance(createNiceMock(OsFamily.class)); + bind(UserDAO.class).toInstance(createMock(UserDAO.class)); + bind(MemberDAO.class).toInstance(createMock(MemberDAO.class)); + bind(PrivilegeDAO.class).toInstance(createMock(PrivilegeDAO.class)); + bind(PasswordEncoder.class).toInstance(createMock(PasswordEncoder.class)); + } + }); + } +} \ No newline at end of file