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 an...@apache.org on 2016/03/15 17:57:30 UTC

svn commit: r1735141 - in /jackrabbit/oak/trunk: oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ o...

Author: angela
Date: Tue Mar 15 16:57:30 2016
New Revision: 1735141

URL: http://svn.apache.org/viewvc?rev=1735141&view=rev
Log:
OAK-3886 : Delegate supported Credentials types to ExternalIdentityProvider

Added:
    jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/CustomCredentialsSupportTest.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/credentials/
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/credentials/CredentialsSupport.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/credentials/SimpleCredentialsSupport.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/credentials/package-info.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/credentials/
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/credentials/SimpleCredentialsSupportTest.java
Modified:
    jackrabbit/oak/trunk/oak-auth-external/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/ExternalLoginModule.java
    jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/authentication.md
    jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/authentication/externalloginmodule.md

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=1735141&r1=1735140&r2=1735141&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 Tue Mar 15 16:57:30 2016
@@ -17,14 +17,13 @@
 package org.apache.jackrabbit.oak.spi.security.authentication.external.impl;
 
 import java.security.Principal;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
 
+import javax.annotation.CheckForNull;
 import javax.annotation.Nonnull;
 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;
@@ -41,11 +40,13 @@ import org.apache.jackrabbit.oak.spi.sec
 import org.apache.jackrabbit.oak.spi.security.authentication.AuthInfoImpl;
 import org.apache.jackrabbit.oak.spi.security.authentication.ImpersonationCredentials;
 import org.apache.jackrabbit.oak.spi.security.authentication.PreAuthenticatedLogin;
+import org.apache.jackrabbit.oak.spi.security.authentication.credentials.SimpleCredentialsSupport;
 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.ExternalIdentityRef;
 import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalUser;
+import org.apache.jackrabbit.oak.spi.security.authentication.credentials.CredentialsSupport;
 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;
@@ -57,7 +58,8 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
- * ExternalLoginModule implements a LoginModule that uses and external identity provider for login.
+ * {@code ExternalLoginModule} implements a {@code LoginModule} that uses an
+ * {@link ExternalIdentityProvider} for authentication.
  */
 public class ExternalLoginModule extends AbstractLoginModule {
 
@@ -79,6 +81,8 @@ public class ExternalLoginModule extends
 
     private SyncManager syncManager;
 
+    private CredentialsSupport credentialsSupport = SimpleCredentialsSupport.getInstance();
+
     /**
      * internal configuration when invoked from a factory rather than jaas
      */
@@ -121,8 +125,8 @@ public class ExternalLoginModule extends
 
     //--------------------------------------------------------< LoginModule >---
     @Override
-    public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> ss, Map<String, ?> opts) {
-        super.initialize(subject, callbackHandler, ss, opts);
+    public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState, Map<String, ?> opts) {
+        super.initialize(subject, callbackHandler, sharedState, opts);
 
         // merge options with osgi options if needed
         if (osgiConfig != null) {
@@ -168,6 +172,12 @@ public class ExternalLoginModule extends
                 }
             }
         }
+
+        if (idp instanceof CredentialsSupport) {
+            credentialsSupport = (CredentialsSupport) idp;
+        } else {
+            log.debug("No 'SupportedCredentials' configured. Using default implementation supporting 'SimpleCredentials'.");
+        }
     }
 
     @Override
@@ -178,18 +188,16 @@ public class ExternalLoginModule extends
         credentials = getCredentials();
 
         // check if we have a pre authenticated login from a previous login module
-        final String userId;
         final PreAuthenticatedLogin preAuthLogin = getSharedPreAuthLogin();
-        if (preAuthLogin != null) {
-            userId = preAuthLogin.getUserId();
-        } else {
-            userId = credentials instanceof SimpleCredentials ? ((SimpleCredentials) credentials).getUserID() : null;
-        }
+        final String userId = getUserId(preAuthLogin, credentials);
+
         if (userId == null && credentials == null) {
-            log.debug("No credentials found for external login module. ignoring.");
+            log.debug("No credentials|userId found for external login module. ignoring.");
             return false;
         }
 
