001// -------------------------------------------------------------------------------- 002// Copyright 2002-2025 Echo Three, LLC 003// 004// Licensed under the Apache License, Version 2.0 (the "License"); 005// you may not use this file except in compliance with the License. 006// You may obtain a copy of the License at 007// 008// http://www.apache.org/licenses/LICENSE-2.0 009// 010// Unless required by applicable law or agreed to in writing, software 011// distributed under the License is distributed on an "AS IS" BASIS, 012// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013// See the License for the specific language governing permissions and 014// limitations under the License. 015// -------------------------------------------------------------------------------- 016 017/* 018 * ==================================================================== 019 * 020 * The Apache Software License, Version 1.1 021 * 022 * Copyright (c) 1999-2003 The Apache Software Foundation. All rights 023 * reserved. 024 * 025 * Redistribution and use in source and binary forms, with or without 026 * modification, are permitted provided that the following conditions 027 * are met: 028 * 029 * 1. Redistributions of source code must retain the above copyright 030 * notice, this list of conditions and the following disclaimer. 031 * 032 * 2. Redistributions in binary form must reproduce the above copyright 033 * notice, this list of conditions and the following disclaimer in 034 * the documentation and/or other materials provided with the 035 * distribution. 036 * 037 * 3. The end-user documentation included with the redistribution, if 038 * any, must include the following acknowlegement: 039 * "This product includes software developed by the 040 * Apache Software Foundation (http://www.apache.org/)." 041 * Alternately, this acknowlegement may appear in the software itself, 042 * if and wherever such third-party acknowlegements normally appear. 043 * 044 * 4. The names "The Jakarta Project", "Struts", and "Apache Software 045 * Foundation" must not be used to endorse or promote products derived 046 * from this software without prior written permission. For written 047 * permission, please contact apache@apache.org. 048 * 049 * 5. Products derived from this software may not be called "Apache" 050 * nor may "Apache" appear in their names without prior written 051 * permission of the Apache Group. 052 * 053 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 054 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 055 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 056 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR 057 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 058 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 059 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 060 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 061 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 062 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 063 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 064 * SUCH DAMAGE. 065 * ==================================================================== 066 * 067 * This software consists of voluntary contributions made by many 068 * individuals on behalf of the Apache Software Foundation. For more 069 * information on the Apache Software Foundation, please see 070 * <http://www.apache.org/>. 071 * 072 */ 073 074package com.echothree.view.client.web.struts.sslext.tiles; 075 076import com.echothree.view.client.web.struts.sslext.action.SecurePlugInInterface; 077import com.echothree.view.client.web.struts.sslext.action.SecureTilesRequestProcessor; 078import java.io.IOException; 079import java.io.InputStream; 080import java.util.ArrayList; 081import java.util.Collection; 082import java.util.MissingResourceException; 083import javax.servlet.ServletException; 084import javax.servlet.UnavailableException; 085import org.apache.commons.digester.Digester; 086import org.apache.commons.logging.Log; 087import org.apache.commons.logging.LogFactory; 088import org.apache.struts.action.ActionServlet; 089import org.apache.struts.action.RequestProcessor; 090import org.apache.struts.config.ModuleConfig; 091import org.apache.struts.tiles.TilesPlugin; 092import org.apache.struts.util.MessageResources; 093import org.apache.struts.util.RequestUtils; 094 095/** 096 * Implements sslext plugin functionality for use with Tiles. 097 */ 098public class SecureTilesPlugin 099 extends TilesPlugin implements SecurePlugInInterface { 100 101 protected String addSession = DEFAULT_ADD_SESSION; 102 protected String httpPort = DEFAULT_HTTP_PORT; 103 protected String httpsPort = DEFAULT_HTTPS_PORT; 104 protected String enable = DEFAULT_ENABLE; 105 106 protected String resourceName = "org.apache.struts.action.SecureResources"; 107 protected MessageResources resources = null; 108 protected String registrations[] = { 109 "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN", 110 "/org/apache/struts/resources/web-app_2_2.dtd", 111 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN", 112 "/org/apache/struts/resources/web-app_2_3.dtd" 113 }; 114 115 protected Log log = LogFactory.getLog(SecureTilesPlugin.class); 116 protected ModuleConfig config = null; 117 protected ActionServlet servlet = null; 118 protected String servletName = null; 119 protected Collection servletMappings = new ArrayList(); 120 121 @Override 122 public void destroy() { 123 super.destroy(); 124 servlet.getServletContext().removeAttribute(SECURE_PLUGIN); 125 } 126 127 public void setHttpsPort(String s) { 128 this.httpsPort = s; 129 } 130 131 public void setHttpPort(String s) { 132 this.httpPort = s; 133 } 134 135 @Override 136 public String getHttpsPort() { 137 return this.httpsPort; 138 } 139 140 @Override 141 public String getHttpPort() { 142 return this.httpPort; 143 } 144 145 @Override 146 public Collection getServletMappings() { 147 return this.servletMappings; 148 } 149 150 @Override 151 public String getEnable() { 152 return enable; 153 } 154 155 public void setEnable(String s) { 156 enable = s; 157 } 158 159 public String getAddSession() { 160 return addSession; 161 } 162 163 public void setAddSession(String addSession) { 164 this.addSession = addSession; 165 } 166 167 @Override 168 public boolean getSslExtAddSession() { 169 170 return Boolean.valueOf(getAddSession()); 171 } 172 173 @Override 174 public boolean getSslExtEnable() { 175 176 return Boolean.valueOf(getEnable()); 177 } 178 179 @Override 180 public void init(ActionServlet servlet, ModuleConfig moduleConfig) 181 throws ServletException { 182 183 this.config = moduleConfig; 184 this.servlet = servlet; 185 initMappings(); 186 servlet.getServletContext().setAttribute(SECURE_PLUGIN, this); 187 super.init(servlet, moduleConfig); 188 } 189 190 @Override 191 protected void initRequestProcessorClass(ModuleConfig config) throws ServletException { 192 var tilesProcessorClassname = SecureTilesRequestProcessor.class.getName(); 193 var ctrlConfig = config.getControllerConfig(); 194 var configProcessorClassname = ctrlConfig.getProcessorClass(); 195 Class configProcessorClass; 196 try { 197 configProcessorClass = RequestUtils.applicationClass(configProcessorClassname); 198 } catch (java.lang.ClassNotFoundException ex) { 199 log.fatal("Can't set TilesRequestProcessor: bad class name '" 200 + configProcessorClassname 201 + "'."); 202 throw new ServletException(ex); 203 } 204 if (configProcessorClassname.equals(RequestProcessor.class.getName()) 205 || configProcessorClassname.endsWith(tilesProcessorClassname)) { 206 ctrlConfig.setProcessorClass(tilesProcessorClassname); 207 return; 208 } 209 Class tilesProcessorClass = SecureTilesRequestProcessor.class; 210 if (!tilesProcessorClass.isAssignableFrom(configProcessorClass)) { // Not compatible 211 var msg = "TilesPlugin : Specified RequestProcessor not compatible with TilesRequestProcessor"; 212 if (log.isFatalEnabled()) { 213 log.fatal(msg); 214 } 215 throw new ServletException(msg); 216 } // end if 217 } 218 219 protected void initMappings() throws ServletException { 220 221 // Get the action servlet name 222 this.servletName = servlet.getServletConfig().getServletName(); 223 224 // Prepare a Digester to scan the web application deployment descriptor 225 var digester = new Digester(); 226 digester.push(this); 227 digester.setNamespaceAware(true); 228 digester.setValidating(false); 229 230 // Register our local copy of the DTDs that we can find 231 for(var i = 0; i < registrations.length; i += 2) { 232 var url = this.getClass().getResource(registrations[i + 1]); 233 if (url != null) { 234 digester.register(registrations[i], url.toString()); 235 } 236 } 237 238 // Configure the processing rules that we need 239 digester.addCallMethod("web-app/servlet-mapping", 240 "addServletMapping", 2); 241 digester.addCallParam("web-app/servlet-mapping/servlet-name", 0); 242 digester.addCallParam("web-app/servlet-mapping/url-pattern", 1); 243 244 // Process the web application deployment descriptor 245 if (log.isDebugEnabled()) { 246 log.debug("Scanning web.xml for ActionServlet URL mappings"); 247 } 248 InputStream input = null; 249 try { 250 input = 251 servlet.getServletContext().getResourceAsStream("/WEB-INF/web.xml"); 252 digester.parse(input); 253 } catch (Throwable e) { 254 log.error(resources.getMessage("configWebXml"), e); 255 } finally { 256 if (input != null) { 257 try { 258 input.close(); 259 } catch (IOException e) { 260 ; 261 } 262 } 263 } 264 } 265 266 protected void initResources() throws ServletException { 267 268 try { 269 resources = MessageResources.getMessageResources(resourceName); 270 } catch (MissingResourceException e) { 271 log.error("Cannot load internal resources from '" + resourceName + "'", 272 e); 273 throw new UnavailableException 274 ("Cannot load internal resources from '" + resourceName + "'"); 275 } 276 } 277 278 public void addServletMapping(String servletName, String urlPattern) { 279 280 if (log.isDebugEnabled()) { 281 log.debug("Process servletName=" + servletName + 282 ", urlPattern=" + urlPattern); 283 } 284 if (servletName == null) { 285 return; 286 } 287 if (servletName.equals(this.servletName)) { 288 this.servletMappings.add(urlPattern); 289 } 290 } 291}