You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by tr...@apache.org on 2014/02/11 01:28:51 UTC

svn commit: r1566895 [1/2] - in /jackrabbit/oak/trunk: ./ oak-auth-external/ oak-auth-external/src/ oak-auth-external/src/main/ oak-auth-external/src/main/java/ oak-auth-external/src/main/java/org/ oak-auth-external/src/main/java/org/apache/ oak-auth-e...

Author: tripod
Date: Tue Feb 11 00:28:49 2014
New Revision: 1566895

URL: http://svn.apache.org/r1566895
Log:
Moving external login module to own bundle

Added:
    jackrabbit/oak/trunk/oak-auth-external/
    jackrabbit/oak/trunk/oak-auth-external/README.md
    jackrabbit/oak/trunk/oak-auth-external/pom.xml   (with props)
    jackrabbit/oak/trunk/oak-auth-external/src/
    jackrabbit/oak/trunk/oak-auth-external/src/main/
    jackrabbit/oak/trunk/oak-auth-external/src/main/java/
    jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/
    jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/
    jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/
    jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/
    jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/
    jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/
    jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/
    jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/
    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/ExternalIdentityException.java
    jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalIdentityProvider.java
    jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalIdentityProviderManager.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/SyncContext.java
    jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/SyncException.java
    jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/SyncHandler.java
    jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/SyncManager.java
    jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/
    jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DefaultSyncConfig.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/ExternalIDPManagerImpl.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/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModuleFactory.java
    jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/SyncManagerImpl.java
    jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/package-info.java
    jackrabbit/oak/trunk/oak-auth-external/src/test/
    jackrabbit/oak/trunk/oak-auth-external/src/test/java/
    jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/
    jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/
    jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/
    jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/
    jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/
    jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/
    jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/
    jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/
    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/ExternalLoginModuleTestBase.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-external/src/test/resources/
    jackrabbit/oak/trunk/oak-auth-external/src/test/resources/META-INF/
    jackrabbit/oak/trunk/oak-auth-external/src/test/resources/META-INF/services/
    jackrabbit/oak/trunk/oak-auth-external/src/test/resources/META-INF/services/org.apache.jackrabbit.mk.test.MicroKernelFixture
    jackrabbit/oak/trunk/oak-auth-external/src/test/resources/logback-test.xml
    jackrabbit/oak/trunk/oak-auth-external/src/test/resources/logging.properties
Removed:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/
Modified:
    jackrabbit/oak/trunk/oak-auth-ldap/README.md
    jackrabbit/oak/trunk/oak-auth-ldap/pom.xml
    jackrabbit/oak/trunk/oak-core/pom.xml
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/SecurityProviderImpl.java
    jackrabbit/oak/trunk/pom.xml

Added: jackrabbit/oak/trunk/oak-auth-external/README.md
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-auth-external/README.md?rev=1566895&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/README.md (added)
+++ jackrabbit/oak/trunk/oak-auth-external/README.md Tue Feb 11 00:28:49 2014
@@ -0,0 +1,25 @@
+Oak External Authentication Support
+===================================
+
+License
+-------
+
+(see the top-level [LICENSE.txt](../LICENSE.txt) for full license details)
+
+Collective work: Copyright 2012 The Apache Software Foundation.
+
+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.
+

Added: jackrabbit/oak/trunk/oak-auth-external/pom.xml
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-auth-external/pom.xml?rev=1566895&view=auto
==============================================================================
Binary file - no diff available.

Propchange: jackrabbit/oak/trunk/oak-auth-external/pom.xml
------------------------------------------------------------------------------
    svn:mime-type = application/xml

Added: 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=1566895&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalGroup.java (added)
+++ jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalGroup.java Tue Feb 11 00:28:49 2014
@@ -0,0 +1,24 @@
+/*
+ * 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.spi.security.authentication.external;
+
+/**
+ * ExternalGroup defines a group that is provided by an external system.
+ */
+public interface ExternalGroup extends ExternalIdentity {
+
+}
\ No newline at end of file

Added: 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=1566895&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalIdentity.java (added)
+++ jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalIdentity.java Tue Feb 11 00:28:49 2014
@@ -0,0 +1,77 @@
+/*
+ * 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.spi.security.authentication.external;
+
+import java.security.Principal;
+import java.util.Map;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+
+/**
+ * {@code ExternalIdentity} defines an identity provided by an external system.
+ */
+public interface ExternalIdentity {
+
+    /**
+     * Returns the id of this identity as used in the external system.
+     * @return the external id.
+     */
+    @Nonnull
+    ExternalIdentityRef getExternalId();
+
+    /**
+     * Returns the local id of this identity as it would be used in this repository. This usually corresponds to
+     * {@link org.apache.jackrabbit.api.security.user.Authorizable#getID()}
+     *
+     * @return the internal id.
+     */
+    @Nonnull
+    String getId();
+
+    /**
+     * Returns the principal name of this identity. This usually corresponds to
+     * {@link org.apache.jackrabbit.api.security.user.Authorizable#getPrincipal()}.{@link java.security.Principal#getName()}
+     * @return the principal name
+     */
+    @Nonnull
+    String getPrincipalName();
+
+    /**
+     * 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.
+     *
+     * @return the intermediate path or {@code null} or empty.
+     */
+    @CheckForNull
+    String getIntermediatePath();
+
+    /**
+     * Returns an iterable of the declared groups of this external identity.
+     * @return the declared groups
+     */
+    @Nonnull
+    Iterable<? extends ExternalIdentityRef> getGroups();
+
+    /**
+     * Returns a map of properties of this external identity.
+     * @return the properties
+     */
+    @Nonnull
+    Map<String, ?> getProperties();
+
+}
\ No newline at end of file

Added: jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalIdentityException.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalIdentityException.java?rev=1566895&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalIdentityException.java (added)
+++ jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalIdentityException.java Tue Feb 11 00:28:49 2014
@@ -0,0 +1,38 @@
+/*
+ * 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.spi.security.authentication.external;
+
+/**
+ * {@code ExternalIdentityException} is used to notify about errors when dealing with external identities.
+ */
+public class ExternalIdentityException extends Exception {
+
+    public ExternalIdentityException() {
+    }
+
+    public ExternalIdentityException(String message) {
+        super(message);
+    }
+
+    public ExternalIdentityException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public ExternalIdentityException(Throwable cause) {
+        super(cause);
+    }
+}
\ No newline at end of file

