knox-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From dillido...@apache.org
Subject git commit: KNOX-231 : knox ldap realm to support ldap dynamic groups
Date Thu, 26 Dec 2013 18:55:24 GMT
Updated Branches:
  refs/heads/master 378426edf -> 602702228


KNOX-231 : knox ldap realm to support ldap dynamic groups


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

Branch: refs/heads/master
Commit: 602702228235dfd60ada3c0a425fa1801ed9eda9
Parents: 378426e
Author: Dilli Dorai Arumugam <darumugam@hortonworks.com>
Authored: Thu Dec 26 10:50:29 2013 -0800
Committer: Dilli Dorai Arumugam <darumugam@hortonworks.com>
Committed: Thu Dec 26 10:50:29 2013 -0800

----------------------------------------------------------------------
 .../shirorealm/KnoxLdapContextFactory.java      |   2 +
 .../gateway/shirorealm/KnoxLdapRealm.java       | 221 +++++++++++++------
 .../gateway/shirorealm/KnoxLdapRealmTest.java   |   4 +-
 .../gateway/GatewayLdapGroupFuncTest.java       |   6 +-
 .../gateway/GatewayLdapGroupFuncTest/users.ldif |   6 +-
 5 files changed, 168 insertions(+), 71 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/60270222/gateway-provider-security-shiro/src/main/java/org/apache/hadoop/gateway/shirorealm/KnoxLdapContextFactory.java
----------------------------------------------------------------------
diff --git a/gateway-provider-security-shiro/src/main/java/org/apache/hadoop/gateway/shirorealm/KnoxLdapContextFactory.java
b/gateway-provider-security-shiro/src/main/java/org/apache/hadoop/gateway/shirorealm/KnoxLdapContextFactory.java
index 86a6fb9..f351c8f 100644
--- a/gateway-provider-security-shiro/src/main/java/org/apache/hadoop/gateway/shirorealm/KnoxLdapContextFactory.java
+++ b/gateway-provider-security-shiro/src/main/java/org/apache/hadoop/gateway/shirorealm/KnoxLdapContextFactory.java
@@ -94,6 +94,8 @@ public class KnoxLdapContextFactory extends JndiLdapContextFactory {
         char[] password = aliasService.getPasswordFromAliasForCluster(clusterName, systemPass);
         if ( password != null ) {
           super.setSystemPassword( new String(password) );
+        } else {
+          super.setSystemPassword( new String(systemPassword) );
         }
       }
       

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/60270222/gateway-provider-security-shiro/src/main/java/org/apache/hadoop/gateway/shirorealm/KnoxLdapRealm.java
----------------------------------------------------------------------
diff --git a/gateway-provider-security-shiro/src/main/java/org/apache/hadoop/gateway/shirorealm/KnoxLdapRealm.java
b/gateway-provider-security-shiro/src/main/java/org/apache/hadoop/gateway/shirorealm/KnoxLdapRealm.java
index c0b3355..f5845bd 100644
--- a/gateway-provider-security-shiro/src/main/java/org/apache/hadoop/gateway/shirorealm/KnoxLdapRealm.java
+++ b/gateway-provider-security-shiro/src/main/java/org/apache/hadoop/gateway/shirorealm/KnoxLdapRealm.java
@@ -31,12 +31,14 @@ import java.util.Set;
 import java.util.StringTokenizer;
 
 import javax.naming.AuthenticationException;
+import javax.naming.InvalidNameException;
 import javax.naming.NamingEnumeration;
 import javax.naming.NamingException;
 import javax.naming.directory.Attribute;
 import javax.naming.directory.SearchControls;
 import javax.naming.directory.SearchResult;
 import javax.naming.ldap.LdapContext;
+import javax.naming.ldap.LdapName;
 
 import org.apache.shiro.SecurityUtils;
 import org.apache.shiro.authz.AuthorizationInfo;
