This is an automated email from the ASF dual-hosted git repository. madhan pushed a commit to branch ranger-2.6 in repository https://gitbox.apache.org/repos/asf/ranger.git
commit 9043ff7a26952e42e124ccab228631e316733936 Author: Ramesh Mani <[email protected]> AuthorDate: Sun Dec 8 19:54:39 2024 -0800 RANGER-5008:Handle creation of federated user in Ranger (cherry picked from commit f056eae1fda73c17e4c5332160144829eb7be949) --- .../apache/ranger/plugin/util/PasswordUtils.java | 118 +++++++++++++++++++++ .../main/java/org/apache/ranger/biz/UserMgr.java | 7 +- .../main/java/org/apache/ranger/biz/XUserMgr.java | 16 +++ .../java/org/apache/ranger/biz/TestXUserMgr.java | 80 ++++++++++++++ 4 files changed, 218 insertions(+), 3 deletions(-) diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/PasswordUtils.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/PasswordUtils.java index 546412b53..6cd4ee044 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/util/PasswordUtils.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/PasswordUtils.java @@ -19,6 +19,8 @@ package org.apache.ranger.plugin.util; import java.io.IOException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import javax.crypto.Cipher; @@ -229,4 +231,120 @@ public class PasswordUtils { } return decryptedPwd; } + + /* Password Generator */ + public static final class PasswordGenerator { + private static final String LOWER = "abcdefghijklmnopqrstuvwxyz"; + private static final String UPPER = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + private static final String DIGITS = "0123456789"; + private static final String SYMBOLS = "!@#$%&*()_+-=[]|,./?><"; + private final boolean useLower; + private final boolean useUpper; + private final boolean useDigits; + private final boolean useSymbols; + + private PasswordGenerator(PasswordGeneratorBuilder builder) { + this.useLower = builder.useLower; + this.useUpper = builder.useUpper; + this.useDigits = builder.useDigits; + this.useSymbols = builder.useSymbols; + } + + public static class PasswordGeneratorBuilder { + private boolean useLower; + private boolean useUpper; + private boolean useDigits; + private boolean useSymbols; + + public PasswordGeneratorBuilder() { + this.useLower = false; + this.useUpper = false; + this.useDigits = false; + this.useSymbols = false; + } + + /** + * @param useLower true in case you would like to include lowercase + * characters (abc...xyz). Default false. + * @return the builder for chaining. + */ + public PasswordGeneratorBuilder useLower(boolean useLower) { + this.useLower = useLower; + return this; + } + + /** + * @param useUpper true in case you would like to include uppercase + * characters (ABC...XYZ). Default false. + * @return the builder for chaining. + */ + public PasswordGeneratorBuilder useUpper(boolean useUpper) { + this.useUpper = useUpper; + return this; + } + + /** + * @param useDigits true in case you would like to include digit + * characters (123...). Default false. + * @return the builder for chaining. + */ + public PasswordGeneratorBuilder useDigits(boolean useDigits) { + this.useDigits = useDigits; + return this; + } + + /** + * @param useSymbols true in case you would like to include + * punctuation characters (!@#...). Default false. + * @return the builder for chaining. + */ + public PasswordGeneratorBuilder useSymbols(boolean useSymbols) { + this.useSymbols = useSymbols; + return this; + } + + /** + * Get an object to use. + * + * @return the {@link PasswordGenerator} + * object. + */ + public PasswordGenerator build() { + return new PasswordGenerator(this); + } + } + + /** + * @param length the length of the password you would like to generate. + * @return a password that uses the categories you define when constructing + * the object with a probability. + */ + public String generate(int length) { + StringBuilder password = new StringBuilder(length); + SecureRandom secureRandom = new SecureRandom(); + + List<String> charCategories = new ArrayList<>(4); + if (useLower) { + charCategories.add(LOWER); + } + if (useUpper) { + charCategories.add(UPPER); + } + if (useDigits) { + charCategories.add(DIGITS); + } + if (useSymbols) { + charCategories.add(SYMBOLS); + } + + // Build the password. + for (int i = 0; i < length; i++) { + int idxCatagory = (i < charCategories.size()) ? i : secureRandom.nextInt(charCategories.size()); + String charCategory = charCategories.get(idxCatagory); + int position = secureRandom.nextInt(charCategory.length()); + password.append(charCategory.charAt(position)); + } + return new String(password); + } + } } diff --git a/security-admin/src/main/java/org/apache/ranger/biz/UserMgr.java b/security-admin/src/main/java/org/apache/ranger/biz/UserMgr.java index 1684a42ec..f7433f6f2 100644 --- a/security-admin/src/main/java/org/apache/ranger/biz/UserMgr.java +++ b/security-admin/src/main/java/org/apache/ranger/biz/UserMgr.java @@ -1065,9 +1065,10 @@ public class UserMgr { } public VXPortalUser createDefaultAccountUser(VXPortalUser userProfile) { - if (userProfile.getPassword() == null - || userProfile.getPassword().trim().isEmpty()) { - userProfile.setUserSource(RangerCommonEnums.USER_EXTERNAL); + if (userProfile.getUserSource() != RangerCommonEnums.USER_FEDERATED) { + if (StringUtils.isBlank(userProfile.getPassword())) { + userProfile.setUserSource(RangerCommonEnums.USER_EXTERNAL); + } } // access control checkAdminAccess(); 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 b6d3855d4..e7c2dbd07 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 @@ -48,6 +48,7 @@ import org.apache.ranger.plugin.model.RangerPolicy.RangerRowFilterPolicyItem; import org.apache.ranger.plugin.model.RangerPrincipal; import org.apache.ranger.plugin.model.UserInfo; import org.apache.ranger.plugin.util.RangerUserStore; +import org.apache.ranger.plugin.util.PasswordUtils.PasswordGenerator; import org.apache.ranger.service.*; import org.apache.ranger.ugsyncutil.model.GroupUserInfo; import org.apache.ranger.ugsyncutil.model.UsersGroupRoleAssignments; @@ -108,6 +109,7 @@ public class XUserMgr extends XUserMgrBase { private static final String USER = "User"; private static final String GROUP = "Group"; private static final int MAX_DB_TRANSACTION_RETRIES = 5; + private static final int PASSWORD_LENGTH = 16; @Autowired RangerBizUtil msBizUtil; @@ -182,6 +184,20 @@ public class XUserMgr extends XUserMgrBase { public VXUser createXUser(VXUser vXUser) { checkAdminAccess(); xaBizUtil.blockAuditorRoleUser(); + + if (vXUser.getUserSource() == RangerCommonEnums.USER_FEDERATED) { + if (StringUtils.isEmpty(vXUser.getPassword())) { + PasswordGenerator passwordGenerator = new PasswordGenerator.PasswordGeneratorBuilder() + .useLower(true) + .useUpper(true) + .useDigits(true) + .useSymbols(true) + .build(); + String passWd = passwordGenerator.generate(PASSWORD_LENGTH); + vXUser.setPassword(passWd); + } + } + validatePassword(vXUser); String userName = vXUser.getName(); String firstName = vXUser.getFirstName(); 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 77aecccf5..e1bc523c7 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 @@ -275,6 +275,23 @@ public class TestXUserMgr { return vxUser; } + private VXUser vxUserFederated() { + Collection<String> userRoleList = new ArrayList<String>(); + userRoleList.add("ROLE_USER"); + Collection<String> groupNameList = new ArrayList<String>(); + groupNameList.add(groupName); + VXUser vxUser = new VXUser(); + vxUser.setId(userId); + vxUser.setDescription("group test working"); + vxUser.setName(userLoginID); + vxUser.setUserRoleList(userRoleList); + vxUser.setGroupNameList(groupNameList); + vxUser.setPassword(null); + vxUser.setEmailAddress("[email protected]"); + vxUser.setUserSource(RangerCommonEnums.USER_FEDERATED); + return vxUser; + } + private XXUser xxUser(VXUser vxUser) { XXUser xXUser = new XXUser(); xXUser.setId(userId); @@ -4651,4 +4668,67 @@ public class TestXUserMgr { Assert.assertNotNull(createdXUser); Assert.assertEquals(createdXUser.getName(), vXUser.getName()); } + + @Test + public void test01CreateXUser_federated() { + destroySession(); + setup(); + VXUser vxUser = vxUserFederated(); + vxUser.setFirstName("user12"); + vxUser.setLastName("test12"); + Collection<Long> groupIdList = new ArrayList<Long>(); + groupIdList.add(userId); + vxUser.setGroupIdList(groupIdList); + VXGroup vxGroup = vxGroup(); + vxGroup.setName("user12Grp"); + VXGroupUser vXGroupUser = new VXGroupUser(); + vXGroupUser.setParentGroupId(userId); + vXGroupUser.setUserId(userId); + vXGroupUser.setName(vxGroup.getName()); + Mockito.when(xGroupService.readResource(userId)).thenReturn(vxGroup); + Mockito.when(xGroupUserService.createResource((VXGroupUser) Mockito.any())).thenReturn(vXGroupUser); + ArrayList<String> userRoleListVXPortaUser = getRoleList(); + VXPortalUser vXPortalUser = new VXPortalUser(); + vXPortalUser.setUserRoleList(userRoleListVXPortaUser); + Mockito.when(xUserService.createResource(vxUser)).thenReturn(vxUser); + XXModuleDefDao value = Mockito.mock(XXModuleDefDao.class); + Mockito.when(daoManager.getXXModuleDef()).thenReturn(value); + Mockito.when(userMgr.createDefaultAccountUser((VXPortalUser) Mockito.any())).thenReturn(vXPortalUser); + Mockito.when(stringUtil.validateEmail("[email protected]")).thenReturn(true); + VXUser dbUser = xUserMgr.createXUser(vxUser); + Assert.assertNotNull(dbUser); + userId = dbUser.getId(); + Assert.assertEquals(userId, dbUser.getId()); + Assert.assertEquals(dbUser.getDescription(), vxUser.getDescription()); + Assert.assertEquals(dbUser.getName(), vxUser.getName()); + Assert.assertEquals(dbUser.getUserRoleList(), vxUser.getUserRoleList()); + Assert.assertEquals(dbUser.getGroupNameList(), + vxUser.getGroupNameList()); + Assert.assertNotNull(dbUser.getPassword()); + Assert.assertEquals(dbUser.getUserSource(), RangerCommonEnums.USER_FEDERATED); + Mockito.verify(xUserService).createResource(vxUser); + Mockito.when(xUserService.readResourceWithOutLogin(userId)).thenReturn(vxUser); + + VXUser loggedInUser = vxUser(); + List<String> loggedInUserRole = new ArrayList<String>(); + loggedInUserRole.add(RangerConstants.ROLE_ADMIN); + loggedInUser.setId(8L); + loggedInUser.setName("testuser"); + loggedInUser.setUserRoleList(loggedInUserRole); + Mockito.when(xUserService.getXUserByUserName("admin")).thenReturn(loggedInUser); + Mockito.when(restErrorUtil.createRESTException(HttpServletResponse.SC_FORBIDDEN, "Logged-In user is not allowed to access requested user data", true)).thenThrow(new WebApplicationException()); + thrown.expect(WebApplicationException.class); + VXUser dbvxUser = xUserMgr.getXUser(userId); + Mockito.verify(userMgr).createDefaultAccountUser((VXPortalUser) Mockito.any()); + Assert.assertNotNull(dbvxUser); + Assert.assertEquals(userId, dbvxUser.getId()); + Assert.assertEquals(dbvxUser.getDescription(), vxUser.getDescription()); + Assert.assertEquals(dbvxUser.getName(), vxUser.getName()); + Assert.assertEquals(dbvxUser.getUserRoleList(),vxUser.getUserRoleList()); + Assert.assertEquals(dbvxUser.getGroupIdList(),vxUser.getGroupIdList()); + Assert.assertEquals(dbvxUser.getGroupNameList(),vxUser.getGroupNameList()); + Assert.assertNotNull(dbvxUser.getPassword()); + Assert.assertEquals(dbvxUser.getUserSource(), RangerCommonEnums.USER_FEDERATED); + Mockito.verify(xUserService).readResourceWithOutLogin(userId); + } }