Added: jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalIdentityProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalIdentityProvider.java?rev=1566895&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalIdentityProvider.java (added)
+++ jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalIdentityProvider.java Tue Feb 11 00:28:49 2014
@@ -0,0 +1,81 @@
+/*
+ * 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.spi.security.authentication.external;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+import javax.jcr.Credentials;
+import javax.security.auth.login.LoginException;
+
+/**
+ * {@code ExternalIdentityProvider} defines an interface to an external system that provides users and groups that
+ * can be synced with local ones.
+ */
+public interface ExternalIdentityProvider {
+
+    /**
+     * Returns the name of this provider.
+     * @return the provider name.
+     */
+    @Nonnull
+    String getName();
+
+    /**
+     * Returns the identity for the given reference or {@code null} if it does not exist. The provider should check if
+     * the {@link ExternalIdentityRef#getProviderName() provider name} matches his own name or is {@code null} and
+     * should not return a foreign identity.
+     *
+     * @param ref the reference
+     * @return an identity or {@code null}
+     *
+     * @throws ExternalIdentityException if an error occurs.
+     */
+    @CheckForNull
+    ExternalIdentity getIdentity(@Nonnull ExternalIdentityRef ref) throws ExternalIdentityException;
+
+    /**
+     * Returns the user for the given (local) id. if the user does not exist {@code null} is returned.
+     * @param userId the user id.
+     * @return the user or {@code null}
+     *
+     * @throws ExternalIdentityException if an error occurs.
+     */
+    @CheckForNull
+    ExternalUser getUser(@Nonnull String userId) throws ExternalIdentityException;
+
+    /**
+     * Authenticates the user represented by the given credentials and returns it. If the user does not exist in this
+     * provider, {@code null} is returned. If the authentication fails, a LoginException is thrown.
+     *
+     * @param credentials the credentials
+     * @return the user or {@code null}
+     * @throws ExternalIdentityException if an error occurs
+     * @throws javax.security.auth.login.LoginException if the user could not be authenticated
+     */
+    @CheckForNull
+    ExternalUser authenticate(@Nonnull Credentials credentials) throws ExternalIdentityException, LoginException;
+
+    /**
+     * Returns the group for the given (local) group name. if the group does not exist {@code null} is returned.
+     * @param name the group name
+     * @return the group or {@code null}
+     *
+     * @throws ExternalIdentityException if an error occurs.
+     */
+    @CheckForNull
+    ExternalGroup getGroup(@Nonnull String name) throws ExternalIdentityException;
+}
\ No newline at end of file

Added: jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalIdentityProviderManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalIdentityProviderManager.java?rev=1566895&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalIdentityProviderManager.java (added)
+++ jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalIdentityProviderManager.java Tue Feb 11 00:28:49 2014
@@ -0,0 +1,37 @@
+/*
+ * 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.spi.security.authentication.external;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+
+/**
+ * The external identity provider management.
+ *
+ * The default manager is registered as OSGi service and can also be retrieved via
+ * {@link org.apache.jackrabbit.oak.spi.security.SecurityProvider#getConfiguration(Class)}
+ */
+public interface ExternalIdentityProviderManager {
+
+    /**
+     * Returns the registered identity provider with the given name.
+     * @param name the provider name
+     * @return the registered provider or {@code null}
+     */
+    @CheckForNull
+    ExternalIdentityProvider getProvider(@Nonnull String name);
+}
\ No newline at end of file

Added: 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=1566895&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalIdentityRef.java (added)
+++ jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalIdentityRef.java Tue Feb 11 00:28:49 2014
@@ -0,0 +1,121 @@
+/*
+ * 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.spi.security.authentication.external;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+
+import org.apache.jackrabbit.util.Text;
+
+/**
+ * {@code ExternalIdentityRef} defines a reference to an external identity.
+ */
+public class ExternalIdentityRef {
+
+    private final String id;
+
+    private final String providerName;
+
+    private final String string;
+
+    /**
+     * Creates a new external identity ref with the given id and provider name
+     * @param id the id of the identity.
+     * @param providerName the name of the identity provider
+     */
+    public ExternalIdentityRef(@Nonnull String id, @CheckForNull String providerName) {
+        this.id = id;
+        this.providerName = providerName;
+
+        StringBuilder b = new StringBuilder();
+        b.append(Text.escape(id));
+        if (providerName != null && providerName.length() > 0) {
+            b.append('@').append(Text.escape(providerName));
+        }
+        string =  b.toString();
+    }
+
+    /**
+     * Returns the name of the identity provider.
+     * @return the name of the identity provider.
+     */
+    @CheckForNull
+    public String getProviderName() {
+        return providerName;
+    }
+
+    /**
+     * Returns the id of the external identity. for example the DN of an LDAP user.
+     * @return the id
+     */
+    @Nonnull
+    public String getId() {
+        return id;
+    }
+
+    /**
+     * Returns a string representation of this external identity reference
+     * @return a string representation.
+     */
+    @Nonnull
+    public String getString() {
+        return string;
+    }
+
+    /**
+     * Creates an external identity reference from a string representation.
+     * @param str the string
+     * @return the reference
+     */
+    public static ExternalIdentityRef fromString(@Nonnull String str) {
+        int idx = str.indexOf('@');
+        if (idx < 0) {
+            return new ExternalIdentityRef(Text.unescape(str), null);
+        } else {
+            return new ExternalIdentityRef(
+                    Text.unescape(str.substring(0, idx)),
+                    Text.unescape(str.substring(idx+1))
+            );
+        }
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder("ExternalIdentityRef{");
+        sb.append("id='").append(id).append('\'');
+        sb.append(", providerName='").append(providerName).append('\'');
+        sb.append('}');
+        return sb.toString();
+    }
+
+    @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;
+    }
+
+    @Override
+    public int hashCode() {
+        return string.hashCode();
+    }
+}
\ No newline at end of file

Added: 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=1566895&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalUser.java (added)
+++ jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalUser.java Tue Feb 11 00:28:49 2014
@@ -0,0 +1,34 @@
+/*
+ * 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.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

Added: jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/SyncContext.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/SyncContext.java?rev=1566895&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/SyncContext.java (added)
+++ jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/SyncContext.java Tue Feb 11 00:28:49 2014
@@ -0,0 +1,42 @@
+/*
+ * 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.spi.security.authentication.external;
+
+import javax.annotation.Nonnull;
+
+/**
+ * {@code SyncContext} is used as scope for sync operations. Implementations are free to associate any resources with
+ * the sync context. The sync context must not be accessed concurrently and must be closed after use.
+ */
+public interface SyncContext {
+
+    /**
+     * Synchronizes an external identity with the repository based on the respective configuration.
+     *
+     * @param identity the identity to sync.
+     * @return {@code true} if the given identity was synced; {@code false} if for no change.
+     * @throws SyncException if an error occurrs
+     */
+    boolean sync(@Nonnull ExternalIdentity identity) throws SyncException;
+
+    /**
+     * Closes this context and releases any resources bound to it. Note that an implementation must not commit the
+     * {@link org.apache.jackrabbit.oak.api.Root} passed during the creation call. This is the responsibility of the
+     * application.
+     */
+    void close();
+}
\ No newline at end of file

