You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@zeppelin.apache.org by mo...@apache.org on 2017/03/05 01:40:19 UTC

zeppelin git commit: ZEPPELIN-2161 Nested Group Support in LdapRealm for AD

Repository: zeppelin
Updated Branches:
  refs/heads/master 91604c878 -> b9fa42dda


ZEPPELIN-2161 Nested Group Support in LdapRealm for AD

### What is this PR for?
A common use case in LDAP/AD setup is the hierarchical structuring of
groups - a.k.a. adding groups to other groups. Such nesting groups can
help reduce the number of roles that need to be managed.

Current zeppelin realm implementations doesn't have support for looking
up memberships throughout nested group structures.

E.g. consider the following nested group scenario:
```
acme_employees
 \__department_a
     \__sub_department_x
```
User 'bob' is in Group 'sub_department_x'.
Notebook 'note1' has a Reader Role assignment for 'department_a' or
'acme_employees'.
Then access must be granted for 'bob' on 'note1'.

In AD enviroments this scenarios can be efficiently implemented using
the so called LDAP_MATCHING_RULE_IN_CHAIN operator
'1.2.840.113556.1.4.1941'.

This PR introduces a property 'groupSearchEnableMatchingRuleInChain' to
org.apache.zeppelin.realm.LdapRealm which defaults to false. If enabled,
all roles of potential nested group hierarchies will be resolved using
the LDAP_MATCHING_RULE_IN_CHAIN operator.

### What type of PR is it?
Improvement

### Todos
-

### What is the Jira issue?
[ZEPPELIN-2161]

### How should this be tested?
Set groupSearchEnableMatchingRuleInChain = true for the ldap realm.

### Screenshots (if appropriate)

### Questions:
* Does the licenses files need update? n
* Is there breaking changes for older versions? n
* Does this needs documentation? y

Author: Andreas Weise <a....@avm.de>

Closes #2062 from weand/ZEPPELIN-2161 and squashes the following commits:

c08d015 [Andreas Weise] ZEPPELIN-2161 Nested group support in LdapRealm for AD


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

Branch: refs/heads/master
Commit: b9fa42dda13b66fb65e01237a6f86eea5dcdc5b5
Parents: 91604c8
Author: Andreas Weise <a....@avm.de>
Authored: Thu Feb 23 22:55:06 2017 +0100
Committer: Lee moon soo <mo...@apache.org>
Committed: Sun Mar 5 10:40:14 2017 +0900

----------------------------------------------------------------------
 .../org/apache/zeppelin/realm/LdapRealm.java    | 58 ++++++++++++++++----
 1 file changed, 48 insertions(+), 10 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/zeppelin/blob/b9fa42dd/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 34dcaa4..5740689 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
@@ -36,7 +36,6 @@ import org.apache.shiro.realm.ldap.LdapUtils;
 import org.apache.shiro.subject.MutablePrincipalCollection;
 import org.apache.shiro.subject.PrincipalCollection;
 import org.apache.shiro.util.StringUtils;
-import org.mortbay.log.Log;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -45,7 +44,6 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
 import java.util.List;
@@ -107,6 +105,8 @@ import javax.naming.ldap.PagedResultsControl;
  * apache,dc=org 
  * ldapRealm.contextFactory.systemPassword=S{ALIAS=ldcSystemPassword} [urls]
  * **=authcBasic
+ * # 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,\
@@ -128,6 +128,12 @@ 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 = 
+      "(&(objectClass=%s)(%s:1.2.840.113556.1.4.1941:=%s))";
 
   private static Pattern TEMPLATE_PATTERN = Pattern.compile("\\{(\\d+?)\\}");
   private static String DEFAULT_PRINCIPAL_REGEX = "(.*)";
@@ -153,13 +159,14 @@ public class LdapRealm extends JndiLdapRealm {
   private String userSearchAttributeTemplate = "{0}";
   private String userSearchScope = "subtree";
   private String groupSearchScope = "subtree";
+  private boolean groupSearchEnableMatchingRuleInChain;
 
 
   private String groupSearchBase;
 
   private String groupObjectClass = "groupOfNames";
 
-  // typical value: member, uniqueMember, meberUrl
+  // typical value: member, uniqueMember, memberUrl
   private String memberAttribute = "member";
 
   private String groupIdAttribute = "cn";
@@ -282,16 +289,38 @@ public class LdapRealm extends JndiLdapRealm {
         NamingEnumeration<SearchResult> searchResultEnum = null;
         SearchControls searchControls = getGroupSearchControls();
         try {
-          searchResultEnum = ldapCtx.search(
+          if (groupSearchEnableMatchingRuleInChain) {
+            searchResultEnum = ldapCtx.search(
+                getGroupSearchBase(),
+                String.format(
+                    MATCHING_RULE_IN_CHAIN_FORMAT, groupObjectClass, memberAttribute, userDn),
+                searchControls);
+            while (searchResultEnum != null && searchResultEnum.hasMore()) { 
+              // searchResults contains all the groups in search scope
+              numResults++;
+              final SearchResult group = searchResultEnum.next();
+
+              Attribute attribute = group.getAttributes().get(getGroupIdAttribute());
+              String groupName = attribute.get().toString();            
+              
+              String roleName = roleNameFor(groupName);
+              if (roleName != null) {
+                roleNames.add(roleName);
+              } else {
+                roleNames.add(groupName);
+              }
+            }                
+          } else {
+            searchResultEnum = ldapCtx.search(
                 getGroupSearchBase(),
                 "objectClass=" + groupObjectClass,
                 searchControls);
-
-          while (searchResultEnum != null && searchResultEnum.hasMore()) { 
-            // searchResults contains all the groups in search scope
-            numResults++;
-            final SearchResult group = searchResultEnum.next();
-            addRoleIfMember(userDn, group, roleNames, groupNames, ldapContextFactory);
+            while (searchResultEnum != null && searchResultEnum.hasMore()) { 
+              // searchResults contains all the groups in search scope
+              numResults++;
+              final SearchResult group = searchResultEnum.next();
+              addRoleIfMember(userDn, group, roleNames, groupNames, ldapContextFactory);
+            }
           }
         } catch (PartialResultException e) {
           log.debug("Ignoring PartitalResultException");
@@ -682,6 +711,15 @@ public class LdapRealm extends JndiLdapRealm {
   public void setGroupSearchScope(final String scope) {
     this.groupSearchScope = (scope == null ? null : scope.trim().toLowerCase());
   }
+  
+  public boolean isGroupSearchEnableMatchingRuleInChain() {
+    return groupSearchEnableMatchingRuleInChain;
+  }
+
+  public void setGroupSearchEnableMatchingRuleInChain(
+      boolean groupSearchEnableMatchingRuleInChain) {
+    this.groupSearchEnableMatchingRuleInChain = groupSearchEnableMatchingRuleInChain;
+  }
 
   private SearchControls getUserSearchControls() {
     SearchControls searchControls = SUBTREE_SCOPE;