/** * Validate the specified password. * * @param password The password to be validated * @param forceMatch If true the specified password is always validated; * otherwise only if it is a plain text password. * @throws RepositoryException If the specified password is too short or * doesn't match the specified password pattern. */ private void validatePassword(@Nullable String password, boolean forceMatch) throws RepositoryException { if (password != null && (forceMatch || PasswordUtil.isPlainTextPassword(password))) { if (pattern != null && !pattern.matcher(password).matches()) { throw new ConstraintViolationException("Password violates password constraint (" + pattern.pattern() + ")."); } } } }
/** * Validate the specified password. * * @param password The password to be validated * @param forceMatch If true the specified password is always validated; * otherwise only if it is a plain text password. * @throws RepositoryException If the specified password is too short or * doesn't match the specified password pattern. */ private void validatePassword(@Nullable String password, boolean forceMatch) throws RepositoryException { if (password != null && (forceMatch || PasswordUtil.isPlainTextPassword(password))) { if (pattern != null && !pattern.matcher(password).matches()) { throw new ConstraintViolationException("Password violates password constraint (" + pattern.pattern() + ")."); } } } }
@Override public Object apply(Object context, Options options, HelperContext pluginContext) throws IOException { if (context == null) { return null; } String password = context.toString(); // if password is already encrypted skip further processing if (!PasswordUtil.isPlainTextPassword(password)) { return password; } ConfigurationParameters config = ConfigurationParameters.of(options.hash); try { return PasswordUtil.buildPasswordHash(password, config); } catch (NoSuchAlgorithmException ex) { throw new IOException("Unable build password hash.", ex); } }
@Override public Object apply(Object context, Options options, HelperContext pluginContext) throws IOException { if (context == null) { return null; } String password = context.toString(); // if password is already encrypted skip further processing if (!PasswordUtil.isPlainTextPassword(password)) { return password; } String encoding = options.hash(HASH_OPTION_ENCODING, DEFAULT_ENCODING); String algorithm = options.hash(HASH_OPTION_ALGORITHM, DEFAULT_HASH_ALGORITHM); return hashPassword(password, algorithm, encoding); }
@Test public void testIsPlainTextForPwHash() { for (String pwHash : hashedPasswords.values()) { assertFalse(pwHash + " should not be plain text.", PasswordUtil.isPlainTextPassword(pwHash)); } }
@Test public void testIsPlainTextPassword() { for (String pw : plainPasswords) { assertTrue(pw + " should be plain text.", PasswordUtil.isPlainTextPassword(pw)); } }
@Test public void testIsPlainTextForNull() { assertTrue(PasswordUtil.isPlainTextPassword(null)); }
@Override public void propertyChanged(PropertyState before, PropertyState after) throws CommitFailedException { if (authorizableType == null) { return; } String name = before.getName(); if (REP_PRINCIPAL_NAME.equals(name) || REP_AUTHORIZABLE_ID.equals(name)) { String msg = "Authorizable property " + name + " may not be altered after user/group creation."; throw constraintViolation(22, msg); } else if (JcrConstants.JCR_UUID.equals(name)) { checkNotNull(parentAfter); if (!isValidUUID(parentAfter, after.getValue(Type.STRING))) { String msg = "Invalid jcr:uuid for authorizable " + parentAfter.getName(); throw constraintViolation(23, msg); } } else if (JcrConstants.JCR_PRIMARYTYPE.equals(name)) { // if primary type changed to authorizable type -> need to perform // validation as if a new authorizable node had been added validateAuthorizable(parentAfter, UserUtil.getType(after.getValue(Type.STRING))); } if (isUser(parentBefore) && REP_PASSWORD.equals(name) && PasswordUtil.isPlainTextPassword(after.getValue(Type.STRING))) { String msg = "Password may not be plain text."; throw constraintViolation(24, msg); } }
@Override public void propertyChanged(PropertyState before, PropertyState after) throws CommitFailedException { if (authorizableType == null) { return; } String name = before.getName(); if (REP_PRINCIPAL_NAME.equals(name) || REP_AUTHORIZABLE_ID.equals(name)) { String msg = "Authorizable property " + name + " may not be altered after user/group creation."; throw constraintViolation(22, msg); } else if (JcrConstants.JCR_UUID.equals(name)) { checkNotNull(parentAfter); if (!isValidUUID(parentAfter, after.getValue(Type.STRING))) { String msg = "Invalid jcr:uuid for authorizable " + parentAfter.getName(); throw constraintViolation(23, msg); } } else if (JcrConstants.JCR_PRIMARYTYPE.equals(name)) { // if primary type changed to authorizable type -> need to perform // validation as if a new authorizable node had been added validateAuthorizable(parentAfter, UserUtil.getType(after.getValue(Type.STRING))); } if (isUser(parentBefore) && REP_PASSWORD.equals(name) && PasswordUtil.isPlainTextPassword(after.getValue(Type.STRING))) { String msg = "Password may not be plain text."; throw constraintViolation(24, msg); } }
@Override public void propertyChanged(PropertyState before, PropertyState after) throws CommitFailedException { if (authorizableType == null) { return; } String name = before.getName(); if (REP_PRINCIPAL_NAME.equals(name) || REP_AUTHORIZABLE_ID.equals(name)) { String msg = "Authorizable property " + name + " may not be altered after user/group creation."; throw constraintViolation(22, msg); } else if (JcrConstants.JCR_UUID.equals(name)) { checkNotNull(parentAfter); if (!isValidUUID(parentAfter, after.getValue(Type.STRING))) { String msg = "Invalid jcr:uuid for authorizable " + parentAfter.getName(); throw constraintViolation(23, msg); } } else if (JcrConstants.JCR_PRIMARYTYPE.equals(name)) { // if primary type changed to authorizable type -> need to perform // validation as if a new authorizable node had been added validateAuthorizable(parentAfter, UserUtil.getType(after.getValue(Type.STRING))); } if (isUser(parentBefore) && REP_PASSWORD.equals(name) && PasswordUtil.isPlainTextPassword(after.getValue(Type.STRING))) { String msg = "Password may not be plain text."; throw constraintViolation(24, msg); } }
void setPassword(@NotNull Tree userTree, @NotNull String userId, @NotNull String password, boolean forceHash) throws RepositoryException { String pwHash; if (forceHash || PasswordUtil.isPlainTextPassword(password)) { try { pwHash = PasswordUtil.buildPasswordHash(password, config); } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) { throw new RepositoryException(e); } } else { pwHash = password; } userTree.setProperty(UserConstants.REP_PASSWORD, pwHash); // set last-modified property if pw-expiry is enabled and the user is not // admin. if initial-pw-change is enabled, we don't set the last modified // for new users, in order to force a pw change upon the next login boolean expiryEnabled = passwordExpiryEnabled(); boolean forceInitialPwChange = forceInitialPasswordChangeEnabled(); boolean isNewUser = userTree.getStatus() == Tree.Status.NEW; if (Utils.canHavePasswordExpired(userId, config) // only expiry is enabled, set in all cases && ((expiryEnabled && !forceInitialPwChange) // as soon as force initial pw is enabled, we set in all cases except new users, // irrespective of password expiry being enabled or not || (forceInitialPwChange && !isNewUser))) { Tree pwdTree = TreeUtil.getOrAddChild(userTree, UserConstants.REP_PWD, UserConstants.NT_REP_PASSWORD); // System.currentTimeMillis() may be inaccurate on windows. This is accepted for this feature. pwdTree.setProperty(UserConstants.REP_PASSWORD_LAST_MODIFIED, System.currentTimeMillis(), Type.LONG); } }
void setPassword(@NotNull Tree userTree, @NotNull String userId, @NotNull String password, boolean forceHash) throws RepositoryException { String pwHash; if (forceHash || PasswordUtil.isPlainTextPassword(password)) { try { pwHash = PasswordUtil.buildPasswordHash(password, config); } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) { throw new RepositoryException(e); } } else { pwHash = password; } userTree.setProperty(UserConstants.REP_PASSWORD, pwHash); // set last-modified property if pw-expiry is enabled and the user is not // admin. if initial-pw-change is enabled, we don't set the last modified // for new users, in order to force a pw change upon the next login boolean expiryEnabled = passwordExpiryEnabled(); boolean forceInitialPwChange = forceInitialPasswordChangeEnabled(); boolean isNewUser = userTree.getStatus() == Tree.Status.NEW; if (Utils.canHavePasswordExpired(userId, config) // only expiry is enabled, set in all cases && ((expiryEnabled && !forceInitialPwChange) // as soon as force initial pw is enabled, we set in all cases except new users, // irrespective of password expiry being enabled or not || (forceInitialPwChange && !isNewUser))) { Tree pwdTree = TreeUtil.getOrAddChild(userTree, UserConstants.REP_PWD, UserConstants.NT_REP_PASSWORD); // System.currentTimeMillis() may be inaccurate on windows. This is accepted for this feature. pwdTree.setProperty(UserConstants.REP_PASSWORD_LAST_MODIFIED, System.currentTimeMillis(), Type.LONG); } }
void setPassword(@Nonnull Tree userTree, @Nonnull String userId, @Nonnull String password, boolean forceHash) throws RepositoryException { String pwHash; if (forceHash || PasswordUtil.isPlainTextPassword(password)) { try { pwHash = PasswordUtil.buildPasswordHash(password, config); } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) { throw new RepositoryException(e); } } else { pwHash = password; } userTree.setProperty(UserConstants.REP_PASSWORD, pwHash); // set last-modified property if pw-expiry is enabled and the user is not // admin. if initial-pw-change is enabled, we don't set the last modified // for new users, in order to force a pw change upon the next login boolean expiryEnabled = passwordExpiryEnabled(); boolean forceInitialPwChange = forceInitialPasswordChangeEnabled(); boolean isNewUser = userTree.getStatus() == Tree.Status.NEW; if (!UserUtil.isAdmin(config, userId) // only expiry is enabled, set in all cases && ((expiryEnabled && !forceInitialPwChange) // as soon as force initial pw is enabled, we set in all cases except new users, // irrespective of password expiry being enabled or not || (forceInitialPwChange && !isNewUser))) { Tree pwdTree = TreeUtil.getOrAddChild(userTree, UserConstants.REP_PWD, UserConstants.NT_REP_PASSWORD); // System.currentTimeMillis() may be inaccurate on windows. This is accepted for this feature. pwdTree.setProperty(UserConstants.REP_PASSWORD_LAST_MODIFIED, System.currentTimeMillis(), Type.LONG); } }
private void validateTokenTree(@NotNull Tree tokenTree) throws CommitFailedException { // enforce changing being made by the TokenProvider implementation verifyCommitInfo(); verifyHierarchy(tokenTree.getPath()); Tree parent = tokenTree.getParent(); if (!isTokensParent(parent) || !UserConstants.NT_REP_USER.equals(TreeUtil.getPrimaryTypeName(parent.getParent()))) { throw constraintViolation(65, "Invalid location of token node."); } // assert mandatory properties are present String key = TreeUtil.getString(tokenTree, TOKEN_ATTRIBUTE_KEY); if (PasswordUtil.isPlainTextPassword(key)) { throw constraintViolation(66, "Invalid token key."); } if (TreeUtil.getString(tokenTree, TOKEN_ATTRIBUTE_EXPIRY) == null) { throw constraintViolation(67, "Mandatory token expiration missing."); } }
private void validateTokenTree(@Nonnull Tree tokenTree) throws CommitFailedException { // enforce changing being made by the TokenProvider implementation verifyCommitInfo(); verifyHierarchy(tokenTree.getPath()); Tree parent = tokenTree.getParent(); if (!isTokensParent(parent) || !UserConstants.NT_REP_USER.equals(TreeUtil.getPrimaryTypeName(parent.getParent()))) { throw constraintViolation(65, "Invalid location of token node."); } // assert mandatory properties are present String key = TreeUtil.getString(tokenTree, TOKEN_ATTRIBUTE_KEY); if (PasswordUtil.isPlainTextPassword(key)) { throw constraintViolation(66, "Invalid token key."); } if (TreeUtil.getString(tokenTree, TOKEN_ATTRIBUTE_EXPIRY) == null) { throw constraintViolation(67, "Mandatory token expiration missing."); } }
private void validateTokenTree(@NotNull Tree tokenTree) throws CommitFailedException { // enforce changing being made by the TokenProvider implementation verifyCommitInfo(); verifyHierarchy(tokenTree.getPath()); Tree parent = tokenTree.getParent(); if (!isTokensParent(parent) || !UserConstants.NT_REP_USER.equals(TreeUtil.getPrimaryTypeName(parent.getParent()))) { throw constraintViolation(65, "Invalid location of token node."); } // assert mandatory properties are present String key = TreeUtil.getString(tokenTree, TOKEN_ATTRIBUTE_KEY); if (PasswordUtil.isPlainTextPassword(key)) { throw constraintViolation(66, "Invalid token key."); } if (TreeUtil.getString(tokenTree, TOKEN_ATTRIBUTE_EXPIRY) == null) { throw constraintViolation(67, "Mandatory token expiration missing."); } }
@Test public void testPBKDF2With() throws Exception { // https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html String algo = "PBKDF2WithHmacSHA512"; // test vector from http://tools.ietf.org/html/rfc6070 String pw = "pass\0word"; int iterations = 4096; String hash = PasswordUtil.buildPasswordHash(pw, algo, 5, iterations); assertTrue(hash.startsWith("{" + algo + "}")); int cntOctets = hash.substring(hash.lastIndexOf('-') + 1).length() / 2; assertEquals(16, cntOctets); assertFalse(PasswordUtil.isPlainTextPassword(hash)); assertTrue(PasswordUtil.isSame(hash, pw)); } }
@Test public void testPasswordValidationActionOnCreate() throws Exception { String hashed = PasswordUtil.buildPasswordHash("DWkej32H"); user = getUserManager(root).createUser("testuser", hashed); root.commit(); String pwValue = root.getTree(user.getPath()).getProperty(UserConstants.REP_PASSWORD).getValue(Type.STRING); assertFalse(PasswordUtil.isPlainTextPassword(pwValue)); assertTrue(PasswordUtil.isSame(pwValue, hashed)); }