Added: jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/SyncException.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/SyncException.java?rev=1566895&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/SyncException.java (added)
+++ jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/SyncException.java Tue Feb 11 00:28:49 2014
@@ -0,0 +1,35 @@
+/*
+ * 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.spi.security.authentication.external;
+
+/**
+ * SyncException... TODO
+ */
+public class SyncException extends Exception {
+
+    public SyncException(String s) {
+        super(s);
+    }
+
+    public SyncException(Throwable throwable) {
+        super(throwable);
+    }
+
+    public SyncException(String s, Throwable throwable) {
+        super(s, throwable);
+    }
+}
\ No newline at end of file

Added: jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/SyncHandler.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/SyncHandler.java?rev=1566895&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/SyncHandler.java (added)
+++ jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/SyncHandler.java Tue Feb 11 00:28:49 2014
@@ -0,0 +1,58 @@
+/*
+ * 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.spi.security.authentication.external;
+
+import javax.annotation.Nonnull;
+
+import org.apache.jackrabbit.api.security.user.UserManager;
+import org.apache.jackrabbit.oak.api.Root;
+
+/**
+ * SyncHandler is used to sync users and groups from an {@link ExternalIdentityProvider}.
+ * The synchronization performed within the scope of a {@link SyncContext} which is acquired during the
+ * {@link #createContext(ExternalIdentityProvider, org.apache.jackrabbit.api.security.user.UserManager, org.apache.jackrabbit.oak.api.Root)} call.
+ *
+ * The exact configuration is managed by the sync handler instance. The system may contain several sync handler
+ * implementations with different configurations. those are managed by the {@link SyncManager}.
+ *
+ * @see org.apache.jackrabbit.oak.spi.security.authentication.external.SyncContext
+ * @see org.apache.jackrabbit.oak.spi.security.authentication.external.SyncManager
+ */
+public interface SyncHandler {
+
+    /**
+     * Returns the name of this sync handler.
+     * @return sync handler name
+     */
+    @Nonnull
+    String getName();
+
+    /**
+     * Initializes a sync context which is used to start the sync operations.
+     *
+     * @param idp the external identity provider used for syncing
+     * @param userManager user manager for managing authorizables
+     * @param root root of the current tree
+     * @return the sync context
+     * @throws SyncException if an error occurs
+     */
+    @Nonnull
+    SyncContext createContext(@Nonnull ExternalIdentityProvider idp,
+                              @Nonnull UserManager userManager,
+                              @Nonnull Root root) throws SyncException;
+
+}
\ No newline at end of file

Added: jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/SyncManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/SyncManager.java?rev=1566895&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/SyncManager.java (added)
+++ jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/SyncManager.java Tue Feb 11 00:28:49 2014
@@ -0,0 +1,37 @@
+/*
+ * 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.spi.security.authentication.external;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+
+/**
+ * The external identity synchronization management.
+ *
+ * The default manager is registered as OSGi service and can also be retrieved via
+ * {@link org.apache.jackrabbit.oak.spi.security.SecurityProvider#getConfiguration(Class)}
+ */
+public interface SyncManager {
+
+    /**
+     * Returns the sync handler with the given name.
+     * @param name the name of the sync handler or {@code null}
+     * @return the sync handler
+     */
+    @CheckForNull
+    SyncHandler getSyncHandler(@Nonnull String name);
+}
\ No newline at end of file

