You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@zeppelin.apache.org by pr...@apache.org on 2018/06/15 07:37:43 UTC

zeppelin git commit: [ZEPPELIN-3539] Add jceks stored password support for LDAP

Repository: zeppelin
Updated Branches:
  refs/heads/master bf4e1f93b -> 1caa6aa2f


[ZEPPELIN-3539] Add jceks stored password support for LDAP

This is to add support for storing password in jceks for LDAP (realm).

If the hadoopSecurityCredentialPath path is present and not empty in the shiro.ini, then the password is read from the keystore file and it need not be stored inside the shiro.ini file.

[Improvement | Feature]

* [x] - Add documentation

* [ZEPPELIN-3539](https://issues.apache.org/jira/browse/ZEPPELIN-3539)

Create a keystore file using the hadoop credential command line
```
hadoop credential create ldapRealm.systemPassword -provider jceks://file/user/zeppelin/conf/zeppelin.jceks
```
Change the following values in the Shiro.ini file, and uncomment the line:
```
ldapRealm.hadoopSecurityCredentialPath = jceks://file/user/zeppelin/conf/zeppelin.jceks
```

* Does the licenses files need update? N/A
* Is there breaking changes for older versions? N/A
* Does this needs documentation? N/A

Author: Prabhjyot Singh <pr...@gmail.com>

Closes #3018 from prabhjyotsingh/ZEPPELIN-3539 and squashes the following commits:

4dcbaabb4 [Prabhjyot Singh] zeppelin server should not start if invaild configuration is applied
228022db3 [Prabhjyot Singh] doc for hadoopSecurityCredentialPath
1428b3d1b [Prabhjyot Singh] [ZEPPELIN-3539] Add jceks stored password support for LDAP

Change-Id: Id50532a0b05de1572efd7fb5dc8a67777ca66fb8


Project: http://git-wip-us.apache.org/repos/asf/zeppelin/repo
Commit: http://git-wip-us.apache.org/repos/asf/zeppelin/commit/1caa6aa2
Tree: http://git-wip-us.apache.org/repos/asf/zeppelin/tree/1caa6aa2
Diff: http://git-wip-us.apache.org/repos/asf/zeppelin/diff/1caa6aa2

Branch: refs/heads/master
Commit: 1caa6aa2fffbf0b2b549dffa5b3b88d2d63da5ad
Parents: bf4e1f9
Author: Prabhjyot Singh <pr...@gmail.com>
Authored: Thu Jun 14 12:46:08 2018 +0530
Committer: Prabhjyot Singh <pr...@gmail.com>
Committed: Fri Jun 15 13:07:36 2018 +0530

----------------------------------------------------------------------
 docs/setup/security/shiro_authentication.md     |  11 +
 .../realm/ActiveDirectoryGroupRealm.java        |  17 +-
 .../org/apache/zeppelin/realm/LdapRealm.java    | 285 +++++++++++--------
 3 files changed, 175 insertions(+), 138 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/zeppelin/blob/1caa6aa2/docs/setup/security/shiro_authentication.md
----------------------------------------------------------------------
diff --git a/docs/setup/security/shiro_authentication.md b/docs/setup/security/shiro_authentication.md
index e1bf650..a9dd13e 100644
--- a/docs/setup/security/shiro_authentication.md
+++ b/docs/setup/security/shiro_authentication.md
@@ -185,6 +185,17 @@ securityManager.sessionManager = $sessionManager
 securityManager.realms = $ldapRealm
 ```
 
+Also instead of specifying systemPassword in clear text in `shiro.ini` administrator can choose to specify the same in "hadoop credential". 
+Create a keystore file using the hadoop credential command line:
+``` 
+hadoop credential create ldapRealm.systemPassword -provider jceks://file/user/zeppelin/conf/zeppelin.jceks
+```
+
+Add the following line in the `shiro.ini` file:
+``` 
+ldapRealm.hadoopSecurityCredentialPath = jceks://file/user/zeppelin/conf/zeppelin.jceks
+```
+
 ### PAM
 [PAM](https://en.wikipedia.org/wiki/Pluggable_authentication_module) authentication support allows the reuse of existing authentication
 moduls on the host where Zeppelin is running. On a typical system modules are configured per service for example sshd, passwd, etc. under `/etc/pam.d/`. You can

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/1caa6aa2/zeppelin-server/src/main/java/org/apache/zeppelin/realm/ActiveDirectoryGroupRealm.java
----------------------------------------------------------------------
diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/realm/ActiveDirectoryGroupRealm.java b/zeppelin-server/src/main/java/org/apache/zeppelin/realm/ActiveDirectoryGroupRealm.java
index 3ca8c4e..624b1ae 100644
--- a/zeppelin-server/src/main/java/org/apache/zeppelin/realm/ActiveDirectoryGroupRealm.java
+++ b/zeppelin-server/src/main/java/org/apache/zeppelin/realm/ActiveDirectoryGroupRealm.java
@@ -17,9 +17,6 @@
 package org.apache.zeppelin.realm;
 
 import org.apache.commons.lang.StringUtils;
-import org.apache.hadoop.conf.Configuration;
-import org.apache.hadoop.security.alias.CredentialProvider;
-import org.apache.hadoop.security.alias.CredentialProviderFactory;
 import org.apache.shiro.authc.AuthenticationException;
 import org.apache.shiro.authc.AuthenticationInfo;
 import org.apache.shiro.authc.AuthenticationToken;
@@ -141,19 +138,7 @@ public class ActiveDirectoryGroupRealm extends AbstractLdapRealm {
     if (StringUtils.isEmpty(this.hadoopSecurityCredentialPath)) {
       password = this.systemPassword;
     } else {
-      try {
-        Configuration configuration = new Configuration();
-        configuration.set(CredentialProviderFactory.CREDENTIAL_PROVIDER_PATH,
-                this.hadoopSecurityCredentialPath);
-        CredentialProvider provider = CredentialProviderFactory.getProviders(configuration).get(0);
-        CredentialProvider.CredentialEntry credEntry = provider.getCredentialEntry(
-                keystorePass);
-        if (credEntry != null) {
-          password = new String(credEntry.getCredential());
-        }
-      } catch (Exception e) {
-        log.debug("ignored error from getting credential entry from keystore", e);
-      }
+      password = LdapRealm.getSystemPassword(hadoopSecurityCredentialPath, keystorePass);
     }
     return password;
   }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/1caa6aa2/zeppelin-server/src/main/java/org/apache/zeppelin/realm/LdapRealm.java
----------------------------------------------------------------------
diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/realm/LdapRealm.java b/zeppelin-server/src/main/java/org/apache/zeppelin/realm/LdapRealm.java
index c6359e5..562ed96 100644
--- a/zeppelin-server/src/main/java/org/apache/zeppelin/realm/LdapRealm.java
+++ b/zeppelin-server/src/main/java/org/apache/zeppelin/realm/LdapRealm.java
@@ -18,27 +18,6 @@
  */
 package org.apache.zeppelin.realm;
 
-import org.apache.shiro.SecurityUtils;
-import org.apache.shiro.authc.AuthenticationInfo;
-import org.apache.shiro.authc.AuthenticationToken;
-import org.apache.shiro.authc.SimpleAuthenticationInfo;
-import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
-import org.apache.shiro.authz.AuthorizationInfo;
-import org.apache.shiro.authz.SimpleAuthorizationInfo;
-import org.apache.shiro.crypto.hash.DefaultHashService;
-import org.apache.shiro.crypto.hash.Hash;
-import org.apache.shiro.crypto.hash.HashRequest;
-import org.apache.shiro.crypto.hash.HashService;
-import org.apache.shiro.realm.ldap.JndiLdapRealm;
-import org.apache.shiro.realm.ldap.LdapContextFactory;
-import org.apache.shiro.realm.ldap.LdapUtils;
-import org.apache.shiro.session.Session;
-import org.apache.shiro.subject.MutablePrincipalCollection;
-import org.apache.shiro.subject.PrincipalCollection;
-import org.apache.shiro.util.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -52,7 +31,6 @@ import java.util.Set;
 import java.util.StringTokenizer;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
-
 import javax.naming.AuthenticationException;
 import javax.naming.Context;
 import javax.naming.NamingEnumeration;
@@ -66,66 +44,89 @@ import javax.naming.ldap.Control;
 import javax.naming.ldap.LdapContext;
 import javax.naming.ldap.LdapName;
 import javax.naming.ldap.PagedResultsControl;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.security.alias.CredentialProvider;
+import org.apache.hadoop.security.alias.CredentialProviderFactory;
+import org.apache.shiro.SecurityUtils;
+import org.apache.shiro.ShiroException;
+import org.apache.shiro.authc.AuthenticationInfo;
+import org.apache.shiro.authc.AuthenticationToken;
+import org.apache.shiro.authc.SimpleAuthenticationInfo;
+import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
+import org.apache.shiro.authz.AuthorizationInfo;
+import org.apache.shiro.authz.SimpleAuthorizationInfo;
+import org.apache.shiro.crypto.hash.DefaultHashService;
+import org.apache.shiro.crypto.hash.Hash;
+import org.apache.shiro.crypto.hash.HashRequest;
+import org.apache.shiro.crypto.hash.HashService;
+import org.apache.shiro.realm.ldap.JndiLdapContextFactory;
+import org.apache.shiro.realm.ldap.JndiLdapRealm;
+import org.apache.shiro.realm.ldap.LdapContextFactory;
+import org.apache.shiro.realm.ldap.LdapUtils;
+import org.apache.shiro.session.Session;
+import org.apache.shiro.subject.MutablePrincipalCollection;
+import org.apache.shiro.subject.PrincipalCollection;
+import org.apache.shiro.util.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
- * Implementation of {@link org.apache.shiro.realm.ldap.JndiLdapRealm} that also
- * returns each user's groups. This implementation is heavily based on
- * org.apache.isis.security.shiro.IsisLdapRealm.
- * 
+ * Implementation of {@link org.apache.shiro.realm.ldap.JndiLdapRealm} that also returns each user's
+ * groups. This implementation is heavily based on org.apache.isis.security.shiro.IsisLdapRealm.
+ *
  * <p>This implementation saves looked up ldap groups in Shiro Session to make them
  * easy to be looked up outside of this object
- * 
- * <p>Sample config for <tt>shiro.ini</tt>:
- * 
- * <p>[main] 
- * ldapRealm = org.apache.zeppelin.realm.LdapRealm
- * ldapRealm.contextFactory.url = ldap://localhost:33389
- * ldapRealm.contextFactory.authenticationMechanism = simple
- * ldapRealm.contextFactory.systemUsername = uid=guest,ou=people,dc=hadoop,dc=
- * apache,dc=org
- * ldapRealm.contextFactory.systemPassword = S{ALIAS=ldcSystemPassword}
- * ldapRealm.userDnTemplate = uid={0},ou=people,dc=hadoop,dc=apache,dc=org
- * # Ability to set ldap paging Size if needed default is 100
- * ldapRealm.pagingSize = 200
- * ldapRealm.authorizationEnabled = true
- * ldapRealm.searchBase = dc=hadoop,dc=apache,dc=org
- * ldapRealm.userSearchBase = dc=hadoop,dc=apache,dc=org
- * ldapRealm.groupSearchBase = ou=groups,dc=hadoop,dc=apache,dc=org
- * ldapRealm.userObjectClass = person
- * ldapRealm.groupObjectClass = groupofnames
- * # Allow userSearchAttribute to be customized
- * ldapRealm.userSearchAttributeName = sAMAccountName
- * ldapRealm.memberAttribute = member
- * # force usernames returned from ldap to lowercase useful for AD
- * ldapRealm.userLowerCase = true 
- * # ability set searchScopes subtree (default), one, base
- * ldapRealm.userSearchScope = subtree;
- * ldapRealm.groupSearchScope = subtree;
- * ldapRealm.userSearchFilter = (&(objectclass=person)(sAMAccountName={0}))
- * ldapRealm.groupSearchFilter = (&(objectclass=groupofnames)(member={0}))
- * ldapRealm.memberAttributeValueTemplate=cn={0},ou=people,dc=hadoop,dc=apache,
- * dc=org
- * # enable support for nested groups using the LDAP_MATCHING_RULE_IN_CHAIN operator
- * ldapRealm.groupSearchEnableMatchingRuleInChain = true
  *
- * <p># optional mapping from physical groups to logical application roles
- * ldapRealm.rolesByGroup = \ LDN_USERS: user_role,\ NYK_USERS: user_role,\
- * HKG_USERS: user_role,\ GLOBAL_ADMIN: admin_role,\ DEMOS: self-install_role
+ * <p>Sample config for <tt>shiro.ini</tt>:
  *
- * <p># optional list of roles that are allowed to authenticate
- * ldapRealm.allowedRolesForAuthentication = admin_role,user_role
- * 
- * <p>ldapRealm.permissionsByRole=\ user_role = *:ToDoItemsJdo:*:*,\
- * *:ToDoItem:*:*; \ self-install_role = *:ToDoItemsFixturesService:install:* ;
- * \ admin_role = *
- * 
- * <p>[urls]
- * **=authcBasic
- * 
- * <p>securityManager.realms = $ldapRealm
- * 
+ * <p>
+ *   [main]
+ *   ldapRealm = org.apache.zeppelin.realm.LdapRealm
+ *   ldapRealm.contextFactory.url = ldap://localhost:33389
+ *   ldapRealm.contextFactory.authenticationMechanism = simple
+ *   ldapRealm.contextFactory.systemUsername = uid=guest,ou=people,dc=hadoop,dc= apache,dc=org
+ *   ldapRealm.contextFactory.systemPassword = S{ALIAS=ldcSystemPassword}
+ *   ldapRealm.hadoopSecurityCredentialPath = jceks://file/user/zeppelin/zeppelin.jceks
+ *   ldapRealm.userDnTemplate = uid={0},ou=people,dc=hadoop,dc=apache,dc=org
+ *   # Ability to set ldap paging Size if needed default is 100
+ *   ldapRealm.pagingSize = 200
+ *   ldapRealm.authorizationEnabled = true
+ *   ldapRealm.searchBase = dc=hadoop,dc=apache,dc=org
+ *   ldapRealm.userSearchBase = dc=hadoop,dc=apache,dc=org
+ *   ldapRealm.groupSearchBase = ou=groups,dc=hadoop,dc=apache,dc=org
+ *   ldapRealm.userObjectClass = person
+ *   ldapRealm.groupObjectClass = groupofnames
+ *   # Allow userSearchAttribute to be customized
+ *   ldapRealm.userSearchAttributeName = sAMAccountName
+ *   ldapRealm.memberAttribute = member
+ *   # force usernames returned from ldap to lowercase useful for AD
+ *   ldapRealm.userLowerCase = true
+ *   # ability set searchScopes subtree (default), one, base
+ *   ldapRealm.userSearchScope = subtree;
+ *   ldapRealm.groupSearchScope = subtree;
+ *   ldapRealm.userSearchFilter = (&(objectclass=person)(sAMAccountName={0}))
+ *   ldapRealm.groupSearchFilter = (&(objectclass=groupofnames)(member={0}))
+ *   ldapRealm.memberAttributeValueTemplate=cn={0},ou=people,dc=hadoop,dc=apache,dc=org
+ *   # enable support for nested groups using the LDAP_MATCHING_RULE_IN_CHAIN operator
+ *   ldapRealm.groupSearchEnableMatchingRuleInChain = true
+ * <p>
+ *   # optional mapping from physical groups to logical application roles
+ *   ldapRealm.rolesByGroup = \ LDN_USERS: user_role,\ NYK_USERS: user_role,\ HKG_USERS: user_role,
+ *   \GLOBAL_ADMIN: admin_role,\ DEMOS: self-install_role
+ * <p>
+ *   # optional list of roles that are allowed to authenticate
+ *   ldapRealm.allowedRolesForAuthentication = admin_role,user_role
+ * <p>
+ *   ldapRealm.permissionsByRole=\ user_role = *:ToDoItemsJdo:*:*,\*:ToDoItem:*:*;
+ *   \ self-install_role = *:ToDoItemsFixturesService:install:* ; \ admin_role = *
+ * <p>
+ *   [urls]
+ *   **=authcBasic
+ * <p>
+ *   securityManager.realms = $ldapRealm
  */
 public class LdapRealm extends JndiLdapRealm {
+
   private static final SearchControls SUBTREE_SCOPE = new SearchControls();
   private static final SearchControls ONELEVEL_SCOPE = new SearchControls();
   private static final SearchControls OBJECT_SCOPE = new SearchControls();
@@ -133,11 +134,11 @@ public class LdapRealm extends JndiLdapRealm {
   private static final String SUBJECT_USER_GROUPS = "subject.userGroups";
   private static final String MEMBER_URL = "memberUrl";
   private static final String POSIX_GROUP = "posixGroup";
-  
+
   // LDAP Operator '1.2.840.113556.1.4.1941'
   // walks the chain of ancestry in objects all the way to the root until it finds a match
   // see https://msdn.microsoft.com/en-us/library/aa746475(v=vs.85).aspx
-  private static final String MATCHING_RULE_IN_CHAIN_FORMAT = 
+  private static final String MATCHING_RULE_IN_CHAIN_FORMAT =
       "(&(objectClass=%s)(%s:1.2.840.113556.1.4.1941:=%s))";
 
   private static final Pattern TEMPLATE_PATTERN = Pattern.compile("\\{(\\d+?)\\}");
@@ -182,6 +183,9 @@ public class LdapRealm extends JndiLdapRealm {
   private final List<String> allowedRolesForAuthentication = new ArrayList<>();
   private final Map<String, List<String>> permissionsByRole = new LinkedHashMap<>();
 
+  private String hadoopSecurityCredentialPath;
+  final String keystorePass = "ldapRealm.systemPassword";
+
   private boolean authorizationEnabled;
 
   private String userSearchAttributeName;
@@ -189,6 +193,12 @@ public class LdapRealm extends JndiLdapRealm {
 
   private HashService hashService = new DefaultHashService();
 
+
+
+  public void setHadoopSecurityCredentialPath(String hadoopSecurityCredentialPath) {
+    this.hadoopSecurityCredentialPath = hadoopSecurityCredentialPath;
+  }
+
   public LdapRealm() {
     HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher(HASHING_ALGORITHM);
     setCredentialsMatcher(credentialsMatcher);
@@ -204,6 +214,37 @@ public class LdapRealm extends JndiLdapRealm {
     }
   }
 
+  protected void onInit() {
+    super.onInit();
+    if (!org.apache.commons.lang.StringUtils.isEmpty(this.hadoopSecurityCredentialPath)
+        && getContextFactory() != null) {
+      ((JndiLdapContextFactory) getContextFactory()).setSystemPassword(
+          getSystemPassword(this.hadoopSecurityCredentialPath, keystorePass));
+    }
+  }
+
+  static String getSystemPassword(String hadoopSecurityCredentialPath,
+      String keystorePass) {
+    String password = "";
+    try {
+      Configuration configuration = new Configuration();
+      configuration.set(CredentialProviderFactory.CREDENTIAL_PROVIDER_PATH,
+          hadoopSecurityCredentialPath);
+      CredentialProvider provider = CredentialProviderFactory.getProviders(configuration).get(0);
+      CredentialProvider.CredentialEntry credEntry = provider.getCredentialEntry(keystorePass);
+      if (credEntry != null) {
+        password = new String(credEntry.getCredential());
+      }
+    } catch (IOException e) {
+      throw new ShiroException("Error from getting credential entry from keystore", e);
+    }
+    if (org.apache.commons.lang.StringUtils.isEmpty(password)) {
+      throw new ShiroException("Error getting SystemPassword from the provided keystore:"
+          + keystorePass + ", in path:" + hadoopSecurityCredentialPath);
+    }
+    return password;
+  }
+
   /**
    * This overrides the implementation of queryForAuthenticationInfo inside JndiLdapRealm.
    * In addition to calling the super method for authentication it also tries to validate
@@ -217,7 +258,7 @@ public class LdapRealm extends JndiLdapRealm {
    */
   @Override
   protected AuthenticationInfo queryForAuthenticationInfo(AuthenticationToken token,
-          LdapContextFactory ldapContextFactory) throws NamingException {
+      LdapContextFactory ldapContextFactory) throws NamingException {
     AuthenticationInfo info = super.queryForAuthenticationInfo(token, ldapContextFactory);
     // Credentials were verified. Verify that the principal has all allowedRulesForAuthentication
     if (!hasAllowedAuthenticationRules(info.getPrincipals(), ldapContextFactory)) {
@@ -241,7 +282,7 @@ public class LdapRealm extends JndiLdapRealm {
   */
   @Override
   public AuthorizationInfo queryForAuthorizationInfo(final PrincipalCollection principals,
-          final LdapContextFactory ldapContextFactory) throws NamingException {
+      final LdapContextFactory ldapContextFactory) throws NamingException {
     if (!isAuthorizationEnabled()) {
       return null;
     }
@@ -260,7 +301,7 @@ public class LdapRealm extends JndiLdapRealm {
     boolean allowed = allowedRolesForAuthentication.isEmpty();
     if (!allowed) {
       Set<String> roles = getRoles(principals, ldapContextFactory);
-      for (String allowedRole: allowedRolesForAuthentication) {
+      for (String allowedRole : allowedRolesForAuthentication) {
         if (roles.contains(allowedRole)) {
           log.debug("Allowed role for user [" + allowedRole + "] found.");
           allowed = true;
@@ -329,7 +370,7 @@ public class LdapRealm extends JndiLdapRealm {
                 String.format(
                     MATCHING_RULE_IN_CHAIN_FORMAT, groupObjectClass, memberAttribute, userDn),
                 searchControls);
-            while (searchResultEnum != null && searchResultEnum.hasMore()) { 
+            while (searchResultEnum != null && searchResultEnum.hasMore()) {
               // searchResults contains all the groups in search scope
               numResults++;
               final SearchResult group = searchResultEnum.next();
@@ -343,7 +384,7 @@ public class LdapRealm extends JndiLdapRealm {
               } else {
                 roleNames.add(groupName);
               }
-            }                
+            }
           } else {
             // Default group search filter
             String searchFilter = String.format("(objectclass=%1$s)", groupObjectClass);
@@ -361,7 +402,7 @@ public class LdapRealm extends JndiLdapRealm {
                 getGroupSearchBase(),
                 searchFilter,
                 searchControls);
-            while (searchResultEnum != null && searchResultEnum.hasMore()) { 
+            while (searchResultEnum != null && searchResultEnum.hasMore()) {
               // searchResults contains all the groups in search scope
               numResults++;
               final SearchResult group = searchResultEnum.next();
@@ -376,12 +417,12 @@ public class LdapRealm extends JndiLdapRealm {
           }
         }
         // Re-activate paged results
-        ldapCtx.setRequestControls(new Control[]{new PagedResultsControl(pageSize, 
-              cookie, Control.CRITICAL)});
+        ldapCtx.setRequestControls(new Control[]{new PagedResultsControl(pageSize,
+            cookie, Control.CRITICAL)});
       } while (cookie != null);
     } catch (SizeLimitExceededException e) {
-      log.info("Only retrieved first " + numResults + 
-            " groups due to SizeLimitExceededException.");
+      log.info("Only retrieved first " + numResults +
+          " groups due to SizeLimitExceededException.");
     } catch (IOException e) {
       log.error("Unabled to setup paged results");
     }
@@ -393,7 +434,7 @@ public class LdapRealm extends JndiLdapRealm {
       ((MutablePrincipalCollection) principals).addAll(groupNames, getName());
     }
     if (log.isDebugEnabled()) {
-      log.debug("User RoleNames: " + userName + "::" + roleNames);  
+      log.debug("User RoleNames: " + userName + "::" + roleNames);
     }
     return roleNames;
   }
@@ -409,8 +450,8 @@ public class LdapRealm extends JndiLdapRealm {
   }
 
   private void addRoleIfMember(final String userDn, final SearchResult group,
-          final Set<String> roleNames, final Set<String> groupNames,
-          final LdapContextFactory ldapContextFactory) throws NamingException {
+      final Set<String> roleNames, final Set<String> groupNames,
+      final LdapContextFactory ldapContextFactory) throws NamingException {
     NamingEnumeration<? extends Attribute> attributeEnum = null;
     NamingEnumeration<?> ne = null;
     try {
@@ -429,7 +470,7 @@ public class LdapRealm extends JndiLdapRealm {
           String attrValue = ne.next().toString();
           if (memberAttribute.equalsIgnoreCase(MEMBER_URL)) {
             boolean dynamicGroupMember = isUserMemberOfDynamicGroup(userLdapDn, attrValue,
-                  ldapContextFactory);
+                ldapContextFactory);
             if (dynamicGroupMember) {
               groupNames.add(groupName);
               String roleName = roleNameFor(groupName);
@@ -477,7 +518,7 @@ public class LdapRealm extends JndiLdapRealm {
   public Map<String, String> getListRoles() {
     Map<String, String> groupToRoles = getRolesByGroup();
     Map<String, String> roles = new HashMap<>();
-    for (Map.Entry<String, String> entry : groupToRoles.entrySet()){
+    for (Map.Entry<String, String> entry : groupToRoles.entrySet()) {
       roles.put(entry.getValue(), entry.getKey());
     }
     return roles;
@@ -521,7 +562,7 @@ public class LdapRealm extends JndiLdapRealm {
   public int getPagingSize() {
     return pagingSize;
   }
-  
+
   public void setPagingSize(int pagingSize) {
     this.pagingSize = pagingSize;
   }
@@ -557,7 +598,7 @@ public class LdapRealm extends JndiLdapRealm {
   public void setGroupIdAttribute(String groupIdAttribute) {
     this.groupIdAttribute = groupIdAttribute;
   }
-  
+
   /**
   * Set Member Attribute Template for LDAP.
   * 
@@ -574,7 +615,7 @@ public class LdapRealm extends JndiLdapRealm {
     int index = template.indexOf(MEMBER_SUBSTITUTION_TOKEN);
     if (index < 0) {
       String msg = "Member attribute value template must contain the '" + MEMBER_SUBSTITUTION_TOKEN
-            + "' replacement token to understand how to " + "parse the group members.";
+          + "' replacement token to understand how to " + "parse the group members.";
       throw new IllegalArgumentException(msg);
     }
     String prefix = template.substring(0, index);
@@ -590,7 +631,7 @@ public class LdapRealm extends JndiLdapRealm {
   public void setRolesByGroup(Map<String, String> rolesByGroup) {
     this.rolesByGroup.putAll(rolesByGroup);
   }
-  
+
   public Map<String, String> getRolesByGroup() {
     return rolesByGroup;
   }
@@ -598,7 +639,7 @@ public class LdapRealm extends JndiLdapRealm {
   public void setPermissionsByRole(String permissionsByRoleStr) {
     permissionsByRole.putAll(parsePermissionByRoleString(permissionsByRoleStr));
   }
-  
+
   public Map<String, List<String>> getPermissionsByRole() {
     return permissionsByRole;
   }
@@ -614,7 +655,7 @@ public class LdapRealm extends JndiLdapRealm {
   public String getUserSearchAttributeName() {
     return userSearchAttributeName;
   }
-  
+
   /**
   * Set User Search Attribute Name for LDAP.
   * 
@@ -660,7 +701,7 @@ public class LdapRealm extends JndiLdapRealm {
   }
 
   boolean isUserMemberOfDynamicGroup(LdapName userLdapDn, String memberUrl,
-          final LdapContextFactory ldapContextFactory) throws NamingException {
+      final LdapContextFactory ldapContextFactory) throws NamingException {
     // ldap://host:port/dn?attributes?scope?filter?extensions
     if (memberUrl == null) {
       return false;
@@ -696,7 +737,7 @@ public class LdapRealm extends JndiLdapRealm {
     NamingEnumeration<SearchResult> searchResultEnum = null;
     try {
       searchResultEnum = systemLdapCtx.search(userLdapDn, searchFilter,
-            searchScope.equalsIgnoreCase("sub") ? SUBTREE_SCOPE : ONELEVEL_SCOPE);
+          searchScope.equalsIgnoreCase("sub") ? SUBTREE_SCOPE : ONELEVEL_SCOPE);
       if (searchResultEnum.hasMore()) {
         return true;
       }
@@ -715,7 +756,7 @@ public class LdapRealm extends JndiLdapRealm {
   public String getPrincipalRegex() {
     return principalRegex;
   }
-  
+
   /**
   * Set Regex for Principal LDAP.
   * 
@@ -749,7 +790,7 @@ public class LdapRealm extends JndiLdapRealm {
   public void setUserSearchFilter(final String filter) {
     this.userSearchFilter = (filter == null ? null : filter.trim());
   }
-  
+
   public String getGroupSearchFilter() {
     return groupSearchFilter;
   }
@@ -761,11 +802,11 @@ public class LdapRealm extends JndiLdapRealm {
   public boolean getUserLowerCase() {
     return userLowerCase;
   }
-  
+
   public void setUserLowerCase(boolean userLowerCase) {
     this.userLowerCase = userLowerCase;
   }
-  
+
   public String getUserSearchScope() {
     return userSearchScope;
   }
@@ -781,7 +822,7 @@ public class LdapRealm extends JndiLdapRealm {
   public void setGroupSearchScope(final String scope) {
     this.groupSearchScope = (scope == null ? null : scope.trim().toLowerCase());
   }
-  
+
   public boolean isGroupSearchEnableMatchingRuleInChain() {
     return groupSearchEnableMatchingRuleInChain;
   }
@@ -800,7 +841,7 @@ public class LdapRealm extends JndiLdapRealm {
     }
     return searchControls;
   }
-  
+
   protected SearchControls getGroupSearchControls() {
     SearchControls searchControls = SUBTREE_SCOPE;
     if ("onelevel".equalsIgnoreCase(groupSearchScope)) {
@@ -819,8 +860,8 @@ public class LdapRealm extends JndiLdapRealm {
   private String matchPrincipal(final String principal) {
     Matcher matchedPrincipal = principalPattern.matcher(principal);
     if (!matchedPrincipal.matches()) {
-      throw new IllegalArgumentException("Principal " 
-            + principal + " does not match " + principalRegex);
+      throw new IllegalArgumentException("Principal "
+          + principal + " does not match " + principalRegex);
     }
     return matchedPrincipal.group();
   }
@@ -850,8 +891,8 @@ public class LdapRealm extends JndiLdapRealm {
   * @see LdapContextFactory#getLdapContext(Object, Object)
   */
   @Override
-  protected String getUserDn(final String principal) throws IllegalArgumentException, 
-          IllegalStateException {
+  protected String getUserDn(final String principal) throws IllegalArgumentException,
+      IllegalStateException {
     String userDn;
     String matchedPrincipal = matchPrincipal(principal);
     String userSearchBase = getUserSearchBase();
@@ -859,7 +900,7 @@ public class LdapRealm extends JndiLdapRealm {
 
     // If not searching use the userDnTemplate and return.
     if ((userSearchBase == null || userSearchBase.isEmpty()) || (userSearchAttributeName == null
-          && userSearchFilter == null && !"object".equalsIgnoreCase(userSearchScope))) {
+        && userSearchFilter == null && !"object".equalsIgnoreCase(userSearchScope))) {
       userDn = expandTemplate(userDnTemplate, matchedPrincipal);
       if (log.isDebugEnabled()) {
         log.debug("LDAP UserDN and Principal: " + userDn + "," + principal);
@@ -875,8 +916,8 @@ public class LdapRealm extends JndiLdapRealm {
         searchFilter = String.format("(objectclass=%1$s)", getUserObjectClass());
       } else {
         searchFilter = String.format("(&(objectclass=%1$s)(%2$s=%3$s))", getUserObjectClass(),
-              userSearchAttributeName, expandTemplate(getUserSearchAttributeTemplate(), 
-              matchedPrincipal));
+            userSearchAttributeName, expandTemplate(getUserSearchAttributeTemplate(),
+                matchedPrincipal));
       }
     } else {
       searchFilter = expandTemplate(userSearchFilter, matchedPrincipal);
@@ -889,8 +930,8 @@ public class LdapRealm extends JndiLdapRealm {
     try {
       systemLdapCtx = getContextFactory().getSystemLdapContext();
       if (log.isDebugEnabled()) {
-        log.debug("SearchBase,SearchFilter,UserSearchScope: " + searchBase 
-              + "," + searchFilter + "," + userSearchScope);
+        log.debug("SearchBase,SearchFilter,UserSearchScope: " + searchBase
+            + "," + searchFilter + "," + userSearchScope);
       }
       searchResultEnum = systemLdapCtx.search(searchBase, searchFilter, searchControls);
       // SearchResults contains all the entries in search scope
@@ -923,16 +964,16 @@ public class LdapRealm extends JndiLdapRealm {
   }
 
   @Override
-  protected AuthenticationInfo createAuthenticationInfo(AuthenticationToken token, 
-          Object ldapPrincipal, Object ldapCredentials, LdapContext ldapContext)
-          throws NamingException {
+  protected AuthenticationInfo createAuthenticationInfo(AuthenticationToken token,
+      Object ldapPrincipal, Object ldapCredentials, LdapContext ldapContext)
+      throws NamingException {
     HashRequest.Builder builder = new HashRequest.Builder();
     Hash credentialsHash = hashService
-          .computeHash(builder.setSource(token.getCredentials())
-                .setAlgorithmName(HASHING_ALGORITHM).build());
-    return new SimpleAuthenticationInfo(token.getPrincipal(), 
-          credentialsHash.toHex(), credentialsHash.getSalt(),
-          getName());
+        .computeHash(builder.setSource(token.getCredentials())
+            .setAlgorithmName(HASHING_ALGORITHM).build());
+    return new SimpleAuthenticationInfo(token.getPrincipal(),
+        credentialsHash.toHex(), credentialsHash.getSalt(),
+        getName());
   }
 
   protected static final String expandTemplate(final String template, final String input) {