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

dmitriusan pushed a commit to branch branch-2.7
in repository https://gitbox.apache.org/repos/asf/ambari.git


The following commit(s) were added to refs/heads/branch-2.7 by this push:
     new 87e65b5  AMBARI-25268. implement configurable password policy for 
Ambari users (dlysnichenko) (#2954)
87e65b5 is described below

commit 87e65b5f1cec08849939033c0958bbf39733c727
Author: Lisnichenko Dmitro <dlysniche...@hortonworks.com>
AuthorDate: Tue Apr 30 20:43:57 2019 +0300

    AMBARI-25268. implement configurable password policy for Ambari users 
(dlysnichenko) (#2954)
---
 .../ambari/server/configuration/Configuration.java  | 15 +++++++++++++++
 .../controller/internal/UserResourceProvider.java   |  8 +++++++-
 .../ambari/server/security/authorization/Users.java | 19 +++++++++++--------
 .../server/security/authorization/TestUsers.java    | 21 +++++++++++++++++----
 4 files changed, 50 insertions(+), 13 deletions(-)

diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java
index 09d90e8..747236c 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java
@@ -505,6 +505,14 @@ public class Configuration {
       "agent.ssl", "true");
 
   /**
+   * Configurable password policy for Ambari users
+   */
+  @Markdown(
+      description = "Determines Ambari user password policy. Passwords should 
match the regex")
+  public static final ConfigurationProperty<String> PASSWORD_POLICY_REGEXP = 
new ConfigurationProperty<>(
+      "security.password.policy.regexp", ".*");
+
+  /**
    * Determines whether the Ambari Agent host names should be validated against
    * a regular expression to ensure that they are well-formed.
    */
@@ -3999,6 +4007,13 @@ public class Configuration {
     return getProperty(MYSQL_JAR_NAME);
   }
 
+  /**
+   * @return Configurable password policy for Ambari users
+   */
+  public String getPasswordPolicyRegexp() {
+    return getProperty(PASSWORD_POLICY_REGEXP);
+  }
+
   public JPATableGenerationStrategy getJPATableGenerationStrategy() {
     return JPATableGenerationStrategy.fromString(
         System.getProperty(SERVER_JDBC_GENERATE_TABLES.getKey()));
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UserResourceProvider.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UserResourceProvider.java
index 1177b57..29f1749 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UserResourceProvider.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UserResourceProvider.java
@@ -379,6 +379,12 @@ public class UserResourceProvider extends 
AbstractControllerResourceProvider imp
       String username = request.getUsername();
       String displayName = 
StringUtils.defaultIfEmpty(request.getDisplayName(), username);
       String localUserName = 
StringUtils.defaultIfEmpty(request.getLocalUserName(), username);
+      String password = request.getPassword();
+      // Setting a user's the password here is to allow for backward 
compatibility with pre-Ambari-3.0
+      // versions so that the contract for REST API V1 is maintained.
+      if (!StringUtils.isEmpty(password)) {
+        users.validatePassword(password);
+      }
 
       UserEntity userEntity = users.createUser(username, localUserName, 
displayName, request.isActive());
       if (userEntity != null) {
@@ -388,7 +394,7 @@ public class UserResourceProvider extends 
AbstractControllerResourceProvider imp
 
         // Setting a user's the password here is to allow for backward 
compatibility with pre-Ambari-3.0
         // versions so that the contract for REST API V1 is maintained.
-        if (!StringUtils.isEmpty(request.getPassword())) {
+        if (!StringUtils.isEmpty(password)) {
           // This is performed as a user administrator since the  
authorization check was done prior
           // to executing #createResourcesAuthorized.
           addOrUpdateLocalAuthenticationSource(true, userEntity, 
request.getPassword(), null);
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/Users.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/Users.java
index 0974a72..3f81c52 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/Users.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/Users.java
@@ -28,6 +28,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
+import java.util.regex.Pattern;
 
 import javax.persistence.EntityManager;
 import javax.persistence.OptimisticLockException;
@@ -1746,20 +1747,22 @@ public class Users {
   }
 
   /**
-   * Validates the password meets configured requirements.
+   * Validates the password meets configured requirements according 
ambari.properties.
+   *
    * <p>
-   * In the future this may be configurable. For now just make sure the 
password is not empty.
    *
    * @param password the password
-   * @return true if the password is valid; false otherwise
+   * @throws  IllegalArgumentException if password does not meet the password 
policy requirements
    */
-  public boolean validatePassword(String password) throws AmbariException {
-    // TODO: validate the new password...
+  public void validatePassword(String password) {
     if (StringUtils.isEmpty(password)) {
-      throw new AmbariException("The new password does not meet the Ambari 
password requirements");
+      throw new IllegalArgumentException("The password does not meet the 
password policy requirements");
+    }
+    String regexp = configuration.getPasswordPolicyRegexp();
+    if (!StringUtils.isEmpty(regexp) && (!Pattern.matches(regexp,password))) {
+      final String msg = "The password does not meet the Ambari user password 
policy regexp:" + regexp;
+      throw new IllegalArgumentException(msg);
     }
-
-    return true;
   }
 
   /**
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/TestUsers.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/TestUsers.java
index 0c1ce05..24cd6d7 100644
--- 
a/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/TestUsers.java
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/TestUsers.java
@@ -35,6 +35,7 @@ import java.util.List;
 
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.H2DatabaseCleaner;
+import org.apache.ambari.server.configuration.Configuration;
 import org.apache.ambari.server.ldap.domain.AmbariLdapConfiguration;
 import org.apache.ambari.server.ldap.service.AmbariLdapConfigurationProvider;
 import org.apache.ambari.server.orm.GuiceJpaInitializer;
@@ -94,6 +95,8 @@ public class TestUsers {
   protected PrincipalDAO principalDAO;
   @Inject
   protected PasswordEncoder passwordEncoder;
+  @Inject
+  protected Configuration configuration;
 
   @Before
   public void setup() throws AmbariException {
@@ -213,16 +216,26 @@ public class TestUsers {
     try {
       users.modifyAuthentication(foundLocalAuthenticationEntity, "user", null, 
true);
       fail("Null password should not be allowed");
-    } catch (AmbariException e) {
-      assertEquals("The new password does not meet the Ambari password 
requirements", e.getLocalizedMessage());
+    } catch (IllegalArgumentException e) {
+      assertEquals("The password does not meet the password policy 
requirements", e.getLocalizedMessage());
     }
 
     try {
       users.modifyAuthentication(foundLocalAuthenticationEntity, "user", "", 
false);
       fail("Empty password should not be allowed");
-    } catch (AmbariException e) {
-      assertEquals("The new password does not meet the Ambari password 
requirements", e.getLocalizedMessage());
+    } catch (IllegalArgumentException e) {
+      assertEquals("The password does not meet the password policy 
requirements", e.getLocalizedMessage());
+    }
+
+    //Minimum eight characters, at least one letter and one number:
+    configuration.setProperty(Configuration.PASSWORD_POLICY_REGEXP, 
"^(?=.*[A-Za-z])(?=.*\\d)[A-Za-z\\d]{8,}$");
+    try {
+      users.modifyAuthentication(foundLocalAuthenticationEntity, "user", 
"abc123", false);
+      fail("Should not pass validation");
+    } catch (IllegalArgumentException e) {
+      assertEquals("The password does not meet the Ambari user password policy 
regexp:^(?=.*[A-Za-z])(?=.*\\d)[A-Za-z\\d]{8,}$", e.getLocalizedMessage());
     }
+    users.modifyAuthentication(foundLocalAuthenticationEntity, "user", 
"abcd1234", false);
   }
 
   @Test

Reply via email to