Added: jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DefaultSyncConfig.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/DefaultSyncConfig.java?rev=1566895&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DefaultSyncConfig.java (added)
+++ jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DefaultSyncConfig.java Tue Feb 11 00:28:49 2014
@@ -0,0 +1,485 @@
+/*
+ * 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.spi.security.authentication.external.impl;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import javax.annotation.Nonnull;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters;
+
+/**
+ * {@code DefaultSyncConfig} defines how users and groups from an external source are synced into the repository using
+ * the {@link DefaultSyncHandler}.
+ */
+@Component(
+        label = "Apache Jackrabbit Oak Default Sync Handler",
+        name = "org.apache.jackrabbit.oak.spi.security.authentication.external.impl.DefaultSyncHandler",
+        configurationFactory = true,
+        metatype = true,
+        ds = false
+)
+public class DefaultSyncConfig {
+
+    /**
+     * @see #getName()
+     */
+    public static final String PARAM_NAME_DEFAULT = "default";
+
+    /**
+     * @see #getName()
+     */
+    @Property(
+            label = "Sync Handler Name",
+            description = "Name of this sync configuration. This is used to reference this handler by the login modules.",
+            value = PARAM_NAME_DEFAULT
+    )
+    public static final String PARAM_NAME = "handler.name";
+
+    /**
+     * @see DefaultSyncConfig.User#getExpirationTime()
+     */
+    public static final long PARAM_USER_EXPIRATION_TIME_DEFAULT = 3600*1000;
+
+    /**
+     * @see DefaultSyncConfig.User#getExpirationTime()
+     */
+    @Property(
+            label = "User Expiration Time",
+            description = "Duration in milliseconds until a synced user gets expired.",
+            longValue = PARAM_USER_EXPIRATION_TIME_DEFAULT
+    )
+    public static final String PARAM_USER_EXPIRATION_TIME = "user.expirationTime";
+
+    /**
+     * @see DefaultSyncConfig.User#getAutoMembership()
+     */
+    public static final String[] PARAM_USER_AUTO_MEMBERSHIP_DEFAULT = {};
+
+    /**
+     * @see DefaultSyncConfig.User#getAutoMembership()
+     */
+    @Property(
+            label = "User auto membership",
+            description = "List of groups that a synced user is added to automatically",
+            value = {},
+            cardinality = Integer.MAX_VALUE
+    )
+    public static final String PARAM_USER_AUTO_MEMBERSHIP = "user.autoMembership";
+
+    /**
+     * @see DefaultSyncConfig.User#getPropertyMapping()
+     */
+    public static final String[] PARAM_USER_PROPERTY_MAPPING_DEFAULT = {"rep:fullname=cn"};
+
+    /**
+     * @see DefaultSyncConfig.User#getPropertyMapping()
+     */
+    @Property(
+            label = "User property mapping",
+            description = "List mapping definition of local properties from external ones. eg: 'profile/email=mail'." +
+                    "Use double quotes for fixed values. eg: 'profile/nt:primaryType=\"nt:unstructured\"",
+            value = {"rep:fullname=cn"},
+            cardinality = Integer.MAX_VALUE
+    )
+    public static final String PARAM_USER_PROPERTY_MAPPING = "user.propertyMapping";
+
+    /**
+     * @see DefaultSyncConfig.User#getPathPrefix()
+     */
+    public static final String PARAM_USER_PATH_PREFIX_DEFAULT = "";
+
+    /**
+     * @see DefaultSyncConfig.User#getPathPrefix()
+     */
+    @Property(
+            label = "User Path Prefix",
+            description = "The path prefix used when creating new users.",
+            value = PARAM_USER_PATH_PREFIX_DEFAULT
+    )
+    public static final String PARAM_USER_PATH_PREFIX = "user.pathPrefix";
+
+    /**
+     * @see DefaultSyncConfig.User#getMembershipExpirationTime()
+     */
+    public static final long PARAM_USER_MEMBERSHIP_EXPIRATION_TIME_DEFAULT = 3600*1000;
+
+    /**
+     * @see DefaultSyncConfig.User#getMembershipExpirationTime()
+     */
+    @Property(
+            label = "User Membership Expiration",
+            description = "Time in milliseconds after which membership expires.",
+            longValue = PARAM_USER_MEMBERSHIP_EXPIRATION_TIME_DEFAULT
+    )
+    public static final String PARAM_USER_MEMBERSHIP_EXPIRATION_TIME = "user.membershipExpTime";
+
+    /**
+     * @see User#getMembershipNestingDepth()
+     */
+    public static final int PARAM_USER_MEMBERSHIP_NESTING_DEPTH_DEFAULT = 0;
+
+    /**
+     * @see User#getMembershipNestingDepth()
+     */
+    @Property(
+            label = "User membership nesting depth",
+            description = "Returns the maximum depth of group nesting when membership relations are synced. " +
+                    "A value of 0 effectively disables group membership lookup. A value of 1 only adds the direct " +
+                    "groups of a user. This value has no effect when syncing individual groups only when syncing a " +
+                    "users membership ancestry.",
+            intValue = PARAM_USER_MEMBERSHIP_NESTING_DEPTH_DEFAULT
+    )
+    public static final String PARAM_USER_MEMBERSHIP_NESTING_DEPTH = "user.membershipNestingDepth";
+
+    /**
+     * @see DefaultSyncConfig.Group#getExpirationTime()
+     */
+    public static final long PARAM_GROUP_EXPIRATION_TIME_DEFAULT = 24*3600*1000;
+
+    /**
+     * @see DefaultSyncConfig.Group#getExpirationTime()
+     */
+    @Property(
+            label = "Group Expiration Time",
+            description = "Duration in milliseconds until a synced group expires.",
+            longValue = PARAM_GROUP_EXPIRATION_TIME_DEFAULT
+    )
+    public static final String PARAM_GROUP_EXPIRATION_TIME = "group.expirationTime";
+
+    /**
+     * @see DefaultSyncConfig.Group#getAutoMembership()
+     */
+    public static final String[] PARAM_GROUP_AUTO_MEMBERSHIP_DEFAULT = {};
+
+    /**
+     * @see DefaultSyncConfig.Group#getAutoMembership()
+     */
+    @Property(
+            label = "Group auto membership",
+            description = "List of groups that a synced group is added to automatically",
+            value = {},
+            cardinality = Integer.MAX_VALUE
+    )
+    public static final String PARAM_GROUP_AUTO_MEMBERSHIP = "group.autoMembership";
+
+    /**
+     * @see DefaultSyncConfig.Group#getPropertyMapping()
+     */
+    public static final String[] PARAM_GROUP_PROPERTY_MAPPING_DEFAULT = {};
+
+    /**
+     * @see DefaultSyncConfig.Group#getPropertyMapping()
+     */
+    @Property(
+            label = "Group property mapping",
+            description = "List mapping definition of local properties from external ones.",
+            value = {},
+            cardinality = Integer.MAX_VALUE
+    )
+    public static final String PARAM_GROUP_PROPERTY_MAPPING = "group.propertyMapping";
+
+    /**
+     * @see DefaultSyncConfig.Group#getPathPrefix()
+     */
+    public static final String PARAM_GROUP_PATH_PREFIX_DEFAULT = "";
+
+    /**
+     * @see DefaultSyncConfig.Group#getPathPrefix()
+     */
+    @Property(
+            label = "Group Path Prefix",
+            description = "The path prefix used when creating new groups.",
+            value = PARAM_GROUP_PATH_PREFIX_DEFAULT
+    )
+    public static final String PARAM_GROUP_PATH_PREFIX = "group.pathPrefix";
+
+    /**
+     * Base config class for users and groups
+     */
+    public abstract static class Authorizable {
+
+        private long expirationTime;
+
+        private Set<String> autoMembership;
+
+        private Map<String, String> propertyMapping;
+
+        private String pathPrefix;
+
+        /**
+         * Returns the duration in milliseconds until a synced authorizable gets expired. An expired authorizable will
+         * be re-synced.
+         * @return the expiration time in milliseconds.
+         */
+        public long getExpirationTime() {
+            return expirationTime;
+        }
+
+        /**
+         * Sets the expiration time.
+         * @param expirationTime time in milliseconds.
+         * @return {@code this}
+         * @see #getExpirationTime()
+         */
+        @Nonnull
+        public Authorizable setExpirationTime(long expirationTime) {
+            this.expirationTime = expirationTime;
+            return this;
+        }
+
+        /**
+         * Defines the set of group names that are automatically added to synced authorizable.
+         * @return set of group names.
+         */
+        @Nonnull
+        public Set<String> getAutoMembership() {
+            return autoMembership;
+        }
+
+        /**
+         * Sets the auto membership
+         * @param autoMembership the membership
+         * @return {@code this}
+         * @see #getAutoMembership()
+         */
+        @Nonnull
+        public Authorizable setAutoMembership(String ... autoMembership) {
+            this.autoMembership = new HashSet<String>(Arrays.asList(autoMembership));
+            return this;
+        }
+
+        /**
+         * Defines the mapping of internal property names from external values. Only the external properties defined as
+         * keys of this map are synced with the mapped internal properties. note that the property names can be relative
+         * paths. the intermediate nodes will be created accordingly.
+         *
+         * Example:
+         * <xmp>
+         *     {
+         *         "rep:fullname": "cn",
+         *         "country", "c",
+         *         "profile/email": "mail",
+         *         "profile/givenName": "cn"
+         *     }
+         * </xmp>
+         *
+         * The implicit properties like userid, groupname, password must not be mapped.
+         *
+         * @return the property mapping where the keys are the local property names and the values the external ones.
+         */
+        @Nonnull
+        public Map<String, String> getPropertyMapping() {
+            return propertyMapping;
+        }
+
+        /**
+         * Sets the property mapping.
+         * @param propertyMapping the mapping
+         * @return {@code this}
+         * @see #getPropertyMapping()
+         */
+        @Nonnull
+        public Authorizable setPropertyMapping(Map<String, String> propertyMapping) {
+            this.propertyMapping = propertyMapping;
+            return this;
+        }
+
+        /**
+         * Defines the authorizables intermediate path prefix that is used when creating new authorizables. This prefix
+         * is always prepended to the path provided by the {@link org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalIdentity}.
+         * @return the intermediate path prefix.
+         */
+        @Nonnull
+        public String getPathPrefix() {
+            return pathPrefix;
+        }
+
+        /**
+         * Sets the path prefix.
+         * @param pathPrefix the path prefix.
+         * @return {@code this}
+         * @see #getPathPrefix()
+         */
+        @Nonnull
+        public Authorizable setPathPrefix(String pathPrefix) {
+            this.pathPrefix = pathPrefix == null ? "" : pathPrefix;
+            return this;
+        }
+    }
+
+    /**
+     * User specific config.
+     */
+    public static class User extends Authorizable {
+
+        private long membershipExpirationTime;
+
+        private long membershipNestingDepth;
+
+        /**
+         * Returns the duration in milliseconds until the group membership of a user is expired. If the
+         * membership information is expired it is re-synced according to the maximum nesting depth.
+         * Note that the membership is the groups an authorizable is member of, not the list of members of a group.
+         * Also note, that the group membership expiration time can be higher than the user expiration time itself and
+         * that value has no effect when syncing individual groups only when syncing a users membership ancestry.
+         *
+         * @return the expiration time in milliseconds.
+         */
+        public long getMembershipExpirationTime() {
+            return membershipExpirationTime;
+        }
+
+        /**
+         * Sets the membership expiration time
+         * @param membershipExpirationTime the time in milliseconds.
+         * @return {@code this}
+         * @see #getMembershipExpirationTime()
+         */
+        @Nonnull
+        public User setMembershipExpirationTime(long membershipExpirationTime) {
+            this.membershipExpirationTime = membershipExpirationTime;
+            return this;
+        }
+
+        /**
+         * Returns the maximum depth of group nesting when membership relations are synced. A value of 0 effectively
+         * disables group membership lookup. A value of 1 only adds the direct groups of a user. This value has no effect
+         * when syncing individual groups only when syncing a users membership ancestry.
+         * @return the group nesting depth
+         */
+        public long getMembershipNestingDepth() {
+            return membershipNestingDepth;
+        }
+
+        /**
+         * Sets the group nesting depth.
+         * @param membershipNestingDepth the depth.
+         * @return {@code this}
+         * @see #getMembershipNestingDepth()
+         */
+        @Nonnull
+        public User setMembershipNestingDepth(long membershipNestingDepth) {
+            this.membershipNestingDepth = membershipNestingDepth;
+            return this;
+        }
+
+    }
+
+    /**
+     * Group specific config
+     */
+    public static class Group extends Authorizable {
+
+    }
+
+    /**
+     * Creates a new LDAP provider configuration based on the properties store in the given parameters.
+     * @param params the configuration parameters.
+     * @return the config
+     */
+    public static DefaultSyncConfig of(ConfigurationParameters params) {
+        DefaultSyncConfig cfg = new DefaultSyncConfig()
+                .setName(params.getConfigValue(PARAM_NAME, PARAM_NAME_DEFAULT));
+
+        cfg.user()
+                .setMembershipExpirationTime(params.getConfigValue(PARAM_USER_MEMBERSHIP_EXPIRATION_TIME, PARAM_USER_MEMBERSHIP_EXPIRATION_TIME_DEFAULT))
+                .setMembershipNestingDepth(params.getConfigValue(PARAM_USER_MEMBERSHIP_NESTING_DEPTH, PARAM_USER_MEMBERSHIP_NESTING_DEPTH_DEFAULT))
+                .setExpirationTime(params.getConfigValue(PARAM_USER_EXPIRATION_TIME, PARAM_USER_EXPIRATION_TIME_DEFAULT))
+                .setPathPrefix(params.getConfigValue(PARAM_USER_PATH_PREFIX, PARAM_USER_PATH_PREFIX_DEFAULT))
+                .setAutoMembership(params.getConfigValue(PARAM_USER_AUTO_MEMBERSHIP, PARAM_USER_AUTO_MEMBERSHIP_DEFAULT))
+                .setPropertyMapping(createMapping(
+                        params.getConfigValue(PARAM_USER_PROPERTY_MAPPING, PARAM_USER_PROPERTY_MAPPING_DEFAULT)));
+
+        cfg.group()
+                .setExpirationTime(params.getConfigValue(PARAM_GROUP_EXPIRATION_TIME, PARAM_GROUP_EXPIRATION_TIME_DEFAULT))
+                .setPathPrefix(params.getConfigValue(PARAM_GROUP_PATH_PREFIX, PARAM_GROUP_PATH_PREFIX_DEFAULT))
+                .setAutoMembership(params.getConfigValue(PARAM_GROUP_AUTO_MEMBERSHIP, PARAM_GROUP_AUTO_MEMBERSHIP_DEFAULT))
+                .setPropertyMapping(createMapping(
+                        params.getConfigValue(PARAM_GROUP_PROPERTY_MAPPING, PARAM_GROUP_PROPERTY_MAPPING_DEFAULT)));
+
+        return cfg;
+    }
+
+    /**
+     * Creates a new property mapping map from a list of patterns.
+     * @param patterns the patterns
+     * @return the mapping map
+     */
+    private static Map<String, String> createMapping(String[] patterns) {
+        Map<String, String> mapping = new HashMap<String, String>();
+        for (String pattern: patterns) {
+            int idx = pattern.indexOf('=');
+            if (idx > 0) {
+                String relPath = pattern.substring(0, idx).trim();
+                String value = pattern.substring(idx+1).trim();
+                mapping.put(relPath, value);
+            }
+        }
+        return mapping;
+    }
+
+    private String name = PARAM_NAME_DEFAULT;
+
+    private final User user = new User();
+
+    private final Group group = new Group();
+
+    /**
+     * Configures the name of this configuration
+     * @return the name
+     */
+    @Nonnull
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Sets the name
+     * @param name the name
+     * @return {@code this}
+     * @see #getName()
+     */
+    public DefaultSyncConfig setName(@Nonnull String name) {
+        this.name = name;
+        return this;
+    }
+
+    /**
+     * Returns the sync configuration for users.
+     * @return the user sync configuration.
+     */
+    public User user() {
+        return user;
+    }
+
+    /**
+     * Returns the sync configuration for groups.
+     * @return the group sync configuration.
+     */
+    public Group group() {
+        return group;
+    }
+
+}
\ No newline at end of file

