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 018 package org.apache.geronimo.web25.deployment; 019 020 import java.io.File; 021 import java.io.IOException; 022 import java.net.URI; 023 import java.net.URISyntaxException; 024 import java.net.URL; 025 import java.security.Permission; 026 import java.security.PermissionCollection; 027 import java.security.Permissions; 028 import java.util.ArrayList; 029 import java.util.Collection; 030 import java.util.Collections; 031 import java.util.Enumeration; 032 import java.util.HashMap; 033 import java.util.HashSet; 034 import java.util.LinkedList; 035 import java.util.List; 036 import java.util.Map; 037 import java.util.Set; 038 import java.util.LinkedHashSet; 039 import java.util.jar.JarEntry; 040 import java.util.jar.JarFile; 041 import java.util.zip.ZipEntry; 042 043 import javax.security.jacc.WebResourcePermission; 044 import javax.security.jacc.WebRoleRefPermission; 045 import javax.security.jacc.WebUserDataPermission; 046 import javax.xml.namespace.QName; 047 048 import org.apache.commons.logging.Log; 049 import org.apache.commons.logging.LogFactory; 050 import org.apache.geronimo.common.DeploymentException; 051 import org.apache.geronimo.deployment.ModuleIDBuilder; 052 import org.apache.geronimo.deployment.NamespaceDrivenBuilderCollection; 053 import org.apache.geronimo.deployment.ClassPathList; 054 import org.apache.geronimo.deployment.ModuleList; 055 import org.apache.geronimo.deployment.util.DeploymentUtil; 056 import org.apache.geronimo.deployment.xbeans.ServiceDocument; 057 import org.apache.geronimo.deployment.xmlbeans.XmlBeansUtil; 058 import org.apache.geronimo.gbean.AbstractName; 059 import org.apache.geronimo.gbean.AbstractNameQuery; 060 import org.apache.geronimo.gbean.GBeanData; 061 import org.apache.geronimo.j2ee.annotation.Holder; 062 import org.apache.geronimo.j2ee.deployment.EARContext; 063 import org.apache.geronimo.j2ee.deployment.Module; 064 import org.apache.geronimo.j2ee.deployment.ModuleBuilder; 065 import org.apache.geronimo.j2ee.deployment.ModuleBuilderExtension; 066 import org.apache.geronimo.j2ee.deployment.NamingBuilder; 067 import org.apache.geronimo.j2ee.deployment.WebModule; 068 import org.apache.geronimo.j2ee.deployment.WebServiceBuilder; 069 import org.apache.geronimo.j2ee.deployment.annotation.SecurityAnnotationHelper; 070 import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory; 071 import org.apache.geronimo.kernel.Kernel; 072 import org.apache.geronimo.kernel.Naming; 073 import org.apache.geronimo.kernel.config.Configuration; 074 import org.apache.geronimo.kernel.config.ConfigurationModuleType; 075 import org.apache.geronimo.kernel.config.ConfigurationStore; 076 import org.apache.geronimo.kernel.repository.Artifact; 077 import org.apache.geronimo.kernel.repository.Environment; 078 import org.apache.geronimo.kernel.repository.ImportType; 079 import org.apache.geronimo.naming.deployment.ResourceEnvironmentSetter; 080 import org.apache.geronimo.schema.SchemaConversionUtils; 081 import org.apache.geronimo.security.jacc.ComponentPermissions; 082 import org.apache.geronimo.security.util.HTTPMethods; 083 import org.apache.geronimo.security.util.URLPattern; 084 import org.apache.geronimo.xbeans.geronimo.j2ee.GerSecurityDocument; 085 import org.apache.geronimo.xbeans.javaee.FilterMappingType; 086 import org.apache.geronimo.xbeans.javaee.FilterType; 087 import org.apache.geronimo.xbeans.javaee.FullyQualifiedClassType; 088 import org.apache.geronimo.xbeans.javaee.ListenerType; 089 import org.apache.geronimo.xbeans.javaee.RoleNameType; 090 import org.apache.geronimo.xbeans.javaee.SecurityConstraintType; 091 import org.apache.geronimo.xbeans.javaee.SecurityRoleRefType; 092 import org.apache.geronimo.xbeans.javaee.SecurityRoleType; 093 import org.apache.geronimo.xbeans.javaee.ServletMappingType; 094 import org.apache.geronimo.xbeans.javaee.ServletType; 095 import org.apache.geronimo.xbeans.javaee.UrlPatternType; 096 import org.apache.geronimo.xbeans.javaee.WebAppDocument; 097 import org.apache.geronimo.xbeans.javaee.WebAppType; 098 import org.apache.geronimo.xbeans.javaee.WebResourceCollectionType; 099 import org.apache.xbean.finder.ClassFinder; 100 import org.apache.xmlbeans.XmlCursor; 101 import org.apache.xmlbeans.XmlDocumentProperties; 102 import org.apache.xmlbeans.XmlException; 103 import org.apache.xmlbeans.XmlObject; 104 105 /** 106 * @version $Rev: 558910 $ $Date: 2007-07-23 21:41:35 -0400 (Mon, 23 Jul 2007) $ 107 */ 108 public abstract class AbstractWebModuleBuilder implements ModuleBuilder { 109 private static final Log log = LogFactory.getLog(AbstractWebModuleBuilder.class); 110 111 private static final QName TAGLIB = new QName(SchemaConversionUtils.JAVAEE_NAMESPACE, "taglib"); 112 113 private static final String LINE_SEP = System.getProperty("line.separator"); 114 115 protected static final AbstractNameQuery MANAGED_CONNECTION_FACTORY_PATTERN; 116 private static final AbstractNameQuery ADMIN_OBJECT_PATTERN; 117 protected static final AbstractNameQuery STATELESS_SESSION_BEAN_PATTERN; 118 protected static final AbstractNameQuery STATEFUL_SESSION_BEAN_PATTERN; 119 protected static final AbstractNameQuery ENTITY_BEAN_PATTERN; 120 protected final Kernel kernel; 121 protected final NamespaceDrivenBuilderCollection securityBuilders; 122 protected final NamespaceDrivenBuilderCollection serviceBuilders; 123 protected final ResourceEnvironmentSetter resourceEnvironmentSetter; 124 protected final Collection<WebServiceBuilder> webServiceBuilder; 125 126 protected final NamingBuilder namingBuilders; 127 protected final Collection<ModuleBuilderExtension> moduleBuilderExtensions; 128 129 private static final QName SECURITY_QNAME = GerSecurityDocument.type.getDocumentElementName(); 130 private static final QName SERVICE_QNAME = ServiceDocument.type.getDocumentElementName(); 131 132 /** 133 * Manifest classpath entries in a war configuration must be resolved relative to the war configuration, not the 134 * enclosing ear configuration. Resolving relative to he war configuration using this offset produces the same 135 * effect as URI.create(module.targetPath()).resolve(mcpEntry) executed in the ear configuration. 136 */ 137 private static final URI RELATIVE_MODULE_BASE_URI = URI.create("../"); 138 139 protected AbstractWebModuleBuilder(Kernel kernel, Collection securityBuilders, Collection serviceBuilders, NamingBuilder namingBuilders, ResourceEnvironmentSetter resourceEnvironmentSetter, Collection<WebServiceBuilder> webServiceBuilder, Collection<ModuleBuilderExtension> moduleBuilderExtensions) { 140 this.kernel = kernel; 141 this.securityBuilders = new NamespaceDrivenBuilderCollection(securityBuilders, SECURITY_QNAME); 142 this.serviceBuilders = new NamespaceDrivenBuilderCollection(serviceBuilders, SERVICE_QNAME); 143 this.namingBuilders = namingBuilders; 144 this.resourceEnvironmentSetter = resourceEnvironmentSetter; 145 this.webServiceBuilder = webServiceBuilder; 146 this.moduleBuilderExtensions = moduleBuilderExtensions == null? new ArrayList<ModuleBuilderExtension>(): moduleBuilderExtensions; 147 } 148 149 static { 150 MANAGED_CONNECTION_FACTORY_PATTERN = new AbstractNameQuery(null, Collections.singletonMap(NameFactory.J2EE_TYPE, NameFactory.JCA_MANAGED_CONNECTION_FACTORY)); 151 ADMIN_OBJECT_PATTERN = new AbstractNameQuery(null, Collections.singletonMap(NameFactory.J2EE_TYPE, NameFactory.JCA_ADMIN_OBJECT)); 152 STATELESS_SESSION_BEAN_PATTERN = new AbstractNameQuery(null, Collections.singletonMap(NameFactory.J2EE_TYPE, NameFactory.STATELESS_SESSION_BEAN)); 153 STATEFUL_SESSION_BEAN_PATTERN = new AbstractNameQuery(null, Collections.singletonMap(NameFactory.J2EE_TYPE, NameFactory.STATEFUL_SESSION_BEAN)); 154 ENTITY_BEAN_PATTERN = new AbstractNameQuery(null, Collections.singletonMap(NameFactory.J2EE_TYPE, NameFactory.ENTITY_BEAN)); 155 156 } 157 158 public NamingBuilder getNamingBuilders() { 159 return namingBuilders; 160 } 161 162 protected void addGBeanDependencies(EARContext earContext, GBeanData webModuleData) { 163 Configuration earConfiguration = earContext.getConfiguration(); 164 addDependencies(earContext.findGBeanDatas(earConfiguration, MANAGED_CONNECTION_FACTORY_PATTERN), webModuleData); 165 addDependencies(earContext.findGBeanDatas(earConfiguration, ADMIN_OBJECT_PATTERN), webModuleData); 166 addDependencies(earContext.findGBeanDatas(earConfiguration, STATELESS_SESSION_BEAN_PATTERN), webModuleData); 167 addDependencies(earContext.findGBeanDatas(earConfiguration, STATEFUL_SESSION_BEAN_PATTERN), webModuleData); 168 addDependencies(earContext.findGBeanDatas(earConfiguration, ENTITY_BEAN_PATTERN), webModuleData); 169 } 170 171 private void addDependencies(LinkedHashSet<GBeanData> dependencies, GBeanData webModuleData) { 172 for (GBeanData dependency: dependencies) { 173 AbstractName dependencyName = dependency.getAbstractName(); 174 webModuleData.addDependency(dependencyName); 175 } 176 } 177 178 public Module createModule(File plan, JarFile moduleFile, Naming naming, ModuleIDBuilder idBuilder) throws DeploymentException { 179 return createModule(plan, moduleFile, ".", null, true, null, null, naming, idBuilder); 180 } 181 182 public Module createModule(Object plan, JarFile moduleFile, String targetPath, URL specDDUrl, Environment environment, Object moduleContextInfo, AbstractName earName, Naming naming, ModuleIDBuilder idBuilder) throws DeploymentException { 183 return createModule(plan, moduleFile, targetPath, specDDUrl, false, (String) moduleContextInfo, earName, naming, idBuilder); 184 } 185 186 protected abstract Module createModule(Object plan, JarFile moduleFile, String targetPath, URL specDDUrl, boolean standAlone, String contextRoot, AbstractName earName, Naming naming, ModuleIDBuilder idBuilder) throws DeploymentException; 187 188 /** 189 * Some servlets will have multiple url patterns. However, webservice servlets 190 * will only have one, which is what this method is intended for. 191 * 192 * @param webApp spec deployment descriptor 193 * @param contextRoot context root for web app from application.xml or geronimo plan 194 * @return map of servlet names to path mapped to them. Possibly inaccurate except for web services. 195 */ 196 protected Map<String, String> buildServletNameToPathMap(WebAppType webApp, String contextRoot) { 197 if (contextRoot == null) { 198 contextRoot = ""; 199 } else if (!contextRoot.startsWith("/")) { 200 contextRoot = "/" + contextRoot; 201 } 202 Map<String, String> map = new HashMap<String, String>(); 203 ServletMappingType[] servletMappings = webApp.getServletMappingArray(); 204 for (ServletMappingType servletMapping : servletMappings) { 205 String servletName = servletMapping.getServletName().getStringValue().trim(); 206 UrlPatternType[] urlPatterns = servletMapping.getUrlPatternArray(); 207 208 for (int i = 0; urlPatterns != null && (i < urlPatterns.length); i++) { 209 map.put(servletName, contextRoot + urlPatterns[i].getStringValue().trim()); 210 } 211 } 212 return map; 213 } 214 215 protected String determineDefaultContextRoot(WebAppType webApp, boolean isStandAlone, JarFile moduleFile, String targetPath) { 216 217 if (webApp != null && webApp.getId() != null) { 218 return webApp.getId(); 219 } 220 221 if (isStandAlone) { 222 // default configId is based on the moduleFile name 223 return "/" + trimPath(new File(moduleFile.getName()).getName()); 224 } 225 226 // default configId is based on the module uri from the application.xml 227 return trimPath(targetPath); 228 } 229 230 private String trimPath(String path) { 231 232 if (path == null) { 233 return null; 234 } 235 236 if (path.endsWith(".war")) { 237 path = path.substring(0, path.length() - 4); 238 } 239 if (path.endsWith("/")) { 240 path = path.substring(0, path.length() - 1); 241 } 242 243 return path; 244 } 245 246 public void installModule(JarFile earFile, EARContext earContext, Module module, Collection configurationStores, ConfigurationStore targetConfigurationStore, Collection repositories) throws DeploymentException { 247 EARContext moduleContext; 248 if (module.isStandAlone()) { 249 moduleContext = earContext; 250 } else { 251 Environment environment = module.getEnvironment(); 252 Artifact earConfigId = earContext.getConfigID(); 253 Artifact configId = new Artifact(earConfigId.getGroupId(), earConfigId.getArtifactId() + "_" + module.getTargetPath(), earConfigId.getVersion(), "car"); 254 environment.setConfigId(configId); 255 environment.addDependency(earConfigId, ImportType.ALL); 256 File configurationDir = new File(earContext.getBaseDir(), module.getTargetPath()); 257 configurationDir.mkdirs(); 258 259 // construct the web app deployment context... this is the same class used by the ear context 260 try { 261 File inPlaceConfigurationDir = null; 262 if (null != earContext.getInPlaceConfigurationDir()) { 263 inPlaceConfigurationDir = new File(earContext.getInPlaceConfigurationDir(), module.getTargetPath()); 264 } 265 moduleContext = new EARContext(configurationDir, 266 inPlaceConfigurationDir, 267 environment, 268 ConfigurationModuleType.WAR, 269 module.getModuleName(), 270 earContext); 271 } catch (DeploymentException e) { 272 cleanupConfigurationDir(configurationDir); 273 throw e; 274 } 275 } 276 module.setEarContext(moduleContext); 277 module.setRootEarContext(earContext); 278 279 try { 280 ClassPathList manifestcp = new ClassPathList(); 281 // add the warfile's content to the configuration 282 JarFile warFile = module.getModuleFile(); 283 Enumeration<JarEntry> entries = warFile.entries(); 284 while (entries.hasMoreElements()) { 285 ZipEntry entry = entries.nextElement(); 286 URI targetPath = new URI(null, entry.getName(), null); 287 if (entry.getName().equals("WEB-INF/web.xml")) { 288 moduleContext.addFile(targetPath, module.getOriginalSpecDD()); 289 } else if (entry.getName().startsWith("WEB-INF/lib") && entry.getName().endsWith(".jar")) { 290 moduleContext.addInclude(targetPath, warFile, entry); 291 manifestcp.add(entry.getName()); 292 } else { 293 moduleContext.addFile(targetPath, warFile, entry); 294 } 295 } 296 297 //always add WEB-INF/classes to the classpath regardless of whether 298 //any classes exist 299 moduleContext.getConfiguration().addToClassPath("WEB-INF/classes/"); 300 manifestcp.add("WEB-INF/classes/"); 301 // add the manifest classpath entries declared in the war to the class loader 302 // we have to explicitly add these since we are unpacking the web module 303 // and the url class loader will not pick up a manifest from an unpacked dir 304 moduleContext.addManifestClassPath(warFile, RELATIVE_MODULE_BASE_URI); 305 moduleContext.getGeneralData().put(ClassPathList.class, manifestcp); 306 307 } catch (IOException e) { 308 throw new DeploymentException("Problem deploying war", e); 309 } catch (URISyntaxException e) { 310 throw new DeploymentException("Could not construct URI for location of war entry", e); 311 } finally { 312 if (!module.isStandAlone()) { 313 try { 314 moduleContext.flush(); 315 } catch (IOException e) { 316 throw new DeploymentException("Problem closing war context", e); 317 } 318 } 319 } 320 for (ModuleBuilderExtension mbe: moduleBuilderExtensions) { 321 mbe.installModule(earFile, earContext, module, configurationStores, targetConfigurationStore, repositories); 322 } 323 } 324 325 protected void basicInitContext(EARContext earContext, Module module, XmlObject gerWebApp, boolean hasSecurityRealmName) throws DeploymentException { 326 //complete manifest classpath 327 EARContext moduleContext = module.getEarContext(); 328 ClassPathList manifestcp = (ClassPathList) moduleContext.getGeneralData().get(ClassPathList.class); 329 ModuleList moduleLocations = (ModuleList) module.getRootEarContext().getGeneralData().get(ModuleList.class); 330 URI baseUri = URI.create(module.getTargetPath()); 331 URI resolutionUri = invertURI(baseUri); 332 earContext.getCompleteManifestClassPath(module.getModuleFile(), baseUri, resolutionUri, manifestcp, moduleLocations); 333 334 335 WebAppType webApp = (WebAppType) module.getSpecDD(); 336 if ((webApp.getSecurityConstraintArray().length > 0 || webApp.getSecurityRoleArray().length > 0) && 337 !hasSecurityRealmName) { 338 throw new DeploymentException("web.xml for web app " + module.getName() + " includes security elements but Geronimo deployment plan is not provided or does not contain <security-realm-name> element necessary to configure security accordingly."); 339 } 340 XmlObject[] securityElements = XmlBeansUtil.selectSubstitutionGroupElements(SECURITY_QNAME, gerWebApp); 341 if (securityElements.length > 0 && !hasSecurityRealmName) { 342 throw new DeploymentException("You have supplied a security configuration for web app " + module.getName() + " but no security-realm-name to allow login"); 343 } 344 getNamingBuilders().buildEnvironment(webApp, module.getVendorDD(), module.getEnvironment()); 345 //this is silly 346 getNamingBuilders().initContext(webApp, gerWebApp, module); 347 348 Map servletNameToPathMap = buildServletNameToPathMap((WebAppType) module.getSpecDD(), ((WebModule) module).getContextRoot()); 349 350 Map sharedContext = module.getSharedContext(); 351 for (Object aWebServiceBuilder : webServiceBuilder) { 352 WebServiceBuilder serviceBuilder = (WebServiceBuilder) aWebServiceBuilder; 353 serviceBuilder.findWebServices(module, false, servletNameToPathMap, module.getEnvironment(), sharedContext); 354 } 355 securityBuilders.build(gerWebApp, earContext, module.getEarContext()); 356 serviceBuilders.build(gerWebApp, earContext, module.getEarContext()); 357 } 358 359 static URI invertURI(URI baseUri) { 360 URI resolutionUri = URI.create("."); 361 for (URI test = baseUri; !test.equals(RELATIVE_MODULE_BASE_URI); test = test.resolve(RELATIVE_MODULE_BASE_URI)) { 362 resolutionUri = resolutionUri.resolve(RELATIVE_MODULE_BASE_URI); 363 } 364 return resolutionUri; 365 } 366 367 protected WebAppDocument convertToServletSchema(XmlObject xmlObject) throws XmlException { 368 369 String schemaLocationURL = "http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"; 370 String version = "2.5"; 371 XmlCursor cursor = xmlObject.newCursor(); 372 try { 373 cursor.toStartDoc(); 374 cursor.toFirstChild(); 375 if ("http://java.sun.com/xml/ns/j2ee".equals(cursor.getName().getNamespaceURI())) { 376 SchemaConversionUtils.convertSchemaVersion(cursor, SchemaConversionUtils.JAVAEE_NAMESPACE, schemaLocationURL, version); 377 XmlObject result = xmlObject.changeType(WebAppDocument.type); 378 XmlBeansUtil.validateDD(result); 379 return (WebAppDocument) result; 380 } 381 382 if ("http://java.sun.com/xml/ns/javaee".equals(cursor.getName().getNamespaceURI())) { 383 SchemaConversionUtils.convertSchemaVersion(cursor, SchemaConversionUtils.JAVAEE_NAMESPACE, schemaLocationURL, version); 384 XmlObject result = xmlObject.changeType(WebAppDocument.type); 385 XmlBeansUtil.validateDD(result); 386 return (WebAppDocument) result; 387 } 388 389 //otherwise assume DTD 390 XmlDocumentProperties xmlDocumentProperties = cursor.documentProperties(); 391 String publicId = xmlDocumentProperties.getDoctypePublicId(); 392 boolean is22 = "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN".equals(publicId); 393 if ("-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN".equals(publicId) || 394 is22) { 395 XmlCursor moveable = xmlObject.newCursor(); 396 try { 397 moveable.toStartDoc(); 398 moveable.toFirstChild(); 399 400 SchemaConversionUtils.convertToSchema(cursor, SchemaConversionUtils.JAVAEE_NAMESPACE, schemaLocationURL, version); 401 cursor.toStartDoc(); 402 cursor.toChild(SchemaConversionUtils.JAVAEE_NAMESPACE, "web-app"); 403 cursor.toFirstChild(); 404 SchemaConversionUtils.convertToDescriptionGroup(SchemaConversionUtils.JAVAEE_NAMESPACE, cursor, moveable); 405 SchemaConversionUtils.convertToJNDIEnvironmentRefsGroup(SchemaConversionUtils.JAVAEE_NAMESPACE, cursor, moveable); 406 cursor.push(); 407 if (cursor.toNextSibling(TAGLIB)) { 408 cursor.toPrevSibling(); 409 moveable.toCursor(cursor); 410 cursor.beginElement("jsp-config", SchemaConversionUtils.JAVAEE_NAMESPACE); 411 while (moveable.toNextSibling(TAGLIB)) { 412 moveable.moveXml(cursor); 413 } 414 } 415 cursor.pop(); 416 do { 417 String name = cursor.getName().getLocalPart(); 418 if ("filter".equals(name) || "servlet".equals(name) || "context-param".equals(name)) { 419 cursor.push(); 420 cursor.toFirstChild(); 421 SchemaConversionUtils.convertToDescriptionGroup(SchemaConversionUtils.JAVAEE_NAMESPACE, cursor, moveable); 422 while (cursor.toNextSibling(SchemaConversionUtils.JAVAEE_NAMESPACE, "init-param")) { 423 cursor.push(); 424 cursor.toFirstChild(); 425 SchemaConversionUtils.convertToDescriptionGroup(SchemaConversionUtils.JAVAEE_NAMESPACE, cursor, moveable); 426 cursor.pop(); 427 } 428 cursor.pop(); 429 cursor.push(); 430 if (cursor.toChild(SchemaConversionUtils.JAVAEE_NAMESPACE, "jsp-file")) { 431 String jspFile = cursor.getTextValue(); 432 if (!jspFile.startsWith("/")){ 433 if (is22) { 434 cursor.setTextValue("/" + jspFile); 435 } else { 436 throw new XmlException("jsp-file does not start with / and this is not a 2.2 web app: " + jspFile); 437 } 438 } 439 } 440 cursor.pop(); 441 } 442 } while (cursor.toNextSibling()); 443 } finally { 444 moveable.dispose(); 445 } 446 } 447 } finally { 448 cursor.dispose(); 449 } 450 XmlObject result = xmlObject.changeType(WebAppDocument.type); 451 if (result != null) { 452 XmlBeansUtil.validateDD(result); 453 return (WebAppDocument) result; 454 } 455 XmlBeansUtil.validateDD(xmlObject); 456 return (WebAppDocument) xmlObject; 457 } 458 459 460 protected void addUnmappedJSPPermissions(Set<String> securityRoles, Map<String, PermissionCollection> rolePermissions) { 461 for (String roleName : securityRoles) { 462 addPermissionToRole(roleName, new WebRoleRefPermission("", roleName), rolePermissions); 463 } 464 } 465 466 protected ComponentPermissions buildSpecSecurityConfig(WebAppType webApp, Set<String> securityRoles, Map<String, PermissionCollection> rolePermissions) { 467 Map<String, URLPattern> uncheckedPatterns = new HashMap<String, URLPattern>(); 468 Map<UncheckedItem, HTTPMethods> uncheckedResourcePatterns = new HashMap<UncheckedItem, HTTPMethods>(); 469 Map<UncheckedItem, HTTPMethods> uncheckedUserPatterns = new HashMap<UncheckedItem, HTTPMethods>(); 470 Map<String, URLPattern> excludedPatterns = new HashMap<String, URLPattern>(); 471 Map<String, URLPattern> rolesPatterns = new HashMap<String, URLPattern>(); 472 Set<URLPattern> allSet = new HashSet<URLPattern>(); // == allMap.values() 473 Map<String, URLPattern> allMap = new HashMap<String, URLPattern>(); //uncheckedPatterns union excludedPatterns union rolesPatterns. 474 475 SecurityConstraintType[] securityConstraintArray = webApp.getSecurityConstraintArray(); 476 for (SecurityConstraintType securityConstraintType : securityConstraintArray) { 477 Map<String, URLPattern> currentPatterns; 478 if (securityConstraintType.isSetAuthConstraint()) { 479 if (securityConstraintType.getAuthConstraint().getRoleNameArray().length == 0) { 480 currentPatterns = excludedPatterns; 481 } else { 482 currentPatterns = rolesPatterns; 483 } 484 } else { 485 currentPatterns = uncheckedPatterns; 486 } 487 488 String transport = ""; 489 if (securityConstraintType.isSetUserDataConstraint()) { 490 transport = securityConstraintType.getUserDataConstraint().getTransportGuarantee().getStringValue().trim().toUpperCase(); 491 } 492 493 WebResourceCollectionType[] webResourceCollectionTypeArray = securityConstraintType.getWebResourceCollectionArray(); 494 for (WebResourceCollectionType webResourceCollectionType : webResourceCollectionTypeArray) { 495 UrlPatternType[] urlPatternTypeArray = webResourceCollectionType.getUrlPatternArray(); 496 for (UrlPatternType urlPatternType : urlPatternTypeArray) { 497 String url = urlPatternType.getStringValue().trim(); 498 URLPattern pattern = currentPatterns.get(url); 499 if (pattern == null) { 500 pattern = new URLPattern(url); 501 currentPatterns.put(url, pattern); 502 } 503 504 URLPattern allPattern = allMap.get(url); 505 if (allPattern == null) { 506 allPattern = new URLPattern(url); 507 allSet.add(allPattern); 508 allMap.put(url, allPattern); 509 } 510 511 String[] httpMethodTypeArray = webResourceCollectionType.getHttpMethodArray(); 512 if (httpMethodTypeArray.length == 0) { 513 pattern.addMethod(""); 514 allPattern.addMethod(""); 515 } else { 516 for (String aHttpMethodTypeArray : httpMethodTypeArray) { 517 String method = (aHttpMethodTypeArray == null ? null : aHttpMethodTypeArray.trim()); 518 if (method != null) { 519 pattern.addMethod(method); 520 allPattern.addMethod(method); 521 } 522 } 523 } 524 if (currentPatterns == rolesPatterns) { 525 RoleNameType[] roleNameTypeArray = securityConstraintType.getAuthConstraint().getRoleNameArray(); 526 for (RoleNameType roleNameType : roleNameTypeArray) { 527 String role = roleNameType.getStringValue().trim(); 528 if (role.equals("*")) { 529 pattern.addAllRoles(securityRoles); 530 } else { 531 pattern.addRole(role); 532 } 533 } 534 } 535 536 pattern.setTransport(transport); 537 } 538 } 539 } 540 541 PermissionCollection excludedPermissions = new Permissions(); 542 PermissionCollection uncheckedPermissions = new Permissions(); 543 544 for (URLPattern pattern : excludedPatterns.values()) { 545 String name = pattern.getQualifiedPattern(allSet); 546 String actions = pattern.getMethods(); 547 548 excludedPermissions.add(new WebResourcePermission(name, actions)); 549 excludedPermissions.add(new WebUserDataPermission(name, actions)); 550 } 551 552 for (URLPattern pattern : rolesPatterns.values()) { 553 String name = pattern.getQualifiedPattern(allSet); 554 String actions = pattern.getMethods(); 555 WebResourcePermission permission = new WebResourcePermission(name, actions); 556 557 for (String roleName : pattern.getRoles()) { 558 addPermissionToRole(roleName, permission, rolePermissions); 559 } 560 HTTPMethods methods = pattern.getHTTPMethods(); 561 int transportType = pattern.getTransport(); 562 563 addOrUpdatePattern(uncheckedUserPatterns, name, methods, transportType); 564 } 565 566 for (URLPattern pattern : uncheckedPatterns.values()) { 567 String name = pattern.getQualifiedPattern(allSet); 568 HTTPMethods methods = pattern.getHTTPMethods(); 569 570 addOrUpdatePattern(uncheckedResourcePatterns, name, methods, URLPattern.NA); 571 572 int transportType = pattern.getTransport(); 573 addOrUpdatePattern(uncheckedUserPatterns, name, methods, transportType); 574 } 575 576 /** 577 * A <code>WebResourcePermission</code> and a <code>WebUserDataPermission</code> must be instantiated for 578 * each <tt>url-pattern</tt> in the deployment descriptor and the default pattern "/", that is not combined 579 * by the <tt>web-resource-collection</tt> elements of the deployment descriptor with ever HTTP method 580 * value. The permission objects must be contructed using the qualified pattern as their name and with 581 * actions defined by the subset of the HTTP methods that do not occur in combination with the pattern. 582 * The resulting permissions that must be added to the unchecked policy statements by calling the 583 * <code>addToUncheckedPolcy</code> method on the <code>PolicyConfiguration</code> object. 584 */ 585 for (URLPattern pattern : allSet) { 586 String name = pattern.getQualifiedPattern(allSet); 587 HTTPMethods methods = pattern.getComplementedHTTPMethods(); 588 589 if (methods.isNone()) { 590 continue; 591 } 592 593 addOrUpdatePattern(uncheckedResourcePatterns, name, methods, URLPattern.NA); 594 addOrUpdatePattern(uncheckedUserPatterns, name, methods, URLPattern.NA); 595 } 596 597 URLPattern pattern = new URLPattern("/"); 598 if (!allSet.contains(pattern)) { 599 String name = pattern.getQualifiedPattern(allSet); 600 HTTPMethods methods = pattern.getComplementedHTTPMethods(); 601 602 addOrUpdatePattern(uncheckedResourcePatterns, name, methods, URLPattern.NA); 603 addOrUpdatePattern(uncheckedUserPatterns, name, methods, URLPattern.NA); 604 } 605 606 //Create the uncheckedPermissions for WebResourcePermissions 607 for (UncheckedItem item : uncheckedResourcePatterns.keySet()) { 608 HTTPMethods methods = uncheckedResourcePatterns.get(item); 609 String actions = URLPattern.getMethodsWithTransport(methods, item.getTransportType()); 610 611 uncheckedPermissions.add(new WebResourcePermission(item.getName(), actions)); 612 } 613 //Create the uncheckedPermissions for WebUserDataPermissions 614 for (UncheckedItem item : uncheckedUserPatterns.keySet()) { 615 HTTPMethods methods = uncheckedUserPatterns.get(item); 616 String actions = URLPattern.getMethodsWithTransport(methods, item.getTransportType()); 617 618 uncheckedPermissions.add(new WebUserDataPermission(item.getName(), actions)); 619 } 620 621 return new ComponentPermissions(excludedPermissions, uncheckedPermissions, rolePermissions); 622 623 } 624 625 protected void addPermissionToRole(String roleName, Permission permission, Map<String, PermissionCollection> rolePermissions) { 626 PermissionCollection permissionsForRole = rolePermissions.get(roleName); 627 if (permissionsForRole == null) { 628 permissionsForRole = new Permissions(); 629 rolePermissions.put(roleName, permissionsForRole); 630 } 631 permissionsForRole.add(permission); 632 } 633 634 private void addOrUpdatePattern(Map<UncheckedItem, HTTPMethods> patternMap, String name, HTTPMethods actions, int transportType) { 635 UncheckedItem item = new UncheckedItem(name, transportType); 636 HTTPMethods existingActions = patternMap.get(item); 637 if (existingActions != null) { 638 patternMap.put(item, existingActions.add(actions)); 639 return; 640 } 641 642 patternMap.put(item, new HTTPMethods(actions, false)); 643 } 644 645 protected static Set<String> collectRoleNames(WebAppType webApp) { 646 Set<String> roleNames = new HashSet<String>(); 647 648 SecurityRoleType[] securityRoles = webApp.getSecurityRoleArray(); 649 for (SecurityRoleType securityRole : securityRoles) { 650 roleNames.add(securityRole.getRoleName().getStringValue().trim()); 651 } 652 653 return roleNames; 654 } 655 656 protected static void check(WebAppType webApp) throws DeploymentException { 657 checkURLPattern(webApp); 658 checkMultiplicities(webApp); 659 } 660 661 private static void checkURLPattern(WebAppType webApp) throws DeploymentException { 662 663 FilterMappingType[] filterMappings = webApp.getFilterMappingArray(); 664 for (FilterMappingType filterMapping : filterMappings) { 665 UrlPatternType[] urlPatterns = filterMapping.getUrlPatternArray(); 666 for (int j = 0; (urlPatterns != null) && (j < urlPatterns.length); j++) { 667 checkString(urlPatterns[j].getStringValue().trim()); 668 } 669 } 670 671 ServletMappingType[] servletMappings = webApp.getServletMappingArray(); 672 for (ServletMappingType servletMapping : servletMappings) { 673 UrlPatternType[] urlPatterns = servletMapping.getUrlPatternArray(); 674 for (int j = 0; (urlPatterns != null) && (j < urlPatterns.length); j++) { 675 checkString(urlPatterns[j].getStringValue().trim()); 676 } 677 } 678 679 SecurityConstraintType[] constraints = webApp.getSecurityConstraintArray(); 680 for (SecurityConstraintType constraint : constraints) { 681 WebResourceCollectionType[] collections = constraint.getWebResourceCollectionArray(); 682 for (WebResourceCollectionType collection : collections) { 683 UrlPatternType[] patterns = collection.getUrlPatternArray(); 684 for (UrlPatternType pattern : patterns) { 685 checkString(pattern.getStringValue().trim()); 686 } 687 } 688 } 689 } 690 691 protected static void checkString(String pattern) throws DeploymentException { 692 //j2ee_1_4.xsd explicitly requires preserving all whitespace. Do not trim. 693 if (pattern.indexOf(0x0D) >= 0) throw new DeploymentException("<url-pattern> must not contain CR(#xD)"); 694 if (pattern.indexOf(0x0A) >= 0) throw new DeploymentException("<url-pattern> must not contain LF(#xA)"); 695 } 696 697 private static void checkMultiplicities(WebAppType webApp) throws DeploymentException { 698 if (webApp.getSessionConfigArray().length > 1) 699 throw new DeploymentException("Multiple <session-config> elements found"); 700 if (webApp.getJspConfigArray().length > 1) 701 throw new DeploymentException("Multiple <jsp-config> elements found"); 702 if (webApp.getLoginConfigArray().length > 1) 703 throw new DeploymentException("Multiple <login-config> elements found"); 704 } 705 706 private boolean cleanupConfigurationDir(File configurationDir) { 707 LinkedList<String> cannotBeDeletedList = new LinkedList<String>(); 708 709 if (!DeploymentUtil.recursiveDelete(configurationDir, cannotBeDeletedList)) { 710 // Output a message to help user track down file problem 711 log.warn("Unable to delete " + cannotBeDeletedList.size() + 712 " files while recursively deleting directory " 713 + configurationDir.getAbsolutePath() + LINE_SEP + 714 "The first file that could not be deleted was:" + LINE_SEP + " " + 715 (!cannotBeDeletedList.isEmpty() ? cannotBeDeletedList.getFirst() : "")); 716 return false; 717 } 718 return true; 719 } 720 721 protected void processRoleRefPermissions(ServletType servletType, Set<String> securityRoles, Map<String, PermissionCollection> rolePermissions) { 722 String servletName = servletType.getServletName().getStringValue().trim(); 723 //WebRoleRefPermissions 724 SecurityRoleRefType[] securityRoleRefTypeArray = servletType.getSecurityRoleRefArray(); 725 Set<String> unmappedRoles = new HashSet<String>(securityRoles); 726 for (SecurityRoleRefType securityRoleRefType : securityRoleRefTypeArray) { 727 String roleName = securityRoleRefType.getRoleName().getStringValue().trim(); 728 String roleLink = securityRoleRefType.getRoleLink().getStringValue().trim(); 729 //jacc 3.1.3.2 730 /* The name of the WebRoleRefPermission must be the servlet-name in whose 731 * context the security-role-ref is defined. The actions of the WebRoleRefPermission 732 * must be the value of the role-name (that is the reference), appearing in the security-role-ref. 733 * The deployment tools must call the addToRole method on the PolicyConfiguration object to add the 734 * WebRoleRefPermission object resulting from the translation to the role 735 * identified in the role-link appearing in the security-role-ref. 736 */ 737 addPermissionToRole(roleLink, new WebRoleRefPermission(servletName, roleName), rolePermissions); 738 unmappedRoles.remove(roleName); 739 } 740 for (String roleName : unmappedRoles) { 741 addPermissionToRole(roleName, new WebRoleRefPermission(servletName, roleName), rolePermissions); 742 } 743 } 744 745 protected ClassFinder createWebAppClassFinder(WebAppType webApp, WebModule webModule) throws DeploymentException { 746 747 //------------------------------------------------------------------------------------ 748 // Find the list of classes from the web.xml we want to search for annotations in 749 //------------------------------------------------------------------------------------ 750 List<Class> classes = new ArrayList<Class>(); 751 752 // Get the classloader from the module's EARContext 753 ClassLoader classLoader = webModule.getEarContext().getClassLoader(); 754 755 // Get all the servlets from the deployment descriptor 756 ServletType[] servlets = webApp.getServletArray(); 757 for (ServletType servlet : servlets) { 758 FullyQualifiedClassType cls = servlet.getServletClass(); 759 if (cls != null) { // Don't try this for JSPs 760 Class<?> clas; 761 try { 762 clas = classLoader.loadClass(cls.getStringValue()); 763 } catch (ClassNotFoundException e) { 764 throw new DeploymentException("AbstractWebModuleBuilder: Could not load servlet class: " + cls.getStringValue(), e); 765 } 766 addClass(classes, clas); 767 } 768 } 769 770 // Get all the listeners from the deployment descriptor 771 ListenerType[] listeners = webApp.getListenerArray(); 772 for (ListenerType listener : listeners) { 773 FullyQualifiedClassType cls = listener.getListenerClass(); 774 Class<?> clas; 775 try { 776 clas = classLoader.loadClass(cls.getStringValue()); 777 } catch (ClassNotFoundException e) { 778 throw new DeploymentException("AbstractWebModuleBuilder: Could not load listener class: " + cls.getStringValue(), e); 779 } 780 addClass(classes, clas); 781 } 782 783 // Get all the filters from the deployment descriptor 784 FilterType[] filters = webApp.getFilterArray(); 785 for (FilterType filter : filters) { 786 FullyQualifiedClassType cls = filter.getFilterClass(); 787 Class<?> clas; 788 try { 789 clas = classLoader.loadClass(cls.getStringValue()); 790 } catch (ClassNotFoundException e) { 791 throw new DeploymentException("AbstractWebModuleBuilder: Could not load filter class: " + cls.getStringValue(), e); 792 } 793 addClass(classes, clas); 794 } 795 796 return new ClassFinder(classes); 797 } 798 799 private void addClass(List<Class> classes, Class<?> clas) { 800 while (clas != Object.class) { 801 classes.add(clas); 802 clas = clas.getSuperclass(); 803 } 804 } 805 806 protected void configureBasicWebModuleAttributes(WebAppType webApp, XmlObject vendorPlan, EARContext moduleContext, EARContext earContext, WebModule webModule, GBeanData webModuleData) throws DeploymentException { 807 Map<NamingBuilder.Key, Object> buildingContext = new HashMap<NamingBuilder.Key, Object>(); 808 buildingContext.put(NamingBuilder.GBEAN_NAME_KEY, moduleContext.getModuleName()); 809 810 if (!webApp.getMetadataComplete()) { 811 // Create a classfinder and populate it for the naming builder(s). The absence of a 812 // classFinder in the module will convey whether metadata-complete is set (or not) 813 webModule.setClassFinder(createWebAppClassFinder(webApp, webModule)); 814 SecurityAnnotationHelper.processAnnotations(webApp, webModule.getClassFinder()); 815 } 816 //N.B. we use the ear context which has all the gbeans we could possibly be looking up from this ear. 817 //nope, persistence units can be in the war. 818 //This means that you cannot use the default environment of the web builder to add configs that will be searched. 819 getNamingBuilders().buildNaming(webApp, vendorPlan, webModule, buildingContext); 820 821 Map compContext = NamingBuilder.JNDI_KEY.get(buildingContext); 822 Holder holder = NamingBuilder.INJECTION_KEY.get(buildingContext); 823 824 webModule.getSharedContext().put(WebModule.WEB_APP_DATA, webModuleData); 825 webModule.getSharedContext().put(NamingBuilder.JNDI_KEY, compContext); 826 webModule.getSharedContext().put(NamingBuilder.INJECTION_KEY, holder); 827 if (moduleContext.getServerName() != null) { 828 webModuleData.setReferencePattern("J2EEServer", moduleContext.getServerName()); 829 } 830 if (!webModule.isStandAlone()) { 831 webModuleData.setReferencePattern("J2EEApplication", earContext.getModuleName()); 832 } 833 834 webModuleData.setAttribute("holder", holder); 835 836 //Add dependencies on managed connection factories and ejbs in this app 837 //This is overkill, but allows for people not using java:comp context (even though we don't support it) 838 //and sidesteps the problem of circular references between ejbs. 839 if (earContext != moduleContext) { 840 addGBeanDependencies(earContext, webModuleData); 841 } 842 843 webModuleData.setAttribute("componentContext", compContext); 844 webModuleData.setReferencePattern("TransactionManager", moduleContext.getTransactionManagerName()); 845 webModuleData.setReferencePattern("TrackedConnectionAssociator", moduleContext.getConnectionTrackerName()); 846 } 847 848 class UncheckedItem { 849 final static int NA = 0x00; 850 final static int INTEGRAL = 0x01; 851 final static int CONFIDENTIAL = 0x02; 852 853 private int transportType = NA; 854 private String name; 855 856 public UncheckedItem(String name, int transportType) { 857 setName(name); 858 setTransportType(transportType); 859 } 860 861 public boolean equals(Object o) { 862 UncheckedItem item = (UncheckedItem) o; 863 return item.transportType == transportType && item.name.equals(this.name); 864 } 865 866 867 public int hashCode() { 868 return name.hashCode() + transportType; 869 } 870 871 public String getName() { 872 return name; 873 } 874 875 public void setName(String name) { 876 this.name = name; 877 } 878 879 public int getTransportType() { 880 return transportType; 881 } 882 883 public void setTransportType(int transportType) { 884 this.transportType = transportType; 885 } 886 } 887 }