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 2012/07/24 15:47:00 UTC

svn commit: r1365052 - in /jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak: api/ security/authentication/ spi/security/authentication/

Author: angela
Date: Tue Jul 24 13:47:00 2012
New Revision: 1365052

URL: http://svn.apache.org/viewvc?rev=1365052&view=rev
Log:
OAK-91 : Implement Authentication Support  (work in progress)

Added:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/AbstractLoginModule.java
Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/api/AuthInfo.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/AuthInfoImpl.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/GuestLoginModule.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/LoginModuleImpl.java

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/api/AuthInfo.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/api/AuthInfo.java?rev=1365052&r1=1365051&r2=1365052&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/api/AuthInfo.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/api/AuthInfo.java Tue Jul 24 13:47:00 2012
@@ -16,6 +16,9 @@
  */
 package org.apache.jackrabbit.oak.api;
 
+import java.security.Principal;
+import java.util.Collections;
+import java.util.Set;
 import javax.annotation.CheckForNull;
 import javax.annotation.Nonnull;
 
@@ -32,6 +35,7 @@ public interface AuthInfo {
             return null;
         }
 
+        @Nonnull
         @Override
         public String[] getAttributeNames() {
             return new String[0];
@@ -41,6 +45,11 @@ public interface AuthInfo {
         public Object getAttribute(String attributeName) {
             return null;
         }
+
+        @Override
+        public Set<Principal> getPrincipals() {
+            return Collections.emptySet();
+        }
     };
 
     /**
@@ -71,4 +80,11 @@ public interface AuthInfo {
      */
     @CheckForNull
     Object getAttribute(String attributeName);
+
+    /**
+     * Returns the set of principals associated with this {@code AuthInfo} instance.
+     *
+     * @return A set of principals.
+     */
+    Set<Principal> getPrincipals();
 }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/AuthInfoImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/AuthInfoImpl.java?rev=1365052&r1=1365051&r2=1365052&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/AuthInfoImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/AuthInfoImpl.java Tue Jul 24 13:47:00 2012
@@ -22,6 +22,7 @@ import java.security.Principal;
 import java.util.Collections;
 import java.util.Map;
 import java.util.Set;
+import javax.annotation.Nonnull;
 
 /**
  * AuthInfoImpl... TODO
@@ -35,11 +36,7 @@ public class AuthInfoImpl implements Aut
     public AuthInfoImpl(String userID, Map<String, ?> attributes, Set<Principal> principals) {
         this.userID = userID;
         this.attributes = (attributes == null) ? Collections.<String, Object>emptyMap() : attributes;
-        this.principals = principals;
-    }
-
-    Set<Principal> getPrincipals() {
-        return principals;
+        this.principals = Collections.unmodifiableSet(principals);
     }
 
     //-----------------------------------------------------------< AuthInfo >---
@@ -48,6 +45,7 @@ public class AuthInfoImpl implements Aut
         return userID;
     }
 
+    @Nonnull
     @Override
     public String[] getAttributeNames() {
         return attributes.keySet().toArray(new String[attributes.size()]);
@@ -57,4 +55,9 @@ public class AuthInfoImpl implements Aut
     public Object getAttribute(String attributeName) {
         return attributes.get(attributeName);
     }
+
+    @Override
+    public Set<Principal> getPrincipals() {
+        return principals;
+    }
 }
\ No newline at end of file

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/GuestLoginModule.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/GuestLoginModule.java?rev=1365052&r1=1365051&r2=1365052&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/GuestLoginModule.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/GuestLoginModule.java Tue Jul 24 13:47:00 2012
@@ -16,11 +16,8 @@
  */
 package org.apache.jackrabbit.oak.security.authentication;
 
-import org.apache.jackrabbit.oak.spi.security.authentication.CredentialsCallback;
-import org.apache.jackrabbit.oak.spi.security.principal.EveryonePrincipal;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
+import java.io.IOException;
+import java.util.Map;
 import javax.jcr.Credentials;
 import javax.jcr.GuestCredentials;
 import javax.security.auth.Subject;