Added: 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=1566895&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DefaultSyncHandler.java (added)
+++ jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DefaultSyncHandler.java Tue Feb 11 00:28:49 2014
@@ -0,0 +1,310 @@
+/*
+ * 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.spi.security.authentication.external.impl;
+
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+import javax.jcr.ValueFactory;
+import javax.jcr.ValueFormatException;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.ConfigurationPolicy;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.jackrabbit.api.security.user.Authorizable;
+import org.apache.jackrabbit.api.security.user.Group;
+import org.apache.jackrabbit.api.security.user.User;
+import org.apache.jackrabbit.api.security.user.UserManager;
+import org.apache.jackrabbit.oak.api.Root;
+import org.apache.jackrabbit.oak.namepath.NamePathMapper;
+import org.apache.jackrabbit.oak.plugins.value.ValueFactoryImpl;
+import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters;
+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.oak.spi.security.authentication.external.SyncContext;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.SyncException;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.SyncHandler;
+import org.apache.jackrabbit.oak.spi.security.principal.PrincipalImpl;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * {@code DefaultSyncHandler} implements an sync handler that synchronizes users and groups from an external identity
+ * provider with the repository users.
+ * <p/>
+ * Please refer to {@link DefaultSyncConfig} for configuration options.
+ */
+@Component(
+        // note that the metatype information is generated from DefaultSyncConfig
+        policy = ConfigurationPolicy.REQUIRE
+)
+@Service
+public class DefaultSyncHandler implements SyncHandler {
+
+    /**
+     * logger instance
+     */
+    private static final Logger log = LoggerFactory.getLogger(DefaultSyncHandler.class);
+
+    /**
+     * internal configuration
+     */
+    private DefaultSyncConfig config;
+
+    /**
+     * Default constructor for OSGi
+     */
+    public DefaultSyncHandler() {
+    }
+
+    /**
+     * Constructor for non-OSGi cases.
+     *
+     * @param config the configuration
+     */
+    public DefaultSyncHandler(DefaultSyncConfig config) {
+        this.config = config;
+    }
+
+    @Activate
+    private void activate(Map<String, Object> properties) {
+        ConfigurationParameters cfg = ConfigurationParameters.of(properties);
+        config = DefaultSyncConfig.of(cfg);
+    }
+
+    @Nonnull
+    @Override
+    public String getName() {
+        return config.getName();
+    }
+
+    @Nonnull
+    @Override
+    public SyncContext createContext(@Nonnull ExternalIdentityProvider idp, @Nonnull UserManager userManager, @Nonnull Root root) throws SyncException {
+        return new ContextImpl(idp, userManager, root);
+    }
+
+    private class ContextImpl implements SyncContext {
+
+        private final ExternalIdentityProvider idp;
+
+        private final UserManager userManager;
+
+        private final Root root;
+
+        private final ValueFactory valueFactory;
+
+        private ContextImpl(ExternalIdentityProvider idp, UserManager userManager, Root root) {
+            this.idp = idp;
+            this.userManager = userManager;
+            this.root = root;
+            valueFactory = new ValueFactoryImpl(root, NamePathMapper.DEFAULT);
+        }
+
+        @Override
+        public void close() {
+            // nothing to do
+        }
+
+        @Override
+        public boolean sync(@Nonnull ExternalIdentity identity) throws SyncException {
+            try {
+                if (identity instanceof ExternalUser) {
+                    User user = getUser(identity);
+                    if (user == null) {
+                        createUser((ExternalUser) identity);
+                    } else {
+                        updateUser((ExternalUser) identity, user);
+                    }
+                    return true;
+                } else if (identity instanceof ExternalGroup) {
+                    // todo
+                    return false;
+
+                } else {
+                    throw new IllegalArgumentException("identity must be user or group but was: " + identity);
+                }
+            } catch (RepositoryException e) {
+                throw new SyncException(e);
+            } catch (ExternalIdentityException e) {
+                throw new SyncException(e);
+            }
+        }
+
+        @CheckForNull
+        private User getUser(@Nonnull ExternalIdentity externalUser) throws RepositoryException {
+            Authorizable authorizable = userManager.getAuthorizable(externalUser.getId());
+            if (authorizable == null) {
+                authorizable = userManager.getAuthorizable(externalUser.getPrincipalName());
+            }
+            if (authorizable == null) {
+                return null;
+            } else if (authorizable instanceof User) {
+                return (User) authorizable;
+            } else {
+                // TODO: deal with colliding authorizable that is group.
+                log.warn("unexpected authorizable: {}", authorizable);
+                return null;
+            }
+        }
+
+        @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,
+                    principal,
+                    concatPaths(config.user().getPathPrefix(), externalUser.getIntermediatePath())
+            );
+            syncAuthorizable(externalUser, user);
+            return user;
+        }
+
+        @CheckForNull
+        private Group createGroup(ExternalGroup externalGroup)
+                throws RepositoryException, SyncException, ExternalIdentityException {
+            Principal principal = new PrincipalImpl(externalGroup.getPrincipalName());
+            Group group = userManager.createGroup(
+                    externalGroup.getId(),
+                    principal,
+                    concatPaths(config.user().getPathPrefix(), externalGroup.getIntermediatePath()));
+            syncAuthorizable(externalGroup, group);
+            return group;
+        }
+
+        private void updateUser(ExternalUser externalUser, User user)
+                throws RepositoryException, SyncException, ExternalIdentityException {
+            syncAuthorizable(externalUser, user);
+        }
+
+        private void syncAuthorizable(ExternalIdentity externalUser, Authorizable authorizable)
+                throws RepositoryException, SyncException, ExternalIdentityException {
+            for (ExternalIdentityRef externalGroupRef : externalUser.getGroups()) {
+                ExternalIdentity id = idp.getIdentity(externalGroupRef);
+                if (id instanceof ExternalGroup) {
+                    ExternalGroup externalGroup = (ExternalGroup) id;
+                    String groupId = externalGroup.getId();
+                    Group group;
+                    Authorizable a = userManager.getAuthorizable(groupId);
+                    if (a == null) {
+                        group = createGroup(externalGroup);
+                    } else {
+                        group = (a.isGroup()) ? (Group) a : null;
+                    }
+
+                    if (group != null) {
+                        group.addMember(authorizable);
+                    } else {
+                        log.debug("No such group " + groupId + "; Ignoring group membership.");
+                    }
+                }
+            }
+
+            Map<String, ?> properties = externalUser.getProperties();
+            for (String key : properties.keySet()) {
+                Object prop = properties.get(key);
+                if (prop instanceof Collection) {
+                    Value[] values = createValues((Collection) prop);
+                    if (values != null) {
+                        authorizable.setProperty(key, values);
+                    }
+                } else {
+                    Value value = createValue(prop);
+                    if (value != null) {
+                        authorizable.setProperty(key, value);
+                    }
+                }
+            }
+        }
+
+        @CheckForNull
+        private Value createValue(Object propValue) throws ValueFormatException {
+            int type = getType(propValue);
+            if (type == PropertyType.UNDEFINED) {
+                return null;
+            } else {
+                return valueFactory.createValue(propValue.toString(), type);
+            }
+        }
+
+        @CheckForNull
+        private Value[] createValues(Collection<?> propValues) throws ValueFormatException {
+            List<Value> values = new ArrayList<Value>();
+            for (Object obj : propValues) {
+                Value v = createValue(obj);
+                if (v != null) {
+                    values.add(v);
+                }
+            }
+            return values.toArray(new Value[values.size()]);
+        }
+
+        private int getType(Object propValue) {
+            // TODO: add proper type detection
+            if (propValue == null) {
+                return PropertyType.UNDEFINED;
+            } else {
+                return PropertyType.STRING;
+            }
+        }
+
+    }
+
+    /**
+     * Robust relative path concatenation.
+     * @param paths relative paths
+     * @return the concatenated path
+     */
+    private static String concatPaths(String ... paths) {
+        StringBuilder result = new StringBuilder();
+        for (String path: paths) {
+            if (path != null && !path.isEmpty()) {
+                int i0 = 0;
+                int i1 = path.length();
+                while (i0 < i1 && path.charAt(i0) == '/') {
+                    i0++;
+                }
+                while (i1 > i0 && path.charAt(i1-1) == '/') {
+                    i1--;
+                }
+                if (i1 > i0) {
+                    if (result.length() > 0) {
+                        result.append('/');
+                    }
+                    result.append(path.substring(i0, i1));
+                }
+            }
+        }
+        return result.length() == 0 ? null : result.toString();
+    }
+}
\ No newline at end of file

