/*
* JBoss, Home of Professional Open Source.
* Copyright 2008, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.web.tomcat.security;
import java.security.Principal;
import java.security.acl.Group;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import javax.management.JMException;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.naming.InitialContext;
import javax.security.auth.Subject;
import javax.security.jacc.PolicyContext;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
import org.jboss.logging.Logger;
import org.jboss.mx.util.MBeanServerLocator;
import org.jboss.security.SubjectSecurityManager;
/**
* JBAS-2151: Look into implementing flushOnSessionInvalidation
* using a session listener
* @author < a href="mailto:Anil.Saldhana@jboss.org">Anil Saldhana</a>
* @since Jan 13, 2006
* @version $Revision: 94433 $
*/
public class SecurityFlushSessionListener implements HttpSessionListener
{
private static Logger log = Logger.getLogger(SecurityFlushSessionListener.class);
private boolean trace = log.isTraceEnabled();
private String securityDomain = null;
private static final String JBOSS_PRINCIPAL = "org.jboss.web.tomcat.security.principal";
/**
*
* Create a new SecurityFlushSessionListener.
*
*/
public SecurityFlushSessionListener()
{
}
public void sessionCreated(HttpSessionEvent httpSessionEvent)
{
if(trace)
log.trace("Session Created with id=" + httpSessionEvent.getSession().getId());
}
public void sessionDestroyed(HttpSessionEvent httpSessionEvent)
{
if(trace)
log.trace("Session Destroy with id=" + httpSessionEvent.getSession().getId());
try
{
Subject subject = getSubjectAndSecurityDomain();
if(trace)
log.trace("securityDomain="+ securityDomain);
if(securityDomain == null)
log.debug("Unable to obtain SecurityDomain");
Principal principal = getPrincipal(subject);
if(principal == null)
{
if(trace)
log.trace("Searching for principal in the session");
principal = (Principal) httpSessionEvent.getSession().getAttribute(JBOSS_PRINCIPAL);
}
if(principal != null && securityDomain != null)
flushAuthenticationCache(principal);
}catch(Exception e)
{
log.error("Exception in sessionDestroyed:",e);
}
}
/**
* Given the security domain and the Principal,
* flush the authentication cache
*
* @param principal
* @throws JMException
*/
private void flushAuthenticationCache(Principal principal) throws JMException
{
MBeanServer server = MBeanServerLocator.locateJBoss();
ObjectName on = new ObjectName("jboss.security:service=JaasSecurityManager");
Object[] obj = new Object[] {securityDomain, principal};
String[] sig = new String[]{"java.lang.String", "java.security.Principal"};
if(trace)
logAuthenticatedPrincipals(on, true);
//Flush the Authentication Cache
server.invoke(on,"flushAuthenticationCache", obj, sig);
if(trace)
logAuthenticatedPrincipals(on, false);
}
/**
* Get the Principal given the authenticated Subject
* Currently the first principal that is not of type
* java.security.acl.Group is considered
*
* @param subject
* @return the authenticated principal
*/
private Principal getPrincipal(Subject subject)
{
Principal principal = null;
if(subject != null)
{
Set principals = subject.getPrincipals();
if(principals != null || !principals.isEmpty())
{
Iterator iter = principals.iterator();
while(iter.hasNext())
{
principal = (Principal)iter.next();
if(principal instanceof Group == false)
break;
}
}
}
if(trace)
log.trace("Authenticated Principal=" + principal);
return principal;
}
/**
* Method that sets the securityDomain
* and then returns the authenticated subject
* First preference is given to the subject available
* from the Jacc SubjectContextPolicyContextHandler.
* As, a fallback, the Subject is obtained from the
* Security Manager Service
*
* @return
*/
private Subject getSubjectAndSecurityDomain() throws Exception
{
SubjectSecurityManager mgr = null;
try
{
mgr = getSecurityManagerService();
}catch(Exception e)
{
log.debug("Obtaining SecurityManagerService failed::",e);
}
//First get the JACC Subject
String SUBJECT_CONTEXT_KEY = "javax.security.auth.Subject.container";
Subject subject = (Subject) PolicyContext.getContext(SUBJECT_CONTEXT_KEY);
if(trace)
log.trace("Jacc Subject = " + subject);
if(mgr != null)
securityDomain = mgr.getSecurityDomain();
//Fallback
if(subject == null && mgr != null)
{
subject = mgr.getActiveSubject();
if(trace)
log.trace("Active Subject from security mgr service = " + subject);
}
return subject;
}
/**
* Get the Security Manager Service
*
* @return
* @throws Exception
*/
private SubjectSecurityManager getSecurityManagerService() throws Exception
{
//Get the SecurityManagerService from JNDI
InitialContext ctx = new InitialContext();
return (SubjectSecurityManager) ctx.lookup("java:comp/env/security/securityMgr");
}
/**
* Method used to log authenticated principals
* remaining in cache (only when TRACE level logging is enabled)
*
* @param on ObjectName of the JaasSecurityManagerService
* @param isBeforeFlush Is the logging done before the auth cache flush
*/
private void logAuthenticatedPrincipals(ObjectName on, boolean isBeforeFlush)
throws JMException
{
if(isBeforeFlush)
log.trace("Before flush of authentication cache::");
else
log.trace("After flush of authentication cache::");
MBeanServer server = MBeanServerLocator.locateJBoss();
List list = (List)server.invoke(on,"getAuthenticationCachePrincipals",
new Object[]{securityDomain}, new String[] {"java.lang.String"} );
int len = list != null ? list.size() : 0;
log.trace("Number of authenticated principals remaining in cache=" + len);
for(int i = 0 ; i < len; i++)
log.trace("Authenticated principal in cache=" + list.get(i));
}
}