001// --------------------------------------------------------------------------------
002// Copyright 2002-2024 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.net.URL;
081import java.util.ArrayList;
082import java.util.Collection;
083import java.util.MissingResourceException;
084import javax.servlet.ServletException;
085import javax.servlet.UnavailableException;
086import org.apache.commons.digester.Digester;
087import org.apache.commons.logging.Log;
088import org.apache.commons.logging.LogFactory;
089import org.apache.struts.action.ActionServlet;
090import org.apache.struts.action.RequestProcessor;
091import org.apache.struts.config.ControllerConfig;
092import org.apache.struts.config.ModuleConfig;
093import org.apache.struts.tiles.TilesPlugin;
094import org.apache.struts.util.MessageResources;
095import org.apache.struts.util.RequestUtils;
096
097/**
098 * Implements sslext plugin functionality for use with Tiles.
099 */
100public class SecureTilesPlugin
101        extends TilesPlugin implements SecurePlugInInterface {
102    
103    protected String addSession = DEFAULT_ADD_SESSION;
104    protected String httpPort = DEFAULT_HTTP_PORT;
105    protected String httpsPort = DEFAULT_HTTPS_PORT;
106    protected String enable = DEFAULT_ENABLE;
107    
108    protected String resourceName = "org.apache.struts.action.SecureResources";
109    protected MessageResources resources = null;
110    protected String registrations[] = {
111        "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN",
112        "/org/apache/struts/resources/web-app_2_2.dtd",
113        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN",
114        "/org/apache/struts/resources/web-app_2_3.dtd"
115    };
116    
117    protected Log log = LogFactory.getLog(SecureTilesPlugin.class);
118    protected ModuleConfig config = null;
119    protected ActionServlet servlet = null;
120    protected String servletName = null;
121    protected Collection servletMappings = new ArrayList();
122    
123    @Override
124    public void destroy() {
125        super.destroy();
126        servlet.getServletContext().removeAttribute(SECURE_PLUGIN);
127    }
128    
129    public void setHttpsPort(String s) {
130        this.httpsPort = s;
131    }
132    
133    public void setHttpPort(String s) {
134        this.httpPort = s;
135    }
136    
137    @Override
138    public String getHttpsPort() {
139        return this.httpsPort;
140    }
141    
142    @Override
143    public String getHttpPort() {
144        return this.httpPort;
145    }
146    
147    @Override
148    public Collection getServletMappings() {
149        return this.servletMappings;
150    }
151    
152    @Override
153    public String getEnable() {
154        return enable;
155    }
156    
157    public void setEnable(String s) {
158        enable = s;
159    }
160    
161    public String getAddSession() {
162        return addSession;
163    }
164    
165    public void setAddSession(String addSession) {
166        this.addSession = addSession;
167    }
168    
169    @Override
170    public boolean getSslExtAddSession() {
171        
172        return Boolean.valueOf(getAddSession());
173    }
174    
175    @Override
176    public boolean getSslExtEnable() {
177        
178        return Boolean.valueOf(getEnable());
179    }
180    
181    @Override
182    public void init(ActionServlet servlet, ModuleConfig moduleConfig)
183    throws ServletException {
184        
185        this.config = moduleConfig;
186        this.servlet = servlet;
187        initMappings();
188        servlet.getServletContext().setAttribute(SECURE_PLUGIN, this);
189        super.init(servlet, moduleConfig);
190    }
191    
192    @Override
193    protected void initRequestProcessorClass(ModuleConfig config) throws ServletException {
194        String tilesProcessorClassname = SecureTilesRequestProcessor.class.getName();
195        ControllerConfig ctrlConfig = config.getControllerConfig();
196        String configProcessorClassname = ctrlConfig.getProcessorClass();
197        Class configProcessorClass;
198        try {
199            configProcessorClass = RequestUtils.applicationClass(configProcessorClassname);
200        } catch (java.lang.ClassNotFoundException ex) {
201            log.fatal("Can't set TilesRequestProcessor: bad class name '"
202            + configProcessorClassname
203            + "'.");
204            throw new ServletException(ex);
205        }
206        if (configProcessorClassname.equals(RequestProcessor.class.getName())
207        || configProcessorClassname.endsWith(tilesProcessorClassname)) {
208            ctrlConfig.setProcessorClass(tilesProcessorClassname);
209            return;
210        }
211        Class tilesProcessorClass = SecureTilesRequestProcessor.class;
212        if (!tilesProcessorClass.isAssignableFrom(configProcessorClass)) { // Not compatible
213            String msg = "TilesPlugin : Specified RequestProcessor not compatible with TilesRequestProcessor";
214            if (log.isFatalEnabled()) {
215                log.fatal(msg);
216            }
217            throw new ServletException(msg);
218        } // end if
219    }
220    
221    protected void initMappings() throws ServletException {
222        
223        // Get the action servlet name
224        this.servletName = servlet.getServletConfig().getServletName();
225        
226        // Prepare a Digester to scan the web application deployment descriptor
227        Digester digester = new Digester();
228        digester.push(this);
229        digester.setNamespaceAware(true);
230        digester.setValidating(false);
231        
232        // Register our local copy of the DTDs that we can find
233        for(int i = 0; i < registrations.length; i += 2) {
234            URL url = this.getClass().getResource(registrations[i + 1]);
235            if (url != null) {
236                digester.register(registrations[i], url.toString());
237            }
238        }
239        
240        // Configure the processing rules that we need
241        digester.addCallMethod("web-app/servlet-mapping",
242        "addServletMapping", 2);
243        digester.addCallParam("web-app/servlet-mapping/servlet-name", 0);
244        digester.addCallParam("web-app/servlet-mapping/url-pattern", 1);
245        
246        // Process the web application deployment descriptor
247        if (log.isDebugEnabled()) {
248            log.debug("Scanning web.xml for ActionServlet URL mappings");
249        }
250        InputStream input = null;
251        try {
252            input =
253            servlet.getServletContext().getResourceAsStream("/WEB-INF/web.xml");
254            digester.parse(input);
255        } catch (Throwable e) {
256            log.error(resources.getMessage("configWebXml"), e);
257        } finally {
258            if (input != null) {
259                try {
260                    input.close();
261                } catch (IOException e) {
262                    ;
263                }
264            }
265        }
266    }
267    
268    protected void initResources() throws ServletException {
269        
270        try {
271            resources = MessageResources.getMessageResources(resourceName);
272        } catch (MissingResourceException e) {
273            log.error("Cannot load internal resources from '" + resourceName + "'",
274            e);
275            throw new UnavailableException
276            ("Cannot load internal resources from '" + resourceName + "'");
277        }
278    }
279    
280    public void addServletMapping(String servletName, String urlPattern) {
281        
282        if (log.isDebugEnabled()) {
283            log.debug("Process servletName=" + servletName +
284            ", urlPattern=" + urlPattern);
285        }
286        if (servletName == null) {
287            return;
288        }
289        if (servletName.equals(this.servletName)) {
290            this.servletMappings.add(urlPattern);
291        }
292    }
293}