@@ -29,10 +26,12 @@ import javax.security.auth.callback.Call
 import javax.security.auth.callback.UnsupportedCallbackException;
 import javax.security.auth.login.LoginException;
 import javax.security.auth.spi.LoginModule;
-import java.io.IOException;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
+
+import org.apache.jackrabbit.oak.spi.security.authentication.AbstractLoginModule;
+import org.apache.jackrabbit.oak.spi.security.authentication.CredentialsCallback;
+import org.apache.jackrabbit.oak.spi.security.principal.EveryonePrincipal;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * The {@code GuestLoginModule} is intended to provide backwards compatibility
@@ -54,10 +53,11 @@ import java.util.Set;
  *     instead of failing to obtain any credentials.</li>
  * </ol>
  *
- * Note however that this implementation does not populate the subject during
- * {@link #commit() phase 2} of the authentication process. This responsibility
- * is delegated to a subsequent login module implementation that may or may not
- * use the {@code GuestCredentials} this module added to the share state.<p/>
+ * If this login module pushed {@link GuestLoginModule} to the shared state
+ * in phase 1 it will add those credentials and the {@link EveryonePrincipal}
+ * to the subject in phase 2 of the login process. Subsequent login modules
+ * my choose to provide additional principals/credentials associated with
+ * a guest login.<p/>
  *
  * The authentication configuration using this {@code LoginModule} could for
  * example look as follows:
@@ -100,16 +100,8 @@ public class GuestLoginModule implements
                 callbackHandler.handle(new Callback[] {ccb});
                 Credentials credentials = ccb.getCredentials();
                 if (credentials == null) {
-                    Set<Credentials> sharedCredentials;
-                    Object sharedObj = sharedState.get(LoginModuleImpl.SHARED_KEY_CREDENTIALS);
-                    if (sharedObj == null || !(sharedObj instanceof Set)) {
-                        sharedCredentials = new HashSet<Credentials>();
-                    } else {
-                        sharedCredentials = (Set) sharedObj;
-                    }
                     guestCredentials = new GuestCredentials();
-                    sharedCredentials.add(guestCredentials);
-                    sharedState.put(LoginModuleImpl.SHARED_KEY_CREDENTIALS, sharedCredentials);
+                    sharedState.put(AbstractLoginModule.SHARED_KEY_CREDENTIALS, guestCredentials);
                     return true;
                 }
             } catch (IOException e) {
@@ -143,4 +135,4 @@ public class GuestLoginModule implements
         // nothing to do.
         return true;
     }
-}
\ No newline at end of file
+}

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/LoginModuleImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/LoginModuleImpl.java?rev=1365052&r1=1365051&r2=1365052&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/LoginModuleImpl.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/LoginModuleImpl.java Tue Jul 24 13:47:00 2012
@@ -16,15 +16,12 @@
  */
 package org.apache.jackrabbit.oak.security.authentication;
 
-import org.apache.jackrabbit.api.security.authentication.token.TokenCredentials;
-import org.apache.jackrabbit.oak.api.AuthInfo;
-import org.apache.jackrabbit.oak.spi.security.authentication.CredentialsCallback;
-import org.apache.jackrabbit.oak.spi.security.authentication.ImpersonationCredentials;
-import org.apache.jackrabbit.oak.spi.security.authentication.PrincipalProviderCallback;
-import org.apache.jackrabbit.oak.spi.security.principal.PrincipalProvider;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
+import java.io.IOException;
+import java.security.Principal;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
 import javax.jcr.Credentials;
 import javax.jcr.GuestCredentials;
 import javax.jcr.SimpleCredentials;
@@ -33,13 +30,14 @@ import javax.security.auth.callback.Call
 import javax.security.auth.callback.CallbackHandler;
 import javax.security.auth.callback.UnsupportedCallbackException;
 import javax.security.auth.login.LoginException;
