001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jboss.web.tomcat.security;
023:
024: import java.io.IOException;
025: import java.security.Principal;
026: import javax.naming.InitialContext;
027: import javax.naming.NamingException;
028: import javax.servlet.ServletException;
029: import javax.servlet.http.HttpSession;
030: import javax.security.auth.Subject;
031:
032: import org.apache.catalina.Session;
033: import org.apache.catalina.Wrapper;
034: import org.apache.catalina.Manager;
035: import org.apache.catalina.connector.Request;
036: import org.apache.catalina.connector.Response;
037: import org.apache.catalina.valves.ValveBase;
038: import org.jboss.logging.Logger;
039: import org.jboss.metadata.WebMetaData;
040: import org.jboss.security.AuthenticationManager;
041: import org.jboss.security.RunAsIdentity;
042: import org.jboss.security.plugins.JaasSecurityManagerServiceMBean;
043:
044: /**
045: * A Valve that sets/clears the SecurityAssociation information associated with
046: * the request thread for identity propagation.
047: *
048: * @author Scott.Stark@jboss.org
049: * @author Thomas.Diesler@jboss.org
050: * @author Anil.Saldhana@redhat.com
051: * @version $Revision: 61325 $
052: */
053: public class SecurityAssociationValve extends ValveBase {
054: private static Logger log = Logger
055: .getLogger(SecurityAssociationValve.class);
056: public static ThreadLocal userPrincipal = new ThreadLocal();
057: /** Maintain the active WebMetaData for request security checks */
058: public static ThreadLocal activeWebMetaData = new ThreadLocal();
059: /** Maintain the Catalina Request for programmatic web login */
060: public static ThreadLocal activeRequest = new ThreadLocal();
061:
062: /** The web app metadata */
063: private WebMetaData metaData;
064: /** The name in the session under which the Subject is stored */
065: private String subjectAttributeName = null;
066: /** The service used to flush authentication cache on session invalidation. */
067: private JaasSecurityManagerServiceMBean secMgrService;
068: private boolean trace;
069:
070: public SecurityAssociationValve(WebMetaData metaData,
071: JaasSecurityManagerServiceMBean secMgrService) {
072: this .metaData = metaData;
073: this .secMgrService = secMgrService;
074: this .trace = log.isTraceEnabled();
075: }
076:
077: /**
078: * The name of the request attribute under with the authenticated JAAS
079: * Subject is stored on successful authentication. If null or empty then
080: * the Subject will not be stored.
081: */
082: public void setSubjectAttributeName(String subjectAttributeName) {
083: this .subjectAttributeName = subjectAttributeName;
084: if (subjectAttributeName != null
085: && subjectAttributeName.length() == 0)
086: this .subjectAttributeName = null;
087: }
088:
089: public void invoke(Request request, Response response)
090: throws IOException, ServletException {
091: Session session = null;
092: // Get the request caller which could be set due to SSO
093: //Principal caller = request.getUserPrincipal();
094: Principal caller = request.getPrincipal();
095: // The cached web container principal
096: JBossGenericPrincipal principal = null;
097: HttpSession hsession = request.getSession(false);
098:
099: if (trace)
100: log.trace("Begin invoke, caller" + caller);
101: // Set the active meta data
102: activeWebMetaData.set(metaData);
103: //Set the active request
104: activeRequest.set(request);
105: try {
106: try {
107: Wrapper servlet = request.getWrapper();
108: if (servlet != null) {
109: String name = servlet.getName();
110: RunAsIdentity identity = metaData
111: .getRunAsIdentity(name);
112: if (identity != null) {
113: if (trace)
114: log.trace(name + ", runAs: " + identity);
115: }
116: SecurityAssociationActions
117: .pushRunAsIdentity(identity);
118: }
119: userPrincipal.set(caller);
120:
121: // If there is a session, get the tomcat session for the principal
122: Manager manager = container.getManager();
123: if (manager != null && hsession != null) {
124: try {
125: session = manager.findSession(hsession.getId());
126: } catch (IOException ignore) {
127: }
128: }
129:
130: if (caller == null
131: || (caller instanceof JBossGenericPrincipal) == false) {
132: // Look to the session for the active caller security context
133: if (session != null) {
134: principal = (JBossGenericPrincipal) session
135: .getPrincipal();
136: }
137: } else {
138: // Use the request principal as the caller identity
139: principal = (JBossGenericPrincipal) caller;
140: }
141:
142: // If there is a caller use this as the identity to propagate
143: if (principal != null) {
144: if (trace)
145: log
146: .trace("Restoring principal info from cache");
147: SecurityAssociationActions.setPrincipalInfo(
148: principal.getAuthPrincipal(), principal
149: .getCredentials(), principal
150: .getSubject());
151: }
152: // Put the authenticated subject in the session if requested
153: if (subjectAttributeName != null) {
154: javax.naming.Context securityCtx = getSecurityContext();
155: if (securityCtx != null) {
156: // Get the JBoss security manager from the ENC context
157: AuthenticationManager securityMgr = (AuthenticationManager) securityCtx
158: .lookup("securityMgr");
159: Subject subject = securityMgr
160: .getActiveSubject();
161: request.getRequest().setAttribute(
162: subjectAttributeName, subject);
163: }
164: }
165: } catch (Throwable e) {
166: log.debug("Failed to determine servlet", e);
167: }
168: // Perform the request
169: getNext().invoke(request, response);
170: SecurityAssociationActions.popRunAsIdentity();
171:
172: /* If the security domain cache is to be kept in synch with the
173: session then flush the cache if the session has been invalidated.
174: */
175: if (secMgrService != null && session != null
176: && session.isValid() == false
177: && metaData.isFlushOnSessionInvalidation() == true) {
178: if (principal != null) {
179: String securityDomain = metaData
180: .getSecurityDomain();
181: if (trace) {
182: log
183: .trace("Session is invalid, security domain: "
184: + securityDomain
185: + ", user="
186: + principal);
187: }
188: try {
189: Principal authPrincipal = principal
190: .getAuthPrincipal();
191: secMgrService.flushAuthenticationCache(
192: securityDomain, authPrincipal);
193: } catch (Exception e) {
194: log.debug("Failed to flush auth cache", e);
195: }
196: }
197: }
198: } finally {
199: if (trace)
200: log.trace("End invoke, caller" + caller);
201: activeWebMetaData.set(null);
202: userPrincipal.set(null);
203: activeRequest.set(null);
204: }
205: }
206:
207: private javax.naming.Context getSecurityContext() {
208: javax.naming.Context securityCtx = null;
209: // Get the JBoss security manager from the ENC context
210: try {
211: InitialContext iniCtx = new InitialContext();
212: securityCtx = (javax.naming.Context) iniCtx
213: .lookup("java:comp/env/security");
214: } catch (NamingException e) {
215: // Apparently there is no security context?
216: }
217: return securityCtx;
218: }
219: }
|