/**
* Copyright (c) 2009 Juwi MacMillan Group GmbH
*
* 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.
*/
package de.juwimm.cms.authorization.jaas.ldap;
import java.security.acl.Group;
import java.util.HashMap;
import java.util.Map;
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.apache.log4j.Logger;
import de.juwimm.cms.authorization.jaas.DatabaseAuthorization;
/**
* This <code>LoginModule</code> wraps two LoginModules, a <code>LdapLoginModule</code> and a <code>DatabaseServerLoginModule</code>.<br/>
* For authentication it delegates to one LoginModule and if the login fails, it delegates to the other one.<br/>
* If the login succeeds the roles for this user are fetched the usual way from the ConQuest-database.
* <p>
* This LoginModule needs a combination of options of both LdapLoginModule and DatabaseServerLoginModule.<br/>
* <h5>Example:</h5>
* <pre>
* <!-- ConQuest -->
* <application-policy name="juwimm-cms-security-domain">
* <authentication>
* <!-- EJB J2EE Security -->
* <login-module code="org.jboss.security.ClientLoginModule" flag="required"/>
* <!-- Authentification (Check Username / Password) -->
* <login-module code="de.juwimm.cms.authorization.jaas.ldap.ActiveDirectoryAuthenticationLoginModule" flag="required">
* <module-option name="java.naming.factory.initial">com.sun.jndi.ldap.LdapCtxFactory</module-option>
* <<module-option name="java.naming.provider.url">ldap://bsnads.ADSTEST.bsn:389</module-option>
* <module-option name="java.naming.security.authentication">simple</module-option>
* <module-option name="principalDNPrefix">CN=</module-option>
* <module-option name="uidAttributeID">sAMAccountName</module-option>
* <module-option name="principalDNSuffix">,OU=JuwiMM,DC=adstest,DC=bsn</module-option>
* <module-option name="dsJndiName">java:/ConQuestDS</module-option>
* <module-option name="password-stacking">useFirstPass</module-option>
* <module-option name="principalsQuery">SELECT passwd FROM usr WHERE user_id = ?</module-option>
* <module-option name="rolesQuery">SELECT user_id, 'Roles' FROM usr WHERE user_id = '' AND user_id = ?</module-option>
* <module-option name="hashAlgorithm">SHA-1</module-option>
* <module-option name="hashEncoding">base64</module-option>
* <module-option name="unauthenticatedIdentity">nobody</module-option>
* </login-module>
* </authentication>
* </application-policy>
* </pre>
* </p>
*
* @author <a href="mailto:carsten.schalm@juwimm.com">Carsten Schalm</a>
* company Juwi|MacMillan Group Gmbh, Walsrode, Germany
* @version $Id$
* @since 2.4.14
*/
public class ActiveDirectoryAuthenticationLoginModule implements LoginModule {
private static final Logger log = Logger.getLogger(ActiveDirectoryAuthenticationLoginModule.class);
private LdapLoginModule ldapLoginModule = new AdLdapLoginModule();
private DatabaseServerLoginModule databaseLoginModule = new AdDatabaseServerLoginModule();
/** The JNDI name of the DataSource to use */
protected String dsJndiName;
private UsernamePasswordLoginModule lastSuccessfullyModule = null;
public ActiveDirectoryAuthenticationLoginModule() {
}
/**
* Fetch additional options
* @see javax.security.auth.spi.LoginModule#initialize(javax.security.auth.Subject, javax.security.auth.callback.CallbackHandler, java.util.Map, java.util.Map)
*/
public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ? > sharedState, Map<String, ? > options) {
this.dsJndiName = (String) options.get("dsJndiName");
if (this.dsJndiName == null) this.dsJndiName = "java:/ConQuestDS";
this.databaseLoginModule.initialize(subject, callbackHandler, sharedState, options);
Map newOptions = new HashMap(options);
if (newOptions.containsKey("hashAlgorithm")) {
// remove hashing to authenticate at Active Directory with plain text password
newOptions.remove("hashAlgorithm");
}
this.ldapLoginModule.initialize(subject, callbackHandler, sharedState, newOptions);
}
/**
* Fetch roles from ConQuest-database
*/
protected Group[] getRoleSets() throws LoginException {
try {
return DatabaseAuthorization.getRoleSets(this.dsJndiName, this.lastSuccessfullyModule.getIdentity().getName());
} catch (Exception e) {
log.error("Error fetching roles: " + e.getMessage(), e);
LoginException le = new LoginException("Error fetching roles: " + e.getMessage());
throw le;
}
}
/**
* @see javax.security.auth.spi.LoginModule#login()
*/
public boolean login() throws LoginException {
boolean loginSuccessfully = false;
if (this.lastSuccessfullyModule == null) this.lastSuccessfullyModule = this.ldapLoginModule;
try {
loginSuccessfully = this.lastSuccessfullyModule.login();
} catch (LoginException le) {
if (log.isDebugEnabled()) log.debug("Login via " + this.lastSuccessfullyModule.getClass().getName() + " failed: " + le.getMessage());
}
if (!loginSuccessfully) {
this.switchLoginModule();
try {
loginSuccessfully = this.lastSuccessfullyModule.login();
} catch (LoginException le) {
if (log.isDebugEnabled()) log.debug("Login via " + this.lastSuccessfullyModule.getClass().getName() + " failed: " + le.getMessage());
}
}
return loginSuccessfully;
}
private void switchLoginModule() {
if (this.lastSuccessfullyModule == null) {
this.lastSuccessfullyModule = this.ldapLoginModule;
} else if (this.lastSuccessfullyModule instanceof AdLdapLoginModule) {
this.lastSuccessfullyModule = this.databaseLoginModule;
} else {
this.lastSuccessfullyModule = this.ldapLoginModule;
}
}
/**
* @see javax.security.auth.spi.LoginModule#commit()
*/
public boolean commit() throws LoginException {
if (this.lastSuccessfullyModule == null) this.lastSuccessfullyModule = this.ldapLoginModule;
return this.lastSuccessfullyModule.commit();
}
/**
* @see javax.security.auth.spi.LoginModule#abort()
*/
public boolean abort() throws LoginException {
if (this.lastSuccessfullyModule == null) this.lastSuccessfullyModule = this.ldapLoginModule;
return this.lastSuccessfullyModule.abort();
}
/**
* @see javax.security.auth.spi.LoginModule#logout()
*/
public boolean logout() throws LoginException {
if (this.lastSuccessfullyModule == null) this.lastSuccessfullyModule = this.ldapLoginModule;
return this.lastSuccessfullyModule.logout();
}
/**
*
* @author <a href="mailto:carsten.schalm@juwimm.com">Carsten Schalm</a>
* company Juwi|MacMillan Group Gmbh, Walsrode, Germany
* @version $Id$
*/
public class AdLdapLoginModule extends LdapLoginModule {
public AdLdapLoginModule() {
super();
}
/**
* @see de.juwimm.cms.authorization.jaas.ldap.LdapLoginModule#getRoleSets()
*/
@Override
protected Group[] getRoleSets() throws LoginException {
try {
return ActiveDirectoryAuthenticationLoginModule.this.getRoleSets();
} catch (Exception e) {
LoginException le = new LoginException(e.getMessage());
throw le;
}
}
}
/**
*
* @author <a href="mailto:carsten.schalm@juwimm.com">Carsten Schalm</a>
* company Juwi|MacMillan Group Gmbh, Walsrode, Germany
* @version $Id$
*/
public class AdDatabaseServerLoginModule extends DatabaseServerLoginModule {
public AdDatabaseServerLoginModule() {
super();
}
/**
* @see de.juwimm.cms.authorization.jaas.ldap.AbstractServerLoginModule#getRoleSets()
*/
@Override
protected Group[] getRoleSets() throws LoginException {
try {
return ActiveDirectoryAuthenticationLoginModule.this.getRoleSets();
} catch (Exception e) {
LoginException le = new LoginException(e.getMessage());
throw le;
}
}
}
}