You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ranger.apache.org by dh...@apache.org on 2026/09/22 16:51:20 UTC

(ranger) branch master updated: RANGER-5800 : UserGroup API require additional checks (#1245)

This is an automated email from the ASF dual-hosted git repository.

dhavalshah9131 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 fb2025626 RANGER-5800 : UserGroup API require additional checks (#1245)
fb2025626 is described below

commit fb2025626862761c7a70929dfbd5ef1e803c310b
Author: dhavalshah9131 <dh...@gmail.com>
AuthorDate: Tue Sep 22 22:21:14 2026 +0530

    RANGER-5800 : UserGroup API require additional checks (#1245)
    
    * RANGER-5800 : UserGroup API require additional checks
    
    * RANGER-5800 : UserGroup API require additional checks UI changes
    
    ---------
    
    Co-authored-by: Dhaval Shah <dh...@gmail.com>
    Co-authored-by: Dhaval Rajpara <dh...@apache.org>
---
 .../main/java/org/apache/ranger/biz/XUserMgr.java  | 21 +++++++++++
 .../users_details/UserForm.jsx                     | 11 ++++--
 .../java/org/apache/ranger/biz/TestXUserMgr.java   | 44 ++++++++++++++++++++++
 3 files changed, 72 insertions(+), 4 deletions(-)

diff --git a/security-admin/src/main/java/org/apache/ranger/biz/XUserMgr.java b/security-admin/src/main/java/org/apache/ranger/biz/XUserMgr.java
index 900d30398..3e97601ab 100755
--- a/security-admin/src/main/java/org/apache/ranger/biz/XUserMgr.java
+++ b/security-admin/src/main/java/org/apache/ranger/biz/XUserMgr.java
@@ -1197,6 +1197,7 @@ public VXUser updateXUser(VXUser vXUser) {
         vXUser.setPassword(hiddenPasswordString);
 
         Long             userId             = vXUser.getId();
+        validateGroupMembershipChangeAccess(userId, groupIdList);
         List<Long>       groupUsersToRemove = new ArrayList<>();
         List<XXTrxLogV2> groupUserTrxLogs   = createOrDelGrpUserWithUpdatedGrpId(vXUser, groupIdList, userId, groupUsersToRemove);
 
@@ -3201,6 +3202,26 @@ protected void validatePassword(VXUser vXUser) {
         }
     }
 
+    private void validateGroupMembershipChangeAccess(Long userId, Collection<Long> groupIdList) {
+        if (groupIdList == null || userId == null) {
+            return;
+        }
+        UserSessionBase session = ContextUtil.getCurrentUserSession();
+        if (session == null || session.isUserAdmin()) {
+            return;
+        }
+        List<Long> existingGroupIds = daoManager.getXXGroupUser().findGroupIdListByUserId(userId);
+        Set<Long> requestedGroupIds = new HashSet<>(groupIdList);
+        Set<Long> currentGroupIds     = new HashSet<>();
+        if (CollectionUtils.isNotEmpty(existingGroupIds)) {
+            currentGroupIds.addAll(existingGroupIds);
+        }
+        if (!requestedGroupIds.equals(currentGroupIds)) {
+            throw restErrorUtil.create403RESTException(
+                    "Logged-in user is not permitted to modify group membership.");
+        }
+    }
+
     private List<XXTrxLogV2> createOrDelGrpUserWithUpdatedGrpId(VXUser vXUser, Collection<Long> groupIdList, Long userId, List<Long> groupUsersToRemove) {
         Collection<String> groupNamesSet = new HashSet<>();
         List<XXTrxLogV2>   trxLogList    = new ArrayList<>();
diff --git a/security-admin/src/main/webapp/react-webapp/src/views/UserGroupRoleListing/users_details/UserForm.jsx b/security-admin/src/main/webapp/react-webapp/src/views/UserGroupRoleListing/users_details/UserForm.jsx
index 4b0a15ff3..cede7266b 100644
--- a/security-admin/src/main/webapp/react-webapp/src/views/UserGroupRoleListing/users_details/UserForm.jsx
+++ b/security-admin/src/main/webapp/react-webapp/src/views/UserGroupRoleListing/users_details/UserForm.jsx
@@ -37,7 +37,7 @@ import {
   UserTypes
 } from "Utils/XAEnums";
 import { toast } from "react-toastify";
-import { InfoIcon, getUserAccessRoleList, serverError } from "Utils/XAUtils";
+import { InfoIcon, getUserAccessRoleList, isSystemAdmin, serverError } from "Utils/XAUtils";
 import { getUserProfile, setUserProfile } from "Utils/appState";
 import { cloneDeep, has, isEmpty, isUndefined } from "lodash";
 import { SyncSourceDetails } from "Views/UserGroupRoleListing/SyncSourceDetails";
@@ -90,6 +90,7 @@ function UserForm(props) {
   const isExternalOrFederatedUser =
     userInfo?.userSource == UserTypes.USER_EXTERNAL.value ||
     userInfo?.userSource == UserTypes.USER_FEDERATED.value;
+  const isInternalUser = userInfo?.userSource == UserTypes.USER_INTERNAL.value;
 
   const handleSubmit = async (formData) => {
     let userFormData = {};
@@ -243,9 +244,7 @@ function UserForm(props) {
         loadOptions={loadOptions}
         defaultOptions
         isMulti
-        isDisabled={
-          isEditView && userInfo && isExternalOrFederatedUser ? true : false
-        }
+        isDisabled={disabledGroupField()}
         styles={selectInputCustomStyles}
         tabSelectsValue={false}
         placeholder="Select Groups"
@@ -290,6 +289,10 @@ function UserForm(props) {
     return disabledUserRoleField;
   };
 
+  const disabledGroupField = () => {
+    return !(isSystemAdmin() && isEditView && isInternalUser)
+  };
+
   const userRoleListData = () => {
     return getUserAccessRoleList();
   };
diff --git a/security-admin/src/test/java/org/apache/ranger/biz/TestXUserMgr.java b/security-admin/src/test/java/org/apache/ranger/biz/TestXUserMgr.java
index a316b440c..e98960850 100644
--- a/security-admin/src/test/java/org/apache/ranger/biz/TestXUserMgr.java
+++ b/security-admin/src/test/java/org/apache/ranger/biz/TestXUserMgr.java
@@ -471,6 +471,50 @@ public void test05UpdateXUser() {
         Assertions.assertNotNull(dbvxUser);
     }
 
+    @Test
+    public void testUpdateXUser_KeyAdminCannotChangeGroupMembership() {
+        destroySession();
+        setupKeyAdmin();
+
+        VXUser vxUser = vxUser();
+        vxUser.setId(userId);
+        vxUser.setFirstName("test");
+        vxUser.setUserRoleList(Collections.singletonList(RangerConstants.ROLE_USER));
+
+        VXPortalUser vXPortalUser = userProfile();
+        vXPortalUser.setUserRoleList(Collections.singletonList(RangerConstants.ROLE_USER));
+
+        Mockito.when(userMgr.getUserProfileByLoginId(vxUser.getName())).thenReturn(vXPortalUser);
+        Mockito.when(xUserService.readResource(userId)).thenReturn(vxUser);
+
+        XXPortalUser xXPortalUser = xxPortalUser(vXPortalUser);
+        Mockito.when(userMgr.updateUserWithPass(Mockito.any())).thenReturn(xXPortalUser);
+        Mockito.when(xUserService.updateResource(vxUser)).thenReturn(vxUser);
+        Mockito.when(xUserService.getTransactionLog(
+                        Mockito.any(VXUser.class),
+                        Mockito.any(),
+                        Mockito.anyInt()))
+                .thenReturn(new ArrayList<>());
+
+        VXUser loggedInUser = vxUser();
+        loggedInUser.setName(keyadminLoginID);
+        loggedInUser.setUserRoleList(Collections.singletonList(RangerConstants.ROLE_KEY_ADMIN));
+        Mockito.when(xUserService.getXUserByUserName(keyadminLoginID)).thenReturn(loggedInUser);
+
+        XXGroupUserDao xxGroupUserDao = Mockito.mock(XXGroupUserDao.class);
+        Mockito.when(daoManager.getXXGroupUser()).thenReturn(xxGroupUserDao);
+        Mockito.when(xxGroupUserDao.findGroupIdListByUserId(userId))
+                .thenReturn(Collections.emptyList());
+
+        vxUser.setGroupIdList(Collections.singletonList(105L));
+
+        Mockito.when(restErrorUtil.create403RESTException(
+                        "Logged-in user is not permitted to modify group membership."))
+                .thenThrow(new WebApplicationException());
+
+        Assertions.assertThrows(WebApplicationException.class, () -> xUserMgr.updateXUser(vxUser));
+    }
+
     @Test
     public void test06ModifyUserVisibilitySetOne() {
         setup();