001/*
002 * (C) Copyright 2013 Nuxeo SA (http://nuxeo.com/) and others.
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 * Contributors:
017 *     Arnaud Kervern
018 */
019
020package org.nuxeo.ecm.platform.web.common.requestcontroller.service;
021
022import java.io.Serializable;
023import java.util.Dictionary;
024import java.util.Enumeration;
025import java.util.Hashtable;
026
027import javax.servlet.FilterConfig;
028import javax.servlet.ServletContext;
029import javax.servlet.http.HttpServletRequest;
030
031import org.apache.commons.lang.StringUtils;
032import org.nuxeo.common.xmap.annotation.XNode;
033import org.nuxeo.common.xmap.annotation.XObject;
034import org.nuxeo.runtime.api.Framework;
035
036import static org.apache.commons.lang.StringUtils.isEmpty;
037
038/**
039 * @author <a href="mailto:ak@nuxeo.com">Arnaud Kervern</a>
040 * @since 5.7.2
041 */
042@XObject(value = "corsConfig")
043public class NuxeoCorsFilterDescriptor implements Serializable, Cloneable {
044
045    private static final String PROPERTIES_PREFIX = "cors.";
046
047    @XNode("@name")
048    protected String name;
049
050    @XNode("@enabled")
051    protected Boolean enabled = true;
052
053    @XNode("@allowGenericHttpRequests")
054    protected Boolean allowGenericHttpRequests = true;
055
056    @XNode("@allowOrigin")
057    protected String allowOrigin;
058
059    @XNode("@allowSubdomains")
060    protected boolean allowSubdomains = false;
061
062    @XNode("@supportedMethods")
063    protected String supportedMethods;
064
065    @XNode("@supportedHeaders")
066    protected String supportedHeaders;
067
068    @XNode("@exposedHeaders")
069    protected String exposedHeaders;
070
071    @XNode("@supportsCredentials")
072    protected Boolean supportsCredentials = true;
073
074    @XNode("@maxAge")
075    protected int maxAge = -1;
076
077    protected String pattern = "";
078
079    @XNode("pattern")
080    public void setPattern(String pattern) {
081        this.pattern = Framework.expandVars(pattern);
082    }
083
084    public FilterConfig buildFilterConfig() {
085        final Dictionary<String, String> parameters = buildDictionary();
086
087        return new FilterConfig() {
088            @Override
089            public String getFilterName() {
090                return "NuxeoCorsFilterDescriptor";
091            }
092
093            @Override
094            public ServletContext getServletContext() {
095                // Not used with @see CORSFilter
096                return null;
097            }
098
099            @Override
100            public String getInitParameter(String name) {
101                return parameters.get(name);
102            }
103
104            @Override
105            public Enumeration getInitParameterNames() {
106                return parameters.keys();
107            }
108        };
109    }
110
111    public boolean isMatching(HttpServletRequest request) {
112        return !StringUtils.isEmpty(pattern) && request.getRequestURI().matches(pattern);
113    }
114
115    public NuxeoCorsFilterDescriptor clone() throws CloneNotSupportedException {
116        NuxeoCorsFilterDescriptor n = new NuxeoCorsFilterDescriptor();
117        n.name = name;
118        n.allowGenericHttpRequests = allowGenericHttpRequests;
119        n.allowOrigin = allowOrigin;
120        n.allowSubdomains = allowSubdomains;
121        n.supportedMethods = supportedMethods;
122        n.supportedHeaders = supportedHeaders;
123        n.exposedHeaders = exposedHeaders;
124        n.supportsCredentials = supportsCredentials;
125        n.maxAge = maxAge;
126        n.pattern = pattern;
127        return n;
128    }
129
130    public void merge(NuxeoCorsFilterDescriptor o) {
131        allowGenericHttpRequests = o.allowGenericHttpRequests;
132        supportsCredentials = o.supportsCredentials;
133        allowSubdomains = o.allowSubdomains;
134
135        if (!StringUtils.isEmpty(o.allowOrigin)) {
136            allowOrigin = o.allowOrigin;
137        }
138
139        if (!StringUtils.isEmpty(o.supportedMethods)) {
140            supportedMethods = o.supportedMethods;
141        }
142
143        if (!StringUtils.isEmpty(o.supportedHeaders)) {
144            supportedHeaders = o.supportedHeaders;
145        }
146
147        if (!StringUtils.isEmpty(o.exposedHeaders)) {
148            exposedHeaders = o.exposedHeaders;
149        }
150
151        if (maxAge == -1) {
152            maxAge = o.maxAge;
153        }
154
155        if (!StringUtils.isEmpty(o.pattern)) {
156            pattern = o.pattern;
157        }
158    }
159
160    protected Dictionary<String, String> buildDictionary() {
161        Dictionary<String, String> params = new Hashtable<>();
162        params.put(PROPERTIES_PREFIX + "allowGenericHttpRequests", Boolean.toString(allowGenericHttpRequests));
163
164        if (!isEmpty(allowOrigin)) {
165            params.put(PROPERTIES_PREFIX + "allowOrigin", allowOrigin);
166        }
167
168        params.put(PROPERTIES_PREFIX + "allowSubdomains", Boolean.toString(allowSubdomains));
169
170        if (!isEmpty(supportedMethods)) {
171            params.put(PROPERTIES_PREFIX + "supportedMethods", supportedMethods);
172        }
173
174        if (!isEmpty(supportedHeaders)) {
175            params.put(PROPERTIES_PREFIX + "supportedHeaders", supportedHeaders);
176        }
177
178        if (!isEmpty(exposedHeaders)) {
179            params.put(PROPERTIES_PREFIX + "exposedHeaders", exposedHeaders);
180        }
181
182        params.put(PROPERTIES_PREFIX + "supportsCredentials", Boolean.toString(supportsCredentials));
183        params.put(PROPERTIES_PREFIX + "maxAge", Integer.toString(maxAge));
184
185        return params;
186    }
187}