/*
* Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Nuxeo - initial API and implementation
*
* $Id$
*/
package org.eclipse.ecr.core.api.local;
import java.security.Principal;
import java.util.Map;
import java.util.Set;
import javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;
/**
* A login module that is propagating the login information into the core login stack.
* <p>
* This login module doesn't make any authentication - it is called only after the authentication
* is successfully done by a previous login module.
* <p>
* The static method of this class can also be used to manage the current login stack.
*
* @author eionica@nuxeo.com
*
*/
public class ClientLoginModule implements LoginModule {
/**
* The global login stack
*/
protected static final LoginStack globalInstance = LoginStack.synchronizedStack();
/**
* The thread local login stack - for per thread logins
*/
protected static final ThreadLocal<LoginStack> threadInstance = new ThreadLocal<LoginStack>() {
@Override
protected LoginStack initialValue() {
return new LoginStack();
}
};
public static LoginStack getThreadLocalLogin() {
return threadInstance.get();
}
public static LoginStack.Entry getCurrentLogin() {
LoginStack.Entry entry = threadInstance.get().peek();
if (entry == null) {
entry = globalInstance.peek();
}
return entry;
}
private Subject subject;
private Map sharedState;
// active login stack
private LoginStack stack;
// whether or not the login was propagated
private boolean commited = false;
/**
* Initialize this LoginModule.
*/
@Override
public void initialize(Subject subject, CallbackHandler callbackHandler,
Map sharedState, Map options) {
this.subject = subject;
this.sharedState = sharedState;
// Check if login must be propagated to entire JVM or only to the current thread.
// the default is per-thread login
boolean globalLogin = true;
String global = (String)options.get("global");
if (global != null) {
globalLogin = Boolean.parseBoolean(global);
}
if (globalLogin) {
// propagate the login only for the current thread
stack = threadInstance.get();
} else {
// propagate the login for all threads in JVM
stack = globalInstance;
}
}
@Override
public boolean login() throws LoginException {
// this login module doesn't make any user authentication
// it simply propagate the login to the login stack.
// So it must be put after the authenticating module.
// The authenticating module should update the sharedState map
// with the login info.
return true;
}
@Override
public boolean commit() throws LoginException {
Principal p = null;
Object user = sharedState.get("javax.security.auth.login.name");
if (user instanceof Principal) {
p = (Principal)user;
} else {
Set<Principal> principals = subject.getPrincipals();
if (!principals.isEmpty()) {
p = principals.iterator().next();
}
}
if (p != null) {
Object credential = sharedState.get("javax.security.auth.login.password");
stack.push(p, credential, subject);
commited = true;
}
return true;
}
@Override
public boolean abort() throws LoginException {
commited = false;
stack.clear();
return true;
}
@Override
public boolean logout() throws LoginException {
if (commited) {
stack.pop();
commited = false;
}
return true;
}
}