Initialization error of custom login module
code-monkey Mar 26, 2014 12:25 PMI have implemented a custom Login-Module for JBoss 6 which extends AbstractServerLoginModule. Unfortunately it fails to initialize after the first request on a protected page.
This is the correpsonding section of my server.log:
2014-03-25 15:14:08,159 DEBUG [org.apache.catalina.session.ManagerBase#processExpires] (ContainerBackgroundProcessor[StandardEngine[jboss.web]]) processExpires End expire sessions StandardManager processingTime 0 expired sessions: 0
2014-03-25 15:14:12,260 TRACE [org.jboss.security.SecurityRolesAssociation#setSecurityRoles] (http-127.0.0.1-21000-1) setSecurityRoles Setting threadlocal:{}
2014-03-25 15:14:12,262 DEBUG [org.apache.catalina.authenticator.AuthenticatorBase#invoke] (http-127.0.0.1-21000-1) invoke Security checking request GET /powerpanel/pages/login.xhtml
2014-03-25 15:14:12,263 DEBUG [org.apache.catalina.realm.RealmBase#findSecurityConstraints] (http-127.0.0.1-21000-1) findSecurityConstraints Checking constraint 'SecurityConstraint[powerpanel]' against GET /pages/login.xhtml --> true
2014-03-25 15:14:12,263 DEBUG [org.apache.catalina.realm.RealmBase#findSecurityConstraints] (http-127.0.0.1-21000-1) findSecurityConstraints Checking constraint 'SecurityConstraint[powerpanel]' against GET /pages/login.xhtml --> true
2014-03-25 15:14:12,263 DEBUG [org.apache.catalina.authenticator.AuthenticatorBase#invoke] (http-127.0.0.1-21000-1) invoke Calling hasUserDataPermission()
2014-03-25 15:14:12,264 DEBUG [org.apache.catalina.realm.RealmBase#hasUserDataPermission] (http-127.0.0.1-21000-1) hasUserDataPermission User data constraint has no restrictions
2014-03-25 15:14:12,277 DEBUG [org.jboss.security.integration.JNDIBasedSecurityManagement#createSecurityDomainContext] (http-127.0.0.1-21000-1) createSecurityDomainContext Creating SDC for domain=i24-panel-ws-auth
2014-03-25 15:14:12,278 TRACE [org.jboss.security.integration.JNDIBasedSecurityManagement#lookUpJNDI] (http-127.0.0.1-21000-1) lookUpJNDI Look up of JNDI for i24-panel-ws-auth/authorizationMgr failed with org.jboss.security.plugins.JaasSecurityManager cannot be cast to org.jboss.security.AuthenticationManager
2014-03-25 15:14:12,279 TRACE [org.jboss.security.integration.JNDIBasedSecurityManagement#getAuthorizationManager] (http-127.0.0.1-21000-1) getAuthorizationManager Exception in getting authorization mgr: java.lang.NullPointerException
at java.util.concurrent.ConcurrentHashMap.put(ConcurrentHashMap.java:881) [:1.6.0_35]
at org.jboss.security.integration.JNDIBasedSecurityManagement.getAuthorizationManager(JNDIBasedSecurityManagement.java:172) [:6.0.0.Final]
at org.jboss.security.plugins.JBossSecurityContext.getAuthorizationManager(JBossSecurityContext.java:268) [:6.0.0.Final]
at org.jboss.security.plugins.javaee.WebAuthorizationHelper.hasUserDataPermission(WebAuthorizationHelper.java:183) [:3.0.0.CR2]
at org.jboss.web.tomcat.security.JBossWebRealm.hasUserDataPermission(JBossWebRealm.java:668) [:6.0.0.Final]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:529) [:6.0.0.Final]
at org.jboss.modcluster.catalina.CatalinaContext$RequestListenerValve.event(CatalinaContext.java:285) [:1.1.0.Final]
at org.jboss.modcluster.catalina.CatalinaContext$RequestListenerValve.invoke(CatalinaContext.java:261) [:1.1.0.Final]
at org.jboss.web.tomcat.security.JaccContextValve.invoke(JaccContextValve.java:88) [:6.0.0.Final]
at org.jboss.web.tomcat.security.SecurityContextEstablishmentValve.invoke(SecurityContextEstablishmentValve.java:100) [:6.0.0.Final]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127) [:6.0.0.Final]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102) [:6.0.0.Final]
at org.jboss.web.tomcat.service.jca.CachedConnectionValve.invoke(CachedConnectionValve.java:158) [:6.0.0.Final]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109) [:6.0.0.Final]
at org.jboss.web.tomcat.service.request.ActiveRequestResponseCacheValve.invoke(ActiveRequestResponseCacheValve.java:53) [:6.0.0.Final]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:362) [:6.0.0.Final]
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:877) [:6.0.0.Final]
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:654) [:6.0.0.Final]
at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:951) [:6.0.0.Final]
at java.lang.Thread.run(Thread.java:662) [:1.6.0_35]
2014-03-25 15:14:12,285 TRACE [org.jboss.security.SecurityRolesAssociation#setSecurityRoles] (http-127.0.0.1-21000-1) setSecurityRoles Setting threadlocal:null
2014-03-25 15:14:12,286 ERROR [org.apache.catalina.connector.CoyoteAdapter#service] (http-127.0.0.1-21000-1) service An exception or error occurred in the container during the request processing: java.lang.IllegalStateException: Authorization Manager is null
at org.jboss.security.plugins.javaee.WebAuthorizationHelper.hasUserDataPermission(WebAuthorizationHelper.java:185) [:3.0.0.CR2]
at org.jboss.web.tomcat.security.JBossWebRealm.hasUserDataPermission(JBossWebRealm.java:668) [:6.0.0.Final]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:529) [:6.0.0.Final]
at org.jboss.modcluster.catalina.CatalinaContext$RequestListenerValve.event(CatalinaContext.java:285) [:1.1.0.Final]
at org.jboss.modcluster.catalina.CatalinaContext$RequestListenerValve.invoke(CatalinaContext.java:261) [:1.1.0.Final]
at org.jboss.web.tomcat.security.JaccContextValve.invoke(JaccContextValve.java:88) [:6.0.0.Final]
at org.jboss.web.tomcat.security.SecurityContextEstablishmentValve.invoke(SecurityContextEstablishmentValve.java:100) [:6.0.0.Final]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127) [:6.0.0.Final]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102) [:6.0.0.Final]
at org.jboss.web.tomcat.service.jca.CachedConnectionValve.invoke(CachedConnectionValve.java:158) [:6.0.0.Final]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109) [:6.0.0.Final]
at org.jboss.web.tomcat.service.request.ActiveRequestResponseCacheValve.invoke(ActiveRequestResponseCacheValve.java:53) [:6.0.0.Final]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:362) [:6.0.0.Final]
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:877) [:6.0.0.Final]
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:654) [:6.0.0.Final]
at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:951) [:6.0.0.Final]
at java.lang.Thread.run(Thread.java:662) [:1.6.0_35]
Obviously the AuthorizationManager cannot be created from the JaasSecurityManager but I'm not getting the reason for this. I thought this would be handled transparently by the SX framework as long as I provide a proper extension/implementation of one of the existing JBoss Login-Modules.
Has anybody tried to extend AbstractServerLoginModule with success or had a similar problem? Also, any clarification of the issue would be highly appreciated!
Here is how I set up the Login-Module:
The implementing class is part of the web-application which I'm trying to secure
package de.ig.ui.kundenpanel.authorization;
import java.io.IOException;
import java.security.Principal;
import java.security.acl.Group;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
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.jboss.security.SimpleGroup;
import org.jboss.security.SimplePrincipal;
import org.jboss.security.auth.spi.AbstractServerLoginModule;
import de.ig.common.bo.i24.authentication.AuthLoginRequest;
import de.ig.common.bo.i24.authentication.AuthLoginResponse;
import de.ig.common.bo.userlogin.UserLogin;
import de.ig.common.exception.SoapClientException;
import de.ig.common.exception.TechnicalException;
import de.ig.common.tool.communication.IgServiceLocator;
import de.ig.common.tool.communication.SessionKeyAuthParams;
import de.ig.common.tool.communication.rmi.delegator.I24AuthenticationClientRMILocal;
import de.ig.common.tool.log.TransactionItem;
import de.ig.ui.kundenpanel.utility.TransactionItemFactory;
/**
* Custom login module that authenticates against I24 Panel Authentication
* Web-Service.
*
* @author mthielsch
*
*/
public class I24PanelLoginModule extends AbstractServerLoginModule {
private static final String I24_AUTHENTICATION_CLIENT = "i24_authentication_client";
/**
* The login identity
*/
private UserContext identity;
/**
* The user roles
*/
private Set<Principal> principalRoles = new HashSet<Principal>();
// user credentials
private String userName;
private String password;
private String callerIpAddress;
private I24AuthenticationClientRMILocal rmiAuthenticationClientDelegator = (I24AuthenticationClientRMILocal) IgServiceLocator
.getInstance().getService(IgServiceLocator.I24_AUTHENTICATION_SERVICE_LOCAL);
public I24PanelLoginModule() {
super();
}
@SuppressWarnings("rawtypes")
@Override
public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) {
super.initialize(subject, callbackHandler, sharedState, options);
}
@Override
public boolean login() throws LoginException {
log.trace("login");
loginOk = false;
try {
// If useFirstPass is true, look for the shared password
if (useFirstPass == true) {
initCredentialsFromSharedState();
} else {
// Use credentials from callback handler
initCredentialsFromCallbackHandler();
}
if (userName != null && password != null) {
AuthLoginRequest authLoginRequest = new AuthLoginRequest();
authLoginRequest.setUsername(userName);
authLoginRequest.setPassword(password);
authLoginRequest.setIp(callerIpAddress);
TransactionItem track = TransactionItemFactory.getTransactionItem();
SessionKeyAuthParams authParams = new SessionKeyAuthParams(null);
authParams.setTransactionItem(track);
AuthLoginResponse authLoginResponse = rmiAuthenticationClientDelegator.login(track, authParams,
authLoginRequest);
this.identity = createIdentity(userName, password, authLoginResponse.getPermissions(),
authLoginResponse.getAuthKey(), authLoginResponse.getCustomer(), callerIpAddress);
for (String role : authLoginResponse.getPermissions()) {
principalRoles.add(new SimplePrincipal(role));
}
loginOk = true;
return true;
}
// Else, fall through and perform the login
} catch (UnsupportedCallbackException uce) {
log.error("CallbackHandler failed to provide login credentials", uce);
throw new LoginException("CallbackHandler failed to provide login credentials");
} catch (SoapClientException sce) {
log.error("Calling I24 Authentication Web-Service failed", sce);
throw new LoginException("Calling I24 Authentication Web-Service failed");
} catch (TechnicalException te) {
log.error("Calling I24 Authentication Web-Service failed", te);
throw new LoginException("Calling I24 Authentication Web-Service failed");
} catch (IOException ioe) {
log.error("Got IOException during login process", ioe);
throw new LoginException("Login failed while reading provided credentials");
}
return false;
}
private void initCredentialsFromCallbackHandler() throws IOException, UnsupportedCallbackException {
Callback[] callbacks = new Callback[3];
callbacks[0] = new NameCallback("username");
callbacks[1] = new PasswordCallback("password", false);
callbacks[2] = new IPAddressCallback();
callbackHandler.handle(callbacks);
userName = ((NameCallback) callbacks[0]).getName();
password = new String(((PasswordCallback) callbacks[1]).getPassword());
callerIpAddress = ((IPAddressCallback) callbacks[2]).getIpAddress();
}
private void initCredentialsFromSharedState() {
Object userNameObject = sharedState.get("javax.security.auth.login.name");
Object passwordObject = sharedState.get("javax.security.auth.login.password");
userName = extractUserName(userNameObject);
password = extractPassword(passwordObject);
}
private String extractUserName(Object userNameObject) {
if (userNameObject instanceof Principal) {
return ((Principal) userNameObject).getName();
} else if (userNameObject != null) {
return userNameObject.toString();
}
return null;
}
private String extractPassword(Object passwordObject) {
if (passwordObject instanceof char[])
return new String((char[]) passwordObject);
else if (passwordObject != null) {
return passwordObject.toString();
}
return null;
}
private UserContext createIdentity(String userName, String password, List<String> roles, String sessionToken,
String customerId, String ipAddress) throws LoginException {
try {
UserContext identity = new UserContext();
identity.setPrincipalName(userName);
identity.setSessionToken(sessionToken);
identity.setRemoteAddr(ipAddress);
UserLogin userLogin = new UserLogin();
userLogin.setUserName(userName);
userLogin.setPassword(password);
userLogin.setRoles(roles);
userLogin.setCustomerId(customerId);
identity.setUserLogin(userLogin);
return identity;
} catch (Exception e) {
log.debug("Failed to create identity", e);
throw new LoginException("Failed to create principal: " + e.getMessage());
}
}
@Override
public boolean commit() throws LoginException {
if (loginOk == false)
return false;
// Add subject principals
Set<Principal> principals = subject.getPrincipals();
if (!principals.contains(identity)) {
principals.add(identity);
}
Group[] roleSets = getRoleSets();
for (int g = 0; g < roleSets.length; g++) {
Group group = roleSets[g];
String name = group.getName();
Group subjectGroup = createGroup(name, principals);
// Copy the group members to the Subject group
Enumeration<? extends Principal> members = group.members();
while (members.hasMoreElements()) {
Principal role = (Principal) members.nextElement();
subjectGroup.addMember(role);
}
}
return true;
}
@Override
protected Principal getIdentity() {
return identity;
}
@Override
public boolean logout() throws LoginException {
subject.getPrincipals().remove(this.identity);
this.identity = null;
this.userName = null;
this.password = null;
this.callerIpAddress = null;
this.principalRoles = null;
return true;
}
@Override
protected Group[] getRoleSets() throws LoginException {
Group roles = new SimpleGroup("Roles");
for (Principal role : principalRoles) {
roles.addMember(role);
}
return new Group[] { roles };
}
}
Definition of the module within /server/default/conf/login-config.xml is as follows
<application-policy name="i24-panel-ws-auth">
<authentication>
<login-module code="de.ig.ui.kundenpanel.authorization.I24PanelLoginModule" flag="required"> </login-module>
</authentication>
</application-policy>
Which is then referenced in the jboss-web.xml within the WAR
<jboss-web>
<security-domain>i24-panel-ws-auth</security-domain>
<context-root>myContextRoot</context-root>
</jboss-web>
And finally the security configuration within the web.xml:
<welcome-file-list> <welcome-file>/pages/login.xhtml</welcome-file> </welcome-file-list> <login-config> <auth-method>FORM</auth-method> <realm-name>i24-panel-ws-auth</realm-name> <form-login-config> <form-login-page>/pages/login.xhtml</form-login-page> <form-error-page>/pages/login.xhtml</form-error-page> </form-login-config> </login-config> <security-role> <role-name>AuthAll</role-name> </security-role> <security-constraint> <web-resource-collection> <web-resource-name>myContextRoot</web-resource-name> <url-pattern>/pages/*</url-pattern> <http-method>GET</http-method> <http-method>POST</http-method> </web-resource-collection> <auth-constraint> <role-name>AuthAll</role-name> </auth-constraint> </security-constraint>