/*
* (C) Copyright 2006-2011 Nuxeo SA (http://nuxeo.com/) and others.
*
* Licensed 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.
*
* Contributors:
* bstefanescu
*
* $Id$
*/
package org.nuxeo.ecm.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;
import org.nuxeo.ecm.core.api.NuxeoPrincipal;
import org.nuxeo.ecm.core.api.SystemPrincipal;
import org.nuxeo.runtime.api.login.LoginComponent;
/**
* 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();
}
};
/**
* @since 5.7
*/
public static void clearThreadLocalLogin() {
threadInstance.remove();
}
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;
}
/**
* Returns the current logged {@link NuxeoPrincipal} from the login stack
*
* @since 5.6
*/
public static NuxeoPrincipal getCurrentPrincipal() {
LoginStack.Entry entry = getCurrentLogin();
if (entry != null) {
Principal p = entry.getPrincipal();
if (p instanceof NuxeoPrincipal) {
return (NuxeoPrincipal) p;
} else if (LoginComponent.isSystemLogin(p)) {
return new SystemPrincipal(p.getName());
}
}
return null;
}
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;
}
}