+        // remember identification for log-output
+        Object logId = (userId != null) ? userId : credentials;
         try {
             SyncedIdentity sId = null;
             UserManager userMgr = getUserManager();
@@ -237,11 +245,7 @@ public class ExternalLoginModule extends
                 return true;
             } else {
                 if (log.isDebugEnabled()) {
-                    if (userId != null) {
-                        log.debug("IDP {} returned null for simple creds of {}", idp.getName(), userId);
-                    } else {
-                        log.debug("IDP {} returned null for {}", idp.getName(), credentials);
-                    }
+                    log.debug("IDP {} returned null for {}", idp.getName(), logId);
                 }
 
                 if (sId != null) {
@@ -252,16 +256,13 @@ public class ExternalLoginModule extends
                 return false;
             }
         } catch (ExternalIdentityException e) {
-            log.error("Error while authenticating '{}' with {}",
-                    userId == null ? credentials : userId, idp.getName(), e);
+            log.error("Error while authenticating '{}' with {}", logId, idp.getName(), e);
             return false;
         } catch (LoginException e) {
-            log.debug("IDP {} throws login exception for '{}': {}",
-                    idp.getName(), userId == null ? credentials : userId, e.getMessage());
+            log.debug("IDP {} throws login exception for '{}': {}", idp.getName(), logId, e.getMessage());
             throw e;
         } catch (Exception e) {
-            log.debug("SyncHandler {} throws sync exception for '{}'",
-                    syncHandler.getName(), userId == null ? credentials : userId, e);
+            log.debug("SyncHandler {} throws sync exception for '{}'", syncHandler.getName(), logId, e);
             LoginException le = new LoginException("Error while syncing user.");
             le.initCause(e);
             throw le;
@@ -299,6 +300,19 @@ public class ExternalLoginModule extends
         return true;
     }
 
+    //------------------------------------------------------------< private >---
+
+    @CheckForNull
+    private String getUserId(@CheckForNull PreAuthenticatedLogin preAuthLogin, @CheckForNull Credentials credentials) {
+        if (preAuthLogin != null) {
+            return preAuthLogin.getUserId();
+        } else if (credentials != null){
+            return credentialsSupport.getUserId(credentials);
+        } else {
+            return null;
+        }
+    }
+
     /**
      * Initiates synchronization of the external user.
      * @param user the external user
@@ -385,14 +399,11 @@ public class ExternalLoginModule extends
         Map<String, Object> attributes = new HashMap<String, Object>();
         Object shared = sharedState.get(SHARED_KEY_ATTRIBUTES);
         if (shared instanceof Map) {
-            for (Object key : ((Map) shared).keySet()) {
-                attributes.put(key.toString(), ((Map) shared).get(key));
-            }
-        } else if (creds instanceof SimpleCredentials) {
-            SimpleCredentials sc = (SimpleCredentials) creds;
-            for (String attrName : sc.getAttributeNames()) {
-                attributes.put(attrName, sc.getAttribute(attrName));
+            for (Map.Entry entry : ((Map<?,?>) shared).entrySet()) {
+                attributes.put(entry.getKey().toString(), entry.getValue());
             }
+        } else if (creds != null) {
+            attributes.putAll(credentialsSupport.getAttributes(creds));
         }
         return new AuthInfoImpl(userId, attributes, principals);
     }
@@ -407,22 +418,22 @@ public class ExternalLoginModule extends
     }
 
     /**
-     * @return An immutable set containing only the {@link SimpleCredentials} class.
+     * @return the set of credentials classes as exposed by the configured
+     * {@link CredentialsSupport} implementation.
      */
     @Nonnull
     @Override
     protected Set<Class> getSupportedCredentials() {
-        // TODO: delegate getSupportedCredentials to IDP (OAK-4000)
-        Class scClass = SimpleCredentials.class;
-        return Collections.singleton(scClass);
+        return credentialsSupport.getCredentialClasses();
     }
 
+    //----------------------------------------------< public setters (JAAS) >---
 
