jackrabbit-oak-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From tri...@apache.org
Subject svn commit: r1568187 - in /jackrabbit/oak/trunk: oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ oak-au...
Date Fri, 14 Feb 2014 01:47:41 GMT
Author: tripod
Date: Fri Feb 14 01:47:40 2014
New Revision: 1568187

URL: http://svn.apache.org/r1568187
Log:
OAK-516 Create LdapLoginModule based on ExternalLoginModule

- adding more test cases for ldap provider

Added:
    jackrabbit/oak/trunk/oak-auth-ldap/src/main/java/org/apache/jackrabbit/oak/security/authentication/ldap/impl/DebugTimer.java
    jackrabbit/oak/trunk/oak-auth-ldap/src/test/java/org/apache/jackrabbit/oak/security/authentication/ldap/LdapProviderTest.java
    jackrabbit/oak/trunk/oak-auth-ldap/src/test/resources/
    jackrabbit/oak/trunk/oak-auth-ldap/src/test/resources/org/
    jackrabbit/oak/trunk/oak-auth-ldap/src/test/resources/org/apache/
    jackrabbit/oak/trunk/oak-auth-ldap/src/test/resources/org/apache/jackrabbit/
    jackrabbit/oak/trunk/oak-auth-ldap/src/test/resources/org/apache/jackrabbit/oak/
    jackrabbit/oak/trunk/oak-auth-ldap/src/test/resources/org/apache/jackrabbit/oak/security/
    jackrabbit/oak/trunk/oak-auth-ldap/src/test/resources/org/apache/jackrabbit/oak/security/authentication/
    jackrabbit/oak/trunk/oak-auth-ldap/src/test/resources/org/apache/jackrabbit/oak/security/authentication/ldap/
    jackrabbit/oak/trunk/oak-auth-ldap/src/test/resources/org/apache/jackrabbit/oak/security/authentication/ldap/apache-ds-tutorial.ldif
Modified:
    jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalGroup.java
    jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalIdentity.java
    jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalIdentityRef.java
    jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalUser.java
    jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DefaultSyncHandler.java
    jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModule.java
    jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalLoginModuleTest.java
    jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/TestIdentityProvider.java
    jackrabbit/oak/trunk/oak-auth-ldap/src/main/java/org/apache/jackrabbit/oak/security/authentication/ldap/impl/LdapGroup.java
    jackrabbit/oak/trunk/oak-auth-ldap/src/main/java/org/apache/jackrabbit/oak/security/authentication/ldap/impl/LdapIdentity.java
    jackrabbit/oak/trunk/oak-auth-ldap/src/main/java/org/apache/jackrabbit/oak/security/authentication/ldap/impl/LdapIdentityProvider.java
    jackrabbit/oak/trunk/oak-auth-ldap/src/main/java/org/apache/jackrabbit/oak/security/authentication/ldap/impl/LdapProviderConfig.java
    jackrabbit/oak/trunk/oak-auth-ldap/src/main/java/org/apache/jackrabbit/oak/security/authentication/ldap/impl/LdapUser.java
    jackrabbit/oak/trunk/oak-auth-ldap/src/test/java/org/apache/jackrabbit/oak/security/authentication/ldap/InternalLdapServer.java

Modified: jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalGroup.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalGroup.java?rev=1568187&r1=1568186&r2=1568187&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalGroup.java (original)
+++ jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalGroup.java Fri Feb 14 01:47:40 2014
@@ -16,9 +16,19 @@
  */
 package org.apache.jackrabbit.oak.spi.security.authentication.external;
 
+import javax.annotation.Nonnull;
+
 /**
  * ExternalGroup defines a group that is provided by an external system.
  */
 public interface ExternalGroup extends ExternalIdentity {
 
+    /**
+     * Returns an iterable of the declared (direct) members of this external group.
+     * @return the declared member
+     * @throws ExternalIdentityException if an error occurrs
+     */
+    @Nonnull
+    Iterable<ExternalIdentityRef> getDeclaredMembers() throws ExternalIdentityException;
+
 }
\ No newline at end of file

Modified: jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalIdentity.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalIdentity.java?rev=1568187&r1=1568186&r2=1568187&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalIdentity.java (original)
+++ jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalIdentity.java Fri Feb 14 01:47:40 2014
@@ -16,7 +16,6 @@
  */
 package org.apache.jackrabbit.oak.spi.security.authentication.external;
 
-import java.security.Principal;
 import java.util.Map;
 
 import javax.annotation.CheckForNull;