@@ -46,7 +48,6 @@ import org.apache.shiro.realm.ldap.LdapContextFactory;
 import org.apache.shiro.realm.ldap.LdapUtils;
 import org.apache.shiro.subject.PrincipalCollection;
 import org.apache.shiro.util.StringUtils;
-//import org.apache.isis.security.shiro.util.Util;
 
 /**
  * Implementation of {@link org.apache.shiro.realm.ldap.JndiLdapRealm} that also
@@ -54,26 +55,29 @@ import org.apache.shiro.util.StringUtils;
  * This implementation is heavily based on org.apache.isis.security.shiro.IsisLdapRealm.
  * 
  * This implementation saves looked up ldap groups in Shiro Session to make them
- * easy to look up outside of this object
+ * easy to be looked up outside of this object
  * 
  * <p>
  * Sample config for <tt>shiro.ini</tt>:
  * 
- * <pre>
- * contextFactory = org.apache.isis.security.shiro.IsisLdapContextFactory
- * contextFactory.url = ldap://localhost:10389
- * contextFactory.authenticationMechanism = CRAM-MD5
- * contextFactory.systemAuthenticationMechanism = simple
- * contextFactory.systemUsername = uid=admin,ou=system
- * contextFactory.systemPassword = secret
- * 
- * ldapRealm = org.apache.isis.security.shiro.IsisLdapRealm
- * ldapRealm.contextFactory = $contextFactory
- * 
- * ldapRealm.searchBase = ou=groups,o=mojo
- * ldapRealm.groupObjectClass = groupOfUniqueNames
- * ldapRealm.uniqueMemberAttribute = uniqueMember
- * ldapRealm.uniqueMemberAttributeValueTemplate = uid={0}
+ * [main]
+ * ldapRealm=org.apache.hadoop.gateway.shirorealm.KnoxLdapRealm
+ * ldapGroupContextFactory=org.apache.hadoop.gateway.shirorealm.KnoxLdapContextFactory
+ * ldapRealm.contextFactory=$ldapGroupContextFactory
+ * ldapRealm.contextFactory.authenticationMechanism=simple
+ * ldapRealm.contextFactory.url=ldap://localhost:33389
+ * ldapRealm.userDnTemplate=uid={0},ou=people,dc=hadoop,dc=apache,dc=org
+ * ldapRealm.authorizationEnabled=true
+ * ldapRealm.contextFactory.systemAuthenticationMechanism=simple
+ * ldapRealm.searchBase=ou=groups,dc=hadoop,dc=apache,dc=org
+ * ldapRealm.groupObjectClass=groupofnames
+ * ldapRealm.memberAttribute=member
+ * ldapRealm.memberAttributeValueTemplate=cn={0},ou=people,dc=hadoop,dc=apache,dc=org
+ * ldapRealm.contextFactory.systemUsername=uid=guest,ou=people,dc=hadoop,dc=apache,dc=org
+ * ldapRealm.contextFactory.clusterName=sandbox
+ * ldapRealm.contextFactory.systemPassword=S{ALIAS=ldcSystemPassword}
+ * [urls]
+ * **=authcBasic
  *
  * # optional mapping from physical groups to logical application roles
  * ldapRealm.rolesByGroup = \
@@ -95,21 +99,31 @@ import org.apache.shiro.util.StringUtils;
  */
 public class KnoxLdapRealm extends JndiLdapRealm {
 
-    private static final String UNIQUEMEMBER_SUBSTITUTION_TOKEN = "{0}";
+    private static final String MEMBER_SUBSTITUTION_TOKEN = "{0}";
     private final static SearchControls SUBTREE_SCOPE = new SearchControls();
+    private final static SearchControls ONELEVEL_SCOPE = new SearchControls();
     
     private final static String  SUBJECT_USER_ROLES = "subject.userRoles";
     private final static String  SUBJECT_USER_GROUPS = "subject.userGroups";
+
+    private final static String  MEMBER_URL = "memberUrl";
    
     static {
         SUBTREE_SCOPE.setSearchScope(SearchControls.SUBTREE_SCOPE);
+        ONELEVEL_SCOPE.setSearchScope(SearchControls.ONELEVEL_SCOPE);
     }
     
     private String searchBase;
-    private String groupObjectClass;
-    private String uniqueMemberAttribute = "uniqueMember";
-    private String uniqueMemberAttributeValuePrefix;
-    private String uniqueMemberAttributeValueSuffix;
+    // typical alue: groupOfNames, groupOfUniqueNames, groupOfUrls
+    private String groupObjectClass = "groupOfNames";
+    
+    //  typical value: member, uniqueMember, meberUrl
+    private String memberAttribute = "member";
+
+    private String groupIdAttribute = "cn";
+    
+    private String memberAttributeValuePrefix = "uid={0}";
+    private String memberAttributeValueSuffix = "";
     
     private final Map<String,String> rolesByGroup = new LinkedHashMap<String, String>();
     private final Map<String,List<String>> permissionsByRole = new LinkedHashMap<String,
List<String>>();
@@ -117,9 +131,6 @@ public class KnoxLdapRealm extends JndiLdapRealm {
     private boolean authorizationEnabled;
 
     public KnoxLdapRealm() {
-        setGroupObjectClass("groupOfUniqueNames");
-        setUniqueMemberAttribute("uniqueMember");
-        setUniqueMemberAttributeValueTemplate("uid={0}");
     }
     
     /**
@@ -136,7 +147,8 @@ public class KnoxLdapRealm extends JndiLdapRealm {
      *             if any LDAP errors occur during the search.
      */
     @Override
-    protected AuthorizationInfo queryForAuthorizationInfo(final PrincipalCollection principals,
final LdapContextFactory ldapContextFactory) throws NamingException {
+    protected AuthorizationInfo queryForAuthorizationInfo(final PrincipalCollection principals,

+        final LdapContextFactory ldapContextFactory) throws NamingException {
       if (!isAuthorizationEnabled()) {
         return null;
       }
@@ -147,13 +159,14 @@ public class KnoxLdapRealm extends JndiLdapRealm {
         return simpleAuthorizationInfo;
     }
 
-    private Set<String> getRoles(final PrincipalCollection principals, final LdapContextFactory
ldapContextFactory) throws NamingException {
+    private Set<String> getRoles(final PrincipalCollection principals, 
+        final LdapContextFactory ldapContextFactory) throws NamingException {
         final String username = (String) getAvailablePrincipal(principals);
 
         LdapContext systemLdapCtx = null;
         try {
             systemLdapCtx = ldapContextFactory.getSystemLdapContext();
-            return rolesFor(username, systemLdapCtx);
+            return rolesFor(username, systemLdapCtx, ldapContextFactory);
         } catch (AuthenticationException ex) {
             // principal was not authenticated on LDAP
             return Collections.emptySet();
@@ -162,13 +175,18 @@ public class KnoxLdapRealm extends JndiLdapRealm {
         }
     }
 
-    private Set<String> rolesFor(final String userName, final LdapContext ldapCtx)
throws NamingException {
+    private Set<String> rolesFor(final String userName, final LdapContext ldapCtx,

+        final LdapContextFactory ldapContextFactory) throws NamingException {
         final Set<String> roleNames = new HashSet();
         final Set<String> groupNames = new HashSet();
+        
+        // ldapsearch -h localhost -p 33389 -D uid=guest,ou=people,dc=hadoop,dc=apache,dc=org
-w  guest-password 
+        //       -b dc=hadoop,dc=apache,dc=org -s sub '(objectclass=*)'
         final NamingEnumeration<SearchResult> searchResultEnum = ldapCtx.search(searchBase,
"objectClass="+groupObjectClass, SUBTREE_SCOPE);
+        
         while (searchResultEnum.hasMore()) { // searchResults contains all the groups in
search scope
             final SearchResult group = searchResultEnum.next();
-            addRoleIfMember(userName, group, roleNames, groupNames);
+            addRoleIfMember(userName, group, roleNames, groupNames, ldapContextFactory);
         }
         
         // save role names and group names in session so that they can be easily looked up
outside of this object
@@ -178,32 +196,54 @@ public class KnoxLdapRealm extends JndiLdapRealm {
         return roleNames;
     }
 
-    private void addRoleIfMember(final String userName, final SearchResult group, final Set<String>
roleNames, 
-              final Set<String> groupNames) throws NamingException {
-        final NamingEnumeration<? extends Attribute> attributeEnum = group.getAttributes().getAll();
-        while (attributeEnum.hasMore()) {
-            final Attribute attr = attributeEnum.next();
-            if (!uniqueMemberAttribute.equalsIgnoreCase(attr.getID())) {
-                continue;
+  private void addRoleIfMember(final String userName, final SearchResult group,
+      final Set<String> roleNames, final Set<String> groupNames,
+      final LdapContextFactory ldapContextFactory) throws NamingException {
+   
+    String userDn = memberAttributeValuePrefix + userName + memberAttributeValueSuffix;
+    Attribute attribute = group.getAttributes().get(getGroupIdAttribute()); 
+    String groupName = attribute.get().toString();
+    
+    final NamingEnumeration<? extends Attribute> attributeEnum = group
+        .getAttributes().getAll();
+    while (attributeEnum.hasMore()) {
+      final Attribute attr = attributeEnum.next();
+      if (!memberAttribute.equalsIgnoreCase(attr.getID())) {
+        continue;
+      }
+      final NamingEnumeration<?> e = attr.getAll();
+      while (e.hasMore()) {
+        String attrValue = e.next().toString();
+        if (memberAttribute.equalsIgnoreCase(MEMBER_URL)) {
+          boolean dynamicGroupMember = isUserMemberOfDynamicGroup(userDn, 
+              attrValue, // memberUrl value
+              ldapContextFactory);
+          if (dynamicGroupMember) {
+            groupNames.add(groupName);
+            String roleName = roleNameFor(groupName);
+            if (roleName != null) {
+              roleNames.add(roleName);
+            } else {
+              roleNames.add(groupName);
             }
-            final NamingEnumeration<?> e = attr.getAll();
-            while (e.hasMore()) {
-                String attrValue = e.next().toString();
-                if ((uniqueMemberAttributeValuePrefix + userName + uniqueMemberAttributeValueSuffix).equals(attrValue))
{
-                    Attribute attribute = group.getAttributes().get("cn");
-                    String groupName = attribute.get().toString();
-                    groupNames.add(groupName);
-                    String roleName = roleNameFor(groupName);
-                    if(roleName != null) {
-                        roleNames.add(roleName);
-                    } else {
-                      roleNames.add(groupName);
-                    }
-                    break;
-                }
+          }
+        } else {
+          if (userDn.equals(attrValue)) {
+         
+            groupNames.add(groupName);
+            String roleName = roleNameFor(groupName);
+            if (roleName != null) {
+              roleNames.add(roleName);
+            } else {
+              roleNames.add(groupName);
             }
+            break;
+          }
         }
+      }
+
     }
+  }
 
     private String roleNameFor(String groupName) {
         return !rolesByGroup.isEmpty() ? rolesByGroup.get(groupName) : groupName;
@@ -237,31 +277,38 @@ public class KnoxLdapRealm extends JndiLdapRealm {
         this.groupObjectClass = groupObjectClassAttribute;
     }
 
-    public String getUniqueMemberAttribute() {
-      return uniqueMemberAttribute;
+    public String getMemberAttribute() {
+      return memberAttribute;
     }
     
-    public void setUniqueMemberAttribute(String uniqueMemberAttribute) {
-        this.uniqueMemberAttribute = uniqueMemberAttribute;
+    public void setMemberAttribute(String memberAttribute) {
+        this.memberAttribute = memberAttribute;
     }
     
+    public String getGroupIdAttribute() {
+      return groupIdAttribute;
+    }
     
-    public void setUniqueMemberAttributeValueTemplate(String template) {
+    public void setGroupIdAttribute(String memberAttribute) {
+        this.groupIdAttribute = groupIdAttribute;
+    }
+    
+    public void setMemberAttributeValueTemplate(String template) {
         if (!StringUtils.hasText(template)) {
             String msg = "User DN template cannot be null or empty.";
             throw new IllegalArgumentException(msg);
         }
-        int index = template.indexOf(UNIQUEMEMBER_SUBSTITUTION_TOKEN);
+        int index = template.indexOf(MEMBER_SUBSTITUTION_TOKEN);
         if (index < 0) {
-            String msg = "UniqueMember attribute value template must contain the '" +
-                    UNIQUEMEMBER_SUBSTITUTION_TOKEN + "' replacement token to understand
how to " +
+            String msg = "Member attribute value template must contain the '" +
+                    MEMBER_SUBSTITUTION_TOKEN + "' replacement token to understand how to
" +
                     "parse the group members.";
             throw new IllegalArgumentException(msg);
         }
         String prefix = template.substring(0, index);
-        String suffix = template.substring(prefix.length() + UNIQUEMEMBER_SUBSTITUTION_TOKEN.length());
-        this.uniqueMemberAttributeValuePrefix = prefix;
-        this.uniqueMemberAttributeValueSuffix = suffix;
+        String suffix = template.substring(prefix.length() + MEMBER_SUBSTITUTION_TOKEN.length());
+        this.memberAttributeValuePrefix = prefix;
+        this.memberAttributeValueSuffix = suffix;
     }
 
     public void setRolesByGroup(Map<String, String> rolesByGroup) {
@@ -303,4 +350,52 @@ public class KnoxLdapRealm extends JndiLdapRealm {
       return perms;
   }
 
+  boolean isUserMemberOfDynamicGroup(String userDnString, String memberUrl,
+      final LdapContextFactory ldapContextFactory) throws NamingException {
+
+    // ldap://host:port/dn?attributes?scope?filter?extensions
+
+    boolean member = false;
+
+    if (memberUrl == null) {
+      return false;
+    }
+    String[] tokens = memberUrl.split("\\?");
+    if (tokens.length < 4) {
+      return false;
+    }
+
+    String searchBaseString = tokens[0]
+        .substring(tokens[0].lastIndexOf("/") + 1);
+    String searchScope = tokens[2];
+    String searchFilter = tokens[3];
+
+    LdapName searchBaseDn = new LdapName(searchBaseString);
+    LdapName userDn = new LdapName(userDnString);
+    // do scope test
+    if (searchScope.equalsIgnoreCase("base")) {
+      return false;
+    }
+    if (!userDn.toString().endsWith(searchBaseDn.toString())) {
+      return false;
+    }
+    if (searchScope.equalsIgnoreCase("one")
+        && (userDn.size() != searchBaseDn.size() - 1)) {
+      return false;
+    }
+    // search for the filter, substituting base with userDn
+    // search for base_dn=userDn, scope=base, filter=filter
+    LdapContext systemLdapCtx = null;
+    systemLdapCtx = ldapContextFactory.getSystemLdapContext();
+    final NamingEnumeration<SearchResult> searchResultEnum = systemLdapCtx
+        .search(userDn, searchFilter,
+            searchScope.equalsIgnoreCase("sub") ? SUBTREE_SCOPE
+                : ONELEVEL_SCOPE);
+    if (searchResultEnum.hasMore()) {
+      return true;
+    }
+
+    return member;
+  }
+   
 }

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/60270222/gateway-provider-security-shiro/src/test/java/org/apache/hadoop/gateway/shirorealm/KnoxLdapRealmTest.java
----------------------------------------------------------------------
diff --git a/gateway-provider-security-shiro/src/test/java/org/apache/hadoop/gateway/shirorealm/KnoxLdapRealmTest.java
b/gateway-provider-security-shiro/src/test/java/org/apache/hadoop/gateway/shirorealm/KnoxLdapRealmTest.java
index 304a137..e7cf7ab 100644
--- a/gateway-provider-security-shiro/src/test/java/org/apache/hadoop/gateway/shirorealm/KnoxLdapRealmTest.java
+++ b/gateway-provider-security-shiro/src/test/java/org/apache/hadoop/gateway/shirorealm/KnoxLdapRealmTest.java
@@ -41,8 +41,8 @@ public class KnoxLdapRealmTest {
   @Test
   public void setGetUniqueMemberAttribute() {
     KnoxLdapRealm realm = new KnoxLdapRealm();
-    realm.setUniqueMemberAttribute("member");
-    assertEquals(realm.getUniqueMemberAttribute(), "member");
+    realm.setMemberAttribute("member");
+    assertEquals(realm.getMemberAttribute(), "member");
   }
   
   

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/60270222/gateway-test/src/test/java/org/apache/hadoop/gateway/GatewayLdapGroupFuncTest.java
----------------------------------------------------------------------
diff --git a/gateway-test/src/test/java/org/apache/hadoop/gateway/GatewayLdapGroupFuncTest.java
b/gateway-test/src/test/java/org/apache/hadoop/gateway/GatewayLdapGroupFuncTest.java
index f6afae8..008914e 100644
--- a/gateway-test/src/test/java/org/apache/hadoop/gateway/GatewayLdapGroupFuncTest.java
+++ b/gateway-test/src/test/java/org/apache/hadoop/gateway/GatewayLdapGroupFuncTest.java
@@ -173,11 +173,11 @@ public class GatewayLdapGroupFuncTest {
         .addTag( "name" ).addText( "main.ldapRealm.groupObjectClass" )
         .addTag( "value" ).addText( "groupofnames" )
         .gotoParent().addTag( "param" )
-        .addTag( "name" ).addText( "main.ldapRealm.uniqueMemberAttribute" )
+        .addTag( "name" ).addText( "main.ldapRealm.memberAttribute" )
         .addTag( "value" ).addText( "member" )
         .gotoParent().addTag( "param" )
-        .addTag( "name" ).addText( "main.ldapRealm.uniqueMemberAttributeValueTemplate" )
-        .addTag( "value" ).addText( "cn={0},ou=people,dc=hadoop,dc=apache,dc=org" )
+        .addTag( "name" ).addText( "main.ldapRealm.memberAttributeValueTemplate" )
+        .addTag( "value" ).addText( "uid={0},ou=people,dc=hadoop,dc=apache,dc=org" )
         .gotoParent().addTag( "param" )
         .addTag( "name" ).addText( "main.ldapRealm.contextFactory.systemUsername" )
         .addTag( "value" ).addText( "uid=guest,ou=people,dc=hadoop,dc=apache,dc=org" )

http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/60270222/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayLdapGroupFuncTest/users.ldif
----------------------------------------------------------------------
diff --git a/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayLdapGroupFuncTest/users.ldif
b/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayLdapGroupFuncTest/users.ldif
index f10851a..80fddc0 100644
--- a/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayLdapGroupFuncTest/users.ldif
+++ b/gateway-test/src/test/resources/org/apache/hadoop/gateway/GatewayLdapGroupFuncTest/users.ldif
@@ -80,8 +80,8 @@ objectclass:top
 objectclass: groupofnames
 cn: analyst
 description:analyst  group
-member: cn=sam,ou=people,dc=hadoop,dc=apache,dc=org
-member: cn=tom,ou=people,dc=hadoop,dc=apache,dc=org
+member: uid=sam,ou=people,dc=hadoop,dc=apache,dc=org
+member: uid=tom,ou=people,dc=hadoop,dc=apache,dc=org
 
 
 # create the scientist group under groups
@@ -90,5 +90,5 @@ objectclass:top
 objectclass: groupofnames
 cn: scientist
 description: scientist group
-member: cn=sam,ou=people,dc=hadoop,dc=apache,dc=org
+member: uid=sam,ou=people,dc=hadoop,dc=apache,dc=org
 


Mime
View raw message