You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ranger.apache.org by vy...@apache.org on 2026/08/20 05:44:57 UTC
(ranger) branch master updated: RANGER-5735: Improve authorization consistency across RoleREST endpoints (#1149)
This is an automated email from the ASF dual-hosted git repository.
vyommani 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 e7aef11f8 RANGER-5735: Improve authorization consistency across RoleREST endpoints (#1149)
e7aef11f8 is described below
commit e7aef11f89d873b803e830acc1871516ab26e26e
Author: Vyom Mani Tiwari <vy...@gmail.com>
AuthorDate: Thu Aug 20 11:14:51 2026 +0530
RANGER-5735: Improve authorization consistency across RoleREST endpoints (#1149)
* RANGER-5735: Improve authorization consistency across RoleREST endpoints
* RANGER-5735: added more tests
---
.../main/java/org/apache/ranger/rest/RoleREST.java | 41 ++-
.../java/org/apache/ranger/rest/TestRoleREST.java | 286 +++++++++++++++++++++
2 files changed, 322 insertions(+), 5 deletions(-)
diff --git a/security-admin/src/main/java/org/apache/ranger/rest/RoleREST.java b/security-admin/src/main/java/org/apache/ranger/rest/RoleREST.java
index df19ce431..b6e1a9c1a 100644
--- a/security-admin/src/main/java/org/apache/ranger/rest/RoleREST.java
+++ b/security-admin/src/main/java/org/apache/ranger/rest/RoleREST.java
@@ -349,7 +349,17 @@ public RangerRole getRole(@PathParam("id") Long id) {
RangerRole ret;
try {
+ UserSessionBase usb = ContextUtil.getCurrentUserSession();
+ String loggedInUser = usb != null ? usb.getLoginId() : null;
ret = roleStore.getRole(id);
+ if (ret != null && !bizUtil.isUserRangerAdmin(loggedInUser)) {
+ try {
+ ensureRoleAccess(loggedInUser, userMgr.getGroupsForUser(loggedInUser), ret);
+ } catch (Exception accessExcp) {
+ LOG.error("User {} does not have permission to get details for role(id={})", loggedInUser, id);
+ throw restErrorUtil.createRESTException("User doesn't have permissions to get details for role(id=" + id + ")");
+ }
+ }
} catch (WebApplicationException excp) {
throw excp;
} catch (Throwable excp) {
@@ -869,6 +879,8 @@ public RESTResponse grantRole(@PathParam("serviceName") String serviceName, Gran
RESTResponse ret = new RESTResponse();
try {
+ bizUtil.blockAuditorRoleUser();
+
validateUsersGroupsAndRoles(grantRoleRequest);
String userName = grantRoleRequest.getGrantor();
@@ -884,8 +896,7 @@ public RESTResponse grantRole(@PathParam("serviceName") String serviceName, Gran
* else deny the operation
* This logic is implemented as part of getRoleIfAccessible(roleName, serviceName, userName, userGroups)
*/
- Set<String> userGroups = CollectionUtils.isNotEmpty(grantRoleRequest.getGrantorGroups()) ? grantRoleRequest.getGrantorGroups() : userMgr.getGroupsForUser(userName);
- RangerRole existingRole = getRoleIfAccessible(roleName, serviceName, userName, userGroups);
+ RangerRole existingRole = getRoleIfAccessible(roleName, serviceName, userName, grantRoleRequest.getGrantorGroups());
if (existingRole == null) {
throw restErrorUtil.createRESTException("User doesn't have permissions to grant role " + roleName);
@@ -927,6 +938,8 @@ public RESTResponse revokeRole(@PathParam("serviceName") String serviceName, Gra
RESTResponse ret = new RESTResponse();
try {
+ bizUtil.blockAuditorRoleUser();
+
validateUsersGroupsAndRoles(revokeRoleRequest);
String userName = revokeRoleRequest.getGrantor();
@@ -942,8 +955,7 @@ public RESTResponse revokeRole(@PathParam("serviceName") String serviceName, Gra
* else deny the operation
* This logic is implemented as part of getRoleIfAccessible(roleName, serviceName, userName, userGroups)
*/
- Set<String> userGroups = CollectionUtils.isNotEmpty(revokeRoleRequest.getGrantorGroups()) ? revokeRoleRequest.getGrantorGroups() : userMgr.getGroupsForUser(userName);
- RangerRole existingRole = getRoleIfAccessible(roleName, serviceName, userName, userGroups);
+ RangerRole existingRole = getRoleIfAccessible(roleName, serviceName, userName, revokeRoleRequest.getGrantorGroups());
if (existingRole == null) {
throw restErrorUtil.createRESTException("User doesn't have permissions to revoke role " + roleName);
@@ -987,6 +999,13 @@ public List<String> getUserRoles(@PathParam("user") String userName, @Context Ht
LOG.debug("==> getUserRoles()");
try {
+ UserSessionBase usb = ContextUtil.getCurrentUserSession();
+ String loggedInUser = usb != null ? usb.getLoginId() : null;
+ if (!StringUtil.equals(userName, loggedInUser) && !bizUtil.isUserRangerAdmin(loggedInUser)) {
+ LOG.error("User {} does not have permission to get roles for user {}", loggedInUser, userName);
+ throw restErrorUtil.createRESTException(HttpServletResponse.SC_FORBIDDEN, "User does not have permission for this operation", true);
+ }
+
if (xUserService.getXUserByUserName(userName) == null) {
throw restErrorUtil.createRESTException(HttpServletResponse.SC_NOT_FOUND, "User:" + userName + " not found", false);
}
@@ -1254,7 +1273,7 @@ private void ensureAdminAccess(String serviceName, String userName) throws Excep
}
}
- private RangerRole getRoleIfAccessible(String roleName, String serviceName, String userName, Set<String> userGroups) {
+ private RangerRole getRoleIfAccessible(String roleName, String serviceName, String userName, Set<String> callerAssertedGroups) {
/* If userName (execUser) is not same as logged in user then check
* If logged-in user is not ranger admin/service admin/service user, then deny the operation
* effective User is execUser
@@ -1265,6 +1284,7 @@ private RangerRole getRoleIfAccessible(String roleName, String serviceName, Stri
*/
RangerRole existingRole;
String effectiveUser;
+ Set<String> userGroups;
UserSessionBase usb = ContextUtil.getCurrentUserSession();
String loggedInUser = usb != null ? usb.getLoginId() : null;
@@ -1276,8 +1296,19 @@ private RangerRole getRoleIfAccessible(String roleName, String serviceName, Stri
}
effectiveUser = userName != null ? userName : loggedInUser;
+
+ /* effectiveUser differs from the logged-in caller only because the caller has already
+ * been confirmed above to be a trusted principal (ranger admin/service admin/service user)
+ * acting on behalf of effectiveUser, so it is safe to fall back to a caller-supplied group
+ * set for effectiveUser when Ranger has no synced group info for them yet. */
+ userGroups = CollectionUtils.isNotEmpty(callerAssertedGroups) ? callerAssertedGroups : userMgr.getGroupsForUser(effectiveUser);
} else {
effectiveUser = loggedInUser;
+
+ /* self-service case: an unprivileged, authenticated caller must never be able to assert
+ * their own group membership via request input for an authorization decision -- always
+ * resolve it server-side. */
+ userGroups = userMgr.getGroupsForUser(loggedInUser);
}
try {
diff --git a/security-admin/src/test/java/org/apache/ranger/rest/TestRoleREST.java b/security-admin/src/test/java/org/apache/ranger/rest/TestRoleREST.java
index d81ad8afb..2656dbb0d 100644
--- a/security-admin/src/test/java/org/apache/ranger/rest/TestRoleREST.java
+++ b/security-admin/src/test/java/org/apache/ranger/rest/TestRoleREST.java
@@ -614,6 +614,37 @@ public void test6bGetRoleById() {
Assertions.assertThrows(Throwable.class, () -> roleRest.getRole(eq(rangerRole.getId())));
}
+ @Test
+ public void test6cGetRoleByIdUnauthorized() {
+ Assertions.assertThrows(Throwable.class, () -> {
+ RangerRole rangerRole = createRangerRole("someOtherUser", false);
+ Mockito.when(bizUtil.isUserRangerAdmin(Mockito.anyString())).thenReturn(false);
+ Mockito.when(userMgr.getGroupsForUser(Mockito.anyString())).thenReturn(new HashSet<>());
+ try {
+ Mockito.when(roleStore.getRole(Mockito.anyLong())).thenReturn(rangerRole);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ roleRest.getRole(rangerRole.getId());
+ Mockito.verify(restErrorUtil, Mockito.times(1)).createRESTException(Mockito.anyString());
+ });
+ }
+
+ @Test
+ public void test6dGetRoleByIdAllowedForRoleAdminOptionMember() {
+ RangerRole rangerRole = createRole(); // "admin" is an admin-option user member of this role
+ Mockito.when(bizUtil.isUserRangerAdmin(Mockito.anyString())).thenReturn(false);
+ Mockito.when(userMgr.getGroupsForUser(Mockito.anyString())).thenReturn(new HashSet<>());
+ try {
+ Mockito.when(roleStore.getRole(Mockito.anyLong())).thenReturn(rangerRole);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ RangerRole returnedRole = roleRest.getRole(rangerRole.getId());
+ Assertions.assertNotNull(returnedRole);
+ Assertions.assertEquals(returnedRole.getId(), rangerRole.getId());
+ }
+
@Test
public void test7bGetAllRoles() {
Assertions.assertThrows(Throwable.class, () -> {
@@ -725,6 +756,93 @@ public void test13bGrantRole() {
});
}
+ @Test
+ public void test13cGrantRoleForgedGrantorGroupsRejected() {
+ Assertions.assertThrows(Throwable.class, () -> {
+ String serviceName = "serviceName";
+ RangerRole.RoleMember groupMember = new RangerRole.RoleMember("forged-admin-group", true);
+ RangerRole rangerRole = new RangerRole("target-role", "target-role", null, new ArrayList<>(), new ArrayList<>(Collections.singletonList(groupMember)));
+ GrantRevokeRoleRequest grantRevokeRoleRequest = createGrantRevokeRoleRequest();
+ rangerRole.setCreatedByUser("target-role");
+ rangerRole.setId(roleId);
+ grantRevokeRoleRequest.setGrantor(adminLoginID);
+ grantRevokeRoleRequest.setGrantorGroups(new HashSet<>(Collections.singletonList("forged-admin-group")));
+ Mockito.when(bizUtil.isUserRangerAdmin(Mockito.anyString())).thenReturn(false);
+ Mockito.when(userMgr.getGroupsForUser(Mockito.anyString())).thenReturn(new HashSet<>());
+ try {
+ Mockito.when(roleStore.getRole(Mockito.anyString())).thenReturn(rangerRole);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ roleRest.grantRole(serviceName, grantRevokeRoleRequest, Mockito.mock(HttpServletRequest.class));
+ Mockito.verify(restErrorUtil, Mockito.times(1)).createRESTException(Mockito.anyString());
+ });
+ }
+
+ @Test
+ public void test13eGrantRoleRealGroupMembershipAllowed() {
+ String serviceName = "serviceName";
+ boolean isRefTableCleanupRequired = true;
+ RangerRole.RoleMember groupMember = new RangerRole.RoleMember("real-admin-group", true);
+ RangerRole rangerRole = new RangerRole("target-role", "target-role", null, new ArrayList<>(), new ArrayList<>(Collections.singletonList(groupMember)));
+ GrantRevokeRoleRequest grantRevokeRoleRequest = createGrantRevokeRoleRequest();
+ rangerRole.setCreatedByUser("target-role");
+ rangerRole.setId(roleId);
+ grantRevokeRoleRequest.setGrantor(adminLoginID);
+ Mockito.when(bizUtil.isUserRangerAdmin(Mockito.anyString())).thenReturn(false);
+ Mockito.when(userMgr.getGroupsForUser(adminLoginID)).thenReturn(new HashSet<>(Collections.singletonList("real-admin-group")));
+ try {
+ Mockito.when(roleStore.getRole(Mockito.anyString())).thenReturn(rangerRole);
+ Mockito.when(roleStore.updateRole(Mockito.any(RangerRole.class), Mockito.anyBoolean(), eq(isRefTableCleanupRequired))).then(AdditionalAnswers.returnsFirstArg());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ RESTResponse resp = roleRest.grantRole(serviceName, grantRevokeRoleRequest, Mockito.mock(HttpServletRequest.class));
+ Assertions.assertNotNull(resp);
+ Assertions.assertEquals(resp.getStatusCode(), RESTResponse.STATUS_SUCCESS);
+ }
+
+ @Test
+ public void test13fGrantRoleRealGroupMembershipMismatchRejected() {
+ String serviceName = "serviceName";
+ RangerRole.RoleMember groupMember = new RangerRole.RoleMember("real-admin-group", true);
+ RangerRole rangerRole = new RangerRole("target-role", "target-role", null, new ArrayList<>(), new ArrayList<>(Collections.singletonList(groupMember)));
+ GrantRevokeRoleRequest grantRevokeRoleRequest = createGrantRevokeRoleRequest();
+ rangerRole.setCreatedByUser("target-role");
+ rangerRole.setId(roleId);
+ grantRevokeRoleRequest.setGrantor(adminLoginID);
+ Mockito.when(bizUtil.isUserRangerAdmin(Mockito.anyString())).thenReturn(false);
+ Mockito.when(userMgr.getGroupsForUser(adminLoginID)).thenReturn(new HashSet<>(Arrays.asList("users", "developers")));
+ try {
+ Mockito.when(roleStore.getRole(Mockito.anyString())).thenReturn(rangerRole);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ boolean denied = false;
+ try {
+ roleRest.grantRole(serviceName, grantRevokeRoleRequest, Mockito.mock(HttpServletRequest.class));
+ } catch (Throwable expected) {
+ denied = true;
+ }
+ Assertions.assertTrue(denied, "expected grantRole to deny access for mismatched real groups");
+ Mockito.verify(userMgr, Mockito.atLeastOnce()).getGroupsForUser(adminLoginID);
+ try {
+ Mockito.verify(roleStore, Mockito.never()).updateRole(Mockito.any(RangerRole.class), Mockito.anyBoolean(), Mockito.anyBoolean());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Test
+ public void test13dGrantRoleBlockedForAuditor() {
+ Assertions.assertThrows(WebApplicationException.class, () -> {
+ String serviceName = "serviceName";
+ GrantRevokeRoleRequest grantRevokeRoleRequest = createGrantRevokeRoleRequest();
+ Mockito.doThrow(new WebApplicationException()).when(bizUtil).blockAuditorRoleUser();
+ roleRest.grantRole(serviceName, grantRevokeRoleRequest, Mockito.mock(HttpServletRequest.class));
+ });
+ }
+
@Test
public void test14bRevokeRole() {
RangerRole rangerRole = createRole();
@@ -760,6 +878,96 @@ public void test14cRevokeRole() {
});
}
+ @Test
+ public void test14dRevokeRoleForgedGrantorGroupsRejected() {
+ Assertions.assertThrows(Throwable.class, () -> {
+ String serviceName = "serviceName";
+ RangerRole.RoleMember groupMember = new RangerRole.RoleMember("forged-admin-group", true);
+ RangerRole rangerRole = new RangerRole("target-role", "target-role", null, new ArrayList<>(), new ArrayList<>(Collections.singletonList(groupMember)));
+ GrantRevokeRoleRequest grantRevokeRoleRequest = createGrantRevokeRoleRequest();
+ rangerRole.setCreatedByUser("target-role");
+ rangerRole.setId(roleId);
+ grantRevokeRoleRequest.setGrantor(adminLoginID);
+ grantRevokeRoleRequest.setGrantOption(Boolean.TRUE);
+ grantRevokeRoleRequest.setGrantorGroups(new HashSet<>(Collections.singletonList("forged-admin-group")));
+ Mockito.when(bizUtil.isUserRangerAdmin(Mockito.anyString())).thenReturn(false);
+ Mockito.when(userMgr.getGroupsForUser(Mockito.anyString())).thenReturn(new HashSet<>());
+ try {
+ Mockito.when(roleStore.getRole(Mockito.anyString())).thenReturn(rangerRole);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ roleRest.revokeRole(serviceName, grantRevokeRoleRequest, Mockito.mock(HttpServletRequest.class));
+ Mockito.verify(restErrorUtil, Mockito.times(1)).createRESTException(Mockito.anyString());
+ });
+ }
+
+ @Test
+ public void test14fRevokeRoleRealGroupMembershipAllowed() {
+ String serviceName = "serviceName";
+ boolean isRefTableCleanupRequired = true;
+ RangerRole.RoleMember groupMember = new RangerRole.RoleMember("real-admin-group", true);
+ RangerRole rangerRole = new RangerRole("target-role", "target-role", null, new ArrayList<>(), new ArrayList<>(Collections.singletonList(groupMember)));
+ GrantRevokeRoleRequest grantRevokeRoleRequest = createGrantRevokeRoleRequest();
+ rangerRole.setCreatedByUser("target-role");
+ rangerRole.setId(roleId);
+ grantRevokeRoleRequest.setGrantor(adminLoginID);
+ grantRevokeRoleRequest.setGrantOption(Boolean.TRUE);
+ Mockito.when(bizUtil.isUserRangerAdmin(Mockito.anyString())).thenReturn(false);
+ Mockito.when(userMgr.getGroupsForUser(adminLoginID)).thenReturn(new HashSet<>(Collections.singletonList("real-admin-group")));
+ try {
+ Mockito.when(roleStore.getRole(Mockito.anyString())).thenReturn(rangerRole);
+ Mockito.when(roleStore.updateRole(Mockito.any(RangerRole.class), Mockito.anyBoolean(), eq(isRefTableCleanupRequired))).then(AdditionalAnswers.returnsFirstArg());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ RESTResponse resp = roleRest.revokeRole(serviceName, grantRevokeRoleRequest, Mockito.mock(HttpServletRequest.class));
+ Assertions.assertNotNull(resp);
+ Assertions.assertEquals(resp.getStatusCode(), RESTResponse.STATUS_SUCCESS);
+ }
+
+ @Test
+ public void test14gRevokeRoleRealGroupMembershipMismatchRejected() {
+ String serviceName = "serviceName";
+ RangerRole.RoleMember groupMember = new RangerRole.RoleMember("real-admin-group", true);
+ RangerRole rangerRole = new RangerRole("target-role", "target-role", null, new ArrayList<>(), new ArrayList<>(Collections.singletonList(groupMember)));
+ GrantRevokeRoleRequest grantRevokeRoleRequest = createGrantRevokeRoleRequest();
+ rangerRole.setCreatedByUser("target-role");
+ rangerRole.setId(roleId);
+ grantRevokeRoleRequest.setGrantor(adminLoginID);
+ grantRevokeRoleRequest.setGrantOption(Boolean.TRUE);
+ Mockito.when(bizUtil.isUserRangerAdmin(Mockito.anyString())).thenReturn(false);
+ Mockito.when(userMgr.getGroupsForUser(adminLoginID)).thenReturn(new HashSet<>(Arrays.asList("users", "developers")));
+ try {
+ Mockito.when(roleStore.getRole(Mockito.anyString())).thenReturn(rangerRole);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ boolean denied = false;
+ try {
+ roleRest.revokeRole(serviceName, grantRevokeRoleRequest, Mockito.mock(HttpServletRequest.class));
+ } catch (Throwable expected) {
+ denied = true;
+ }
+ Assertions.assertTrue(denied, "expected revokeRole to deny access for mismatched real groups");
+ Mockito.verify(userMgr, Mockito.atLeastOnce()).getGroupsForUser(adminLoginID);
+ try {
+ Mockito.verify(roleStore, Mockito.never()).updateRole(Mockito.any(RangerRole.class), Mockito.anyBoolean(), Mockito.anyBoolean());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Test
+ public void test14eRevokeRoleBlockedForAuditor() {
+ Assertions.assertThrows(WebApplicationException.class, () -> {
+ String serviceName = "serviceName";
+ GrantRevokeRoleRequest grantRevokeRoleRequest = createGrantRevokeRoleRequest();
+ Mockito.doThrow(new WebApplicationException()).when(bizUtil).blockAuditorRoleUser();
+ roleRest.revokeRole(serviceName, grantRevokeRoleRequest, Mockito.mock(HttpServletRequest.class));
+ });
+ }
+
@Test
public void test15bGetUserRoles() {
Assertions.assertThrows(Throwable.class, () -> {
@@ -775,6 +983,84 @@ public void test15bGetUserRoles() {
});
}
+ @Test
+ public void test15cGetUserRolesUnauthorized() {
+ Assertions.assertThrows(Throwable.class, () -> {
+ Mockito.when(bizUtil.isUserRangerAdmin(Mockito.anyString())).thenReturn(false);
+ roleRest.getUserRoles("someOtherUser", Mockito.mock(HttpServletRequest.class));
+ Mockito.verify(restErrorUtil, Mockito.times(1)).createRESTException(Mockito.anyString());
+ });
+ }
+
+ @Test
+ public void test15dGetUserRolesSelfLookupAllowedWithoutAdmin() {
+ Set<RangerRole> rangerRoles = new HashSet<>();
+ RangerRole rangerRole = createRole();
+ rangerRoles.add(rangerRole);
+ Set<String> groups = new HashSet<>();
+ Mockito.when(xUserService.getXUserByUserName(Mockito.anyString())).thenReturn(createVXUser());
+ Mockito.when(userMgr.getGroupsForUser(Mockito.anyString())).thenReturn(groups);
+ try {
+ Mockito.when(roleStore.getRoleNames(Mockito.anyString(), eq(groups))).thenReturn(rangerRoles);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ List<String> returnedRoles = roleRest.getUserRoles(adminLoginID, Mockito.mock(HttpServletRequest.class));
+ Assertions.assertNotNull(returnedRoles);
+ Assertions.assertEquals(returnedRoles.size(), rangerRoles.size());
+ }
+
+ @Test
+ public void test13gGrantRoleImpersonationByPrivilegedCallerHonorsCallerAssertedGroups() {
+ String serviceName = "serviceName";
+ String impersonatedGrantor = "proxy-service-admin";
+ boolean isRefTableCleanupRequired = true;
+ RangerRole.RoleMember groupMember = new RangerRole.RoleMember("asserted-group", true);
+ RangerRole rangerRole = new RangerRole("target-role", "target-role", null, new ArrayList<>(), new ArrayList<>(Collections.singletonList(groupMember)));
+ GrantRevokeRoleRequest grantRevokeRoleRequest = createGrantRevokeRoleRequest();
+
+ rangerRole.setCreatedByUser("target-role");
+ rangerRole.setId(roleId);
+
+ // grantor != the real logged-in user ("admin", per @BeforeEach) -- this is the impersonation branch.
+ grantRevokeRoleRequest.setGrantor(impersonatedGrantor);
+ grantRevokeRoleRequest.setGrantorGroups(new HashSet<>(Collections.singletonList("asserted-group")));
+
+ // the REAL caller (loggedInUser="admin") is a proven ranger admin -- only then is entering the
+ // impersonation branch, and trusting its group assertion for the impersonated user, safe.
+ Mockito.when(bizUtil.isUserRangerAdmin(adminLoginID)).thenReturn(true);
+ Mockito.when(bizUtil.isUserRangerAdmin(Mockito.argThat(u -> !adminLoginID.equals(u)))).thenReturn(false);
+ try {
+ Mockito.when(roleStore.getRole(Mockito.anyString())).thenReturn(rangerRole);
+ Mockito.when(roleStore.updateRole(Mockito.any(RangerRole.class), Mockito.anyBoolean(), eq(isRefTableCleanupRequired))).then(AdditionalAnswers.returnsFirstArg());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ RESTResponse resp = roleRest.grantRole(serviceName, grantRevokeRoleRequest, Mockito.mock(HttpServletRequest.class));
+ Assertions.assertNotNull(resp);
+ Assertions.assertEquals(RESTResponse.STATUS_SUCCESS, resp.getStatusCode());
+ // the impersonated user's groups came from the request, not a directory lookup
+ Mockito.verify(userMgr, Mockito.never()).getGroupsForUser(impersonatedGrantor);
+ }
+
+ @Test
+ public void test13hGrantRoleImpersonationByUnprivilegedCallerRejectedRegardlessOfAssertedGroups() {
+ Assertions.assertThrows(Throwable.class, () -> {
+ String serviceName = "serviceName";
+ String impersonatedGrantor = "proxy-service-admin";
+ GrantRevokeRoleRequest grantRevokeRoleRequest = createGrantRevokeRoleRequest();
+ grantRevokeRoleRequest.setGrantor(impersonatedGrantor);
+ grantRevokeRoleRequest.setGrantorGroups(new HashSet<>(Collections.singletonList("asserted-group")));
+ // the REAL caller ("admin") is NOT a ranger admin and NOT a service admin/service user for
+ // this service -- the impersonation gate rejects before the target role is even looked up
+ // (roleStore.getRole() is never reached on this path), so callerAssertedGroups is never read.
+ Mockito.when(bizUtil.isUserRangerAdmin(Mockito.anyString())).thenReturn(false);
+ Mockito.when(svcStore.isServiceAdminUser(Mockito.anyString(), Mockito.anyString())).thenReturn(false);
+ roleRest.grantRole(serviceName, grantRevokeRoleRequest, Mockito.mock(HttpServletRequest.class));
+ Mockito.verify(restErrorUtil, Mockito.times(1)).createRESTException(Mockito.anyString());
+ });
+ }
+
@Test
public void test16bGetRangerRolesIfUpdated() {
Assertions.assertThrows(Throwable.class, () -> {