1 /*
2 * JBoss, Home of Professional Open Source
3 * Copyright 2005, JBoss Inc., and individual contributors as indicated
4 * by the @authors tag. See the copyright.txt in the distribution for a
5 * full listing of individual contributors.
6 *
7 * This is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation; either version 2.1 of
10 * the License, or (at your option) any later version.
11 *
12 * This software is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this software; if not, write to the Free
19 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21 */
22 package org.jboss.web.tomcat.security;
23
24 import java.io.IOException;
25 import java.security.Principal;
26
27 import javax.naming.InitialContext;
28 import javax.naming.NamingException;
29 import javax.security.auth.Subject;
30 import javax.servlet.ServletException;
31 import javax.servlet.http.HttpSession;
32
33 import org.apache.catalina.Manager;
34 import org.apache.catalina.Session;
35 import org.apache.catalina.Wrapper;
36 import org.apache.catalina.connector.Request;
37 import org.apache.catalina.connector.Response;
38 import org.apache.catalina.valves.ValveBase;
39 import org.jboss.logging.Logger;
40 import org.jboss.metadata.javaee.jboss.RunAsIdentityMetaData;
41 import org.jboss.metadata.web.jboss.JBossWebMetaData;
42 import org.jboss.security.AuthenticationManager;
43 import org.jboss.security.RunAsIdentity;
44 import org.jboss.security.plugins.JaasSecurityManagerServiceMBean;
45
46 /**
47 * A Valve that sets/clears the SecurityAssociation information associated with
48 * the request thread for identity propagation.
49 *
50 * @author Scott.Stark@jboss.org
51 * @author Thomas.Diesler@jboss.org
52 * @author Anil.Saldhana@jboss.org
53 * @version $Revision: 67525 $
54 */
55 public class SecurityAssociationValve extends ValveBase
56 {
57 private static Logger log = Logger.getLogger(SecurityAssociationValve.class);
58 public static ThreadLocal<Principal> userPrincipal = new ThreadLocal<Principal>();
59 /** Maintain the active WebMetaData for request security checks */
60 public static ThreadLocal<JBossWebMetaData> activeWebMetaData = new ThreadLocal<JBossWebMetaData>();
61 /** Maintain the Catalina Request for programmatic web login */
62 public static ThreadLocal<Request> activeRequest = new ThreadLocal<Request>();
63 /** Maintain the Catalina Response for programmatic web login */
64 public static ThreadLocal activeResponse = new ThreadLocal();
65
66 /** The web app metadata */
67 private JBossWebMetaData metaData;
68 /** The name in the session under which the Subject is stored */
69 private String subjectAttributeName = null;
70 /** The service used to flush authentication cache on session invalidation. */
71 private JaasSecurityManagerServiceMBean secMgrService;
72 private boolean trace;
73
74 public SecurityAssociationValve(JBossWebMetaData metaData,
75 JaasSecurityManagerServiceMBean secMgrService)
76 {
77 this.metaData = metaData;
78 this.secMgrService = secMgrService;
79 this.trace = log.isTraceEnabled();
80 }
81
82 /**
83 * The name of the request attribute under with the authenticated JAAS
84 * Subject is stored on successful authentication. If null or empty then
85 * the Subject will not be stored.
86 */
87 public void setSubjectAttributeName(String subjectAttributeName)
88 {
89 this.subjectAttributeName = subjectAttributeName;
90 if (subjectAttributeName != null && subjectAttributeName.length() == 0)
91 this.subjectAttributeName = null;
92 }
93
94 public void invoke(Request request, Response response)
95 throws IOException, ServletException
96 {
97 Session session = null;
98 // Get the request caller which could be set due to SSO
99 Principal caller = request.getPrincipal();
100 // The cached web container principal
101 JBossGenericPrincipal principal = null;
102 HttpSession hsession = request.getSession(false);
103
104 if( trace )
105 log.trace("Begin invoke, caller"+caller);
106 // Set the active meta data
107 activeWebMetaData.set(metaData);
108 //Set the active request and response objects
109 activeRequest.set(request);
110 activeResponse.set(response);
111
112 try
113 {
114 Wrapper servlet = null;
115 try
116 {
117 servlet = request.getWrapper();
118 if (servlet != null)
119 {
120 String name = servlet.getName();
121 RunAsIdentityMetaData identity = metaData.getRunAsIdentity(name);
122 RunAsIdentity runAsIdentity = null;
123 if(identity != null)
124 {
125 if (trace)
126 log.trace(name + ", runAs: " + identity);
127 runAsIdentity = new RunAsIdentity(identity.getRoleName(),
128 identity.getPrincipalName(), identity.getRunAsRoles());
129 }
130 SecurityAssociationActions.pushRunAsIdentity(runAsIdentity);
131 }
132 userPrincipal.set(caller);
133
134 // If there is a session, get the tomcat session for the principal
135 Manager manager = container.getManager();
136 if (manager != null && hsession != null)
137 {
138 try
139 {
140 session = manager.findSession(hsession.getId());
141 }
142 catch (IOException ignore)
143 {
144 }
145 }
146
147 if (caller == null || (caller instanceof JBossGenericPrincipal) == false)
148 {
149 // Look to the session for the active caller security context
150 if (session != null)
151 {
152 principal =
153 (JBossGenericPrincipal) session.getPrincipal();
154 }
155 }
156 else
157 {
158 // Use the request principal as the caller identity
159 principal = (JBossGenericPrincipal) caller;
160 }
161
162 // If there is a caller use this as the identity to propagate
163 if (principal != null)
164 {
165 if (trace)
166 log.trace("Restoring principal info from cache");
167 SecurityAssociationActions.setPrincipalInfo(principal.getAuthPrincipal(),
168 principal.getCredentials(), principal.getSubject());
169 }
170 // Put the authenticated subject in the session if requested
171 if (subjectAttributeName != null)
172 {
173 javax.naming.Context securityNamingCtx = getSecurityNamingContext();
174 if (securityNamingCtx != null)
175 {
176 // Get the JBoss security manager from the ENC context
177 AuthenticationManager securityMgr = (AuthenticationManager) securityNamingCtx.lookup("securityMgr");
178 Subject subject = securityMgr.getActiveSubject();
179 request.getRequest().setAttribute(subjectAttributeName, subject);
180 }
181 }
182 }
183 catch (Throwable e)
184 {
185 log.debug("Failed to determine servlet", e);
186 }
187
188 // Perform the request
189 getNext().invoke(request, response);
190 if(servlet != null)
191 {
192 SecurityAssociationActions.popRunAsIdentity();
193 }
194
195 /* If the security domain cache is to be kept in synch with the
196 session then flush the cache if the session has been invalidated.
197 */
198 if( secMgrService != null &&
199 session != null && session.isValid() == false &&
200 metaData.isFlushOnSessionInvalidation() == true )
201 {
202 if( principal != null )
203 {
204 String securityDomain = metaData.getSecurityDomain();
205 if (trace)
206 {
207 log.trace("Session is invalid, security domain: "+securityDomain
208 +", user="+principal);
209 }
210 try
211 {
212 Principal authPrincipal = principal.getAuthPrincipal();
213 secMgrService.flushAuthenticationCache(securityDomain, authPrincipal);
214 }
215 catch(Exception e)
216 {
217 log.debug("Failed to flush auth cache", e);
218 }
219 }
220 }
221 }
222 finally
223 {
224 if( trace )
225 log.trace("End invoke, caller"+caller);
226 activeWebMetaData.set(null);
227 userPrincipal.set(null);
228 activeRequest.set(null);
229 activeResponse.set(null);
230 }
231 }
232
233 private javax.naming.Context getSecurityNamingContext()
234 {
235 javax.naming.Context securityCtx = null;
236 // Get the JBoss security manager from the ENC context
237 try
238 {
239 InitialContext iniCtx = new InitialContext();
240 securityCtx = (javax.naming.Context) iniCtx.lookup("java:comp/env/security");
241 }
242 catch (NamingException e)
243 {
244 // Apparently there is no security context?
245 }
246 return securityCtx;
247 }
248 }