001 /** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package org.apache.geronimo.tomcat; 018 019 import java.beans.PropertyChangeListener; 020 import java.io.IOException; 021 import java.util.HashMap; 022 import java.util.List; 023 import java.util.Map; 024 025 import javax.security.auth.Subject; 026 import javax.security.jacc.PolicyContext; 027 import javax.servlet.Servlet; 028 import javax.servlet.ServletException; 029 030 import org.apache.catalina.Container; 031 import org.apache.catalina.LifecycleException; 032 import org.apache.catalina.LifecycleListener; 033 import org.apache.catalina.Loader; 034 import org.apache.catalina.Manager; 035 import org.apache.catalina.Valve; 036 import org.apache.catalina.Wrapper; 037 import org.apache.catalina.connector.Request; 038 import org.apache.catalina.connector.Response; 039 import org.apache.catalina.core.StandardContext; 040 import org.apache.catalina.ha.CatalinaCluster; 041 import org.apache.catalina.valves.ValveBase; 042 import org.apache.geronimo.common.DeploymentException; 043 import org.apache.geronimo.common.GeronimoSecurityException; 044 import org.apache.geronimo.security.ContextManager; 045 import org.apache.geronimo.security.jacc.RunAsSource; 046 import org.apache.geronimo.tomcat.interceptor.BeforeAfter; 047 import org.apache.geronimo.tomcat.interceptor.ComponentContextBeforeAfter; 048 import org.apache.geronimo.tomcat.interceptor.InstanceContextBeforeAfter; 049 import org.apache.geronimo.tomcat.interceptor.PolicyContextBeforeAfter; 050 import org.apache.geronimo.tomcat.interceptor.UserTransactionBeforeAfter; 051 import org.apache.geronimo.tomcat.listener.RunAsInstanceListener; 052 import org.apache.geronimo.tomcat.util.SecurityHolder; 053 import org.apache.geronimo.tomcat.valve.DefaultSubjectValve; 054 import org.apache.geronimo.tomcat.valve.GeronimoBeforeAfterValve; 055 import org.apache.geronimo.webservices.POJOWebServiceServlet; 056 import org.apache.geronimo.webservices.WebServiceContainer; 057 import org.apache.geronimo.webservices.WebServiceContainerInvoker; 058 059 060 public class GeronimoStandardContext extends StandardContext { 061 062 private static final long serialVersionUID = 3834587716552831032L; 063 064 private Subject defaultSubject = null; 065 private RunAsSource runAsSource = RunAsSource.NULL; 066 067 private Map webServiceMap = null; 068 069 private boolean pipelineInitialized; 070 071 private BeforeAfter beforeAfter = null; 072 private int contextCount = 0; 073 074 075 public void setContextProperties(TomcatContext ctx) throws DeploymentException { 076 077 // Create ReadOnlyContext 078 javax.naming.Context enc = ctx.getJndiContext(); 079 setInstanceManager(ctx.getInstanceManager()); 080 081 //try to make sure this mbean properties match those of the TomcatWebAppContext 082 if (ctx instanceof TomcatWebAppContext) { 083 TomcatWebAppContext tctx = (TomcatWebAppContext) ctx; 084 setJavaVMs(tctx.getJavaVMs()); 085 setServer(tctx.getServer()); 086 setJ2EEApplication(tctx.getJ2EEApplication()); 087 setJ2EEServer(tctx.getJ2EEServer()); 088 //install jasper injection support if required 089 if (tctx.getRuntimeCustomizer() != null) { 090 Map<String, Object> servletContext = new HashMap<String, Object>(); 091 Map<Class, Object> customizerContext = new HashMap<Class, Object>(); 092 customizerContext.put(Map.class, servletContext); 093 customizerContext.put(javax.naming.Context.class, enc); 094 tctx.getRuntimeCustomizer().customize(customizerContext); 095 for (Map.Entry<String, Object> entry: servletContext.entrySet()) { 096 getServletContext().setAttribute(entry.getKey(), entry.getValue()); 097 } 098 } 099 } 100 101 int index = 0; 102 BeforeAfter interceptor = new InstanceContextBeforeAfter(null, 103 index++, 104 index++, ctx.getUnshareableResources(), 105 ctx.getApplicationManagedSecurityResources(), 106 ctx.getTrackedConnectionAssociator()); 107 108 // Set ComponentContext BeforeAfter 109 if (enc != null) { 110 interceptor = new ComponentContextBeforeAfter(interceptor, index++, enc); 111 } 112 113 //Set a PolicyContext BeforeAfter 114 SecurityHolder securityHolder = ctx.getSecurityHolder(); 115 if (securityHolder != null) { 116 117 // save the role designates for mapping servlets to their run-as roles 118 runAsSource = securityHolder.getRunAsSource(); 119 120 if (securityHolder.getPolicyContextID() != null) { 121 122 PolicyContext.setContextID(securityHolder.getPolicyContextID()); 123 124 /** 125 * Register our default subject with the ContextManager 126 */ 127 defaultSubject = securityHolder.getDefaultSubject(); 128 129 if (defaultSubject == null) { 130 defaultSubject = ContextManager.EMPTY; 131 } 132 133 interceptor = new PolicyContextBeforeAfter(interceptor, index++, index++, index++, securityHolder.getPolicyContextID(), defaultSubject); 134 135 } 136 } 137 138 //Set a UserTransactionBeforeAfter 139 interceptor = new UserTransactionBeforeAfter(interceptor, index++, ctx.getUserTransaction()); 140 141 //Set the BeforeAfters as a valve 142 GeronimoBeforeAfterValve geronimoBAValve = new GeronimoBeforeAfterValve(interceptor, index); 143 addValve(geronimoBAValve); 144 beforeAfter = interceptor; 145 contextCount = index; 146 147 //Not clear if user defined valves should be involved in init processing. Probably not since 148 //request and response are null. 149 150 addValve(new SystemMethodValve()); 151 152 // Add User Defined Valves 153 List valveChain = ctx.getValveChain(); 154 if (valveChain != null) { 155 for (Object valve : valveChain) { 156 addValve((Valve)valve); 157 } 158 } 159 160 // Add User Defined Listeners 161 List listenerChain = ctx.getLifecycleListenerChain(); 162 if (listenerChain != null) { 163 for (Object listener : listenerChain) { 164 addLifecycleListener((LifecycleListener)listener); 165 } 166 } 167 168 CatalinaCluster cluster = ctx.getCluster(); 169 if (cluster != null) 170 this.setCluster(cluster); 171 172 Manager manager = ctx.getManager(); 173 if (manager != null) 174 this.setManager(manager); 175 176 pipelineInitialized = true; 177 this.webServiceMap = ctx.getWebServices(); 178 179 this.setCrossContext(ctx.isCrossContext()); 180 181 this.setCookies(!ctx.isDisableCookies()); 182 183 //Set the Dispatch listener 184 this.addInstanceListener("org.apache.geronimo.tomcat.listener.DispatchListener"); 185 186 //Set the run-as listener. listeners must be added before start() is called 187 if (runAsSource != null) { 188 this.addInstanceListener(RunAsInstanceListener.class.getName()); 189 } 190 } 191 192 public synchronized void start() throws LifecycleException { 193 if (pipelineInitialized) { 194 try { 195 Valve valve = getFirst(); 196 valve.invoke(null, null); 197 //Install the DefaultSubjectValve after the authentication valve so the default subject is supplied 198 //only if no real subject is authenticated. 199 200 Valve defaultSubjectValve = new DefaultSubjectValve(defaultSubject); 201 addValve(defaultSubjectValve); 202 203 // if a servlet uses run-as then make sure role desgnates have been provided 204 if (hasRunAsServlet()) { 205 if (runAsSource == null) { 206 throw new GeronimoSecurityException("web.xml or annotation specifies a run-as role but no subject configuration supplied for run-as roles"); 207 } 208 } else { 209 // optimization 210 this.removeInstanceListener(RunAsInstanceListener.class.getName()); 211 } 212 213 } catch (IOException e) { 214 if (e.getCause() instanceof LifecycleException) { 215 throw (LifecycleException) e.getCause(); 216 } 217 throw new LifecycleException(e); 218 } catch (ServletException e) { 219 throw new LifecycleException(e); 220 } 221 } else 222 super.start(); 223 } 224 225 public void addChild(Container child) { 226 Wrapper wrapper = (Wrapper) child; 227 228 String servletClassName = wrapper.getServletClass(); 229 if (servletClassName == null) { 230 super.addChild(child); 231 return; 232 } 233 234 ClassLoader cl = this.getParentClassLoader(); 235 236 Class baseServletClass; 237 Class servletClass; 238 try { 239 baseServletClass = cl.loadClass(Servlet.class.getName()); 240 servletClass = cl.loadClass(servletClassName); 241 //Check if the servlet is of type Servlet class 242 if (!baseServletClass.isAssignableFrom(servletClass)) { 243 //Nope - its probably a webservice, so lets see... 244 if (webServiceMap != null) { 245 WebServiceContainer webServiceContainer = (WebServiceContainer) webServiceMap.get(wrapper.getName()); 246 247 if (webServiceContainer != null) { 248 //Yep its a web service 249 //So swap it out with a POJOWebServiceServlet 250 wrapper.setServletClass("org.apache.geronimo.webservices.POJOWebServiceServlet"); 251 252 //Set the WebServiceContainer stuff 253 String webServicecontainerID = wrapper.getName() + WebServiceContainerInvoker.WEBSERVICE_CONTAINER + webServiceContainer.hashCode(); 254 getServletContext().setAttribute(webServicecontainerID, webServiceContainer); 255 wrapper.addInitParameter(WebServiceContainerInvoker.WEBSERVICE_CONTAINER, webServicecontainerID); 256 257 //Set the SEI Class in the attribute 258 String pojoClassID = wrapper.getName() + POJOWebServiceServlet.POJO_CLASS + servletClass.hashCode(); 259 getServletContext().setAttribute(pojoClassID, servletClass); 260 wrapper.addInitParameter(POJOWebServiceServlet.POJO_CLASS, pojoClassID); 261 } 262 } 263 } 264 } catch (ClassNotFoundException e) { 265 throw new RuntimeException(e.getMessage(), e); 266 } 267 268 super.addChild(child); 269 } 270 271 public synchronized void setLoader(final Loader delegate) { 272 Loader loader = new Loader() { 273 274 public void backgroundProcess() { 275 delegate.backgroundProcess(); 276 } 277 278 public ClassLoader getClassLoader() { 279 // Implementation Note: the actual CL to be used by this 280 // context is the Geronimo one and not the Tomcat one. 281 return parentClassLoader; 282 } 283 284 public Container getContainer() { 285 return delegate.getContainer(); 286 } 287 288 public void setContainer(Container container) { 289 delegate.setContainer(container); 290 } 291 292 public boolean getDelegate() { 293 return delegate.getDelegate(); 294 } 295 296 public void setDelegate(boolean delegateBoolean) { 297 delegate.setDelegate(delegateBoolean); 298 } 299 300 public String getInfo() { 301 return delegate.getInfo(); 302 } 303 304 public boolean getReloadable() { 305 return false; 306 } 307 308 public void setReloadable(boolean reloadable) { 309 if (reloadable) { 310 throw new UnsupportedOperationException("Reloadable context is not supported."); 311 } 312 } 313 314 public void addPropertyChangeListener(PropertyChangeListener listener) { 315 delegate.addPropertyChangeListener(listener); 316 } 317 318 public void addRepository(String repository) { 319 delegate.addRepository(repository); 320 } 321 322 public String[] findRepositories() { 323 return delegate.findRepositories(); 324 } 325 326 public boolean modified() { 327 return delegate.modified(); 328 } 329 330 public void removePropertyChangeListener(PropertyChangeListener listener) { 331 delegate.removePropertyChangeListener(listener); 332 } 333 }; 334 335 super.setLoader(loader); 336 } 337 338 private class SystemMethodValve extends ValveBase { 339 340 public void invoke(Request request, Response response) throws IOException, ServletException { 341 if (request == null && response == null) { 342 try { 343 GeronimoStandardContext.super.start(); 344 } catch (LifecycleException e) { 345 throw (IOException) new IOException("wrapping lifecycle exception").initCause(e); 346 } 347 if (GeronimoStandardContext.this.getState() != 1 || !GeronimoStandardContext.this.getAvailable()){ 348 throw new IOException("Context did not start for an unknown reason"); 349 } 350 } else { 351 getNext().invoke(request, response); 352 } 353 354 } 355 } 356 357 358 public BeforeAfter getBeforeAfter() { 359 return beforeAfter; 360 } 361 362 public int getContextCount() { 363 return contextCount; 364 } 365 366 /** 367 * Determine if the context has at least one servlet that specifies a run-as role 368 * @return true if at least one servlet specifies a run-as role, false otherwise 369 */ 370 protected boolean hasRunAsServlet() { 371 for (Container servlet : findChildren()) { 372 if (servlet instanceof Wrapper) { 373 if (((Wrapper)servlet).getRunAs() != null) { 374 return true; 375 } 376 } 377 } 378 return false; 379 } 380 381 /** 382 * Get the Subject for the servlet's run-as role 383 * @param runAsRole Name of run as role to get Subject for 384 * @return Subject for the servlet's run-as role, if specified. otherwise null. 385 */ 386 public Subject getSubjectForRole(String runAsRole) { 387 return runAsSource.getSubjectForRole(runAsRole); 388 } 389 }