This is an automated email from the ASF dual-hosted git repository.
dhavalshah9131 pushed a commit to branch ranger-2.10
in repository https://gitbox.apache.org/repos/asf/ranger.git
The following commit(s) were added to refs/heads/ranger-2.10 by this push:
new 15a268e6e RANGER-5800 : UserGroup API require additional checks (#1253)
15a268e6e is described below
commit 15a268e6e1cf62e0510911d56c7f55625ed26980
Author: dhavalshah9131 <[email protected]>
AuthorDate: Thu Sep 24 18:45:50 2026 +0530
RANGER-5800 : UserGroup API require additional checks (#1253)
Co-authored-by: Dhaval Shah <dhavalshah9131@@gmail.com>
---
.../main/java/org/apache/ranger/biz/XUserMgr.java | 22 +++++++++++
.../users_details/UserForm.jsx | 11 ++++--
.../java/org/apache/ranger/biz/TestXUserMgr.java | 44 ++++++++++++++++++++++
3 files changed, 73 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 1b013b314..973936239 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
@@ -513,6 +513,7 @@ else if (oldUserProfile.getUserSource() ==
RangerCommonEnums.USER_EXTERNAL) {
vXUser.setPassword(hiddenPasswordString);
Long userId = vXUser.getId();
+ validateGroupMembershipChangeAccess(userId, groupIdList);
List<Long> groupUsersToRemove = new ArrayList<Long>();
List<XXTrxLogV2> groupUserTrxLogs =
createOrDelGrpUserWithUpdatedGrpId(vXUser, groupIdList,userId,
groupUsersToRemove);
@@ -530,6 +531,27 @@ else if (oldUserProfile.getUserSource() ==
RangerCommonEnums.USER_EXTERNAL) {
return 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<String>();
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 2eb9e5a85..fbeaaceb0 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
@@ -35,7 +35,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";
@@ -84,6 +84,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 = {};
@@ -237,9 +238,7 @@ function UserForm(props) {
loadOptions={loadOptions}
defaultOptions
isMulti
- isDisabled={
- isEditView && userInfo && isExternalOrFederatedUser ? true : false
- }
+ isDisabled={disabledGroupField()}
styles={selectInputCustomStyles}
/>
);
@@ -278,6 +277,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 f6ab5b116..708507809 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
@@ -815,6 +815,50 @@ public void test05UpdateXUser() {
Assert.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();