Browse Source

HADOOP-15995. Add ldap.bind.password.alias in LdapGroupsMapping to distinguish aliases when using multiple providers through CompositeGroupsMapping. Contributed by Lukas Majercak.

Giovanni Matteo Fumarola 6 years ago
parent
commit
76efeacd5f

+ 17 - 6
hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/LdapGroupsMapping.java

@@ -149,6 +149,10 @@ public class LdapGroupsMapping
   public static final String BIND_PASSWORD_FILE_KEY = BIND_PASSWORD_KEY + ".file";
   public static final String BIND_PASSWORD_FILE_DEFAULT = "";
 
+  public static final String BIND_PASSWORD_ALIAS_KEY =
+      BIND_PASSWORD_KEY + ".alias";
+  public static final String BIND_PASSWORD_ALIAS_DEFAULT = "";
+
   /*
    * Base distinguished name to use for searches
    */
@@ -662,12 +666,19 @@ public class LdapGroupsMapping
     }
     
     bindUser = conf.get(BIND_USER_KEY, BIND_USER_DEFAULT);
-    bindPassword = getPassword(conf, BIND_PASSWORD_KEY, BIND_PASSWORD_DEFAULT);
+
+    String alias = conf.get(BIND_PASSWORD_ALIAS_KEY,
+        BIND_PASSWORD_ALIAS_DEFAULT);
+    bindPassword = getPasswordFromCredentialProviders(conf, alias, "");
     if (bindPassword.isEmpty()) {
-      bindPassword = extractPassword(
-          conf.get(BIND_PASSWORD_FILE_KEY, BIND_PASSWORD_FILE_DEFAULT));
+      bindPassword = getPassword(conf, BIND_PASSWORD_KEY,
+          BIND_PASSWORD_DEFAULT);
+      if (bindPassword.isEmpty()) {
+        bindPassword = extractPassword(
+            conf.get(BIND_PASSWORD_FILE_KEY, BIND_PASSWORD_FILE_DEFAULT));
+      }
     }
-    
+
     String baseDN = conf.getTrimmed(BASE_DN_KEY, BASE_DN_DEFAULT);
 
     // User search base which defaults to base dn.
@@ -755,10 +766,10 @@ public class LdapGroupsMapping
   }
 
   String getPasswordFromCredentialProviders(
-      Configuration conf, String alias, String defaultPass) {
+      Configuration config, String alias, String defaultPass) {
     String password = defaultPass;
     try {
-      char[] passchars = conf.getPasswordFromCredentialProviders(alias);
+      char[] passchars = config.getPasswordFromCredentialProviders(alias);
       if (passchars != null) {
         password = new String(passchars);
       }

+ 10 - 0
hadoop-common-project/hadoop-common/src/main/resources/core-default.xml

@@ -370,6 +370,16 @@
   </description>
 </property>
 
+<property>
+  <name>hadoop.security.group.mapping.ldap.bind.password.alias</name>
+  <value></value>
+  <description>
+    The alias of the bind user to be used to get the password from credential
+    providers. If the alias is empty, property
+    hadoop.security.group.mapping.ldap.bind.password is used instead.
+  </description>
+</property>
+
 <property>
   <name>hadoop.security.group.mapping.ldap.bind.password.file</name>
   <value></value>

+ 41 - 1
hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/TestLdapGroupsMapping.java

@@ -300,13 +300,53 @@ public class TestLdapGroupsMapping extends TestLdapGroupsMappingBase {
         mapping.getPassword(conf, LdapGroupsMapping.BIND_PASSWORD_KEY, ""));
     Assert.assertEquals("storepass",
         mapping.getPassword(conf, LdapGroupsMapping.LDAP_KEYSTORE_PASSWORD_KEY,
-           ""));
+            ""));
     // let's make sure that a password that doesn't exist returns an
     // empty string as currently expected and used to trigger a call to
     // extract password
     Assert.assertEquals("", mapping.getPassword(conf,"invalid-alias", ""));
   }
 
+  @Test
+  public void testConfGetPasswordUsingAlias() throws Exception {
+    File testDir = GenericTestUtils.getTestDir();
+    Configuration conf = getBaseConf();
+    final Path jksPath = new Path(testDir.toString(), "test.jks");
+    final String ourUrl =
+        JavaKeyStoreProvider.SCHEME_NAME + "://file" + jksPath.toUri();
+
+    File file = new File(testDir, "test.jks");
+    file.delete();
+    conf.set(CredentialProviderFactory.CREDENTIAL_PROVIDER_PATH, ourUrl);
+
+    // Set alias
+    String bindpassAlias = "bindpassAlias";
+    conf.set(LdapGroupsMapping.BIND_PASSWORD_ALIAS_KEY, bindpassAlias);
+
+    CredentialProvider provider =
+        CredentialProviderFactory.getProviders(conf).get(0);
+    char[] bindpass = "bindpass".toCharArray();
+
+    // Ensure that we get null when the key isn't there
+    assertNull(provider.getCredentialEntry(bindpassAlias));
+
+    // Create credential for the alias
+    provider.createCredentialEntry(bindpassAlias, bindpass);
+    provider.flush();
+
+    // Make sure we get back the right key
+    assertArrayEquals(bindpass, provider.getCredentialEntry(
+        bindpassAlias).getCredential());
+
+    LdapGroupsMapping mapping = new LdapGroupsMapping();
+    Assert.assertEquals("bindpass",
+        mapping.getPasswordFromCredentialProviders(conf, bindpassAlias, ""));
+
+    // Empty for an invalid alias
+    Assert.assertEquals("", mapping.getPasswordFromCredentialProviders(
+        conf, "invalid-alias", ""));
+  }
+
   /**
    * Test that if the {@link LdapGroupsMapping#CONNECTION_TIMEOUT} is set in the
    * configuration, the LdapGroupsMapping connection will timeout by this value