This is an automated email from the ASF dual-hosted git repository. pradeep pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/ranger.git
The following commit(s) were added to refs/heads/master by this push: new 8202ed4 RANGER-2408 : Restrict Ranger User's capabilities according to their role 8202ed4 is described below commit 8202ed4aed53ad93a21b27dcf83cdf7102678fa0 Author: Bhavik Patel <bhavikpatel...@gmail.com> AuthorDate: Mon Apr 29 12:04:26 2019 +0530 RANGER-2408 : Restrict Ranger User's capabilities according to their role Signed-off-by: Pradeep <prad...@apache.org> --- .../ranger/plugin/model/RangerSecurityZone.java | 2 +- .../org/apache/ranger/rest/SecurityZoneREST.java | 157 ++++++++++++++++++++- .../java/org/apache/ranger/rest/ServiceREST.java | 142 ++++++++++++------- .../org/apache/ranger/rest/TestServiceREST.java | 20 ++- 4 files changed, 261 insertions(+), 60 deletions(-) diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerSecurityZone.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerSecurityZone.java index 98ef6cf..4988777 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerSecurityZone.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerSecurityZone.java @@ -109,7 +109,7 @@ public class RangerSecurityZone extends RangerBaseModelObject implements java.io } public void setTagServices(List<String> tagServices) { - this.tagServices = tagServices; + this.tagServices = (tagServices != null) ? tagServices : new ArrayList<String>(); } @Override diff --git a/security-admin/src/main/java/org/apache/ranger/rest/SecurityZoneREST.java b/security-admin/src/main/java/org/apache/ranger/rest/SecurityZoneREST.java index 6ce5365..4f6fa89 100644 --- a/security-admin/src/main/java/org/apache/ranger/rest/SecurityZoneREST.java +++ b/security-admin/src/main/java/org/apache/ranger/rest/SecurityZoneREST.java @@ -19,7 +19,9 @@ package org.apache.ranger.rest; +import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -57,6 +59,8 @@ import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; +import com.google.common.collect.Sets; + @Path("zones") @Component @Scope("request") @@ -84,6 +88,9 @@ public class SecurityZoneREST { @Autowired RangerBizUtil bizUtil; + + @Autowired + ServiceREST serviceRest; @POST @Path("/zones") @@ -124,7 +131,7 @@ public class SecurityZoneREST { throw restErrorUtil.createRESTException("Cannot update unzoned zone"); } - ensureAdminAccess(); + ensureUserAllowOperationOnServiceForZone(securityZone); removeEmptyEntries(securityZone); if (securityZone.getId() != null && !zoneId.equals(securityZone.getId())) { throw restErrorUtil.createRESTException("zoneId mismatch!!"); @@ -282,6 +289,154 @@ public class SecurityZoneREST { throw restErrorUtil.createRESTException(HttpServletResponse.SC_FORBIDDEN, "Ranger Security Zone is not accessible for user '" + userName + "'.", true); } } + + private void ensureUserAllowOperationOnServiceForZone( + RangerSecurityZone securityZone){ + if (!bizUtil.isAdmin()) { + String userName = bizUtil.getCurrentUserLoginId(); + RangerSecurityZone existingSecurityZone = null; + try { + existingSecurityZone = svcStore + .getSecurityZone(securityZone.getId()); + } catch (Exception ex) { + LOG.error("Unable to get Security Zone with id : " + securityZone.getId(), ex); + throw restErrorUtil.createRESTException(ex.getMessage()); + } + if (existingSecurityZone != null) { + /* Validation for non service related fields of security zone */ + + + if (!securityZone.getName().equals( + existingSecurityZone.getName())) { + throwRestError("User : " + userName + + " is not allowed to edit zone name of zone : " + existingSecurityZone.getName()); + } else if (!securityZone.getDescription().equals( + existingSecurityZone.getDescription())) { + throwRestError("User : " + userName + + " is not allowed to edit zone description of zone : " + existingSecurityZone.getName()); + } + if (!serviceRest.isZoneAdmin(existingSecurityZone.getName())) { + if (!securityZone.getAdminUserGroups().equals( + existingSecurityZone.getAdminUserGroups())) { + throwRestError("User : " + + userName + + " is not allowed to edit zone Admin User Group of zone : " + existingSecurityZone.getName()); + } else if (!securityZone.getAdminUsers().equals( + existingSecurityZone.getAdminUsers())) { + throwRestError("User : " + userName + + " is not allowed to edit zone Admin User of zone : " + existingSecurityZone.getName()); + } else if (!securityZone.getAuditUsers().equals( + existingSecurityZone.getAuditUsers())) { + throwRestError("User : " + userName + + " is not allowed to edit zone Audit User of zone : " + existingSecurityZone.getName()); + } else if (!securityZone.getAuditUserGroups().equals( + existingSecurityZone.getAuditUserGroups())) { + throwRestError("User : " + + userName + + " is not allowed to edit zone Audit User Group of zone : " + existingSecurityZone.getName()); + } + } + + /* + * Validation on tag service association / disassociation with + * security zone + * */ + + List<String> dbTagServices = existingSecurityZone + .getTagServices(); + List<String> uiTagServices = securityZone.getTagServices(); + List<String> addRmvTagSvc = new ArrayList<String>(); + if (!dbTagServices.equals(uiTagServices)) { + for (String svc : dbTagServices) { + if (!uiTagServices.contains(svc)) { + addRmvTagSvc.add(svc); + } + } + + for (String svc : uiTagServices) { + if (!dbTagServices.contains(svc)) { + addRmvTagSvc.add(svc); + } + } + } + if (!addRmvTagSvc.isEmpty()) { + for (String svc : addRmvTagSvc) { + /* + * if user is neither svc admin nor admin then + * add/remove of svc in zone is not allowed + */ + if (!svcStore.isServiceAdminUser(svc, userName)) { + throwRestError("User : " + + userName + + " is not allowed to add/remove tag service : " + + svc + " in Ranger Security zone : " + existingSecurityZone.getName()); + + } + } + } + + + /* + * Validation on service association / disassociation with + * security zone + */ + Set<String> existingRangerSecurityZoneService = existingSecurityZone + .getServices().keySet(); + Set<String> newRangerSecurityZoneService = securityZone.getServices() + .keySet(); + Set<String> diffServiceSet = new HashSet<>(Sets.difference( + newRangerSecurityZoneService, + existingRangerSecurityZoneService)); + diffServiceSet.addAll(Sets.difference( + existingRangerSecurityZoneService, + newRangerSecurityZoneService)); + + if (diffServiceSet != null && diffServiceSet.size() > 0) { + for (String svc : diffServiceSet) { + /* + * if user is neither svc admin nor admin then + * add/remove of svc in zone is not allowed + */ + if (!svcStore.isServiceAdminUser(svc, userName)) { + throwRestError("User : " + + userName + + " is not allowed to add/remove service : " + + svc + " in Ranger Security zone : " + existingSecurityZone.getName()); + + } + } + } + + /* Validation for resources on existing svc in security zone */ + for (String svc : existingRangerSecurityZoneService) { + RangerSecurityZoneService rangerSecurityZnSvcFromDB = existingSecurityZone + .getServices().get(svc); + + RangerSecurityZoneService rangerSecurityZnSvcFromUI = securityZone + .getServices().get(svc); + + if (rangerSecurityZnSvcFromUI != null) { + if (!rangerSecurityZnSvcFromDB.getResources().equals( + rangerSecurityZnSvcFromUI.getResources())) { + if (!svcStore.isServiceAdminUser(svc, userName)) { + throwRestError("User : " + + userName + + " is not allowed to edit resource in service : " + + svc + " in Ranger Security zone : " + existingSecurityZone.getName()); + } + } + } + + } + } + + } + } + + private void throwRestError(String message){ + throw restErrorUtil.createRESTException(HttpServletResponse.SC_FORBIDDEN, message, true); + } + private void removeEmptyEntries(RangerSecurityZone securityZone) { bizUtil.removeEmptyStrings(securityZone.getTagServices()); diff --git a/security-admin/src/main/java/org/apache/ranger/rest/ServiceREST.java b/security-admin/src/main/java/org/apache/ranger/rest/ServiceREST.java index 70088f9..c4ccee9 100644 --- a/security-admin/src/main/java/org/apache/ranger/rest/ServiceREST.java +++ b/security-admin/src/main/java/org/apache/ranger/rest/ServiceREST.java @@ -191,7 +191,7 @@ public class ServiceREST { RangerPolicyService policyService; @Autowired - RangerPolicyLabelsService policyLabelsService; + RangerPolicyLabelsService policyLabelsService; @Autowired RangerServiceService svcService; @@ -3343,41 +3343,41 @@ public class ServiceREST { servicePoliciesMap.put(serviceName, policyList); } - - policyList.add(policy); - } + policyList.add(policy); + } for (Map.Entry<String, List<RangerPolicy>> entry : servicePoliciesMap.entrySet()) { String serviceName = entry.getKey(); List<RangerPolicy> listToFilter = entry.getValue(); if (CollectionUtils.isNotEmpty(listToFilter)) { - boolean isServiceAdminUser=isAdmin || svcStore.isServiceAdminUser(serviceName, userName) || isZoneAdmin(serviceName); - if (isAdmin || isKeyAdmin || isAuditAdmin || isAuditKeyAdmin || isServiceAdminUser) { - XXService xService = daoManager.getXXService().findByName(serviceName); - Long serviceDefId = xService.getType(); - boolean isKmsService = serviceDefId.equals(EmbeddedServiceDefsUtil.instance().getKmsServiceDefId()); + boolean isServiceAdminUser = isAdmin || svcStore.isServiceAdminUser(serviceName, userName); + if (isAdmin || isKeyAdmin || isAuditAdmin + || isAuditKeyAdmin) { + XXService xService = daoManager.getXXService() + .findByName(serviceName); + Long serviceDefId = xService.getType(); + boolean isKmsService = serviceDefId + .equals(EmbeddedServiceDefsUtil.instance() + .getKmsServiceDefId()); if (isAdmin) { if (!isKmsService) { ret.addAll(listToFilter); } - } else if (isAuditAdmin) { - if (!isKmsService) { - ret.addAll(listToFilter); - } - } else if (isAuditKeyAdmin) { - if (isKmsService) { - ret.addAll(listToFilter); - } + } else if (isAuditAdmin) { + if (!isKmsService) { + ret.addAll(listToFilter); + } + } else if (isAuditKeyAdmin) { + if (isKmsService) { + ret.addAll(listToFilter); + } } else if (isKeyAdmin) { if (isKmsService) { ret.addAll(listToFilter); } - } else if (isServiceAdminUser) { - ret.addAll(listToFilter); - } - + } continue; } @@ -3389,7 +3389,9 @@ public class ServiceREST { } for (RangerPolicy policy : listToFilter) { - if (policyEngine.isAccessAllowed(policy, userName, userGroups, RangerPolicyEngine.ADMIN_ACCESS)) { + if (policyEngine.isAccessAllowed(policy, userName, userGroups, RangerPolicyEngine.ADMIN_ACCESS) + || (!StringUtils.isEmpty(policy.getZoneName()) && (isZoneAdmin(policy.getZoneName()) || isZoneAuditor(policy.getZoneName()))) + || isServiceAdminUser) { ret.add(policy); } } @@ -3404,62 +3406,98 @@ public class ServiceREST { return ret; } - boolean isZoneAdmin(String serviceName) { + public boolean isZoneAdmin(String zoneName) { boolean isZoneAdmin = false; - Map<String, RangerSecurityZone.RangerSecurityZoneService> securityZonForServices = zoneStore - .getSecurityZonesForService(serviceName); - if (securityZonForServices == null) { - return false; + RangerSecurityZone securityZone = null; + try { + securityZone = zoneStore.getSecurityZoneByName(zoneName); + } catch (Exception e) { + LOG.error("Unexpected error when fetching security zone with name:[" + zoneName + "] from database", e); } - for (Map.Entry<String, RangerSecurityZone.RangerSecurityZoneService> entry : securityZonForServices - .entrySet()) { - String zoneName = entry.getKey(); - - RangerSecurityZone securityZone = null; - try { - securityZone = zoneStore.getSecurityZoneByName(zoneName); - } catch (Exception e) { - LOG.debug("==> securityZone not found "); - } - + if (securityZone != null) { String userId = bizUtil.getCurrentUserLoginId(); - List<XXGroupUser> groupUsers = groupUserDao.findByUserId(bizUtil.getXUserId()); + List<XXGroupUser> groupUsers = groupUserDao.findByUserId(bizUtil + .getXUserId()); List<String> loggedInUsersGroups = new ArrayList<>(); for (XXGroupUser groupUser : groupUsers) { loggedInUsersGroups.add(groupUser.getName()); } for (String loggedInUsersGroup : loggedInUsersGroups) { - if (securityZone != null && securityZone.getAdminUserGroups() != null - && securityZone.getAdminUserGroups().contains(loggedInUsersGroup)) { + if (securityZone != null + && securityZone.getAdminUserGroups() != null + && securityZone.getAdminUserGroups().contains( + loggedInUsersGroup)) { isZoneAdmin = true; break; } } - if ((securityZone != null && securityZone.getAdminUsers() != null - && securityZone.getAdminUsers().contains(userId))) { + if ((securityZone != null && securityZone.getAdminUsers() != null && securityZone + .getAdminUsers().contains(userId))) { isZoneAdmin = true; - break; } - } + return isZoneAdmin; } + + + public boolean isZoneAuditor(String zoneName) { + boolean isZoneAuditor = false; + RangerSecurityZone securityZone = null; + try { + securityZone = zoneStore.getSecurityZoneByName(zoneName); + } catch (Exception e) { + LOG.error("Unexpected error when fetching security zone with name:[" + zoneName + "] from database", e); + } + + if (securityZone != null) { + String userId = bizUtil.getCurrentUserLoginId(); + + List<XXGroupUser> groupUsers = groupUserDao.findByUserId(bizUtil + .getXUserId()); + List<String> loggedInUsersGroups = new ArrayList<>(); + for (XXGroupUser groupUser : groupUsers) { + loggedInUsersGroups.add(groupUser.getName()); + } + for (String loggedInUsersGroup : loggedInUsersGroups) { + if (securityZone != null + && securityZone.getAuditUserGroups() != null + && securityZone.getAuditUserGroups().contains( + loggedInUsersGroup)) { + isZoneAuditor = true; + break; + } + } + if ((securityZone != null && securityZone.getAuditUsers() != null && securityZone + .getAuditUsers().contains(userId))) { + isZoneAuditor = true; + } + } + return isZoneAuditor; + } + void ensureAdminAccess(RangerPolicy policy) { boolean isAdmin = bizUtil.isAdmin(); boolean isKeyAdmin = bizUtil.isKeyAdmin(); String userName = bizUtil.getCurrentUserLoginId(); boolean isSvcAdmin = isAdmin || svcStore.isServiceAdminUser(policy.getService(), userName); - String serviceName = policy.getService(); - boolean isZoneAdmin = isZoneAdmin(serviceName); - if (!isAdmin && !isKeyAdmin && !isSvcAdmin && !isZoneAdmin) { + if (!isAdmin && !isKeyAdmin && !isSvcAdmin) { boolean isAllowed = false; Set<String> userGroups = userMgr.getGroupsForUser(userName); - isAllowed = hasAdminAccess(policy, userName, userGroups); + + //for zone policy create /update / delete + if(!StringUtils.isEmpty(policy.getZoneName()) && isZoneAdmin(policy.getZoneName())){ + isAllowed = true; + }else{ + isAllowed = hasAdminAccess(policy, userName, userGroups); + } + + if (!isAllowed) { throw restErrorUtil.createRESTException(HttpServletResponse.SC_UNAUTHORIZED, @@ -3537,8 +3575,8 @@ public class ServiceREST { return isAllowed; } - private RangerPolicyEngine getDelegatedAdminPolicyEngine(String serviceName) { - return RangerPolicyEngineCacheForEngineOptions.getInstance().getPolicyEngine(serviceName, svcStore, zoneStore, delegateAdminOptions); + public RangerPolicyEngine getDelegatedAdminPolicyEngine(String serviceName) { + return RangerPolicyEngineCacheForEngineOptions.getInstance().getPolicyEngine(serviceName, svcStore, delegateAdminOptions); } private RangerPolicyEngine getPolicySearchPolicyEngine(String serviceName) throws Exception { @@ -3789,7 +3827,7 @@ public class ServiceREST { String userName = bizUtil.getCurrentUserLoginId(); boolean isAuditAdmin = bizUtil.isAuditAdmin(); boolean isAuditKeyAdmin = bizUtil.isAuditKeyAdmin(); - boolean isSvcAdmin = isAdmin || svcStore.isServiceAdminUser(policy.getService(), userName) || isZoneAdmin(policy.getService()); + boolean isSvcAdmin = isAdmin || svcStore.isServiceAdminUser(policy.getService(), userName) || (!StringUtils.isEmpty(policy.getZoneName()) && (isZoneAdmin(policy.getZoneName()) || isZoneAuditor(policy.getZoneName()))); if (!isAdmin && !isKeyAdmin && !isSvcAdmin && !isAuditAdmin && !isAuditKeyAdmin) { boolean isAllowed = false; diff --git a/security-admin/src/test/java/org/apache/ranger/rest/TestServiceREST.java b/security-admin/src/test/java/org/apache/ranger/rest/TestServiceREST.java index 0d75192..19f162b 100644 --- a/security-admin/src/test/java/org/apache/ranger/rest/TestServiceREST.java +++ b/security-admin/src/test/java/org/apache/ranger/rest/TestServiceREST.java @@ -55,6 +55,7 @@ import org.apache.ranger.common.UserSessionBase; import org.apache.ranger.db.RangerDaoManager; import org.apache.ranger.db.XXSecurityZoneDao; import org.apache.ranger.db.XXSecurityZoneRefServiceDao; +import org.apache.ranger.db.XXGroupUserDao; import org.apache.ranger.db.XXServiceDao; import org.apache.ranger.db.XXServiceDefDao; import org.apache.ranger.entity.XXPortalUser; @@ -79,6 +80,7 @@ import org.apache.ranger.plugin.model.RangerServiceDef.RangerServiceConfigDef; import org.apache.ranger.plugin.model.validation.RangerPolicyValidator; import org.apache.ranger.plugin.model.validation.RangerServiceDefValidator; import org.apache.ranger.plugin.model.validation.RangerServiceValidator; +import org.apache.ranger.plugin.policyengine.RangerPolicyEngine; import org.apache.ranger.plugin.policyengine.RangerPolicyEngineImpl; import org.apache.ranger.plugin.service.ResourceLookupContext; import org.apache.ranger.plugin.store.EmbeddedServiceDefsUtil; @@ -221,7 +223,11 @@ public class TestServiceREST { @Mock RangerPolicyEngineImpl rpImpl; + + @Mock + RangerPolicyEngine policyEngine; + @Rule public ExpectedException thrown = ExpectedException.none(); @@ -1058,8 +1064,12 @@ public class TestServiceREST { SearchFilter filter = new SearchFilter(); XXService xs = Mockito.mock(XXService.class); xs.setType(3L); - XXServiceDao xSDao = Mockito.mock(XXServiceDao.class); + XXGroupUserDao xGroupDao = Mockito.mock(XXGroupUserDao.class); + ServiceREST spySVCRest = Mockito.spy(serviceREST); List<RangerPolicy> policies = new ArrayList<RangerPolicy>(); + ServicePolicies svcPolicies = new ServicePolicies(); + svcPolicies.setPolicies(policies); + svcPolicies.setServiceName("HDFS_1-1-20150316062453"); RangerPolicy rPol=rangerPolicy(); policies.add(rPol); filter.setParam(SearchFilter.POLICY_NAME, "policyName"); @@ -1067,18 +1077,16 @@ public class TestServiceREST { Mockito.when(searchUtil.getSearchFilter(request, policyService.sortFields)).thenReturn(filter); Mockito.when(svcStore.getPolicies(filter)).thenReturn(policies); /*here we are setting serviceAdminRole, so we will get the required policy with serviceAdmi role*/ - Mockito.when(daoManager.getXXService()).thenReturn(xSDao); + Mockito.when(daoManager.getXXGroupUser()).thenReturn(xGroupDao); Mockito.when(svcStore.isServiceAdminUser(rPol.getService(), null)).thenReturn(true); - Mockito.when(xSDao.findByName(rPol.getName())).thenReturn(xs); - RangerPolicyList dbRangerPolicy = serviceREST.getPolicies(request); + Mockito.doReturn(policyEngine).when(spySVCRest).getDelegatedAdminPolicyEngine("HDFS_1-1-20150316062453"); + RangerPolicyList dbRangerPolicy = spySVCRest.getPolicies(request); Assert.assertNotNull(dbRangerPolicy); Assert.assertEquals(dbRangerPolicy.getListSize(), 1); Mockito.verify(searchUtil).getSearchFilter(request, policyService.sortFields); Mockito.verify(svcStore).getPolicies(filter); Mockito.verify(svcStore).isServiceAdminUser(rPol.getService(), null); - Mockito.verify(daoManager).getXXService(); - Mockito.verify(xSDao).findByName(rPol.getName()); }