1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package io.wcm.caravan.commons.cors.impl;
21
22 import java.io.IOException;
23 import java.util.Arrays;
24 import java.util.Map;
25 import java.util.Set;
26 import java.util.stream.Collectors;
27
28 import javax.servlet.Filter;
29 import javax.servlet.FilterChain;
30 import javax.servlet.FilterConfig;
31 import javax.servlet.ServletException;
32 import javax.servlet.ServletRequest;
33 import javax.servlet.ServletResponse;
34 import javax.servlet.http.HttpServletRequest;
35 import javax.servlet.http.HttpServletResponse;
36
37 import org.apache.commons.lang3.StringUtils;
38 import org.apache.felix.scr.annotations.Activate;
39 import org.apache.felix.scr.annotations.Component;
40 import org.apache.felix.scr.annotations.Properties;
41 import org.apache.felix.scr.annotations.Property;
42 import org.apache.felix.scr.annotations.Service;
43 import org.apache.sling.commons.osgi.PropertiesUtil;
44 import org.osgi.framework.Constants;
45
46
47
48
49 @Component(immediate = true, metatype = true,
50 label = "wcm.io Caravan CORS Filter",
51 description = "Servlet filter that sends CORS response header to allow cross-origin access.")
52 @Service(Filter.class)
53 @Properties({
54 @Property(name = Constants.SERVICE_RANKING, intValue = 10000),
55 @Property(name = "pattern", value = "/.*")
56 })
57 public class CorsServletFilter implements Filter {
58
59 @Property(boolValue = CorsServletFilter.DEFAULT_ENABLED,
60 label = "Enabled",
61 description = "Enable the CORS Filter.")
62 static final String PROPERTY_ENABLED = "enabled";
63 static final boolean DEFAULT_ENABLED = true;
64
65 @Property(boolValue = CorsServletFilter.DEFAULT_ALLOW_ALL_HOSTS,
66 label = "Allow all hosts",
67 description = "Always send '*' in the 'Access-Control-Allow-Origin' header. "
68 + "If not enabled only hosts in the white list and not in the blacklist are accepted. "
69 + "If both lists are empty, every origin is accepted and always included in the 'Access-Control-Allow-Origin' header.")
70 static final String PROPERTY_ALLOW_ALL_HOSTS = "allowAllHosts";
71 static final boolean DEFAULT_ALLOW_ALL_HOSTS = true;
72
73 @Property(label = "Host Whitelist",
74 description = "List of hosts (origins) allowed to access the resources protected by this filter.",
75 cardinality = Integer.MAX_VALUE)
76 static final String PROPERTY_HOST_WHITELIST = "hostWhitelist";
77
78 @Property(label = "Host Blacklist",
79 description = "List of hosts (origins) not allowed to access the resources protected by this filter.",
80 cardinality = Integer.MAX_VALUE)
81 static final String PROPERTY_HOST_BLACKLIST = "hostBlacklist";
82
83 private boolean enabled;
84 private boolean allowAllHosts;
85 private Set<String> hostWhitelist;
86 private Set<String> hostBlacklist;
87
88
89 @Activate
90 void activate(Map<String, Object> config) {
91 enabled = PropertiesUtil.toBoolean(config.get(PROPERTY_ENABLED), DEFAULT_ENABLED);
92 allowAllHosts = PropertiesUtil.toBoolean(config.get(PROPERTY_ALLOW_ALL_HOSTS), DEFAULT_ALLOW_ALL_HOSTS);
93
94 String[] whitelist = PropertiesUtil.toStringArray(config.get(PROPERTY_HOST_WHITELIST), new String[0]);
95 hostWhitelist = Arrays.stream(whitelist)
96 .filter(StringUtils::isNotBlank)
97 .collect(Collectors.toSet());
98
99 String[] blacklist = PropertiesUtil.toStringArray(config.get(PROPERTY_HOST_BLACKLIST), new String[0]);
100 hostBlacklist = Arrays.stream(blacklist)
101 .filter(StringUtils::isNotBlank)
102 .collect(Collectors.toSet());
103 }
104
105 @Override
106 public void init(FilterConfig filterConfig) throws ServletException {
107
108 }
109
110 @Override
111 public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
112 if (!enabled) {
113 chain.doFilter(servletRequest, servletResponse);
114 return;
115 }
116
117 HttpServletRequest request = (HttpServletRequest)servletRequest;
118 HttpServletResponse response = (HttpServletResponse)servletResponse;
119
120 String origin = request.getHeader(HttpHeader.ORIGIN);
121 if (StringUtils.isNotEmpty(origin)) {
122 if (allowAllHosts) {
123 response.setHeader(HttpHeader.ACCESS_CONTROL_ALLOW_ORIGIN, "*");
124 }
125 else if ((hostWhitelist.isEmpty() || hostWhitelist.contains(origin))
126 && (hostBlacklist.isEmpty() || !hostBlacklist.contains(origin))) {
127 response.setHeader(HttpHeader.ACCESS_CONTROL_ALLOW_ORIGIN, origin);
128 response.addHeader(HttpHeader.VARY, HttpHeader.ORIGIN);
129 }
130 }
131
132 chain.doFilter(request, response);
133 }
134
135 @Override
136 public void destroy() {
137
138 }
139
140 }