Added: jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalIDPManagerImpl.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/ExternalIDPManagerImpl.java?rev=1566895&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalIDPManagerImpl.java (added)
+++ jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalIDPManagerImpl.java Tue Feb 11 00:28:49 2014
@@ -0,0 +1,76 @@
+/*
+ * 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.spi.security.authentication.external.impl;
+
+import javax.annotation.Nonnull;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.jackrabbit.oak.osgi.OsgiWhiteboard;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalIdentityProvider;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalIdentityProviderManager;
+import org.apache.jackrabbit.oak.spi.whiteboard.AbstractServiceTracker;
+import org.apache.jackrabbit.oak.spi.whiteboard.Whiteboard;
+import org.osgi.service.component.ComponentContext;
+
+/**
+ * {@code ExternalIDPManagerImpl} is used to manage registered external identity provider. This class automatically
+ * tracks the IDPs that are registered via OSGi but can also be used in non-OSGi environments by manually adding and
+ * removing the providers.
+ */
+@Component(immediate = true)
+@Service
+public class ExternalIDPManagerImpl extends AbstractServiceTracker<ExternalIdentityProvider> implements ExternalIdentityProviderManager {
+
+    /**
+     * Default constructor used by OSGi
+     */
+    public ExternalIDPManagerImpl() {
+        super(ExternalIdentityProvider.class);
+    }
+
+    /**
+     * Constructor used by non OSGi
+     * @param whiteboard the whiteboard
+     */
+    public ExternalIDPManagerImpl(Whiteboard whiteboard) {
+        super(ExternalIdentityProvider.class);
+        start(whiteboard);
+    }
+
+    @Activate
+    private void activate(ComponentContext ctx) {
+        start(new OsgiWhiteboard(ctx.getBundleContext()));
+    }
+
+    @Deactivate
+    private void deactivate() {
+        stop();
+    }
+
+    @Override
+    public ExternalIdentityProvider getProvider(@Nonnull String name) {
+        for (ExternalIdentityProvider provider: getServices()) {
+            if (name.equals(provider.getName())) {
+                return provider;
+            }
+        }
+        return null;
+    }
+}
\ No newline at end of file