-    public void setSyncManager(SyncManager syncManager) {
+    public void setSyncManager(@Nonnull SyncManager syncManager) {
         this.syncManager = syncManager;
     }
 
-    public void setIdpManager(ExternalIdentityProviderManager idpManager) {
+    public void setIdpManager(@Nonnull ExternalIdentityProviderManager idpManager) {
         this.idpManager = idpManager;
     }
 }
\ No newline at end of file

Added: jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/CustomCredentialsSupportTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/CustomCredentialsSupportTest.java?rev=1735141&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/CustomCredentialsSupportTest.java (added)
+++ jackrabbit/oak/trunk/oak-auth-external/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/CustomCredentialsSupportTest.java Tue Mar 15 16:57:30 2016
@@ -0,0 +1,223 @@
+/*
+ * 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.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+import javax.jcr.Credentials;
+import javax.jcr.GuestCredentials;
+import javax.jcr.SimpleCredentials;
+import javax.security.auth.login.LoginException;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import org.apache.jackrabbit.oak.api.AuthInfo;
+import org.apache.jackrabbit.oak.api.ContentSession;
+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.ExternalIdentityProvider;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalIdentityRef;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalLoginModuleTestBase;
+import org.apache.jackrabbit.oak.spi.security.authentication.external.ExternalUser;
+import org.apache.jackrabbit.oak.spi.security.authentication.credentials.CredentialsSupport;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+/**
+ * Test login against the {@link ExternalLoginModule} with a setup that includes
+ * a custom implementation of {@link CredentialsSupport} and an {@link ExternalIdentityProvider}
+ * that deals with these supported credentials.
+ */
+public class CustomCredentialsSupportTest extends ExternalLoginModuleTestBase {
+
+    private final IDP idp = new IDP();
+
+    private static void assertAttributes(@Nonnull Map<String, ?> expected, @Nonnull AuthInfo info) {
+        assertEquals(expected.size(), info.getAttributeNames().length);
+        for (String aName : info.getAttributeNames()) {
+            assertEquals(expected.get(aName), info.getAttribute(aName));
+        }
+    }
+
+    @Test
+    public void testLogin() throws Exception {
+        TestCredentials creds = new TestCredentials("testUser");
+
+        ContentSession cs = login(creds);
+        try {
+            AuthInfo info = cs.getAuthInfo();
+            assertEquals("testUser", info.getUserID());
+            assertAttributes(idp.getAttributes(creds), info);
+        } finally {
+            cs.close();
+        }
+    }
+
+    @Test
+    public void testLoginWithUnsupportedCredentials() throws Exception {
+        List<Credentials> creds = ImmutableList.of(
+                new SimpleCredentials("testUser", new char[0]),
+                new GuestCredentials());
+
+        for (Credentials c : creds) {
+            try {
+                login(c).close();
+                fail("login must fail for credentials " + c);
+            } catch (LoginException e) {
+                // success
+            }
+        }
+    }
+
+    @Override
+    protected ExternalIdentityProvider createIDP() {
+        return idp;
+    }
+
+    @Override
+    protected void destroyIDP(ExternalIdentityProvider idp) {
+        // ignore
+    }
+
+    private static final class TestCredentials implements Credentials {
+
+        private final String uid;
+
+        private TestCredentials(@Nonnull String uid) {
+            this.uid = uid;
+        }
+    }
+
+    private static final class IDP implements ExternalIdentityProvider, CredentialsSupport {
+        @Nonnull
+        @Override
+        public String getName() {
+            return "creds_test";
+        }
+
+        @CheckForNull
+        @Override
+        public ExternalIdentity getIdentity(@Nonnull ExternalIdentityRef ref) {
+            throw new UnsupportedOperationException();
+        }
+
+        @CheckForNull
+        @Override
+        public ExternalUser getUser(@Nonnull String userId) {
+            throw new UnsupportedOperationException();
+        }
+
+        @CheckForNull
+        @Override
+        public ExternalUser authenticate(@Nonnull Credentials credentials) {
+            if (credentials instanceof TestCredentials) {
+                final String uid = ((TestCredentials) credentials).uid;
+                return new ExternalUser() {
+                    @Nonnull
+                    @Override
+                    public ExternalIdentityRef getExternalId() {
+                        return new ExternalIdentityRef(uid, "test");
+                    }
+
+                    @Nonnull
+                    @Override
+                    public String getId() {
+                        return uid;
+                    }
+
+                    @Nonnull
+                    @Override
+                    public String getPrincipalName() {
+                        return "principal" + uid;
+                    }
+
+                    @CheckForNull
+                    @Override
+                    public String getIntermediatePath() {
+                        return null;
+                    }
+
+                    @Nonnull
+                    @Override
+                    public Iterable<ExternalIdentityRef> getDeclaredGroups() {
+                        return Collections.emptySet();
+                    }
+
+                    @Nonnull
+                    @Override
+                    public Map<String, ?> getProperties() {
+                        return Collections.emptyMap();
+                    }
+                };
+            } else {
+                return null;
+            }
+        }
+
+        @CheckForNull
+        @Override
+        public ExternalGroup getGroup(@Nonnull String name) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Nonnull
+        @Override
+        public Iterator<ExternalUser> listUsers() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Nonnull
+        @Override
+        public Iterator<ExternalGroup> listGroups() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Nonnull
+        @Override
+        public Set<Class> getCredentialClasses() {
+            return ImmutableSet.<Class>of(TestCredentials.class);
+        }
+
+        @CheckForNull
+        @Override
+        public String getUserId(@Nonnull Credentials credentials) {
+            if (credentials instanceof TestCredentials) {
+                return ((TestCredentials) credentials).uid;
+            } else {
+                return null;
+            }
+        }
+
+        @Nonnull
+        @Override
+        public Map<String, ?> getAttributes(@Nonnull Credentials credentials) {
+            if (credentials instanceof TestCredentials) {
+                return ImmutableMap.of("a", "a");
+            } else {
+                return ImmutableMap.of();
+            }
+        }
+    }
+}
\ No newline at end of file

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/credentials/CredentialsSupport.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/credentials/CredentialsSupport.java?rev=1735141&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/credentials/CredentialsSupport.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/credentials/CredentialsSupport.java Tue Mar 15 16:57:30 2016
@@ -0,0 +1,61 @@
+/*
+ * 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.credentials;
+
+import java.util.Map;
+import java.util.Set;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+import javax.jcr.Credentials;
+
+/**
+ * Simple helper interface that allows to easily plug support for additional or
+ * custom{@link Credentials} implementations during external authentication.
+ */
+public interface CredentialsSupport {
+
+    /**
+     * Returns all {@link Credentials credentials} classes supported by this
+     * implemenation.
+     *
+     * @return the supported {@link Credentials credentials} classes.
+     */
+    @Nonnull
+    Set<Class> getCredentialClasses();
+
+    /**
+     * Retrieves the user identifier from the specified {@code Credentials}.
+     * If the specified credentials are not supported or don't contain any
+     * user id information this method will return {@code null}.
+     *
+     * @param credentials The credentials as passed to the repository login.
+     * @return The user id present in the given {@code Credentials} or {@code null}.
+     */
+    @CheckForNull
+    String getUserId(@Nonnull Credentials credentials);
+
+    /**
+     * Obtains the attributes as present with the specified {@code Credentials}.
+     * If the specified credentials are not supported or don't contain any
+     * attributes this method will return an empty {@code Map}.
+     *
+     * @param credentials The credentials as passed to the repository login.
+     * @return The credential attributes or an empty {@code Map}.
+     */
+    @Nonnull
+    Map<String, ?> getAttributes(@Nonnull Credentials credentials);
+}
\ No newline at end of file

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/credentials/SimpleCredentialsSupport.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/credentials/SimpleCredentialsSupport.java?rev=1735141&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/credentials/SimpleCredentialsSupport.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/credentials/SimpleCredentialsSupport.java Tue Mar 15 16:57:30 2016
@@ -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.credentials;
+
+import java.util.Collections;
+import java.util.Map;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.jcr.Credentials;
+import javax.jcr.SimpleCredentials;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+
+/**
+ * Implementation of the {@code SupportedCredentials} interface that handles
+ * {@link javax.jcr.SimpleCredentials}.
+ */
+public final class SimpleCredentialsSupport implements CredentialsSupport {
+
+    private static final SimpleCredentialsSupport INSTANCE = new SimpleCredentialsSupport();
+
+    private SimpleCredentialsSupport() {};
+
+    public static CredentialsSupport getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    @Nonnull
+    public ImmutableSet<Class> getCredentialClasses() {
+        return ImmutableSet.<Class>of(SimpleCredentials.class);
+    }
+
+    @Override
+    @CheckForNull
+    public String getUserId(@Nonnull Credentials credentials) {
+        if (credentials instanceof SimpleCredentials) {
+            return ((SimpleCredentials) credentials).getUserID();
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    @Nonnull
+    public Map<String, ?> getAttributes(@Nonnull Credentials credentials) {
+        if (credentials instanceof SimpleCredentials) {
+            final SimpleCredentials sc = (SimpleCredentials) credentials;
+            return Maps.asMap(ImmutableSet.copyOf(sc.getAttributeNames()), new Function<String, Object>() {
+                @Nullable
+                @Override
+                public Object apply(String input) {
+                    return sc.getAttribute(input);
+                }
+            });
+        } else {
+            return Collections.emptyMap();
+        }
+    }
+}
\ No newline at end of file

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/credentials/package-info.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/credentials/package-info.java?rev=1735141&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/credentials/package-info.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/credentials/package-info.java Tue Mar 15 16:57:30 2016
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+@Version("1.0.0")
+package org.apache.jackrabbit.oak.spi.security.authentication.credentials;
+
+import aQute.bnd.annotation.Version;
\ No newline at end of file

Added: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/credentials/SimpleCredentialsSupportTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/credentials/SimpleCredentialsSupportTest.java?rev=1735141&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/credentials/SimpleCredentialsSupportTest.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/spi/security/authentication/credentials/SimpleCredentialsSupportTest.java Tue Mar 15 16:57:30 2016
@@ -0,0 +1,75 @@
+/*
+ * 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.credentials;
+
+import java.util.Map;
+import java.util.Set;
+import javax.jcr.Credentials;
+import javax.jcr.SimpleCredentials;
+
+import com.google.common.collect.ImmutableMap;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+public class SimpleCredentialsSupportTest {
+
+    private final CredentialsSupport credentialsSupport = SimpleCredentialsSupport.getInstance();
+
+    @Test
+    public void testGetCredentialClasses() {
+        Set<Class> supported = credentialsSupport.getCredentialClasses();
+
+        assertNotNull(supported);
+        assertEquals(1, supported.size());
+        assertEquals(SimpleCredentials.class, supported.iterator().next());
+    }
+
+    @Test
+    public void testGetUserId() {
+        assertNull(credentialsSupport.getUserId(new TestCredentials()));
+        assertNull(credentialsSupport.getUserId(new SimpleCredentials(null, new char[0])));
+        assertEquals("uid", credentialsSupport.getUserId(new SimpleCredentials("uid", new char[0])));
+    }
+
+    @Test
+    public void testGetAttributes() {
+        Map<String, ?> attributes = credentialsSupport.getAttributes(new TestCredentials());
+        assertNotNull(attributes);
+        assertTrue(attributes.isEmpty());
+
+        SimpleCredentials sc = new SimpleCredentials("uid", new char[0]);
+        attributes = credentialsSupport.getAttributes(sc);
+        assertNotNull(attributes);
+        assertTrue(attributes.isEmpty());
+
+        Map<String, ?> expected = ImmutableMap.of("a", "a", "b", Boolean.TRUE, "c", new TestCredentials());
+        for (Map.Entry<String, ?> entry : expected.entrySet()) {
+            sc.setAttribute(entry.getKey(), entry.getValue());
+        }
+
+        attributes = credentialsSupport.getAttributes(sc);
+        assertNotNull(attributes);
+        assertEquals(3, attributes.size());
+        assertEquals(expected, attributes);
+    }
+
+    private static final class TestCredentials implements Credentials {}
+}
\ No newline at end of file

Modified: jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/authentication.md
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/authentication.md?rev=1735141&r1=1735140&r2=1735141&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/authentication.md (original)
+++ jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/authentication.md Tue Mar 15 16:57:30 2016
@@ -76,7 +76,7 @@ LoginModule is configured and succeeds,
 LoginModule need to have succeeded for the overall authentication to succeed. If no Required or Requisite LoginModules 
 are configured for an application, then at least one Sufficient or Optional LoginModule must succeed.
 
-<a href="jcr_api"/>
+<a name="jcr_api"/>
 ### JCR API
 
 Within the scope of JCR `Repository.login` is used to authenticate a given user.
@@ -104,7 +104,7 @@ for further details.
 In addition JCR defines `Session.impersonate(Credentials)` to impersonate another
 user or - as of JSR 333 -  clone an existing session.
 
-<a href="oak_api"/>
+<a name="oak_api"/>
 ### Oak API
 
 The Oak API contains the following authentication related methods and interfaces
@@ -138,7 +138,7 @@ security related interfaces (e.g. `Princ
 
 Subclasses are required to implement the following methods:
 
-- `getSupportedCredentials()`: return a set of supported credential classes.
+- `getSupportedCredentials()`: return a set of supported credential classes. See also section [Supported Credentials](#supported_credentials)
 - `login()`: The login method defined by `LoginModule`
 - `commit()`: The commit method defined by `LoginModule`
 
@@ -185,7 +185,17 @@ Subclasses are required to implement the
         }
     }
 
-<a href="default_implementation"/>
+<a name="supported_credentials"/>
+#### Supported Credentials
+
+Since Oak 1.5.1 the extensions additionally contain a dedicated interface that
+eases the support for different `Credentials` in the package space 
+`org.apache.jackrabbit.oak.spi.security.authentication.credentials`:
+                                                   
+- [CredentialsSupport]: Interface definition exposing the set of supported `Credentials` classes and some common utility methods.
+- [SimpleCredentialsSupport]: Default implementation for the widely used `SimpleCredentials`
+
+<a name="default_implementation"/>
 ### Oak Authentication Implementation
 
 A description of the various requirements covered by Oak by default as well
@@ -251,4 +261,6 @@ implementation on various levels:
 [AuthInfo]: /oak/docs/apidocs/org/apache/jackrabbit/oak/api/AuthInfo.html
 [AbstractLoginModule]: /oak/docs/apidocs/org/apache/jackrabbit/oak/spi/security/authentication/AbstractLoginModule.html
 [AuthenticationConfiguration]: /oak/docs/apidocs/org/apache/jackrabbit/oak/spi/security/authentication/AuthenticationConfiguration.html
-[JAAS config]: http://docs.oracle.com/javase/6/docs/technotes/guides/security/jaas/JAASRefGuide.html
\ No newline at end of file
+[JAAS config]: http://docs.oracle.com/javase/6/docs/technotes/guides/security/jaas/JAASRefGuide.html
+[CredentialsSupport]: /oak/docs/apidocs/org/apache/jackrabbit/oak/spi/security/authentication/credentials/CredentialsSupport.html
+[SimpleCredentialsSupport]: /oak/docs/apidocs/org/apache/jackrabbit/oak/spi/security/authentication/credentials/SimpleCredentialsSupport.html

Modified: jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/authentication/externalloginmodule.md
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/authentication/externalloginmodule.md?rev=1735141&r1=1735140&r2=1735141&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/authentication/externalloginmodule.md (original)
+++ jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/authentication/externalloginmodule.md Tue Mar 15 16:57:30 2016
@@ -70,9 +70,13 @@ Note:
 
 ##### Supported Credentials
 
-Currently this login module supports the following credentials:
-
-- `SimpleCredentials`
+As of Oak 1.5.1 the `ExternalLoginModule` can deal for any kind of `Credentials`
+implementations. By default (i.e. unless configured otherwise) the module supports
+`SimpleCredentials` and thus behaves backwards compatible to previous versions.
+
+Additional/other credentials can be supported by providing an `ExternalIdentityProvider` 
+that additionally implements the [CredentialsSupport] interface.
+See section [Pluggability](#pluggability) for instructions and an example.
 
 ##### Authentication in Detail 
 
@@ -162,6 +166,82 @@ The default implementations ([ExternalID
 extend `AbstractServiceTracker` and will automatically keep track of 
 new [ExternalIdentityProvider] and [SyncHandler] services, respectively.
 
+Since Oak 1.5.1 support for different or multiple types of `Credentials` can easily
+be plugged by providing an [ExternalIdentityProvider] that additionally implements 
+[CredentialsSupport]. This is an optional extension point for each IDP; if 
+missing the `ExternalLoginModule` will fall back to a default implementation and 
+assume the IDP only supports `SimpleCredentials`. See details below.
+ 
+#### Supported Credentials
+ 
+The following steps are required in order to change or extend the set credential 
+classes supported by the `ExternalLoginModule`:
+
+- Extend your `ExternalIdentityProvider` to additionally implement the [CredentialsSupport] interface.
+
+Don't forget to make sure that `ExternalIdentityProvider.authenticate(Credentials)` 
+handles the same set of supported credentials!
+
+##### Examples
+ 
+###### Example CredentialsSupport
+
+      @Component()
+      @Service(ExternalIdentityProvider.class, CredentialsSupport.class)
+      public class MyIdentityProvider implements ExternalIdentityProvider, CredentialsSupport {
+    
+          public MyCredentialsSupport() {}
+    
+          //-----------------------------------------< CredentialsSupport >---
+          @Nonnull
+          @Override
+          public Set<Class> getCredentialClasses() {
+              return ImmutableSet.<Class>of(MyCredentials.class);
+          }
+  
+          @CheckForNull
+          @Override
+          public String getUserId(@Nonnull Credentials credentials) {
+              if (credentials instanceof MyCredentials) {
+                  return ((MyCredentials) credentials).getID();
+              } else {
+                  return null;
+              }
+          }
+  
+          @Nonnull
+          @Override
+          public Map<String, ?> getAttributes(@Nonnull Credentials credentials) {
+              // our credentials never contain additional attributes
+              return ImmutableMap.of();
+          }
+          
+          //-------------------------------------< ExternalIdentityProvider >---
+          
+          @CheckForNull
+          @Override
+          public ExternalUser authenticate(@Nonnull Credentials credentials) {
+              if (credentials instanceof MyCredentials) {
+                  MyCredentials mc = (MyCredentials) credentials;
+                  if (internalAuthenticate(mc)) {
+                      return new MyExternalUser(mc.getID());
+                  } else {
+                      throw new LoginException();
+                  }
+              } else {
+                  return null;
+              }
+          }
+    
+          [...]
+          
+          //----------------------------------------------< SCR Integration >---
+          @Activate
+          private void activate() {
+              // TODO
+          }
+      }
+
 <!-- references -->
 [DefaultSyncConfig]: /oak/docs/apidocs/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/DefaultSyncConfig.html
 [ExternalIdentityProvider]: /oak/docs/apidocs/org/apache/jackrabbit/oak/spi/security/authentication/external/ExternalIdentityProvider.html
@@ -173,3 +253,4 @@ new [ExternalIdentityProvider] and [Sync
 [SyncHandler]: /oak/docs/apidocs/org/apache/jackrabbit/oak/spi/security/authentication/external/SyncHandler.html
 [SyncManager]: /oak/docs/apidocs/org/apache/jackrabbit/oak/spi/security/authentication/external/SyncManager.html
 [SyncManagerImpl]: /oak/docs/apidocs/org/apache/jackrabbit/oak/spi/security/authentication/external/impl/SyncManagerImpl.html
+[CredentialsSupport]: /oak/docs/apidocs/org/apache/jackrabbit/oak/spi/security/authentication/credentials/CredentialsSupport.html