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 }