001/*
002 * (C) Copyright 2013 Nuxeo SA (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl-2.1.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     Arnaud Kervern
016 */
017
018package org.nuxeo.ecm.platform.web.common.requestcontroller.service;
019
020import java.io.Serializable;
021import java.util.Dictionary;
022import java.util.Enumeration;
023import java.util.Hashtable;
024
025import javax.servlet.FilterConfig;
026import javax.servlet.ServletContext;
027import javax.servlet.http.HttpServletRequest;
028
029import org.apache.commons.lang.StringUtils;
030import org.nuxeo.common.xmap.annotation.XNode;
031import org.nuxeo.common.xmap.annotation.XObject;
032import org.nuxeo.runtime.api.Framework;
033
034import static org.apache.commons.lang.StringUtils.isEmpty;
035
036/**
037 * @author <a href="mailto:ak@nuxeo.com">Arnaud Kervern</a>
038 * @since 5.7.2
039 */
040@XObject(value = "corsConfig")
041public class NuxeoCorsFilterDescriptor implements Serializable, Cloneable {
042
043    private static final String PROPERTIES_PREFIX = "cors.";
044
045    @XNode("@name")
046    protected String name;
047
048    @XNode("@enabled")
049    protected Boolean enabled = true;
050
051    @XNode("@allowGenericHttpRequests")
052    protected Boolean allowGenericHttpRequests = true;
053
054    @XNode("@allowOrigin")
055    protected String allowOrigin;
056
057    @XNode("@allowSubdomains")
058    protected boolean allowSubdomains = false;
059
060    @XNode("@supportedMethods")
061    protected String supportedMethods;
062
063    @XNode("@supportedHeaders")
064    protected String supportedHeaders;
065
066    @XNode("@exposedHeaders")
067    protected String exposedHeaders;
068
069    @XNode("@supportsCredentials")
070    protected Boolean supportsCredentials = true;
071
072    @XNode("@maxAge")
073    protected int maxAge = -1;
074
075    protected String pattern = "";
076
077    @XNode("pattern")
078    public void setPattern(String pattern) {
079        this.pattern = Framework.expandVars(pattern);
080    }
081
082    public FilterConfig buildFilterConfig() {
083        final Dictionary<String, String> parameters = buildDictionary();
084
085        return new FilterConfig() {
086            @Override
087            public String getFilterName() {
088                return "NuxeoCorsFilterDescriptor";
089            }
090
091            @Override
092            public ServletContext getServletContext() {
093                // Not used with @see CORSFilter
094                return null;
095            }
096
097            @Override
098            public String getInitParameter(String name) {
099                return parameters.get(name);
100            }
101
102            @Override
103            public Enumeration getInitParameterNames() {
104                return parameters.keys();
105            }
106        };
107    }
108
109    public boolean isMatching(HttpServletRequest request) {
110        return !StringUtils.isEmpty(pattern) && request.getRequestURI().matches(pattern);
111    }
112
113    public NuxeoCorsFilterDescriptor clone() throws CloneNotSupportedException {
114        NuxeoCorsFilterDescriptor n = new NuxeoCorsFilterDescriptor();
115        n.name = name;
116        n.allowGenericHttpRequests = allowGenericHttpRequests;
117        n.allowOrigin = allowOrigin;
118        n.allowSubdomains = allowSubdomains;
119        n.supportedMethods = supportedMethods;
120        n.supportedHeaders = supportedHeaders;
121        n.exposedHeaders = exposedHeaders;
122        n.supportsCredentials = supportsCredentials;
123        n.maxAge = maxAge;
124        n.pattern = pattern;
125        return n;
126    }
127
128    public void merge(NuxeoCorsFilterDescriptor o) {
129        allowGenericHttpRequests = o.allowGenericHttpRequests;
130        supportsCredentials = o.supportsCredentials;
131        allowSubdomains = o.allowSubdomains;
132
133        if (!StringUtils.isEmpty(o.allowOrigin)) {
134            allowOrigin = o.allowOrigin;
135        }
136
137        if (!StringUtils.isEmpty(o.supportedMethods)) {
138            supportedMethods = o.supportedMethods;
139        }
140
141        if (!StringUtils.isEmpty(o.supportedHeaders)) {
142            supportedHeaders = o.supportedHeaders;
143        }
144
145        if (!StringUtils.isEmpty(o.exposedHeaders)) {
146            exposedHeaders = o.exposedHeaders;
147        }
148
149        if (maxAge == -1) {
150            maxAge = o.maxAge;
151        }
152
153        if (!StringUtils.isEmpty(o.pattern)) {
154            pattern = o.pattern;
155        }
156    }
157
158    protected Dictionary<String, String> buildDictionary() {
159        Dictionary<String, String> params = new Hashtable<>();
160        params.put(PROPERTIES_PREFIX + "allowGenericHttpRequests", Boolean.toString(allowGenericHttpRequests));
161
162        if (!isEmpty(allowOrigin)) {
163            params.put(PROPERTIES_PREFIX + "allowOrigin", allowOrigin);
164        }
165
166        params.put(PROPERTIES_PREFIX + "allowSubdomains", Boolean.toString(allowSubdomains));
167
168        if (!isEmpty(supportedMethods)) {
169            params.put(PROPERTIES_PREFIX + "supportedMethods", supportedMethods);
170        }
171
172        if (!isEmpty(supportedHeaders)) {
173            params.put(PROPERTIES_PREFIX + "supportedHeaders", supportedHeaders);
174        }
175
176        if (!isEmpty(exposedHeaders)) {
177            params.put(PROPERTIES_PREFIX + "exposedHeaders", exposedHeaders);
178        }
179
180        params.put(PROPERTIES_PREFIX + "supportsCredentials", Boolean.toString(supportsCredentials));
181        params.put(PROPERTIES_PREFIX + "maxAge", Integer.toString(maxAge));
182
183        return params;
184    }
185}