diff --git a/src/main/java/org/codelibs/fess/filter/CorsFilter.java b/src/main/java/org/codelibs/fess/filter/CorsFilter.java new file mode 100644 index 000000000..057ad71d0 --- /dev/null +++ b/src/main/java/org/codelibs/fess/filter/CorsFilter.java @@ -0,0 +1,102 @@ +/* + * Copyright 2012-2019 CodeLibs Project and the Others. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ +package org.codelibs.fess.filter; + +import java.io.IOException; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.codelibs.core.lang.StringUtil; +import org.codelibs.fess.mylasta.direction.FessConfig; +import org.codelibs.fess.util.ComponentUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class CorsFilter implements Filter { + + private static final Logger logger = LoggerFactory.getLogger(CorsFilter.class); + + protected static final String OPTIONS = "OPTIONS"; + + protected static final String ACCESS_CONTROL_ALLOW_CREDENTIALS = "Access-Control-Allow-Credentials"; + + protected static final String ACCESS_CONTROL_MAX_AGE = "Access-Control-Max-Age"; + + protected static final String ACCESS_CONTROL_ALLOW_HEADERS = "Access-Control-Allow-Headers"; + + protected static final String ACCESS_CONTROL_ALLOW_METHODS = "Access-Control-Allow-Methods"; + + protected static final String ACCESS_CONTROL_ALLOW_ORIGIN = "Access-Control-Allow-Origin"; + + protected static final String WILDCARD = "*"; + + @Override + public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException, + ServletException { + final HttpServletRequest httpRequest = (HttpServletRequest) request; + final String origin = httpRequest.getHeader("Origin"); + if (StringUtil.isBlank(origin)) { + chain.doFilter(request, response); + return; + } + + if (logger.isDebugEnabled()) { + logger.debug("HTTP Request: " + httpRequest.getMethod()); + } + + final FessConfig fessConfig = ComponentUtil.getFessConfig(); + + final String allowOrigin = getAllowOrigin(fessConfig, origin); + if (StringUtil.isNotBlank(allowOrigin)) { + if (logger.isDebugEnabled()) { + logger.debug("allowOrigin: " + allowOrigin); + } + final HttpServletResponse httpResponse = (HttpServletResponse) response; + httpResponse.addHeader(ACCESS_CONTROL_ALLOW_ORIGIN, allowOrigin); + httpResponse.addHeader(ACCESS_CONTROL_ALLOW_METHODS, fessConfig.getApiCorsAllowMethods()); + httpResponse.addHeader(ACCESS_CONTROL_ALLOW_HEADERS, fessConfig.getApiCorsAllowHeaders()); + httpResponse.addHeader(ACCESS_CONTROL_MAX_AGE, fessConfig.getApiCorsMaxAge()); + httpResponse.addHeader(ACCESS_CONTROL_ALLOW_CREDENTIALS, fessConfig.getApiCorsAllowCredentials()); + + if (httpRequest.getMethod().equals(OPTIONS)) { + httpResponse.setStatus(HttpServletResponse.SC_ACCEPTED); + return; + } + } + + chain.doFilter(request, response); + } + + protected String getAllowOrigin(final FessConfig fessConfig, final String origin) { + final String allowOrigin = fessConfig.getApiCorsAllowOrigin(); + if (StringUtil.isBlank(allowOrigin)) { + return StringUtil.EMPTY; + } + + if (WILDCARD.equals(allowOrigin)) { + return allowOrigin; + } + + return fessConfig.getApiCorsAllowOriginList().stream().filter(s -> s.equals(origin)).findFirst().orElse(StringUtil.EMPTY); + } + +} diff --git a/src/main/java/org/codelibs/fess/mylasta/direction/FessConfig.java b/src/main/java/org/codelibs/fess/mylasta/direction/FessConfig.java index 960d81c2b..744799d6b 100644 --- a/src/main/java/org/codelibs/fess/mylasta/direction/FessConfig.java +++ b/src/main/java/org/codelibs/fess/mylasta/direction/FessConfig.java @@ -199,6 +199,21 @@ public interface FessConfig extends FessEnv, org.codelibs.fess.mylasta.direction /** The key of the configuration. e.g. */ String API_DASHBOARD_RESPONSE_HEADERS = "api.dashboard.response.headers"; + /** The key of the configuration. e.g. * */ + String API_CORS_ALLOW_ORIGIN = "api.cors.allow.origin"; + + /** The key of the configuration. e.g. GET, POST, OPTIONS, DELETE, PUT */ + String API_CORS_ALLOW_METHODS = "api.cors.allow.methods"; + + /** The key of the configuration. e.g. 3600 */ + String API_CORS_MAX_AGE = "api.cors.max.age"; + + /** The key of the configuration. e.g. Origin, Content-Type, Accept, Authorization */ + String API_CORS_ALLOW_HEADERS = "api.cors.allow.headers"; + + /** The key of the configuration. e.g. true */ + String API_CORS_ALLOW_CREDENTIALS = "api.cors.allow.credentials"; + /** The key of the configuration. e.g. */ String VIRTUAL_HOST_HEADERS = "virtual.host.headers"; @@ -1847,6 +1862,56 @@ public interface FessConfig extends FessEnv, org.codelibs.fess.mylasta.direction */ Integer getApiDashboardResponseHeadersAsInteger(); + /** + * Get the value for the key 'api.cors.allow.origin'.
+ * The value is, e.g. *
+ * @return The value of found property. (NotNull: if not found, exception but basically no way) + */ + String getApiCorsAllowOrigin(); + + /** + * Get the value for the key 'api.cors.allow.methods'.
+ * The value is, e.g. GET, POST, OPTIONS, DELETE, PUT
+ * @return The value of found property. (NotNull: if not found, exception but basically no way) + */ + String getApiCorsAllowMethods(); + + /** + * Get the value for the key 'api.cors.max.age'.
+ * The value is, e.g. 3600
+ * @return The value of found property. (NotNull: if not found, exception but basically no way) + */ + String getApiCorsMaxAge(); + + /** + * Get the value for the key 'api.cors.max.age' as {@link Integer}.
+ * The value is, e.g. 3600
+ * @return The value of found property. (NotNull: if not found, exception but basically no way) + * @throws NumberFormatException When the property is not integer. + */ + Integer getApiCorsMaxAgeAsInteger(); + + /** + * Get the value for the key 'api.cors.allow.headers'.
+ * The value is, e.g. Origin, Content-Type, Accept, Authorization
+ * @return The value of found property. (NotNull: if not found, exception but basically no way) + */ + String getApiCorsAllowHeaders(); + + /** + * Get the value for the key 'api.cors.allow.credentials'.
+ * The value is, e.g. true
+ * @return The value of found property. (NotNull: if not found, exception but basically no way) + */ + String getApiCorsAllowCredentials(); + + /** + * Is the property for the key 'api.cors.allow.credentials' true?
+ * The value is, e.g. true
+ * @return The determination, true or false. (if not found, exception but basically no way) + */ + boolean isApiCorsAllowCredentials(); + /** * Get the value for the key 'virtual.host.headers'.
* The value is, e.g.
@@ -6202,6 +6267,34 @@ public interface FessConfig extends FessEnv, org.codelibs.fess.mylasta.direction return getAsInteger(FessConfig.API_DASHBOARD_RESPONSE_HEADERS); } + public String getApiCorsAllowOrigin() { + return get(FessConfig.API_CORS_ALLOW_ORIGIN); + } + + public String getApiCorsAllowMethods() { + return get(FessConfig.API_CORS_ALLOW_METHODS); + } + + public String getApiCorsMaxAge() { + return get(FessConfig.API_CORS_MAX_AGE); + } + + public Integer getApiCorsMaxAgeAsInteger() { + return getAsInteger(FessConfig.API_CORS_MAX_AGE); + } + + public String getApiCorsAllowHeaders() { + return get(FessConfig.API_CORS_ALLOW_HEADERS); + } + + public String getApiCorsAllowCredentials() { + return get(FessConfig.API_CORS_ALLOW_CREDENTIALS); + } + + public boolean isApiCorsAllowCredentials() { + return is(FessConfig.API_CORS_ALLOW_CREDENTIALS); + } + public String getVirtualHostHeaders() { return get(FessConfig.VIRTUAL_HOST_HEADERS); } @@ -8491,6 +8584,11 @@ public interface FessConfig extends FessEnv, org.codelibs.fess.mylasta.direction defaultMap.put(FessConfig.API_JSON_RESPONSE_HEADERS, ""); defaultMap.put(FessConfig.API_GSA_RESPONSE_HEADERS, ""); defaultMap.put(FessConfig.API_DASHBOARD_RESPONSE_HEADERS, ""); + defaultMap.put(FessConfig.API_CORS_ALLOW_ORIGIN, "*"); + defaultMap.put(FessConfig.API_CORS_ALLOW_METHODS, "GET, POST, OPTIONS, DELETE, PUT"); + defaultMap.put(FessConfig.API_CORS_MAX_AGE, "3600"); + defaultMap.put(FessConfig.API_CORS_ALLOW_HEADERS, "Origin, Content-Type, Accept, Authorization"); + defaultMap.put(FessConfig.API_CORS_ALLOW_CREDENTIALS, "true"); defaultMap.put(FessConfig.VIRTUAL_HOST_HEADERS, ""); defaultMap.put(FessConfig.HTTP_PROXY_HOST, ""); defaultMap.put(FessConfig.HTTP_PROXY_PORT, "8080"); diff --git a/src/main/java/org/codelibs/fess/mylasta/direction/FessProp.java b/src/main/java/org/codelibs/fess/mylasta/direction/FessProp.java index 614dd9fda..b3329cdfb 100644 --- a/src/main/java/org/codelibs/fess/mylasta/direction/FessProp.java +++ b/src/main/java/org/codelibs/fess/mylasta/direction/FessProp.java @@ -70,6 +70,8 @@ import org.lastaflute.web.validation.theme.typed.LongTypeValidator; public interface FessProp { + String CORS_ALLOW_ORIGIN = "CorsAllowOrigin"; + String API_DASHBOARD_RESPONSE_HEADER_LIST = "apiDashboardResponseHeaderList"; String API_JSON_RESPONSE_HEADER_LIST = "apiJsonResponseHeaderList"; @@ -1969,4 +1971,17 @@ public interface FessProp { } return list; } + + String getApiCorsAllowOrigin(); + + default List getApiCorsAllowOriginList() { + List list = (List) propMap.get(CORS_ALLOW_ORIGIN); + if (list == null) { + list = + split(getApiCorsAllowOrigin(), "\n").get( + stream -> stream.map(String::trim).filter(StringUtil::isNotEmpty).collect(Collectors.toList())); + propMap.put(CORS_ALLOW_ORIGIN, list); + } + return list; + } } diff --git a/src/main/resources/fess_config.properties b/src/main/resources/fess_config.properties index 84ebd8998..950281c6d 100644 --- a/src/main/resources/fess_config.properties +++ b/src/main/resources/fess_config.properties @@ -136,6 +136,11 @@ api.search.scroll=false api.json.response.headers= api.gsa.response.headers= api.dashboard.response.headers= +api.cors.allow.origin=* +api.cors.allow.methods=GET, POST, OPTIONS, DELETE, PUT +api.cors.max.age=3600 +api.cors.allow.headers=Origin, Content-Type, Accept, Authorization +api.cors.allow.credentials=true # Virtual Host: Host:fess.codelibs.org=fess virtual.host.headers= diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml index d76fb9f00..6cc636738 100644 --- a/src/main/webapp/WEB-INF/web.xml +++ b/src/main/webapp/WEB-INF/web.xml @@ -35,6 +35,11 @@ + + corsFilter + org.codelibs.fess.filter.CorsFilter + + webApiFilter org.codelibs.fess.filter.WebApiFilter @@ -77,6 +82,12 @@ INCLUDE + + corsFilter + /* + REQUEST + + webApiFilter /*