@@ -53,7 +52,8 @@ public interface ExternalIdentity {
 
     /**
      * Returns the desired intermediate relative path of the authorizable to be created. For example, one could map
-     * an external hierarchy into the local users and groups hierarchy.
+     * an external hierarchy into the local users and groups hierarchy. The provider must escape all characters so that
+     * the path is a valid JCR path. The path is always considered relative, even if it starts with a '/'.
      *
      * @return the intermediate path or {@code null} or empty.
      */
@@ -63,9 +63,10 @@ public interface ExternalIdentity {
     /**
      * Returns an iterable of the declared groups of this external identity.
      * @return the declared groups
+     * @throws ExternalIdentityException if an error occurrs
      */
     @Nonnull
-    Iterable<? extends ExternalIdentityRef> getGroups();
+    Iterable<ExternalIdentityRef> getDeclaredGroups() throws ExternalIdentityException;
 
     /**
      * Returns a map of properties of this external identity.

Modified: jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalIdentityRef.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalIdentityRef.java?rev=1568187&r1=1568186&r2=1568187&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalIdentityRef.java (original)
+++ jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalIdentityRef.java Fri Feb 14 01:47:40 2014
@@ -102,18 +102,22 @@ public class ExternalIdentityRef {
         return sb.toString();
     }
 
+    /**
+     * Tests if the given object is an external identity reference and if it's getString() is equal to this.
+     */
     @Override
     public boolean equals(Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-
-        ExternalIdentityRef that = (ExternalIdentityRef) o;
-
-        if (!string.equals(that.string)) return false;
-
-        return true;
+        try {
+            // assuming that we never compare other types of classes
+            return this == o || string.equals(((ExternalIdentityRef) o).string);
+        } catch (Exception e) {
+            return false;
+        }
     }
 
+    /**
+     * @return same as {@code this.getString().hashCode()}
+     */
     @Override
     public int hashCode() {
         return string.hashCode();

Modified: jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalUser.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalUser.java?rev=1568187&r1=1568186&r2=1568187&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalUser.java (original)
+++ jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalUser.java Fri Feb 14 01:47:40 2014
@@ -16,19 +16,9 @@
  */
 package org.apache.jackrabbit.oak.spi.security.authentication.external;
 
-import javax.annotation.CheckForNull;
-
 /**
  * ExternalUser defines a user provided by an external system.
  */
 public interface ExternalUser extends ExternalIdentity {
 
-    /**
-     * Returns the plaintext password of this user if available. This is usually only the case when the
-     * external user is accessible during a login call.
-     *
-     * @return the password.
-     */
-    @CheckForNull
-    String getPassword();
 }
\ No newline at end of file

Modified: jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DefaultSyncHandler.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DefaultSyncHandler.java?rev=1568187&r1=1568186&r2=1568187&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DefaultSyncHandler.java (original)
+++ jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DefaultSyncHandler.java Fri Feb 14 01:47:40 2014
@@ -178,11 +178,10 @@ public class DefaultSyncHandler implemen
         @CheckForNull
         private User createUser(ExternalUser externalUser)
                 throws RepositoryException, SyncException, ExternalIdentityException {
-            String password = externalUser.getPassword(); // todo: make configurable
             Principal principal = new PrincipalImpl(externalUser.getPrincipalName());
             User user = userManager.createUser(
                     externalUser.getId(),
-                    password,
+                    null,
                     principal,
                     concatPaths(config.user().getPathPrefix(), externalUser.getIntermediatePath())
             );
@@ -209,7 +208,7 @@ public class DefaultSyncHandler implemen
 
         private void syncAuthorizable(ExternalIdentity externalUser, Authorizable authorizable)
                 throws RepositoryException, SyncException, ExternalIdentityException {
-            for (ExternalIdentityRef externalGroupRef : externalUser.getGroups()) {
+            for (ExternalIdentityRef externalGroupRef : externalUser.getDeclaredGroups()) {
                 ExternalIdentity id = idp.getIdentity(externalGroupRef);
                 if (id instanceof ExternalGroup) {
                     ExternalGroup externalGroup = (ExternalGroup) id;

Modified: jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModule.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModule.java?rev=1568187&r1=1568186&r2=1568187&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModule.java (original)
+++ jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModule.java Fri Feb 14 01:47:40 2014
@@ -91,6 +91,7 @@ public class ExternalLoginModule extends
     /**
      * Default constructor for the OSGIi LoginModuleFactory case and the default non-OSGi JAAS case.
      */
+    @SuppressWarnings("UnusedDeclaration")
     public ExternalLoginModule() {
     }
 

Modified: jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalLoginModuleTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalLoginModuleTest.java?rev=1568187&r1=1568186&r2=1568187&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalLoginModuleTest.java (original)
+++ jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalLoginModuleTest.java Fri Feb 14 01:47:40 2014
@@ -121,7 +121,7 @@ public class ExternalLoginModuleTest ext
         // create user upfront in order to test update mode
         UserManager userManager = getUserManager(root);
         ExternalUser externalUser = idp.getUser(userId);
-        Authorizable user = userManager.createUser(externalUser.getId(), externalUser.getPassword());
+        Authorizable user = userManager.createUser(externalUser.getId(), null);
         root.commit();
 
         ContentSession cs = null;

Modified: jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/TestIdentityProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/TestIdentityProvider.java?rev=1568187&r1=1568186&r2=1568187&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/TestIdentityProvider.java (original)
+++ jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/TestIdentityProvider.java Fri Feb 14 01:47:40 2014
@@ -79,7 +79,7 @@ public class TestIdentityProvider implem
             return null;
         }
         SimpleCredentials creds = (SimpleCredentials) credentials;
-        ExternalUser user = getUser(creds.getUserID());
+        TestUser user = (TestUser) getUser(creds.getUserID());
         if (user != null) {
             if (!new String(creds.getPassword()).equals(user.getPassword())) {
                 throw new LoginException("Invalid User/Password");
@@ -128,7 +128,7 @@ public class TestIdentityProvider implem
         }
 
         @Override
-        public Iterable<ExternalIdentityRef> getGroups() {
+        public Iterable<ExternalIdentityRef> getDeclaredGroups() {
             return groups;
         }
 
@@ -156,7 +156,6 @@ public class TestIdentityProvider implem
             super(userId);
         }
 
-        @Override
         public String getPassword() {
             return "";
         }
@@ -169,5 +168,10 @@ public class TestIdentityProvider implem
             super(userId);
         }
 
+        @Nonnull
+        @Override
+        public Iterable<ExternalIdentityRef> getDeclaredMembers() throws ExternalIdentityException {
+            return null;
+        }
     }
 }
\ No newline at end of file

Added: jackrabbit/oak/trunk/oak-auth-ldap/src/main/java/org/apache/jackrabbit/oak/security/authentication/ldap/impl/DebugTimer.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-auth-ldap/src/main/java/org/apache/jackrabbit/oak/security/authentication/ldap/impl/DebugTimer.java?rev=1568187&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-ldap/src/main/java/org/apache/jackrabbit/oak/security/authentication/ldap/impl/DebugTimer.java (added)
+++ jackrabbit/oak/trunk/oak-auth-ldap/src/main/java/org/apache/jackrabbit/oak/security/authentication/ldap/impl/DebugTimer.java Fri Feb 14 01:47:40 2014
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.security.authentication.ldap.impl;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * {@code DebugTimer}...
+ */
+public class DebugTimer {
+
+    private static String[] units = {"ns", "us", "ms", "s"};
+
+    private List<TimeStamp> timestamps = new LinkedList<TimeStamp>();
+
+    private long now;
+
+    public DebugTimer() {
+        now = System.nanoTime();
+    }
+
+    public void mark(String msg) {
+        long then = now;
+        now = System.nanoTime();
+        timestamps.add(new TimeStamp(now-then, msg));
+    }
+
+    public String getString() {
+        if (timestamps.isEmpty()) {
+            return "";
+        }
+        StringBuilder b = new StringBuilder("(");
+        for (TimeStamp t: timestamps) {
+            if (b.length() > 0) {
+                b.append(", ");
+            }
+            int u = 0;
+            double time = t.time;
+            while (time > 1000 && u<units.length-1) {
+                time = time / 1000;
+                u++;
+            }
+            b.append(String.format("%s=%f.2%s", t.msg, time, units[u]));
+        }
+        return b.append(')').toString();
+    }
+
+    private static class TimeStamp {
+
+        private final long time;
+
+        private final String msg;
+
+        private TimeStamp(long time, String msg) {
+            this.time = time;
+            this.msg = msg;
+        }
+    }
+}
\ No newline at end of file

Modified: jackrabbit/oak/trunk/oak-auth-ldap/src/main/java/org/apache/jackrabbit/oak/security/authentication/ldap/impl/LdapGroup.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-auth-ldap/src/main/java/org/apache/jackrabbit/oak/security/authentication/ldap/impl/LdapGroup.java?rev=1568187&r1=1568186&r2=1568187&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-ldap/src/main/java/org/apache/jackrabbit/oak/security/authentication/ldap/impl/LdapGroup.java (original)
+++ jackrabbit/oak/trunk/oak-auth-ldap/src/main/java/org/apache/jackrabbit/oak/security/authentication/ldap/impl/LdapGroup.java Fri Feb 14 01:47:40 2014
@@ -16,12 +16,28 @@
  */
 package org.apache.jackrabbit.oak.security.authentication.ldap.impl;
 
+import java.util.Map;
+
+import javax.annotation.Nonnull;
+
 import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalGroup;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalIdentityException;
 import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalIdentityRef;
 
 public class LdapGroup extends LdapIdentity implements ExternalGroup {
 
-    public LdapGroup(LdapIdentityProvider provider, ExternalIdentityRef ref, String id) {
-        super(provider, ref, id);
+    private Map<String, ExternalIdentityRef> members;
+
+    public LdapGroup(LdapIdentityProvider provider, ExternalIdentityRef ref, String id, String path) {
+        super(provider, ref, id, path);
+    }
+
+    @Nonnull
+    @Override
+    public Iterable<ExternalIdentityRef> getDeclaredMembers() throws ExternalIdentityException {
+        if (members == null) {
+            members = provider.getDeclaredMemberRefs(ref);
+        }
+        return members.values();
     }
 }

Modified: jackrabbit/oak/trunk/oak-auth-ldap/src/main/java/org/apache/jackrabbit/oak/security/authentication/ldap/impl/LdapIdentity.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-auth-ldap/src/main/java/org/apache/jackrabbit/oak/security/authentication/ldap/impl/LdapIdentity.java?rev=1568187&r1=1568186&r2=1568187&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-ldap/src/main/java/org/apache/jackrabbit/oak/security/authentication/ldap/impl/LdapIdentity.java (original)
+++ jackrabbit/oak/trunk/oak-auth-ldap/src/main/java/org/apache/jackrabbit/oak/security/authentication/ldap/impl/LdapIdentity.java Fri Feb 14 01:47:40 2014
@@ -24,6 +24,7 @@ import javax.annotation.Nonnull;
 
 import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalGroup;
 import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalIdentity;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalIdentityException;
 import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalIdentityRef;
 
 /**
@@ -31,18 +32,23 @@ import org.apache.jackrabbit.oak.spi.sec
  */
 public abstract class LdapIdentity implements ExternalIdentity {
 
-    private final LdapIdentityProvider provider;
+    protected final LdapIdentityProvider provider;
 
-    private final ExternalIdentityRef ref;
+    protected final ExternalIdentityRef ref;
 
-    private final String id;
+    protected final String id;
+
+    protected final String path;
+
+    private Map<String, ExternalIdentityRef> groups;
 
     private final Map<String, Object> properties = new HashMap<String, Object>();
 
-    protected LdapIdentity(LdapIdentityProvider provider, ExternalIdentityRef ref, String id) {
+    protected LdapIdentity(LdapIdentityProvider provider, ExternalIdentityRef ref, String id, String path) {
         this.provider = provider;
         this.ref = ref;
         this.id = id;
+        this.path = path;
     }
 
     /**
@@ -78,7 +84,7 @@ public abstract class LdapIdentity imple
      */
     @Override
     public String getIntermediatePath() {
-        return null;
+        return path;
     }
 
     /**
@@ -86,8 +92,11 @@ public abstract class LdapIdentity imple
      */
     @Nonnull
     @Override
-    public Iterable<? extends ExternalIdentityRef> getGroups() {
-        return Collections.emptyList();
+    public Iterable<ExternalIdentityRef> getDeclaredGroups() throws ExternalIdentityException {
+        if (groups == null) {
+            groups = provider.getDeclaredGroupRefs(ref);
+        }
+        return groups.values();
     }
 
     /**

Modified: jackrabbit/oak/trunk/oak-auth-ldap/src/main/java/org/apache/jackrabbit/oak/security/authentication/ldap/impl/LdapIdentityProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-auth-ldap/src/main/java/org/apache/jackrabbit/oak/security/authentication/ldap/impl/LdapIdentityProvider.java?rev=1568187&r1=1568186&r2=1568187&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-ldap/src/main/java/org/apache/jackrabbit/oak/security/authentication/ldap/impl/LdapIdentityProvider.java (original)
+++ jackrabbit/oak/trunk/oak-auth-ldap/src/main/java/org/apache/jackrabbit/oak/security/authentication/ldap/impl/LdapIdentityProvider.java Fri Feb 14 01:47:40 2014
@@ -16,9 +16,12 @@
  */
 package org.apache.jackrabbit.oak.security.authentication.ldap.impl;
 
+import java.util.Collections;
+import java.util.HashMap;
 import java.util.Map;
 
 import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
 import javax.jcr.Credentials;
 import javax.jcr.SimpleCredentials;
 import javax.security.auth.login.LoginException;
@@ -29,6 +32,7 @@ import org.apache.directory.api.ldap.mod
 import org.apache.directory.api.ldap.model.cursor.SearchCursor;
 import org.apache.directory.api.ldap.model.entry.Attribute;
 import org.apache.directory.api.ldap.model.entry.Entry;
+import org.apache.directory.api.ldap.model.entry.Value;
 import org.apache.directory.api.ldap.model.exception.LdapAuthenticationException;
 import org.apache.directory.api.ldap.model.exception.LdapException;
 import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeValueException;
@@ -38,6 +42,7 @@ import org.apache.directory.api.ldap.mod
 import org.apache.directory.api.ldap.model.message.SearchResultEntry;
 import org.apache.directory.api.ldap.model.message.SearchScope;
 import org.apache.directory.api.ldap.model.name.Dn;
+import org.apache.directory.api.ldap.model.name.Rdn;
 import org.apache.directory.ldap.client.api.LdapConnection;
 import org.apache.directory.ldap.client.api.LdapConnectionConfig;
 import org.apache.directory.ldap.client.api.LdapConnectionPool;
@@ -54,6 +59,7 @@ import org.apache.jackrabbit.oak.spi.sec
 import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalIdentityProvider;
 import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalIdentityRef;
 import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalUser;
+import org.apache.jackrabbit.util.Text;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -93,6 +99,7 @@ public class LdapIdentityProvider implem
     /**
      * Default constructor for OSGi
      */
+    @SuppressWarnings("UnusedDeclaration")
     public LdapIdentityProvider() {
     }
 
@@ -188,7 +195,9 @@ public class LdapIdentityProvider implem
         LdapConnection connection = connect();
         try {
             Entry entry = connection.lookup(ref.getId(), "*");
-            if (entry.hasObjectClass(config.getUserConfig().getObjectClasses())) {
+            if (entry == null) {
+                return null;
+            } else if (entry.hasObjectClass(config.getUserConfig().getObjectClasses())) {
                 return createUser(entry, null);
             } else if (entry.hasObjectClass(config.getGroupConfig().getObjectClasses())) {
                 return createGroup(entry, null);
@@ -206,14 +215,14 @@ public class LdapIdentityProvider implem
 
     @Override
     public ExternalUser getUser(@Nonnull String userId) throws ExternalIdentityException {
-        long t0 = System.nanoTime();
+        DebugTimer timer = new DebugTimer();
         LdapConnection connection = connect();
-        long t1 = System.nanoTime();
+        timer.mark("connect");
         try {
             Entry entry = getEntry(connection, config.getUserConfig(), userId);
-            long t2 = System.nanoTime();
+            timer.mark("lookup");
             if (log.isDebugEnabled()) {
-                log.debug("getUser({}) connect: {}us, lookup: {}us", new Object[]{userId, (t1-t0)/1000, (t2-t1)/1000});
+                log.debug("getUser({}) {}", userId, timer.getString());
             }
             if (entry != null) {
                 return createUser(entry, userId);
@@ -221,10 +230,10 @@ public class LdapIdentityProvider implem
                 return null;
             }
         } catch (LdapException e) {
-            log.error("Error during ldap lookup", e);
+            log.error("Error during ldap lookup. " + timer.getString(), e);
             throw new ExternalIdentityException("Error during ldap lookup.", e);
         } catch (CursorException e) {
-            log.error("Error during ldap lookup", e);
+            log.error("Error during ldap lookup. " + timer.getString(), e);
             throw new ExternalIdentityException("Error during ldap lookup.", e);
         } finally {
             disconnect(connection);
@@ -233,19 +242,25 @@ public class LdapIdentityProvider implem
 
     @Override
     public ExternalGroup getGroup(@Nonnull String name) throws ExternalIdentityException {
+        DebugTimer timer = new DebugTimer();
         LdapConnection connection = connect();
+        timer.mark("connect");
         try {
             Entry entry = getEntry(connection, config.getGroupConfig(), name);
+            timer.mark("lookup");
+            if (log.isDebugEnabled()) {
+                log.debug("getGroup({}) {}", name, timer.getString());
+            }
             if (entry != null) {
                 return createGroup(entry, name);
             } else {
                 return null;
             }
         } catch (LdapException e) {
-            log.error("Error during ldap lookup", e);
+            log.error("Error during ldap lookup. " + timer.getString(), e);
             throw new ExternalIdentityException("Error during ldap lookup.", e);
         } catch (CursorException e) {
-            log.error("Error during ldap lookup", e);
+            log.error("Error during ldap lookup. " + timer.getString(), e);
             throw new ExternalIdentityException("Error during ldap lookup.", e);
         } finally {
             disconnect(connection);
@@ -294,7 +309,10 @@ public class LdapIdentityProvider implem
         if (id == null) {
             id = e.get(config.getUserConfig().getIdAttribute()).getString();
         }
-        LdapUser user = new LdapUser(this, ref, id, null);
+        String path = config.getUserConfig().makeDnPath()
+                ? createDNPath(e.getDn())
+                : null;
+        LdapUser user = new LdapUser(this, ref, id, path);
         Map<String, Object> props = user.getProperties();
         for (Attribute attr: e.getAttributes()) {
             if (attr.isHumanReadable()) {
@@ -310,7 +328,10 @@ public class LdapIdentityProvider implem
         if (name == null) {
             name = e.get(config.getGroupConfig().getIdAttribute()).getString();
         }
-        LdapGroup group = new LdapGroup(this, ref, name);
+        String path = config.getGroupConfig().makeDnPath()
+                ? createDNPath(e.getDn())
+                : null;
+        LdapGroup group = new LdapGroup(this, ref, name, path);
         Map<String, Object> props = group.getProperties();
         for (Attribute attr: e.getAttributes()) {
             if (attr.isHumanReadable()) {
@@ -321,6 +342,7 @@ public class LdapIdentityProvider implem
 
     }
 
+    @Nonnull
     private LdapConnection connect() throws ExternalIdentityException {
         try {
             return adminPool.getConnection();
@@ -330,9 +352,11 @@ public class LdapIdentityProvider implem
         }
     }
 
-    private void disconnect(LdapConnection connection) throws ExternalIdentityException {
+    private void disconnect(@Nullable LdapConnection connection) throws ExternalIdentityException {
         try {
-            adminPool.releaseConnection(connection);
+            if (connection != null) {
+                adminPool.releaseConnection(connection);
+            }
         } catch (Exception e) {
             log.warn("Error while disconnecting from the ldap server.", e);
         }
@@ -350,13 +374,13 @@ public class LdapIdentityProvider implem
             // authenticate
             LdapConnection connection = null;
             try {
-                long t0 = System.nanoTime();
+                DebugTimer timer = new DebugTimer();
                 connection = userPool.getConnection();
-                long t1 = System.nanoTime();
+                timer.mark("connect");
                 connection.bind(user.getExternalId().getId(), new String(creds.getPassword()));
-                long t2 = System.nanoTime();
+                timer.mark("bind");
                 if (log.isDebugEnabled()) {
-                    log.debug("authenticate({}) connect: {}us, bind: {}us", new Object[]{user.getId(), (t1-t0)/1000, (t2-t1)/1000});
+                    log.debug("authenticate({}) {}", user.getId(), timer.getString());
                 }
             } catch (LdapAuthenticationException e) {
                 throw new LoginException("Unable to authenticate against LDAP server: " + e.getMessage());
@@ -375,9 +399,113 @@ public class LdapIdentityProvider implem
         return user;
     }
 
-    private boolean isMyRef(ExternalIdentityRef ref) {
+    private boolean isMyRef(@Nonnull ExternalIdentityRef ref) {
         final String refProviderName = ref.getProviderName();
         return refProviderName == null || refProviderName.length() == 0 || getName().equals(refProviderName);
     }
 
+    /**
+     * Collects the declared (direct) groups of an identity
+     * @param ref reference to the identity
+     * @return map of identities where the key is the DN of the LDAP entity
+     */
+    public Map<String, ExternalIdentityRef> getDeclaredGroupRefs(ExternalIdentityRef ref) throws ExternalIdentityException {
+        if (!isMyRef(ref)) {
+            return Collections.emptyMap();
+        }
+        String searchFilter = config.getMemberOfSearchFilter(ref.getId());
+
+        LdapConnection connection = null;
+        try {
+            // Create the SearchRequest object
+            SearchRequest req = new SearchRequestImpl();
+            req.setScope(SearchScope.SUBTREE);
+            req.addAttributes(SchemaConstants.NO_ATTRIBUTE);
+            req.setTimeLimit(config.getSearchTimeout());
+            req.setBase(new Dn(config.getGroupConfig().getBaseDN()));
+            req.setFilter(searchFilter);
+
+            Map<String, ExternalIdentityRef> groups = new HashMap<String, ExternalIdentityRef>();
+            DebugTimer timer = new DebugTimer();
+            connection = connect();
+            timer.mark("connect");
+
+            SearchCursor searchCursor = connection.search(req);
+            timer.mark("search");
+            while (searchCursor.next()) {
+                Response response = searchCursor.get();
+                if (response instanceof SearchResultEntry) {
+                    Entry resultEntry = ((SearchResultEntry) response).getEntry();
+                    ExternalIdentityRef groupRef = new ExternalIdentityRef(resultEntry.getDn().toString(), this.getName());
+                    groups.put(groupRef.getId(), groupRef);
+                }
+            }
+            timer.mark("iterate");
+            if (log.isDebugEnabled()) {
+                log.debug("search below {} with {} found {} entries. {}", new Object[]{
+                        config.getGroupConfig().getBaseDN(), searchFilter, groups.size(), timer.getString()
+                });
+            }
+            return groups;
+        } catch (Exception e) {
+            log.error("Error during ldap membership search." ,e);
+            throw new ExternalIdentityException("Error during ldap membership search.", e);
+        } finally {
+            disconnect(connection);
+        }
+    }
+
+    /**
+     * Collects the declared (direct) members of a group
+     * @param ref the reference to the group
+     * @return map of identity refers
+     * @throws ExternalIdentityException if an error occurs
+     */
+    public Map<String, ExternalIdentityRef> getDeclaredMemberRefs(ExternalIdentityRef ref) throws ExternalIdentityException {
+        if (!isMyRef(ref)) {
+            return Collections.emptyMap();
+        }
+        LdapConnection connection = null;
+        try {
+            Map<String, ExternalIdentityRef> members = new HashMap<String, ExternalIdentityRef>();
+            DebugTimer timer = new DebugTimer();
+            connection = connect();
+            timer.mark("connect");
+            Entry entry = connection.lookup(ref.getId());
+            timer.mark("lookup");
+            Attribute attr = entry.get(config.getGroupMemberAttribute());
+            for (Value value: attr) {
+                ExternalIdentityRef memberRef = new ExternalIdentityRef(value.getString(), this.getName());
+                members.put(memberRef.getId(), memberRef);
+            }
+            timer.mark("iterate");
+            if (log.isDebugEnabled()) {
+                log.debug("members lookup of {} found {} members. {}", new Object[]{
+                        ref.getId(), members.size(), timer.getString()
+                });
+            }
+            return members;
+        } catch (Exception e) {
+            log.error("Error during ldap group members lookup." ,e);
+            throw new ExternalIdentityException("Error during ldap group members lookup.", e);
+        } finally {
+            disconnect(connection);
+        }
+    }
+
+    /**
+     * Makes the intermediate path of an DN by splitting along the RDNs
+     * @param dn the dn of the identity
+     * @return the intermediate path or {@code null} if disabled by config
+     */
+    public String createDNPath(Dn dn) {
+        StringBuilder path = new StringBuilder();
+        for (Rdn rnd: dn.getRdns()) {
+            if (path.length() > 0) {
+                path.append('/');
+            }
+            path.append(Text.escapeIllegalJcrChars(rnd.toString()));
+        }
+        return path.toString();
+    }
 }
\ No newline at end of file

Modified: jackrabbit/oak/trunk/oak-auth-ldap/src/main/java/org/apache/jackrabbit/oak/security/authentication/ldap/impl/LdapProviderConfig.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-auth-ldap/src/main/java/org/apache/jackrabbit/oak/security/authentication/ldap/impl/LdapProviderConfig.java?rev=1568187&r1=1568186&r2=1568187&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-ldap/src/main/java/org/apache/jackrabbit/oak/security/authentication/ldap/impl/LdapProviderConfig.java (original)
+++ jackrabbit/oak/trunk/oak-auth-ldap/src/main/java/org/apache/jackrabbit/oak/security/authentication/ldap/impl/LdapProviderConfig.java Fri Feb 14 01:47:40 2014
@@ -313,7 +313,7 @@ public class LdapProviderConfig {
     /**
      * Defines the configuration of an identity (user or group).
      */
-    public static class Identity {
+    public class Identity {
 
         private String baseDN;
 
@@ -368,6 +368,7 @@ public class LdapProviderConfig {
         public Identity setObjectClasses(@Nonnull String ... objectClasses) {
             this.objectClasses = objectClasses;
             filterTemplate = null;
+            memberOfFilterTemplate = null;
             return this;
         }
 
@@ -393,6 +394,7 @@ public class LdapProviderConfig {
         public Identity setIdAttribute(@Nonnull String idAttribute) {
             this.idAttribute = idAttribute;
             filterTemplate = null;
+            memberOfFilterTemplate = null;
             return this;
         }
 
@@ -417,6 +419,7 @@ public class LdapProviderConfig {
         public Identity setExtraFilter(@Nullable String extraFilter) {
             this.extraFilter = extraFilter;
             filterTemplate = null;
+            memberOfFilterTemplate = null;
             return this;
         }
 
@@ -473,55 +476,6 @@ public class LdapProviderConfig {
             }
             return String.format(filterTemplate, encodeFilterValue(id));
         }
-
-        /**
-         * Copied from org.apache.directory.api.ldap.model.filter.FilterEncoder#encodeFilterValue(java.lang.String)
-         * in order to keep this configuration LDAP client independent.
-         *
-         * Handles encoding of special characters in LDAP search filter assertion values using the
-         * &lt;valueencoding&gt; rule as described in <a href="http://www.ietf.org/rfc/rfc4515.txt">RFC 4515</a>.
-         *
-         * @param value Right hand side of "attrId=value" assertion occurring in an LDAP search filter.
-         * @return Escaped version of <code>value</code>
-         */
-        private static String encodeFilterValue(String value) {
-            StringBuilder sb = null;
-            for (int i = 0; i < value.length(); i++) {
-                char ch = value.charAt(i);
-                String replace = null;
-                switch (ch) {
-                    case '*':
-                        replace = "\\2A";
-                        break;
-
-                    case '(':
-                        replace = "\\28";
-                        break;
-
-                    case ')':
-                        replace = "\\29";
-                        break;
-
-                    case '\\':
-                        replace = "\\5C";
-                        break;
-
-                    case '\0':
-                        replace = "\\00";
-                        break;
-                }
-                if (replace != null) {
-                    if (sb == null) {
-                        sb = new StringBuilder(value.length() * 2);
-                        sb.append(value.substring(0, i));
-                    }
-                    sb.append(replace);
-                } else if (sb != null) {
-                    sb.append(ch);
-                }
-            }
-            return (sb == null ? value : sb.toString());
-        }
     }
 
     /**
@@ -573,6 +527,8 @@ public class LdapProviderConfig {
 
     private String groupMemberAttribute = PARAM_GROUP_MEMBER_ATTRIBUTE;
 
+    private String memberOfFilterTemplate;
+
     private final Identity userConfig = new Identity()
             .setBaseDN(PARAM_USER_BASE_DN_DEFAULT)
             .setExtraFilter(PARAM_USER_EXTRA_FILTER_DEFAULT)
@@ -766,6 +722,39 @@ public class LdapProviderConfig {
     }
 
     /**
+     * Returns the LDAP filter that is used when searching for groups where an identity is member of.
+     * The filter is based on the configuration and has the following format:
+     *
+     * <pre>
+     *     (&(${memberAttribute}=${dn})(objectclass=${objectclass})${extraFilter})
+     * </pre>
+     *
+     * Note that the objectclass part is repeated according to the specified objectclasses in
+     * {@link Identity#getObjectClasses()} of the group configuration.
+     *
+     * @param dn the dn of the identity to search for
+     * @return the search filter
+     */
+    public String getMemberOfSearchFilter(@Nonnull String dn) {
+        if (memberOfFilterTemplate == null) {
+            StringBuilder filter = new StringBuilder("(&(")
+                    .append(groupMemberAttribute)
+                    .append("=%s)");
+            for (String objectClass: groupConfig.objectClasses) {
+                filter.append("(objectclass=")
+                        .append(encodeFilterValue(objectClass))
+                        .append(')');
+            }
+            if (groupConfig.extraFilter != null && groupConfig.extraFilter.length() > 0) {
+                filter.append(groupConfig.extraFilter);
+            }
+            filter.append(')');
+            memberOfFilterTemplate = filter.toString();
+        }
+        return String.format(memberOfFilterTemplate, encodeFilterValue(dn));
+    }
+
+    /**
      * Returns the user specific configuration.
      * @return the user config.
      */
@@ -782,4 +771,53 @@ public class LdapProviderConfig {
     public Identity getGroupConfig() {
         return groupConfig;
     }
+
+    /**
+     * Copied from org.apache.directory.api.ldap.model.filter.FilterEncoder#encodeFilterValue(java.lang.String)
+     * in order to keep this configuration LDAP client independent.
+     *
+     * Handles encoding of special characters in LDAP search filter assertion values using the
+     * &lt;valueencoding&gt; rule as described in <a href="http://www.ietf.org/rfc/rfc4515.txt">RFC 4515</a>.
+     *
+     * @param value Right hand side of "attrId=value" assertion occurring in an LDAP search filter.
+     * @return Escaped version of <code>value</code>
+     */
+    private static String encodeFilterValue(String value) {
+        StringBuilder sb = null;
+        for (int i = 0; i < value.length(); i++) {
+            char ch = value.charAt(i);
+            String replace = null;
+            switch (ch) {
+                case '*':
+                    replace = "\\2A";
+                    break;
+
+                case '(':
+                    replace = "\\28";
+                    break;
+
+                case ')':
+                    replace = "\\29";
+                    break;
+
+                case '\\':
+                    replace = "\\5C";
+                    break;
+
+                case '\0':
+                    replace = "\\00";
+                    break;
+            }
+            if (replace != null) {
+                if (sb == null) {
+                    sb = new StringBuilder(value.length() * 2);
+                    sb.append(value.substring(0, i));
+                }
+                sb.append(replace);
+            } else if (sb != null) {
+                sb.append(ch);
+            }
+        }
+        return (sb == null ? value : sb.toString());
+    }
 }

Modified: jackrabbit/oak/trunk/oak-auth-ldap/src/main/java/org/apache/jackrabbit/oak/security/authentication/ldap/impl/LdapUser.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-auth-ldap/src/main/java/org/apache/jackrabbit/oak/security/authentication/ldap/impl/LdapUser.java?rev=1568187&r1=1568186&r2=1568187&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-ldap/src/main/java/org/apache/jackrabbit/oak/security/authentication/ldap/impl/LdapUser.java (original)
+++ jackrabbit/oak/trunk/oak-auth-ldap/src/main/java/org/apache/jackrabbit/oak/security/authentication/ldap/impl/LdapUser.java Fri Feb 14 01:47:40 2014
@@ -21,19 +21,8 @@ import org.apache.jackrabbit.oak.spi.sec
 
 public class LdapUser extends LdapIdentity implements ExternalUser {
 
-    private final String pwd;
-
-    public LdapUser(LdapIdentityProvider provider, ExternalIdentityRef ref, String id, String pwd) {
-        super(provider, ref, id);
-        this.pwd = pwd;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public String getPassword() {
-        return pwd;
+    public LdapUser(LdapIdentityProvider provider, ExternalIdentityRef ref, String id, String path) {
+        super(provider, ref, id, path);
     }
 
 }

Modified: jackrabbit/oak/trunk/oak-auth-ldap/src/test/java/org/apache/jackrabbit/oak/security/authentication/ldap/InternalLdapServer.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-auth-ldap/src/test/java/org/apache/jackrabbit/oak/security/authentication/ldap/InternalLdapServer.java?rev=1568187&r1=1568186&r2=1568187&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-ldap/src/test/java/org/apache/jackrabbit/oak/security/authentication/ldap/InternalLdapServer.java (original)
+++ jackrabbit/oak/trunk/oak-auth-ldap/src/test/java/org/apache/jackrabbit/oak/security/authentication/ldap/InternalLdapServer.java Fri Feb 14 01:47:40 2014
@@ -17,12 +17,21 @@
 package org.apache.jackrabbit.oak.security.authentication.ldap;
 
 import java.io.File;
+import java.io.InputStream;
+
+import javax.naming.NamingException;
 import javax.naming.directory.BasicAttributes;
 import javax.naming.directory.DirContext;
 import javax.naming.ldap.LdapContext;
 
+import org.apache.directory.api.ldap.model.entry.Entry;
+import org.apache.directory.api.ldap.model.exception.LdapException;
+import org.apache.directory.api.ldap.model.exception.LdapInvalidDnException;
+import org.apache.directory.api.ldap.model.name.Dn;
 import org.apache.directory.server.constants.ServerDNConstants;
+import org.apache.directory.server.core.entry.ServerEntry;
 import org.apache.directory.server.unit.AbstractServerTest;
+import org.apache.directory.shared.ldap.name.LdapDN;
 
 class InternalLdapServer extends AbstractServerTest {
 
@@ -89,6 +98,10 @@ class InternalLdapServer extends Abstrac
         ctxt.modifyAttributes(groupDN, DirContext.REMOVE_ATTRIBUTE, attrs);
     }
 
+    public void loadLdif(InputStream in) throws Exception {
+        super.loadLdif(in, false);
+    }
+
     private static String buildDn(String name, boolean isGroup) {
         StringBuilder dn = new StringBuilder();
         dn.append("cn=").append(name).append(',');

Added: jackrabbit/oak/trunk/oak-auth-ldap/src/test/java/org/apache/jackrabbit/oak/security/authentication/ldap/LdapProviderTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-auth-ldap/src/test/java/org/apache/jackrabbit/oak/security/authentication/ldap/LdapProviderTest.java?rev=1568187&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-ldap/src/test/java/org/apache/jackrabbit/oak/security/authentication/ldap/LdapProviderTest.java (added)
+++ jackrabbit/oak/trunk/oak-auth-ldap/src/test/java/org/apache/jackrabbit/oak/security/authentication/ldap/LdapProviderTest.java Fri Feb 14 01:47:40 2014
@@ -0,0 +1,285 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jackrabbit.oak.security.authentication.ldap;
+
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.jcr.SimpleCredentials;
+import javax.security.auth.login.LoginException;
+
+import org.apache.directory.server.constants.ServerDNConstants;
+import org.apache.jackrabbit.oak.security.authentication.ldap.impl.LdapIdentityProvider;
+import org.apache.jackrabbit.oak.security.authentication.ldap.impl.LdapProviderConfig;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalGroup;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalIdentity;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalIdentityException;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalIdentityProvider;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalIdentityRef;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalUser;
+import org.apache.jackrabbit.util.Text;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.fail;
+
+public class LdapProviderTest {
+
+    protected static final InternalLdapServer LDAP_SERVER = new InternalLdapServer();
+
+    //initialize LDAP server only once (fast, but might turn out to be not sufficiently flexible in the future)
+    protected static final boolean USE_COMMON_LDAP_FIXTURE = false;
+
+    private static final String TUTORIAL_LDIF = "apache-ds-tutorial.ldif";
+
+    public static final String IDP_NAME = "ldap";
+
+    protected ExternalIdentityProvider idp;
+
+    protected LdapProviderConfig providerConfig;
+
+    @BeforeClass
+    public static void beforeClass() throws Exception {
+        if (USE_COMMON_LDAP_FIXTURE) {
+            LDAP_SERVER.setUp();
+            initLdapFixture(LDAP_SERVER);
+        }
+    }
+
+    @AfterClass
+    public static void afterClass() throws Exception {
+        if (USE_COMMON_LDAP_FIXTURE) {
+            LDAP_SERVER.tearDown();
+        }
+    }
+
+    @Before
+    public void before() throws Exception {
+        if (!USE_COMMON_LDAP_FIXTURE) {
+            LDAP_SERVER.setUp();
+            initLdapFixture(LDAP_SERVER);
+        }
+        idp = createIDP();
+    }
+
+    @After
+    public void after() throws Exception {
+        if (!USE_COMMON_LDAP_FIXTURE) {
+            LDAP_SERVER.tearDown();
+        }
+    }
+
+    protected ExternalIdentityProvider createIDP() {
+        providerConfig = new LdapProviderConfig()
+                .setName(IDP_NAME)
+                .setHostname("127.0.0.1")
+                .setPort(LDAP_SERVER.getPort())
+                .setBindDN(ServerDNConstants.ADMIN_SYSTEM_DN)
+                .setBindPassword(InternalLdapServer.ADMIN_PW)
+                .setGroupMemberAttribute("uniquemember");
+
+        providerConfig.getUserConfig()
+                .setBaseDN(ServerDNConstants.USERS_SYSTEM_DN)
+                .setObjectClasses("inetOrgPerson");
+        providerConfig.getGroupConfig()
+                .setBaseDN(ServerDNConstants.GROUPS_SYSTEM_DN)
+                .setObjectClasses("groupOfUniqueNames");
+
+        return new LdapIdentityProvider(providerConfig);
+    }
+
+    protected static void initLdapFixture(InternalLdapServer server) throws Exception {
+        InputStream tutorialLDIF = LdapProviderTest.class.getResourceAsStream(TUTORIAL_LDIF);
+        server.loadLdif(tutorialLDIF);
+    }
+
+    public static final String TEST_USER0_DN = "cn=Rat Ratterson,ou=users,ou=system";
+    public static final String TEST_USER0_UID = "ratty";
+
+    public static final String TEST_USER1_DN = "cn=Horatio Hornblower,ou=users,ou=system";
+    public static final String TEST_USER1_UID = "hhornblo";
+    public static final String TEST_USER1_PATH = "cn=Horatio Hornblower/ou=users/ou=system";
+
+    public static final String TEST_USER2_DN = "cn=William Bush,ou=users,ou=system";
+    public static final String TEST_USER3_DN = "cn=Thomas Quist,ou=users,ou=system";
+    public static final String TEST_USER4_DN = "cn=Moultrie Crystal,ou=users,ou=system";
+
+    public static final String TEST_USER5_UID = "=007=";
+    public static final String TEST_USER5_DN = "cn=Special\\, Agent [007],ou=users,ou=system";
+    public static final String TEST_USER5_PATH = "cn=Special\\, Agent %5B007%5D/ou=users/ou=system";
+
+    public static final String TEST_GROUP1_DN = "cn=HMS Lydia,ou=crews,ou=groups,ou=system";
+    public static final String TEST_GROUP1_NAME = "HMS Lydia";
+    public static final String[] TEST_GROUP1_MEMBERS = {
+            TEST_USER0_DN, TEST_USER1_DN, TEST_USER2_DN, TEST_USER3_DN, TEST_USER4_DN
+    };
+
+    public static final String TEST_GROUP2_DN = "cn=HMS Victory,ou=crews,ou=groups,ou=system";
+    public static final String TEST_GROUP2_NAME = "HMS Victory";
+
+    public static final String TEST_GROUP3_DN = "cn=HMS Bounty,ou=crews,ou=groups,ou=system";
+    public static final String TEST_GROUP3_NAME = "HMS Bounty";
+
+    public static final String[] TEST_USER0_GROUPS = {TEST_GROUP1_DN, TEST_GROUP2_DN, TEST_GROUP3_DN};
+    public static final String[] TEST_USER1_GROUPS = {TEST_GROUP1_DN};
+
+    @Test
+    public void testGetUserByRef() throws Exception {
+        ExternalIdentityRef ref = new ExternalIdentityRef(TEST_USER1_DN, IDP_NAME);
+        ExternalIdentity id = idp.getIdentity(ref);
+        assertTrue("User instance", id instanceof ExternalUser);
+        assertEquals("User ID", TEST_USER1_UID, id.getId());
+    }
+
+    @Test
+    public void testGetUserByUserId() throws Exception {
+        ExternalUser user = idp.getUser(TEST_USER1_UID);
+        assertNotNull("User 1 must exist", user);
+        assertEquals("User Ref", TEST_USER1_DN, user.getExternalId().getId());
+    }
+
+    @Test
+    public void testAuthenticate() throws Exception {
+        SimpleCredentials creds = new SimpleCredentials(TEST_USER1_UID, "pass".toCharArray());
+        ExternalUser user = idp.authenticate(creds);
+        assertNotNull("User 1 must authenticate", user);
+        assertEquals("User Ref", TEST_USER1_DN, user.getExternalId().getId());
+    }
+
+    @Test
+    public void testAuthenticateFail() throws Exception {
+        SimpleCredentials creds = new SimpleCredentials(TEST_USER1_UID, "foobar".toCharArray());
+        try {
+            idp.authenticate(creds);
+            fail("Authenticate must fail with LoginException for wrong password");
+        } catch (LoginException e) {
+            // ok
+        }
+    }
+
+    @Test
+    public void testAuthenticateMissing() throws Exception {
+        SimpleCredentials creds = new SimpleCredentials("foobar" + TEST_USER1_UID, "pass".toCharArray());
+        ExternalUser user = idp.authenticate(creds);
+        assertNull("Authenticate must return NULL for unknown user", user);
+    }
+
+    @Test
+    public void testGetUserByForeignRef() throws Exception {
+        ExternalIdentityRef ref = new ExternalIdentityRef(TEST_USER1_DN, "foobar");
+        ExternalIdentity id = idp.getIdentity(ref);
+        assertNull("Foreign ref must be null", id);
+    }
+
+    @Test
+    public void testGetUnknownUserByRef() throws Exception {
+        ExternalIdentityRef ref = new ExternalIdentityRef("bla=foo," + TEST_USER1_DN, IDP_NAME);
+        ExternalIdentity id = idp.getIdentity(ref);
+        assertNull("Unknown user must return null", id);
+    }
+
+    @Test
+    public void testGetGroupByRef() throws Exception {
+        ExternalIdentityRef ref = new ExternalIdentityRef(TEST_GROUP1_DN, IDP_NAME);
+        ExternalIdentity id = idp.getIdentity(ref);
+        assertTrue("Group instance", id instanceof ExternalGroup);
+        assertEquals("Group Name", TEST_GROUP1_NAME, id.getId());
+    }
+
+    @Test
+    public void testGetGroupByName() throws Exception {
+        ExternalGroup group = idp.getGroup(TEST_GROUP1_NAME);
+        assertNotNull("Group 1 must exist", group);
+        assertEquals("Group Ref", TEST_GROUP1_DN, group.getExternalId().getId());
+    }
+
+
+    @Test
+    public void testGetMembers() throws Exception {
+        ExternalIdentityRef ref = new ExternalIdentityRef(TEST_GROUP1_DN, IDP_NAME);
+        ExternalIdentity id = idp.getIdentity(ref);
+        assertTrue("Group instance", id instanceof ExternalGroup);
+
+        ExternalGroup grp = (ExternalGroup) id;
+        assertIfEquals("Group members", TEST_GROUP1_MEMBERS, grp.getDeclaredMembers());
+    }
+
+    @Test
+    public void testGetGroups() throws Exception {
+        ExternalIdentityRef ref = new ExternalIdentityRef(TEST_USER1_DN, IDP_NAME);
+        ExternalIdentity id = idp.getIdentity(ref);
+        assertTrue("User instance", id instanceof ExternalUser);
+        assertIfEquals("Groups", TEST_USER1_GROUPS, id.getDeclaredGroups());
+    }
+
+    @Test
+    public void testGetGroups2() throws Exception {
+        ExternalIdentityRef ref = new ExternalIdentityRef(TEST_USER0_DN, IDP_NAME);
+        ExternalIdentity id = idp.getIdentity(ref);
+        assertTrue("User instance", id instanceof ExternalUser);
+        assertIfEquals("Groups", TEST_USER0_GROUPS, id.getDeclaredGroups());
+    }
+
+    @Test
+    public void testNullIntermediatePath() throws Exception {
+        providerConfig.getUserConfig().setMakeDnPath(false);
+        ExternalUser user = idp.getUser(TEST_USER1_UID);
+        assertNotNull("User 1 must exist", user);
+        assertNull("Intermediate path must be null", user.getIntermediatePath());
+    }
+
+    @Test
+    public void testSplitDNIntermediatePath() throws Exception {
+        providerConfig.getUserConfig().setMakeDnPath(true);
+        ExternalUser user = idp.getUser(TEST_USER1_UID);
+        assertNotNull("User 1 must exist", user);
+        assertEquals("Intermediate path must be the split dn", TEST_USER1_PATH, user.getIntermediatePath());
+    }
+
+    @Test
+    public void testSplitDNIntermediatePath2() throws Exception {
+        providerConfig.getUserConfig().setMakeDnPath(true);
+        ExternalUser user = idp.getUser(TEST_USER5_UID);
+        assertNotNull("User 5 must exist", user);
+        assertEquals("Intermediate path must be the split dn", TEST_USER5_PATH, user.getIntermediatePath());
+    }
+
+
+    public static void assertIfEquals(String message, String[] expected, Iterable<ExternalIdentityRef> result) {
+        List<String> dns = new LinkedList<String>();
+        for (ExternalIdentityRef ref: result) {
+            dns.add(ref.getId());
+        }
+        Collections.sort(dns);
+        Arrays.sort(expected);
+        String exp = Text.implode(expected, ",\n");
+        String res = Text.implode(dns.toArray(new String[dns.size()]), ",\n");
+        assertEquals(message, exp, res);
+    }
+
+}

Added: jackrabbit/oak/trunk/oak-auth-ldap/src/test/resources/org/apache/jackrabbit/oak/security/authentication/ldap/apache-ds-tutorial.ldif
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-auth-ldap/src/test/resources/org/apache/jackrabbit/oak/security/authentication/ldap/apache-ds-tutorial.ldif?rev=1568187&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-ldap/src/test/resources/org/apache/jackrabbit/oak/security/authentication/ldap/apache-ds-tutorial.ldif (added)
+++ jackrabbit/oak/trunk/oak-auth-ldap/src/test/resources/org/apache/jackrabbit/oak/security/authentication/ldap/apache-ds-tutorial.ldif Fri Feb 14 01:47:40 2014
@@ -0,0 +1,238 @@
+# Sample LDIF data for the ApacheDS v1.0 Basic User's Guide
+#
+# Some sailors and their ships
+# userpassword for all persons is "pass"
+#
+version: 1
+
+dn: ou=crews,ou=groups,ou=system
+objectclass: organizationalUnit
+objectclass: top
+description: Contains entries which describe ship crews
+ou: crews
+
+dn: ou=ranks,ou=groups,ou=system
+objectclass: organizationalUnit
+objectclass: top
+description: Contains entries which describe naval ranks (e.g. captain)
+ou: ranks
+
+# Super Rat
+# ---------
+dn: cn=Rat Ratterson,ou=users,ou=system
+objectclass: person
+objectclass: organizationalPerson
+objectclass: inetOrgPerson
+objectclass: top
+cn: Rat Ratterson
+description: Ship Rat
+givenname: Rat
+sn: Ratterson
+uid: ratty
+mail: ratty@royalnavy.mod.uk
+userpassword: {SHA}nU4eI71bcnBGqeO0t9tXvY1u5oQ=
+
+# Special Agent
+# ---------
+dn: cn=Special\, Agent [007],ou=users,ou=system
+objectclass: person
+objectclass: organizationalPerson
+objectclass: inetOrgPerson
+objectclass: top
+cn: Special, Agent [007]
+description: Special Characters in DN
+givenname: Special
+sn: Agent
+uid: =007=
+mail: oo7@mi6.gov
+userpassword: {SHA}nU4eI71bcnBGqeO0t9tXvY1u5oQ=
+
+# HMS Lydia Crew
+# --------------
+
+dn: cn=Horatio Hornblower,ou=users,ou=system
+objectclass: person
+objectclass: organizationalPerson
+objectclass: inetOrgPerson
+objectclass: top
+cn: Horatio Hornblower
+description: Capt. Horatio Hornblower, R.N
+givenname: Horatio
+sn: Hornblower
+uid: hhornblo
+mail: hhornblo@royalnavy.mod.uk
+userpassword: {SHA}nU4eI71bcnBGqeO0t9tXvY1u5oQ=
+
+dn: cn=William Bush,ou=users,ou=system
+objectclass: person
+objectclass: organizationalPerson
+objectclass: inetOrgPerson
+objectclass: top
+cn: William Bush
+description: Lt. William Bush
+givenname: William
+manager: cn=Horatio Hornblower,ou=users,ou=system
+sn: Bush
+uid: wbush
+mail: wbush@royalnavy.mod.uk
+userpassword: {SHA}nU4eI71bcnBGqeO0t9tXvY1u5oQ=
+
+dn: cn=Thomas Quist,ou=users,ou=system
+objectclass: person
+objectclass: organizationalPerson
+objectclass: inetOrgPerson
+objectclass: top
+cn: Thomas Quist
+description: Seaman Quist
+givenname: Thomas
+manager: cn=Horatio Hornblower,ou=users,ou=system
+sn: Quist
+uid: tquist
+mail: tquist@royalnavy.mod.uk
+userpassword: {SHA}nU4eI71bcnBGqeO0t9tXvY1u5oQ=
+
+dn: cn=Moultrie Crystal,ou=users,ou=system
+objectclass: person
+objectclass: organizationalPerson
+objectclass: inetOrgPerson
+objectclass: top
+cn: Moultrie Crystal
+description: Lt. Crystal
+givenname: Moultrie
+manager: cn=Horatio Hornblower,ou=users,ou=system
+sn: Crystal
+uid: mchrysta
+mail: mchrysta@royalnavy.mod.uk
+userpassword: {SHA}nU4eI71bcnBGqeO0t9tXvY1u5oQ=
+
+dn: cn=HMS Lydia,ou=crews,ou=groups,ou=system
+objectclass: groupOfUniqueNames
+objectclass: top
+cn: HMS Lydia
+uniquemember: cn=Horatio Hornblower,ou=users,ou=system
+uniquemember: cn=William Bush,ou=users,ou=system
+uniquemember: cn=Thomas Quist,ou=users,ou=system
+uniquemember: cn=Moultrie Crystal,ou=users,ou=system
+uniquemember: cn=Rat Ratterson,ou=users,ou=system
+
+# HMS Victory Crew
+# ----------------
+
+dn: cn=Horatio Nelson,ou=users,ou=system
+objectclass: person
+objectclass: organizationalPerson
+objectclass: inetOrgPerson
+objectclass: top
+cn: Horatio Nelson
+description: Lord Horatio Nelson
+givenname: Horatio
+sn: Nelson
+uid: hnelson
+mail: hnelson@royalnavy.mod.uk
+userpassword: {SHA}nU4eI71bcnBGqeO0t9tXvY1u5oQ=
+
+dn: cn=Thomas Masterman Hardy,ou=users,ou=system
+objectclass: person
+objectclass: organizationalPerson
+objectclass: inetOrgPerson
+objectclass: top
+cn: Thomas Masterman Hardy
+description: Sir Thomas Masterman Hardy
+givenname: Thomas
+manager: cn=Horatio Nelson,ou=users,ou=system
+sn: Hardy
+uid: thardy
+mail: thardy@royalnavy.mod.uk
+userpassword: {SHA}nU4eI71bcnBGqeO0t9tXvY1u5oQ=
+
+dn: cn=Cornelius Buckley,ou=users,ou=system
+objectclass: person
+objectclass: organizationalPerson
+objectclass: inetOrgPerson
+objectclass: top
+cn: Cornelius Buckley
+description: LM Cornelius Buckley
+givenname: Cornelius
+manager: cn=Horatio Nelson,ou=users,ou=system
+sn: Buckley
+uid: cbuckley
+mail: cbuckley@royalnavy.mod.uk
+userpassword: {SHA}nU4eI71bcnBGqeO0t9tXvY1u5oQ=
+
+dn: cn=HMS Victory,ou=crews,ou=groups,ou=system
+objectclass: groupOfUniqueNames
+objectclass: top
+cn: HMS Victory
+uniquemember: cn=Horatio Nelson,ou=users,ou=system
+uniquemember: cn=Thomas Masterman Hardy,ou=users,ou=system
+uniquemember: cn=Cornelius Buckley,ou=users,ou=system
+uniquemember: cn=Rat Ratterson,ou=users,ou=system
+
+# HMS Bounty Crew
+# ---------------
+
+dn: cn=William Bligh,ou=users,ou=system
+objectclass: person
+objectclass: organizationalPerson
+objectclass: inetOrgPerson
+objectclass: top
+cn: William Bligh
+description: Captain William Bligh
+givenname: William
+sn: Bligh
+uid: wbligh
+mail: wbligh@royalnavy.mod.uk
+userpassword: {SHA}nU4eI71bcnBGqeO0t9tXvY1u5oQ=
+
+dn: cn=Fletcher Christian,ou=users,ou=system
+objectclass: person
+objectclass: organizationalPerson
+objectclass: inetOrgPerson
+objectclass: top
+cn: Fletcher Christian
+description: Lieutenant Fletcher Christian
+givenname: Fletcher
+manager: cn=William Bligh,ou=users,ou=system
+sn: Christian
+uid: fchristi
+mail: fchristi@royalnavy.mod.uk
+userpassword: {SHA}nU4eI71bcnBGqeO0t9tXvY1u5oQ=
+
+dn: cn=John Fryer,ou=users,ou=system
+objectclass: person
+objectclass: organizationalPerson
+objectclass: inetOrgPerson
+objectclass: top
+cn: John Fryer
+description: Master John Fryer
+givenname: John
+manager: cn=William Bligh,ou=users,ou=system
+sn: Fryer
+uid: jfryer
+mail: jfryer@royalnavy.mod.uk
+userpassword: {SHA}nU4eI71bcnBGqeO0t9tXvY1u5oQ=
+
+dn: cn=John Hallett,ou=users,ou=system
+objectclass: person
+objectclass: organizationalPerson
+objectclass: inetOrgPerson
+objectclass: top
+cn: John Hallett
+description: Midshipman John Hallett
+givenname: John
+manager: cn=William Bligh,ou=users,ou=system
+sn: Hallett
+uid: jhallett
+mail: jhallett@royalnavy.mod.uk
+userpassword: {SHA}nU4eI71bcnBGqeO0t9tXvY1u5oQ=
+
+dn: cn=HMS Bounty,ou=crews,ou=groups,ou=system
+objectclass: groupOfUniqueNames
+objectclass: top
+cn: HMS Bounty
+uniquemember: cn=William Bligh,ou=users,ou=system
+uniquemember: cn=Fletcher Christian,ou=users,ou=system
+uniquemember: cn=John Fryer,ou=users,ou=system
+uniquemember: cn=John Hallett,ou=users,ou=system
+uniquemember: cn=Rat Ratterson,ou=users,ou=system
+



Mime
View raw message