Author: angela Date: Thu May 10 15:56:25 2012 New Revision: 1336763 URL: http://svn.apache.org/viewvc?rev=1336763&view=rev Log: OAK-91 - Implement Authentication Support (WIP)
Modified: 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/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=1336763&r1=1336762&r2=1336763&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 Thu May 10 15:56:25 2012 @@ -36,7 +36,51 @@ import java.util.Map; import java.util.Set; /** - * LoginModuleImpl... + * Default login module implementation that authenticates JCR {@code Credentials} + * against the repository. Based on the credentials the {@link Principal}s + * associated with user are retrieved from a configurable {@link PrincipalProvider}. + * + * <h3>Credentials</h3> + * + * The {@code Credentials} are collected during {@link #login()} using the + * following logic: + * + * <ul> + * <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> + * <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> + * </ul> + * + * This implementation of the {@code LoginModule} currently supports the following + * types of JCR Credentials: + * + * <ul> + * <li>{@link SimpleCredentials}</li> + * <li>{@link GuestCredentials}</li> + * <li>// TODO {@link TokenCredentials}</li> + * </ul> + * + * The {@link Credentials} obtained during the {@link #login()} are added to + * the shared state and - upon successful {@link #commit()} to the {@link Subject}. + * + * <h3>Principals</h3> + * + * TODO + * + * <h3>Login vs. Impersonation</h3> + * + * TODO + * + * + * + * */ public class LoginModuleImpl implements LoginModule { @@ -45,11 +89,34 @@ public class LoginModuleImpl implements */ 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"; + + //public static final String SHARED_KEY_LOGIN_NAME = "javax.security.auth.login.name"; + + protected static final Set<Class> SUPPORTED_CREDENTIALS = new HashSet<Class>(2); + static { + SUPPORTED_CREDENTIALS.add(SimpleCredentials.class); + SUPPORTED_CREDENTIALS.add(GuestCredentials.class); + } + private Subject subject; private CallbackHandler callbackHandler; + private Map sharedState; private Set<Credentials> credentials; - private String principalName; + private Set<Principal> principals; //--------------------------------------------------------< LoginModule >--- @Override @@ -58,14 +125,15 @@ public class LoginModuleImpl implements this.subject = subject; this.callbackHandler = callbackHandler; + this.sharedState = sharedState; } @Override public boolean login() throws LoginException { // TODO - credentials = retrieveCredentials(); + credentials = getCredentials(); if (supportsCredentials()) { - principalName = retrievePrincipalName(); + principals = getPrincipals(); return true; } else { return false; @@ -76,91 +144,156 @@ public class LoginModuleImpl implements public boolean commit() throws LoginException { // TODO - subject.getPrincipals().addAll(retrievePrincipals(principalName)); - subject.getPublicCredentials().addAll(credentials); + if (!subject.isReadOnly()) { + subject.getPrincipals().addAll(principals); + subject.getPublicCredentials().addAll(credentials); + } else { + log.debug("Could not add information to read only subject {}", subject); + } return true; } @Override public boolean abort() throws LoginException { - // TODO + credentials = null; + principals = null; return true; } @Override public boolean logout() throws LoginException { - // TODO - return true; + 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; + } } //-------------------------------------------------------------------------- - private Set<Credentials> retrieveCredentials() { + private Set<Credentials> getCredentials() { Set<Credentials> credentials = new HashSet<Credentials>(); + credentials.addAll(getSharedCredentials()); - try { - CredentialsCallback callback = new CredentialsCallback(); - callbackHandler.handle(new Callback[]{callback}); - Credentials creds = callback.getCredentials(); - if (creds != null) { - credentials.add(creds); - } - } catch (UnsupportedCallbackException e) { - log.warn(e.getMessage()); - } catch (IOException e) { - log.error(e.getMessage()); + 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()) { - credentials.addAll(subject.getPublicCredentials(SimpleCredentials.class)); - credentials.addAll(subject.getPublicCredentials(GuestCredentials.class)); + 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 boolean supportsCredentials() { for (Credentials creds : credentials) { - if (creds instanceof SimpleCredentials) { - return true; - } else if (creds instanceof GuestCredentials) { + if (isSupportedCredentials(creds)) { return true; } } return false; } - private String retrievePrincipalName() { - for (Credentials creds : credentials) { - if (creds instanceof SimpleCredentials) { - return ((SimpleCredentials) creds).getUserID(); - } else if (creds instanceof GuestCredentials) { - return "anonymous"; + private static boolean isSupportedCredentials(Credentials credentials) { + return SUPPORTED_CREDENTIALS.contains(credentials.getClass()); + } + + private static Principal getPrincipal(Credentials credentials, PrincipalProvider principalProvider) { + Principal principal = null; + if (isSupportedCredentials(credentials)) { + if (credentials instanceof SimpleCredentials) { + String userID = ((SimpleCredentials) credentials).getUserID(); + principal = principalProvider.getPrincipal(userID); // FIXME + } else if (credentials instanceof GuestCredentials) { + principal = principalProvider.getPrincipal("anonymous"); // FIXME } } - return null; + return principal; } - private Set<Principal> retrievePrincipals(String principalName) { + private Set<Principal> getPrincipals() { Set<Principal> principals = new HashSet<Principal>(); - try { - PrincipalProviderCallback principalCallBack = new PrincipalProviderCallback(); - callbackHandler.handle(new Callback[] {principalCallBack}); - PrincipalProvider pp = principalCallBack.getPrincipalProvider(); - if (pp != null) { - Principal p = pp.getPrincipal(principalName); + PrincipalProvider principalProvider = getPrincipalProvider(); + if (principalProvider != null) { + for (Credentials creds : credentials) { + Principal p = getPrincipal(creds, principalProvider); if (p != null) { principals.add(p); - principals.addAll(pp.getGroupMembership(p)); + principals.addAll(principalProvider.getGroupMembership(p)); + } else { + log.debug("Commit: Cannot retrieve principal for Credentials '{}'.", creds); } } - - } catch (IOException e) { - log.warn(e.getMessage()); - } catch (UnsupportedCallbackException e) { - log.warn(e.getMessage()); + } else { + log.debug("Commit: Cannot retrieve principals. No principal provider configured."); } return principals; } + + private PrincipalProvider getPrincipalProvider() { + PrincipalProvider principalProvider = null; + if (callbackHandler != null) { + try { + PrincipalProviderCallback principalCallBack = new PrincipalProviderCallback(); + callbackHandler.handle(new Callback[] {principalCallBack}); + principalProvider = principalCallBack.getPrincipalProvider(); + } catch (IOException e) { + log.warn(e.getMessage()); + } catch (UnsupportedCallbackException e) { + log.warn(e.getMessage()); + } + } + return principalProvider; + } } \ No newline at end of file