/*
* (C) Copyright 2006-2007 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:
* Nuxeo - initial API and implementation
*
* $Id: JOOoConvertPluginImpl.java 18651 2007-05-13 20:28:53Z sfermigier $
*/
package org.nuxeo.ecm.platform.login;
import java.io.IOException;
import java.security.Principal;
import java.security.acl.Group;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.LoginException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.nuxeo.ecm.core.api.NuxeoPrincipal;
import org.nuxeo.ecm.core.api.SystemPrincipal;
import org.nuxeo.ecm.core.api.security.SecurityConstants;
import org.nuxeo.ecm.directory.DirectoryException;
import org.nuxeo.ecm.platform.api.login.RestrictedLoginHelper;
import org.nuxeo.ecm.platform.api.login.UserIdentificationInfo;
import org.nuxeo.ecm.platform.api.login.UserIdentificationInfoCallback;
import org.nuxeo.ecm.platform.usermanager.NuxeoPrincipalImpl;
import org.nuxeo.ecm.platform.usermanager.UserManager;
import org.nuxeo.runtime.RuntimeService;
import org.nuxeo.runtime.api.Framework;
import org.nuxeo.runtime.api.login.LoginComponent;
public class NuxeoLoginModule extends NuxeoAbstractServerLoginModule {
private static final Log log = LogFactory.getLog(NuxeoLoginModule.class);
private UserManager manager;
private Random random;
private NuxeoPrincipal identity;
private LoginPluginRegistry loginPluginManager;
private boolean useUserIdentificationInfoCB = false;
@Override
public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState,
Map<String, ?> options) {
// explicit cast to match the direct superclass method declaration
// (JBoss implementation)
// rather than the newer (jdk1.5) LoginModule (... Map<String,?>...)
// This is needed to avoid compilation errors when the linker wants to
// bind
// with the (interface) LoginModule method (which is abstract of course
// and cannot be called)
String useUIICB = (String) options.get("useUserIdentificationInfoCB");
if (useUIICB != null && useUIICB.equalsIgnoreCase("true")) {
useUserIdentificationInfoCB = true;
}
super.initialize(subject, callbackHandler, sharedState, options);
random = new Random(System.currentTimeMillis());
manager = Framework.getService(UserManager.class);
log.debug("NuxeoLoginModule initialized");
final RuntimeService runtime = Framework.getRuntime();
loginPluginManager = (LoginPluginRegistry) runtime.getComponent(LoginPluginRegistry.NAME);
}
/**
* Gets the roles the user belongs to.
*/
@Override
protected Group[] getRoleSets() throws LoginException {
log.debug("getRoleSets");
if (manager == null) {
// throw new LoginException("UserManager implementation not found");
}
String username = identity.getName();
List<String> roles = identity.getRoles();
Group roleSet = new GroupImpl("Roles");
log.debug("Getting roles for user=" + username);
for (String roleName : roles) {
Principal role = new PrincipalImpl(roleName);
log.debug("Found role=" + roleName);
roleSet.addMember(role);
}
Group callerPrincipal = new GroupImpl("CallerPrincipal");
callerPrincipal.addMember(identity);
return new Group[] { roleSet, callerPrincipal };
}
@SuppressWarnings({ "unchecked" })
protected NuxeoPrincipal getPrincipal() throws LoginException {
UserIdentificationInfo userIdent = null;
// **** init the callbacks
// Std login/password callbacks
NameCallback nc = new NameCallback("Username: ", SecurityConstants.ANONYMOUS);
PasswordCallback pc = new PasswordCallback("Password: ", false);
// Nuxeo specific cb : handle LoginPlugin initialization
UserIdentificationInfoCallback uic = new UserIdentificationInfoCallback();
// JBoss specific cb : handle web=>ejb propagation
// SecurityAssociationCallback ac = new SecurityAssociationCallback();
// ObjectCallback oc = new ObjectCallback("UserInfo:");
// **** handle callbacks
// We can't check the callback handler class to know what will be
// supported
// because the cbh is wrapped by JAAS
// => just try and swalow exceptions
// => will be externalised to plugins via EP to avoid JBoss dependency
boolean cb_handled = false;
try {
// only try this cbh when called from the web layer
if (useUserIdentificationInfoCB) {
callbackHandler.handle(new Callback[] { uic });
// First check UserInfo CB return
userIdent = uic.getUserInfo();
cb_handled = true;
}
} catch (UnsupportedCallbackException e) {
log.debug("UserIdentificationInfoCallback is not supported");
} catch (IOException e) {
log.warn("Error calling callback handler with UserIdentificationInfoCallback : " + e.getMessage());
}
Principal principal = null;
Object credential = null;
if (!cb_handled) {
CallbackResult result = loginPluginManager.handleSpecifcCallbacks(callbackHandler);
if (result != null && result.cb_handled) {
if (result.userIdent != null && result.userIdent.containsValidIdentity()) {
userIdent = result.userIdent;
cb_handled = true;
} else {
principal = result.principal;
credential = result.credential;
if (principal != null) {
cb_handled = true;
}
}
}
}
if (!cb_handled) {
try {
// Std CBH : will only works for L/P
callbackHandler.handle(new Callback[] { nc, pc });
cb_handled = true;
} catch (UnsupportedCallbackException e) {
LoginException le = new LoginException("Authentications Failure - " + e.getMessage());
le.initCause(e);
} catch (IOException e) {
LoginException le = new LoginException("Authentications Failure - " + e.getMessage());
le.initCause(e);
}
}
// Login via the Web Interface : may be using a plugin
if (userIdent != null && userIdent.containsValidIdentity()) {
NuxeoPrincipal nxp = validateUserIdentity(userIdent);
if (nxp != null) {
sharedState.put("javax.security.auth.login.name", nxp.getName());
sharedState.put("javax.security.auth.login.password", userIdent);
}
return nxp;
}
if (LoginComponent.isSystemLogin(principal)) {
return new SystemPrincipal(principal.getName());
}
// if (principal instanceof NuxeoPrincipal) { // a nuxeo principal
// return validatePrincipal((NuxeoPrincipal) principal);
// } else
if (principal != null) { // a non null principal
String password = null;
if (credential instanceof char[]) {
password = new String((char[]) credential);
} else if (credential != null) {
password = credential.toString();
}
return validateUsernamePassword(principal.getName(), password);
} else { // we don't have a principal - try the username &
// password
String username = nc.getName();
if (username == null) {
return null;
}
char[] password = pc.getPassword();
return validateUsernamePassword(username, password != null ? new String(password) : null);
}
}
public boolean login() throws LoginException {
if (manager == null) {
// throw new LoginException("UserManager implementation not found");
}
loginOk = false;
identity = getPrincipal();
if (identity == null) { // auth failed
throw new LoginException("Authentication Failed");
}
if (RestrictedLoginHelper.isRestrictedModeActivated()) {
if (!identity.isAdministrator()) {
throw new LoginException("Only Administrators can login when restricted mode is activated");
}
}
loginOk = true;
log.trace("User '" + identity + "' authenticated");
/*
* if( getUseFirstPass() == true ) { // Add the username and password to the shared state map // not sure it's
* needed sharedState.put("javax.security.auth.login.name", identity.getName());
* sharedState.put("javax.security.auth.login.password", identity.getPassword()); }
*/
return true;
}
@Override
public Principal getIdentity() {
return identity;
}
@Override
public Principal createIdentity(String username) throws LoginException {
log.debug("createIdentity: " + username);
try {
NuxeoPrincipal principal;
if (manager == null) {
principal = new NuxeoPrincipalImpl(username);
} else {
principal = manager.getPrincipal(username);
if (principal == null) {
throw new LoginException(String.format("principal %s does not exist", username));
}
}
String principalId = String.valueOf(random.nextLong());
principal.setPrincipalId(principalId);
return principal;
} catch (LoginException e) {
log.error("createIdentity failed", e);
LoginException le = new LoginException("createIdentity failed for user " + username);
le.initCause(e);
throw le;
}
}
protected NuxeoPrincipal validateUserIdentity(UserIdentificationInfo userIdent) throws LoginException {
String loginPluginName = userIdent.getLoginPluginName();
if (loginPluginName == null) {
// we don't use a specific plugin
boolean authenticated;
try {
authenticated = manager.checkUsernamePassword(userIdent.getUserName(), userIdent.getPassword());
} catch (DirectoryException e) {
throw (LoginException) new LoginException("Unable to validate identity").initCause(e);
}
if (authenticated) {
return (NuxeoPrincipal) createIdentity(userIdent.getUserName());
} else {
return null;
}
} else {
LoginPlugin lp = loginPluginManager.getPlugin(loginPluginName);
if (lp == null) {
log.error("Can't authenticate against a null loginModul plugin");
return null;
}
// set the parameters and reinit if needed
LoginPluginDescriptor lpd = loginPluginManager.getPluginDescriptor(loginPluginName);
if (!lpd.getInitialized()) {
Map<String, String> existingParams = lp.getParameters();
if (existingParams == null) {
existingParams = new HashMap<String, String>();
}
Map<String, String> loginParams = userIdent.getLoginParameters();
if (loginParams != null) {
existingParams.putAll(loginParams);
}
boolean init = lp.initLoginModule();
if (init) {
lpd.setInitialized(true);
} else {
log.error("Unable to initialize LoginModulePlugin " + lp.getName());
return null;
}
}
String username = lp.validatedUserIdentity(userIdent);
if (username == null) {
return null;
} else {
return (NuxeoPrincipal) createIdentity(username);
}
}
}
protected NuxeoPrincipal validateUsernamePassword(String username, String password) throws LoginException {
if (!manager.checkUsernamePassword(username, password)) {
return null;
}
return (NuxeoPrincipal) createIdentity(username);
}
}