-import javax.security.auth.spi.LoginModule;
-import java.io.IOException;
-import java.security.Principal;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
+
+import org.apache.jackrabbit.oak.api.AuthInfo;
+import org.apache.jackrabbit.oak.spi.security.authentication.AbstractLoginModule;
+import org.apache.jackrabbit.oak.spi.security.authentication.ImpersonationCredentials;
+import org.apache.jackrabbit.oak.spi.security.authentication.PrincipalProviderCallback;
+import org.apache.jackrabbit.oak.spi.security.principal.PrincipalProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * Default login module implementation that authenticates JCR {@code Credentials}
@@ -55,10 +53,7 @@ import java.util.Set;
  *     <li>{@code Credentials} as specified in {@link javax.jcr.Repository#login(javax.jcr.Credentials)}
  *     in which case they are retrieved from the {@code CallbackHandler}.</li>
  *     <li>A {@link #SHARED_KEY_CREDENTIALS} entry in the shared state. The
- *     expected value is a set of {@code Credentials}. Ffor backwards compatibility
- *     with the Jackrabbit 2.x) the former {@link #SHARED_KEY_JR_CREDENTIALS}
- *     entry in the shared state is also respected. In the latter case
- *     the expected value is a single {@code Credentials} object.</li>
+ *     expected value is a validated single {@code Credentials} object.</li>
  *     <li>If neither of the above variants provides Credentials this module
  *     tries to obtain them from the subject. See also
  *     {@link Subject#getSubject(java.security.AccessControlContext)}</li>
@@ -70,7 +65,6 @@ import java.util.Set;
  * <ul>
  *     <li>{@link SimpleCredentials}</li>
  *     <li>{@link GuestCredentials}</li>
- *     <li>{@link TokenCredentials}</li>
  *     <li>{@link ImpersonationCredentials}</li>
  * </ul>
  *
@@ -91,48 +85,27 @@ import java.util.Set;
  *
  *
  */
-public class LoginModuleImpl implements LoginModule {
+public class LoginModuleImpl extends AbstractLoginModule {
 
     private static final Logger log = LoggerFactory.getLogger(LoginModuleImpl.class);
 
-    /**
-     * Backwards compatibility: Key of the sharedState entry referring to a
-     * Credentials object being passed between multiple login modules.
-     *
-     * @deprecated Use {@link #SHARED_KEY_CREDENTIALS} instead.
-     */
-    private static final String SHARED_KEY_JR_CREDENTIALS = "org.apache.jackrabbit.credentials";
-
-    /**
-     * Key of the sharedState entry referring to a Set of Credentials that is
-     * shared between multiple login modules.
-     */
-    public static final String SHARED_KEY_CREDENTIALS = "org.apache.jackrabbit.oak.credentials";
-
-    protected static final Set<Class> SUPPORTED_CREDENTIALS = new HashSet<Class>(2);
+    protected static final Set<Class> SUPPORTED_CREDENTIALS = new HashSet<Class>(3);
     static {
         SUPPORTED_CREDENTIALS.add(SimpleCredentials.class);
         SUPPORTED_CREDENTIALS.add(GuestCredentials.class);
         SUPPORTED_CREDENTIALS.add(ImpersonationCredentials.class);
-        SUPPORTED_CREDENTIALS.add(TokenCredentials.class);
     }
 
-    private Subject subject;
-    private CallbackHandler callbackHandler;
-    private Map sharedState;
-
-    private Set<Credentials> credentials;
+    private Credentials credentials;
     private Set<Principal> principals;
     private String userID;
 
     //--------------------------------------------------------< LoginModule >---
     @Override
     public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState, Map<String, ?> options) {
-        // TODO
+        super.initialize(subject, callbackHandler, sharedState, options);
 
-        this.subject = subject;
-        this.callbackHandler = callbackHandler;
-        this.sharedState = sharedState;
+        // TODO
     }
 
     @Override
@@ -143,25 +116,32 @@ public class LoginModuleImpl implements 
         principals = getPrincipals(userID);
 
         Authentication authentication = new AuthenticationImpl(userID);
-        boolean success = authenticate(authentication);
+        boolean success = authentication.authenticate(credentials);
         if (!success) {
             success = impersonate(authentication);
         }
+
+        if (success) {
+            log.debug("Login: adding Credentials to shared state.");
+            sharedState.put(SHARED_KEY_CREDENTIALS, credentials);
+        }
         return success;
     }
 
     @Override
     public boolean commit() throws LoginException {
-        // TODO
-
-        if (!subject.isReadOnly()) {
-            subject.getPrincipals().addAll(principals);
-            subject.getPublicCredentials().addAll(credentials);
-            subject.getPublicCredentials().add(getAuthInfo());
+        if (credentials == null || principals.isEmpty()) {
+            return false;
         } else {
-            log.debug("Could not add information to read only subject {}", subject);
+            if (!subject.isReadOnly()) {
+                subject.getPrincipals().addAll(principals);
+                subject.getPublicCredentials().add(credentials);
+                subject.getPublicCredentials().add(getAuthInfo());
+            } else {
+                log.debug("Could not add information to read only subject {}", subject);
+            }
+            return true;
         }
-        return true;
     }
 
     @Override
@@ -171,102 +151,25 @@ public class LoginModuleImpl implements 
         return true;
     }
 
+    //------------------------------------------------< AbstractLoginModule >---
+
     @Override
-    public boolean logout() throws LoginException {
-        if (subject.getPrincipals().isEmpty() || subject.getPublicCredentials(Credentials.class).isEmpty()) {
-            return false;
-        } else {
-            // clear subject if not readonly
-            if (!subject.isReadOnly()) {
-                subject.getPrincipals().clear();
-                subject.getPublicCredentials().clear();
-            }
-            return true;
-        }
+    protected Set<Class> getSupportedCredentials() {
+        return SUPPORTED_CREDENTIALS;
     }
 
     //--------------------------------------------------------------------------
 
-    private Set<Credentials> getCredentials() {
-        Set<Credentials> credentials = new HashSet<Credentials>();
-        credentials.addAll(getSharedCredentials());
-
-        if (callbackHandler != null) {
-            log.debug("Login: retrieving Credentials using callback.");
-            try {
-                CredentialsCallback callback = new CredentialsCallback();
-                callbackHandler.handle(new Callback[]{callback});
-                Credentials creds = callback.getCredentials();
-                if (creds != null) {
-                    log.debug("Login: Credentials '{}' obtained from callback", creds);
-                    credentials.add(creds);
-                }
-            } catch (UnsupportedCallbackException e) {
-                log.warn(e.getMessage());
-            } catch (IOException e) {
-                log.error(e.getMessage());
-            }
-        }
-
-        log.debug("Login: adding Credentials to shared state.");
-        sharedState.put(SHARED_KEY_CREDENTIALS, credentials);
-
-        if (credentials.isEmpty()) {
-            log.debug("Login: No credentials found; looking for supported credentials in subject.");
-            for (Class clz : SUPPORTED_CREDENTIALS) {
-                credentials.addAll(subject.getPublicCredentials(clz));
-            }
-        }
-        return credentials;
-    }
-
-    private Set<Credentials> getSharedCredentials() {
-        Set<Credentials> sharedCredentials = new HashSet<Credentials>();
-        if (sharedState.containsKey(SHARED_KEY_JR_CREDENTIALS)) {
-            Object sc = sharedState.get(SHARED_KEY_JR_CREDENTIALS);
-            if (sc instanceof Credentials) {
-                sharedCredentials.add((Credentials) sc);
-            } else {
-                log.debug("Login: Invalid value for share state entry " + SHARED_KEY_JR_CREDENTIALS + ". Credentials expected.");
-            }
-        }
-        if (sharedState.containsKey(SHARED_KEY_CREDENTIALS)) {
-            Object scSet = sharedState.get(SHARED_KEY_CREDENTIALS);
-            if (scSet instanceof Set) {
-                 for (Object sc : (Set) scSet) {
-                     if (sc instanceof Credentials) {
-                         sharedCredentials.add((Credentials) sc);
-                     }
-                 }
-            } else {
-                log.debug("Login: Invalid value for share state entry " + SHARED_KEY_CREDENTIALS + ". Set of Credentials expected.");            }
-        }
-
-        return sharedCredentials;
-    }
-
-    private <T extends Credentials> java.util.Set<T> getCredentials(java.lang.Class<T> credentialsClass) {
-        Set<T> cds = new HashSet<T>();
-        for (Credentials c : credentials) {
-            if (credentialsClass.isAssignableFrom(c.getClass())) {
-                cds.add((T) c);
-            }
-        }
-        return cds;
-    }
-
     private Set<Principal> getPrincipals(String userID) {
         Set<Principal> principals = new HashSet<Principal>();
         PrincipalProvider principalProvider = getPrincipalProvider();
-        if (principalProvider != null) {
-            for (Credentials creds : credentials) {
-                Principal p = principalProvider.getPrincipal(userID); // TODO FIXME
-                if (p != null) {
-                    principals.add(p);
-                    principals.addAll(principalProvider.getGroupMembership(p));
-                } else {
-                    log.debug("Commit: Cannot retrieve principal for Credentials '{}'.", creds);
-                }
+        if (principalProvider != null && userID != null) {
+            Principal p = principalProvider.getPrincipal(userID); // TODO FIXME
+            if (p != null) {
+                principals.add(p);
+                principals.addAll(principalProvider.getGroupMembership(p));
+            } else {
+                log.debug("Commit: Cannot retrieve principal for userID '{}'.", userID);
             }
         } else {
             log.debug("Commit: Cannot retrieve principals. No principal provider configured.");
@@ -294,14 +197,13 @@ public class LoginModuleImpl implements 
     private String getUserID() {
         // TODO add proper implementation
         String userID = null;
-        if (!credentials.isEmpty()) {
-            Credentials c = credentials.iterator().next();
-            if (c instanceof SimpleCredentials) {
-                userID = ((SimpleCredentials) c).getUserID();
-            } else if (c instanceof GuestCredentials) {
+        if (credentials != null) {
+            if (credentials instanceof SimpleCredentials) {
+                userID = ((SimpleCredentials) credentials).getUserID();
+            } else if (credentials instanceof GuestCredentials) {
                 userID = "anonymous";
-            } else if (c instanceof ImpersonationCredentials) {
-                Credentials bc = ((ImpersonationCredentials) c).getBaseCredentials();
+            } else if (credentials instanceof ImpersonationCredentials) {
+                Credentials bc = ((ImpersonationCredentials) credentials).getBaseCredentials();
                 if (bc instanceof SimpleCredentials) {
                     userID = ((SimpleCredentials) bc).getUserID();
                 }
@@ -311,20 +213,9 @@ public class LoginModuleImpl implements 
     }
 
     private boolean impersonate(Authentication authentication) {
-        for (ImpersonationCredentials ic : getCredentials(ImpersonationCredentials.class)) {
-            AuthInfo info = ic.getImpersonatorInfo();
-            if (info instanceof AuthInfoImpl) {
-                if (authentication.impersonate(((AuthInfoImpl) info).getPrincipals())) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    private boolean authenticate(Authentication authentication) {
-        for (Credentials creds : credentials) {
-            if (authentication.authenticate(creds)) {
+        if (credentials instanceof ImpersonationCredentials) {
+            AuthInfo info = ((ImpersonationCredentials) credentials).getImpersonatorInfo();
+            if (authentication.impersonate(info.getPrincipals())) {
                 return true;
             }
         }
@@ -333,11 +224,12 @@ public class LoginModuleImpl implements 
 
     private AuthInfo getAuthInfo() {
         Map<String, Object> attributes = new HashMap<String, Object>();
-        for (SimpleCredentials sc : getCredentials(SimpleCredentials.class)) {
+        if (credentials instanceof SimpleCredentials) {
+            SimpleCredentials sc = (SimpleCredentials) credentials;
             for (String attrName : sc.getAttributeNames()) {
                 attributes.put(attrName, sc.getAttribute(attrName));
             }
         }
         return new AuthInfoImpl(userID, attributes, principals);
     }
-}
\ No newline at end of file
+}

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/AbstractLoginModule.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/AbstractLoginModule.java?rev=1365052&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/AbstractLoginModule.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/authentication/AbstractLoginModule.java Tue Jul 24 13:47:00 2012
@@ -0,0 +1,127 @@
+/*
+ * 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;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.Set;
+import javax.jcr.Credentials;
+import javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.auth.login.LoginException;
+import javax.security.auth.spi.LoginModule;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * AbstractLoginModule... TODO
+ */
+public abstract class AbstractLoginModule implements LoginModule {
+
+    /**
+     * logger instance
+     */
+    private static final Logger log = LoggerFactory.getLogger(AbstractLoginModule.class);
+
+    /**
+     * Key of the sharedState entry referring to validated Credentials that is
+     * shared between multiple login modules.
+     */
+    public static final String SHARED_KEY_CREDENTIALS = "org.apache.jackrabbit.credentials";
+
+    protected Subject subject;
+    protected CallbackHandler callbackHandler;
+    protected Map sharedState;
+
+    //--------------------------------------------------------< LoginModule >---
+    @Override
+    public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState, Map<String, ?> options) {
+        this.subject = subject;
+        this.callbackHandler = callbackHandler;
+        this.sharedState = sharedState;
+    }
+
+    @Override
+    public boolean logout() throws LoginException {
+        if (subject.getPrincipals().isEmpty() || subject.getPublicCredentials(Credentials.class).isEmpty()) {
+            return false;
+        } else {
+            // clear subject if not readonly
+            if (!subject.isReadOnly()) {
+                subject.getPrincipals().clear();
+                subject.getPublicCredentials().clear();
+            }
+            return true;
+        }
+    }
+
+    //--------------------------------------------------------------------------
+    protected abstract Set<Class> getSupportedCredentials();
+
+    protected Credentials getCredentials() {
+        if (callbackHandler != null) {
+            log.debug("Login: retrieving Credentials using callback.");
+            try {
+                CredentialsCallback callback = new CredentialsCallback();
+                callbackHandler.handle(new Callback[]{callback});
+                Credentials creds = callback.getCredentials();
+                if (creds != null) {
+                    log.debug("Login: Credentials '{}' obtained from callback", creds);
+                    return creds;
+                } else {
+                    log.debug("Login: No credentials obtained from callback; trying shared state.");
+                }
+            } catch (UnsupportedCallbackException e) {
+                log.warn(e.getMessage());
+            } catch (IOException e) {
+                log.error(e.getMessage());
+            }
+        }
+
+        Credentials creds = getSharedCredentials();
+        if (creds == null) {
+            log.debug("Login: No credentials found in shared state; looking for supported credentials in subject.");
+            for (Class clz : getSupportedCredentials()) {
+                Set<Credentials> cds = subject.getPublicCredentials(clz);
+                if (!cds.isEmpty()) {
+                    log.debug("Login: Credentials found in subject.");
+                    return cds.iterator().next();
+                }
+            }
+        }
+
+        log.debug("No credentials found.");
+        return null;
+    }
+
+    protected Credentials getSharedCredentials() {
+        Credentials shared = null;
+        if (sharedState.containsKey(SHARED_KEY_CREDENTIALS)) {
+            Object sc = sharedState.get(SHARED_KEY_CREDENTIALS);
+            if (sc instanceof Credentials) {
+                shared = (Credentials) sc;
+            } else {
+                log.debug("Login: Invalid value for share state entry " + SHARED_KEY_CREDENTIALS + ". Credentials expected.");
+            }
+        }
+
+        return shared;
+    }
+}