Added: 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=1566895&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModule.java (added)
+++ jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModule.java Tue Feb 11 00:28:49 2014
@@ -0,0 +1,253 @@
+/*
+ * 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.spi.security.authentication.external.impl;
+
+import java.security.Principal;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+import javax.jcr.Credentials;
+import javax.jcr.SimpleCredentials;
+import javax.security.auth.Subject;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.login.LoginException;
+
+import org.apache.jackrabbit.api.security.user.UserManager;
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.api.Root;
+import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters;
+import org.apache.jackrabbit.oak.spi.security.authentication.AbstractLoginModule;
+import org.apache.jackrabbit.oak.spi.security.authentication.AuthInfoImpl;
+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.ExternalIdentityProviderManager;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalUser;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.SyncContext;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.SyncException;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.SyncHandler;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.SyncManager;
+import org.apache.jackrabbit.oak.spi.whiteboard.Whiteboard;
+import org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * ExternalLoginModule implements a LoginModule that uses and external identity provider for login.
+ */
+public class ExternalLoginModule extends AbstractLoginModule {
+
+    private static final Logger log = LoggerFactory.getLogger(ExternalLoginModule.class);
+
+    /**
+     * Name of the parameter that configures the name of the external identity provider.
+     */
+    public static final String PARAM_IDP_NAME = "idp.name";
+
+    /**
+     * Name of the parameter that configures the name of the synchronization handler.
+     */
+    public static final String PARAM_SYNC_HANDLER_NAME = "sync.handlerName";
+
+    /**
+     * internal configuration when invoked from a factory rather than jaas
+     */
+    private ConfigurationParameters osgiConfig;
+
+    /**
+     * The external identity provider as specified by the {@link #PARAM_IDP_NAME}
+     */
+    private ExternalIdentityProvider idp;
+
+    /**
+     * The configured sync handler as specified by the {@link #PARAM_SYNC_HANDLER_NAME}
+     */
+    private SyncHandler syncHandler;
+
+    /**
+     * The external user as resolved in the login call.
+     */
+    private ExternalUser externalUser;
+
+    /**
+     * Login credentials
+     */
+    private Credentials credentials;
+
+    /**
+     * Default constructor for the OSGIi LoginModuleFactory case and the default non-OSGi JAAS case.
+     */
+    public ExternalLoginModule() {
+    }
+
+    /**
+     * Creates a new ExternalLoginModule with the given OSGi config.
+     * @param osgiConfig the config
+     */
+    public ExternalLoginModule(ConfigurationParameters osgiConfig) {
+        this.osgiConfig = osgiConfig;
+    }
+
+    @Override
+    public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> ss, Map<String, ?> opts) {
+        super.initialize(subject, callbackHandler, ss, opts);
+
+        // merge options with osgi options if needed
+        if (osgiConfig != null) {
+            options = ConfigurationParameters.of(osgiConfig, options);
+        }
+
+        Whiteboard whiteboard = getSecurityProvider().getConfiguration(Whiteboard.class);
+
+        String idpName = options.getConfigValue(PARAM_IDP_NAME, "");
+        if (idpName.length() == 0) {
+            log.error("External login module needs IPD name. Will not be used for login.");
+        } else {
+            ExternalIdentityProviderManager idpMgr = WhiteboardUtils.getService(whiteboard, ExternalIdentityProviderManager.class);
+            if (idpMgr == null) {
+                log.error("External login module needs IDPManager. Will not be used for login.");
+            } else {
+                idp = idpMgr.getProvider(idpName);
+                if (idp == null) {
+                    log.error("No IDP found with name {}. Will not be used for login.", idpName);
+                }
+            }
+        }
+
+        String syncHandlerName = options.getConfigValue(PARAM_SYNC_HANDLER_NAME, "");
+        if (syncHandlerName.length() == 0) {
+            log.error("External login module needs SyncHandler name. Will not be used for login.");
+        } else {
+            SyncManager syncMgr = WhiteboardUtils.getService(whiteboard, SyncManager.class);
+            if (syncMgr == null) {
+                log.error("External login module needs SyncManager. Will not be used for login.");
+            } else {
+                syncHandler = syncMgr.getSyncHandler(syncHandlerName);
+                if (syncHandler == null) {
+                    log.error("No SyncHandler found with name {}. Will not be used for login.", syncHandlerName);
+                }
+            }
+        }
+    }
+
+    @Override
+    public boolean login() throws LoginException {
+        if (idp == null || syncHandler == null) {
+            return false;
+        }
+
+        credentials = getCredentials();
+        if (credentials == null) {
+            log.debug("No credentials found for external login module. ignoring.");
+            return false;
+        }
+
+        try {
+            externalUser = idp.authenticate(credentials);
+            if (externalUser != null) {
+                if (log.isDebugEnabled()) {
+                    log.debug("IDP {} returned valid user {}", idp.getName(), externalUser);
+                }
+
+                //noinspection unchecked
+                sharedState.put(SHARED_KEY_CREDENTIALS, credentials);
+
+                //noinspection unchecked
+                sharedState.put(SHARED_KEY_LOGIN_NAME, externalUser.getId());
+
+                return true;
+            } else {
+                if (log.isDebugEnabled()) {
+                    if (credentials instanceof SimpleCredentials) {
+                        log.debug("IDP {} returned null for simple creds of {}", idp.getName(), ((SimpleCredentials) credentials).getUserID());
+                    } else {
+                        log.debug("IDP {} returned null for {}", idp.getName(), credentials);
+                    }
+                }
+            }
+        } catch (ExternalIdentityException e) {
+            log.error("Error while authenticating credentials {} with {}: {}", new Object[]{
+                    credentials, idp.getName(), e.toString()});
+            return false;
+        } catch (LoginException e) {
+            if (log.isDebugEnabled()) {
+                log.debug("IDP {} throws login exception for {}", idp.getName(), credentials);
+            }
+            throw e;
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @return An immutable set containing only the {@link SimpleCredentials} class.
+     */
+    @Override
+    protected Set<Class> getSupportedCredentials() {
+        // todo: maybe delegate getSupportedCredentials to IDP
+        Class scClass = SimpleCredentials.class;
+        return Collections.singleton(scClass);
+    }
+
+    @Override
+    public boolean commit() throws LoginException {
+        if (externalUser == null || syncHandler == null) {
+            return false;
+        }
+
+        SyncContext context = null;
+        try {
+            Root root = getRoot();
+            UserManager userManager = getUserManager();
+            if (root == null || userManager == null) {
+                throw new LoginException("Cannot synchronize user.");
+            }
+            context = syncHandler.createContext(idp, userManager, root);
+            context.sync(externalUser);
+            root.commit();
+
+            Set<? extends Principal> principals = getPrincipals(externalUser.getId());
+            if (!principals.isEmpty()) {
+                if (!subject.isReadOnly()) {
+                    subject.getPrincipals().addAll(principals);
+                    subject.getPublicCredentials().add(credentials);
+                    setAuthInfo(new AuthInfoImpl(externalUser.getId(), null, principals), subject);
+                } else {
+                    log.debug("Could not add information to read only subject {}", subject);
+                }
+                return true;
+            }
+            return false;
+        } catch (SyncException e) {
+            throw new LoginException("User synchronization failed: " + e);
+        } catch (CommitFailedException e) {
+            throw new LoginException("User synchronization failed: " + e);
+        } finally {
+            if (context != null) {
+                context.close();
+            }
+        }
+    }
+
+    @Override
+    protected void clearState() {
+        super.clearState();
+        externalUser = null;
+        credentials = null;
+    }
+}
\ No newline at end of file