├── .classpath
├── .gitignore
├── .project
├── .settings
└── org.codehaus.groovy.eclipse.preferences.prefs
├── .travis.yml
├── CorsGrailsPlugin.groovy
├── README.md
├── application.properties
├── grails-app
└── conf
│ ├── BuildConfig.groovy
│ ├── Config.groovy
│ └── DataSource.groovy
├── grailsw
├── grailsw.bat
├── src
└── java
│ └── com
│ └── brandseye
│ └── cors
│ ├── CorsCompatibleBasicAuthenticationEntryPoint.java
│ └── CorsFilter.java
├── test
└── unit
│ └── com
│ └── brandseye
│ └── cors
│ ├── CorsCompatibleBasicAuthenticationEntryPointTests.groovy
│ └── CorsFilterTest.groovy
└── wrapper
├── grails-wrapper-runtime-2.2.1.jar
├── grails-wrapper.properties
└── springloaded-core-1.1.1.jar
/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .idea
3 | grails
4 | plugin.xml
5 | *.zip
6 | target
7 | web-app
8 | *.log
9 | .settings
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 | cors
20 |
21 |
22 |
23 |
24 |
25 | org.eclipse.jdt.core.javabuilder
26 |
27 |
28 |
29 |
30 |
31 | org.grails.ide.eclipse.core.nature
32 | org.eclipse.jdt.groovy.core.groovyNature
33 | org.eclipse.jdt.core.javanature
34 |
35 |
36 |
37 | .link_to_grails_plugins
38 | 2
39 | GRAILS_ROOT/grails-cors/target/plugins
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/.settings/org.codehaus.groovy.eclipse.preferences.prefs:
--------------------------------------------------------------------------------
1 | #Created by grails
2 | eclipse.preferences.version=1
3 | groovy.dont.generate.class.files=true
4 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: groovy
2 | jdk:
3 | - oraclejdk7
4 |
5 | branches:
6 | only:
7 | - master
8 |
9 | before_script:
10 | - chmod +x grailsw
11 |
12 | script: ./grailsw refresh-dependencies
13 | && ./grailsw test-app
--------------------------------------------------------------------------------
/CorsGrailsPlugin.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 BrandsEye (http://www.brandseye.com/)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import com.brandseye.cors.CorsCompatibleBasicAuthenticationEntryPoint
18 | import com.brandseye.cors.CorsFilter
19 |
20 | class CorsGrailsPlugin {
21 | def version = "1.3.0"
22 | def grailsVersion = "2.0 > *"
23 | def title = "CORS Plugin"
24 | def author = "David Tinker"
25 | def authorEmail = "david.tinker@gmail.com"
26 | def description = 'Installs a servlet filter to set Access-Control-Allow-Origin and other CORS related headers to enable cross site AJAX requests to your Grails application'
27 | def documentation = "https://github.com/davidtinker/grails-cors"
28 |
29 | // Make sure this loads after Spring Security otherwise we'll lose our custom BasicAuthenticationEntryPoint bean
30 | def loadAfter = ['springSecurityCore']
31 |
32 | def license = "APACHE"
33 | def organization = [ name: "BrandsEye", url: "http://www.brandseye.com/" ]
34 | def issueManagement = [ system: "Github", url: "https://github.com/davidtinker/grails-cors/issues" ]
35 | def scm = [ url: "https://github.com/davidtinker/grails-cors" ]
36 |
37 | def doWithSpring = {
38 | // If using Spring Security's Basic Auth, swap out the BasicAuthenticationEntryPoint bean to prevent
39 | // authenticating on OPTIONS requests. See https://github.com/davidtinker/grails-cors/issues/12 for more info.
40 | if (application.config.grails.plugin.springsecurity?.useBasicAuth) {
41 | basicAuthenticationEntryPoint(CorsCompatibleBasicAuthenticationEntryPoint) { bean ->
42 | realmName = application.config.grails.plugin.springsecurity.basic.realmName
43 | }
44 | }
45 | }
46 |
47 | def doWithWebDescriptor = { xml ->
48 | def cfg = application.config.cors
49 |
50 | if (cfg.containsKey('enabled') && !cfg.enabled) return
51 |
52 | def contextParam = xml.'context-param'
53 | contextParam[contextParam.size() - 1] + {
54 | 'filter' {
55 | 'filter-name'('cors-headers')
56 | 'filter-class'(CorsFilter.name)
57 | if (cfg.allow.origin.regex) {
58 | 'init-param' {
59 | 'param-name'('allow.origin.regex')
60 | 'param-value'(cfg.allow.origin.regex.toString())
61 | }
62 | }
63 | if (cfg.headers instanceof Map) {
64 | cfg.headers.each { k,v ->
65 | 'init-param' {
66 | 'param-name'('header:' + k)
67 | 'param-value'(v)
68 | }
69 | }
70 | } else if (cfg.headers instanceof String) {
71 | def headerMap = Eval.me(cfg.headers?.toString())
72 | if(headerMap instanceof Map){
73 | headerMap.each { k,v ->
74 | 'init-param' {
75 | 'param-name'('header:' + k)
76 | 'param-value'(v)
77 | }
78 | }
79 | }
80 | }
81 | if (cfg.expose.headers) {
82 | 'init-param' {
83 | 'param-name'('expose.headers')
84 | 'param-value'(cfg.expose.headers.toString())
85 | }
86 | }
87 | if (cfg.enable.logging) {
88 | 'init-param' {
89 | 'param-name'('enable.logging')
90 | 'param-value'(cfg.enable.logging.toString())
91 | }
92 | }
93 | }
94 | }
95 |
96 | def patterns = cfg.url.pattern ?: '/*'
97 | def urlPatterns = patterns instanceof List ? patterns : patterns.split(',').collect{ it.trim() }
98 |
99 | def filter = xml.'filter'
100 | urlPatterns.each { pattern ->
101 | filter[0] + {
102 | 'filter-mapping'{
103 | 'filter-name'('cors-headers')
104 | 'url-pattern'(pattern)
105 | }
106 | }
107 | }
108 | }
109 |
110 | def getWebXmlFilterOrder() {
111 | def FilterManager = getClass().getClassLoader().loadClass('grails.plugin.webxml.FilterManager')
112 | // Be before the earliest Resource filter.
113 | ['cors-headers': FilterManager.DEFAULT_POSITION - 400]
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | CORS Plugin
2 | ===========
3 |
4 | [](https://travis-ci.org/Grails-Plugin-Consortium/grails-cors)
5 |
6 | Grails plugin to add Cross-Origin Resource Sharing (CORS) headers for Grails applications.
7 | These headers make it possible for Javascript code served from a different host to easily make calls to your
8 | application.
9 |
10 | * http://en.wikipedia.org/wiki/Cross-origin_resource_sharing
11 | * http://www.w3.org/TR/access-control/
12 |
13 | It is not easy to do this in a Grails application due to the following bug: http://jira.grails.org/browse/GRAILS-5531
14 |
15 | Grails 3+
16 | ---------
17 |
18 | This plugin does not work with Grails 3. To handle CORS in a Grails 3 project you can just create a servlet filter
19 | somewhere under ```src/main/java```:
20 |
21 | @Priority(Integer.MIN_VALUE)
22 | public class CorsFilter extends OncePerRequestFilter {
23 |
24 | public CorsFilter() { }
25 |
26 | @Override
27 | protected void doFilterInternal(HttpServletRequest req, HttpServletResponse resp, FilterChain chain)
28 | throws ServletException, IOException {
29 |
30 | String origin = req.getHeader("Origin");
31 |
32 | boolean options = "OPTIONS".equals(req.getMethod());
33 | if (options) {
34 | if (origin == null) return;
35 | resp.addHeader("Access-Control-Allow-Headers", "origin, authorization, accept, content-type, x-requested-with");
36 | resp.addHeader("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS");
37 | resp.addHeader("Access-Control-Max-Age", "3600");
38 | }
39 |
40 | resp.addHeader("Access-Control-Allow-Origin", origin == null ? "*" : origin);
41 | resp.addHeader("Access-Control-Allow-Credentials", "true");
42 |
43 | if (!options) chain.doFilter(req, resp);
44 | }
45 | }
46 |
47 | Reference the filter in ```grails-app/conf/spring/resources.groovy```:
48 |
49 | beans = {
50 | corsFilter(CorsFilter)
51 | }
52 |
53 | Using
54 | -----
55 |
56 | Add a dependency to BuildConfig.groovy:
57 |
58 | plugins {
59 | runtime ":cors:1.3.0"
60 | ...
61 | }
62 |
63 | The default configuration installs a servlet filter that adds the following headers to all OPTIONS requests:
64 |
65 | Access-Control-Allow-Origin:
66 | Access-Control-Allow-Credentials: true
67 | Access-Control-Allow-Headers: origin, authorization, accept, content-type, x-requested-with
68 | Access-Control-Allow-Methods: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS
69 | Access-Control-Max-Age: 3600
70 |
71 | The 'Access-Control-Allow-Origin' and 'Access-Control-Allow-Credentials' headers are also added to non-OPTIONS
72 | requests (GET, POST et al.). If the plugin is configured to produce an 'Access-Control-Expose-Headers' header,
73 | it will be added to non-OPTIONS requests as well.
74 |
75 | These headers permit Javascript code loaded from anywhere to make AJAX calls to your application including specifying
76 | an 'Authorization' header (e.g. for Basic authentication). The browser will cache the results of the OPTIONS request
77 | for 1 hour (3600 seconds).
78 |
79 | Configuration
80 | -------------
81 |
82 | The CORS plugin is configured through Config.groovy.
83 |
84 | You can limit the URL patterns the filter is applied to:
85 |
86 | cors.url.pattern = '/rest/*'
87 |
88 | This parameter also accepts a list of patterns to match:
89 |
90 | cors.url.pattern = ['/service1/*', '/service2/*']
91 |
92 | Due to the 'Stringy' nature of external properties files, url patterns can be configured using a comma seperated string such as:
93 |
94 | cors.url.pattern = /api/*, /api-docs/*
95 |
96 | You can override the default values used for the headers by supplying a headers map:
97 |
98 | cors.headers = [
99 | 'Access-Control-Allow-Origin': 'http://app.example.com',
100 | 'My-Custom-Header': 'some value']
101 |
102 | Due to the 'Stringy' nature of external properties files, headers can be configured using a single line 'string' map:
103 |
104 | cors.headers = ['Access-Control-Allow-Origin': 'http://app.example.com','My-Custom-Header': 'some value']
105 |
106 | Note that if you want to specify more than one host for 'Access-Control-Allow-Origin' there are issues with
107 | browser support. The recommended approach is to check the 'Origin' header of the request and echo it back
108 | if you are happy with it. The CORS plugin implements this using a regex to match allowed origins:
109 |
110 | cors.allow.origin.regex = '.*\\.example\\.com'
111 |
112 | If 'Origin' header matches the regex then it is echoed back as 'Access-Control-Allow-Origin' otherwise no CORS
113 | headers are sent back to the client and the browser will deny the request.
114 |
115 | Note that you can always send back '*' instead of echoing the 'Origin' header by including:
116 |
117 | cors.headers = ['Access-Control-Allow-Origin': '*']
118 |
119 | This can be combined with cors.allow.origin.regex to limit allowed domains.
120 |
121 | You can specify a comma-delimited list of response headers that should be exposed to the client:
122 |
123 | cors.expose.headers = 'X-app-header1,X-app-header2'
124 |
125 | You can accept any Access-Control-Request-Headers as follows:
126 |
127 | cors.headers = ['Access-Control-Allow-Headers': '*']
128 |
129 | You can enable logging of failed requests:
130 |
131 | cors.enable.logging = true
132 |
133 | You can disable the filter if needed. Note that the filter defaults to enabled.
134 |
135 | cors.enabled = false
136 |
137 | Client Performance Notes
138 | ------------------------
139 |
140 | The browser only has to make an extra OPTIONS request for a cross-site AJAX GET if additional headers are specified.
141 | So this cross site jQuery AJAX call does not result in an OPTIONS request:
142 |
143 | $.ajax("https://api.notmyserver.com/rest/stuff/123", {
144 | data: data,
145 | dataType: 'json',
146 | type: 'get',
147 | success: callback,
148 | });
149 |
150 | Whereas this one does (at least the first time):
151 |
152 | $.ajax("https://api.notmyserver.com/rest/stuff/123", {
153 | data: data,
154 | dataType: 'json',
155 | type: 'get',
156 | success: callback,
157 | headers: {Authorization: "Basic ..."}
158 | });
159 |
160 | So if you can authenticate or whatever using query parameters instead of headers it can reduce latency.
161 |
162 | License
163 | -------
164 |
165 | Copyright 2013-2022 DataEQ (http://www.dataeq.com/)
166 |
167 | Licensed under the Apache License, Version 2.0 (the "License");
168 | you may not use this file except in compliance with the License.
169 | You may obtain a copy of the License at
170 |
171 | http://www.apache.org/licenses/LICENSE-2.0
172 |
173 | Unless required by applicable law or agreed to in writing, software
174 | distributed under the License is distributed on an "AS IS" BASIS,
175 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
176 | See the License for the specific language governing permissions and
177 | limitations under the License.
178 |
179 | Changelog
180 | ---------
181 | 1.3.0:
182 | - Added support for Access-Control-Allow-Headers="*" to echo back requested headers (thanks pablitar)
183 |
184 | 1.2.0:
185 | - Added support for logging failed requests (cors.enable.logging=true, thanks arrucard)
186 |
187 | 1.1.9:
188 | - Fixed bug with external config eval
189 |
190 | 1.1.8:
191 | - Adding support for external configuration files and travis.yml file (thanks ctoestreich)
192 |
193 | 1.1.7:
194 | - Merged PR for Spring Security 2 plugin (thanks neoecos). This plugin will no longer work with Spring Security < 2.0.
195 |
196 | 1.1.6:
197 | - Reverted to building plugin with Grails 2.2.1. Version 1.1.5 wasn't working with a Grails 2.0.3 app
198 |
199 | 1.1.5:
200 | - Got rid of deprecated ConfigHolder so plugin works with Grails 2.4
201 | - Removed css and other junk (was causing issues with asset pipeline)
202 |
203 | 1.1.4:
204 | - Fixed issue with Access-Control-Allow-Origin in cors.headers being ignored. If cors.headers does not contain
205 | Access-Control-Allow-Origin then any Origin accepted (any or those matching cors.allow.origin.regex) is echoed
206 | back. If cors.headers does contain Access-Control-Allow-Origin then this value is returned for accepted Origin's
207 | (i.e. you can use this in combination with cors.allow.origin.regex or set it to '*' to always send back '*' instead
208 | of the Origin header, useful if the result is cached by a CDN and the Origin varies).
209 |
210 | 1.1.3:
211 | - Fixed issue with getWebXmlFilterOrder not working in some circumstances (thanks Danilo Tuler)
212 |
213 | 1.1.2:
214 | - The CORS servlet filter now processes requests ahead of the resources filters (thanks Steve Loeppky)
215 | - OPTIONS requests are no longer passed down the filter chain (thanks Marcus Krantz)
216 |
217 | 1.1.1: Now works with Spring Security basic authentication (thanks James Hardwick)
218 |
219 | 1.1.0:
220 | - If 'Access-Control-Allow-Origin' is '*' (the default) then the 'Origin' header is echoed back instead of '*'. This
221 | is potentially a breaking change but is theoretically "more compliant" with the spec
222 | - No CORS headers are sent back if the client does not supply an 'Origin' header
223 | - Added 'Access-Control-Expose-Headers' option via 'cors.expose.headers' Config.groovy setting
224 | - Added 'cors.enabled' Config.groovy setting to explicitly enable/disabled the filter (filter is enabled by default)
225 |
226 | 1.0.4: Added Access-Control-Allow-Credentials: true
227 |
228 | 1.0.3: Bumped version no. to workaround plugin publishing issue
229 |
230 | 1.0.2: Added Access-Control-Allow-Methods header to OPTIONS request
231 |
232 | 1.0.1: Added Content-Type to default Access-Control-Allow-Headers
233 |
--------------------------------------------------------------------------------
/application.properties:
--------------------------------------------------------------------------------
1 | #Grails Metadata file
2 | #Fri May 23 17:59:16 SAST 2014
3 | app.grails.version=2.2.1
4 | app.name=grails-cors
5 | app.servlet.version=2.5
6 |
--------------------------------------------------------------------------------
/grails-app/conf/BuildConfig.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 BrandsEye (http://www.brandseye.com/)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | grails.project.work.dir = "target"
18 |
19 | grails.project.dependency.resolution = {
20 | inherits "global"
21 | log "warn"
22 |
23 | repositories {
24 | grailsPlugins()
25 | grailsHome()
26 | grailsCentral()
27 | mavenLocal()
28 | mavenCentral()
29 | mavenRepo "https://repo.grails.org/grails/plugins"
30 | }
31 |
32 | dependencies {
33 | //test ":grails-test-suite-base:$grailsVersion"
34 | String springSecurityVersion = '3.0.7.RELEASE'
35 | compile("org.springframework.security:spring-security-core:$springSecurityVersion") {
36 | transitive = false
37 | }
38 | compile("org.springframework.security:spring-security-web:$springSecurityVersion") {
39 | transitive = false
40 | }
41 | test 'org.hamcrest:hamcrest-core:1.3'
42 | }
43 |
44 | plugins {
45 | compile ":webxml:1.4.1"
46 | build (':release:2.2.1', ':rest-client-builder:1.0.3') {
47 | export = false
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/grails-app/conf/Config.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 BrandsEye (http://www.brandseye.com/)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | log4j = {
18 | error 'org.codehaus.groovy.grails',
19 | 'org.springframework',
20 | 'org.hibernate',
21 | 'net.sf.ehcache.hibernate'
22 | }
23 |
24 | // Uncomment and edit the following lines to start using Grails encoding & escaping improvements
25 |
26 | /* remove this line
27 | // GSP settings
28 | grails {
29 | views {
30 | gsp {
31 | encoding = 'UTF-8'
32 | htmlcodec = 'xml' // use xml escaping instead of HTML4 escaping
33 | codecs {
34 | expression = 'html' // escapes values inside null
35 | scriptlet = 'none' // escapes output from scriptlets in GSPs
36 | taglib = 'none' // escapes output from taglibs
37 | staticparts = 'none' // escapes output from static template parts
38 | }
39 | }
40 | // escapes all not-encoded output at final stage of outputting
41 | filteringCodecForContentType {
42 | //'text/html' = 'html'
43 | }
44 | }
45 | }
46 | remove this line */
47 |
--------------------------------------------------------------------------------
/grails-app/conf/DataSource.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 BrandsEye (http://www.brandseye.com/)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | dataSource {
18 | pooled = true
19 | driverClassName = 'org.h2.Driver'
20 | username = 'sa'
21 | password = ''
22 | dbCreate = 'update'
23 | url = 'jdbc:h2:mem:testDb'
24 | }
25 |
26 | hibernate {
27 | cache.use_second_level_cache = false
28 | cache.use_query_cache = false
29 | cache.provider_class = 'org.hibernate.cache.EhCacheProvider'
30 | }
31 |
--------------------------------------------------------------------------------
/grailsw:
--------------------------------------------------------------------------------
1 | ##############################################################################
2 | ## ##
3 | ## Grails JVM Bootstrap for UN*X ##
4 | ## ##
5 | ##############################################################################
6 |
7 | PROGNAME=`basename "$0"`
8 | DIRNAME=`dirname "$0"`
9 |
10 | # Use the maximum available, or set MAX_FD != -1 to use that
11 | MAX_FD="maximum"
12 |
13 | warn() {
14 | echo "${PROGNAME}: $*"
15 | }
16 |
17 | die() {
18 | warn "$*"
19 | exit 1
20 | }
21 |
22 | earlyInit() {
23 | return
24 | }
25 | lateInit() {
26 | return
27 | }
28 |
29 | GROOVY_STARTUP=~/.groovy/startup
30 | if [ -r "$GROOVY_STARTUP" ]; then
31 | . "$GROOVY_STARTUP"
32 | fi
33 |
34 | earlyInit
35 |
36 | # OS specific support (must be 'true' or 'false').
37 | cygwin=false;
38 | darwin=false;
39 | case "`uname`" in
40 | CYGWIN*)
41 | cygwin=true
42 | ;;
43 |
44 | Darwin*)
45 | darwin=true
46 | ;;
47 | esac
48 |
49 | # Attempt to set JAVA_HOME if it's not already set
50 | if [ -z "$JAVA_HOME" ]; then
51 |
52 | # Set JAVA_HOME for Darwin
53 | if $darwin; then
54 |
55 | [ -z "$JAVA_HOME" -a -d "/Library/Java/Home" ] &&
56 | export JAVA_HOME="/Library/Java/Home"
57 |
58 | [ -z "$JAVA_HOME" -a -d "/System/Library/Frameworks/JavaVM.framework/Home" ] &&
59 | export JAVA_HOME="/System/Library/Frameworks/JavaVM.framework/Home"
60 |
61 | fi
62 |
63 | fi
64 |
65 | # For Cygwin, ensure paths are in UNIX format before anything is touched
66 | if $cygwin ; then
67 | [ -n "$GRAILS_HOME" ] &&
68 | GRAILS_HOME=`cygpath --unix "$GRAILS_HOME"`
69 | [ -n "$JAVACMD" ] &&
70 | JAVACMD=`cygpath --unix "$JAVACMD"`
71 | [ -n "$JAVA_HOME" ] &&
72 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
73 | [ -n "$CP" ] &&
74 | CP=`cygpath --path --unix "$CP"`
75 | fi
76 |
77 | # Remove possible trailing slash (after possible cygwin correction)
78 | GRAILS_HOME=`echo $GRAILS_HOME | sed -e 's|/$||g'`
79 |
80 | # Locate GRAILS_HOME if not it is not set
81 | if [ -z "$GRAILS_HOME" -o ! -d "$GRAILS_HOME" ] ; then
82 | # resolve links - $0 may be a link to groovy's home
83 | PRG="$0"
84 |
85 | # need this for relative symlinks
86 | while [ -h "$PRG" ] ; do
87 | ls=`ls -ld "$PRG"`
88 | link=`expr "$ls" : '.*-> \(.*\)$'`
89 | if expr "$link" : '/.*' > /dev/null; then
90 | PRG="$link"
91 | else
92 | PRG=`dirname "$PRG"`"/$link"
93 | fi
94 | done
95 |
96 | SAVED="`pwd`"
97 | cd "`dirname \"$PRG\"`/.."
98 | GRAILS_HOME="`pwd -P`"
99 | cd "$SAVED"
100 | fi
101 |
102 | # Warn the user if JAVA_HOME and/or GRAILS_HOME are not set.
103 | if [ -z "$JAVA_HOME" ] ; then
104 | die "JAVA_HOME environment variable is not set"
105 | elif [ ! -d "$JAVA_HOME" ] ; then
106 | die "JAVA_HOME is not a directory: $JAVA_HOME"
107 | fi
108 |
109 | if [ -z "$GRAILS_HOME" ] ; then
110 | warn "GRAILS_HOME environment variable is not set"
111 | fi
112 |
113 | if [ ! -d "$GRAILS_HOME" ] ; then
114 | die "GRAILS_HOME is not a directory: $GRAILS_HOME"
115 | fi
116 |
117 | # Use default groovy-conf config
118 | if [ -z "$STARTER_CONF" ]; then
119 | STARTER_CONF="$GRAILS_HOME/conf/groovy-starter.conf"
120 | fi
121 | STARTER_CLASSPATH="wrapper/grails-wrapper-runtime-2.2.1.jar:wrapper:."
122 |
123 | # Allow access to Cocoa classes on OS X
124 | if $darwin; then
125 | STARTER_CLASSPATH="$STARTER_CLASSPATH:/System/Library/Java/Support"
126 | fi
127 |
128 | # Create the final classpath
129 | # Setting a classpath using the -cp or -classpath option means not to use
130 | # the global classpath. Groovy behaves then the same as the java
131 | # interpreter
132 | if [ -n "$CP" ] ; then
133 | CP="$CP"
134 | elif [ -n "$CLASSPATH" ] ; then
135 | CP="$CLASSPATH"
136 | fi
137 |
138 | # Determine the Java command to use to start the JVM
139 | if [ -z "$JAVACMD" ]; then
140 | if [ -n "$JAVA_HOME" ]; then
141 | if [ -x "$JAVA_HOME/jre/sh/java" ]; then
142 | # IBM's JDK on AIX uses strange locations for the executables
143 | JAVACMD="$JAVA_HOME/jre/sh/java"
144 | else
145 | JAVACMD="$JAVA_HOME/bin/java"
146 | fi
147 | else
148 | JAVACMD="java"
149 | fi
150 | fi
151 | if [ ! -x "$JAVACMD" ]; then
152 | die "JAVA_HOME is not defined correctly; can not execute: $JAVACMD"
153 | fi
154 |
155 | # Increase the maximum file descriptors if we can
156 | if [ "$cygwin" = "false" ]; then
157 | MAX_FD_LIMIT=`ulimit -H -n`
158 | if [ "$MAX_FD_LIMIT" != "unlimited" ]; then
159 | if [ $? -eq 0 ]; then
160 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ]; then
161 | # use the businessSystem max
162 | MAX_FD="$MAX_FD_LIMIT"
163 | fi
164 |
165 | ulimit -n $MAX_FD
166 | if [ $? -ne 0 ]; then
167 | warn "Could not set maximum file descriptor limit: $MAX_FD"
168 | fi
169 | else
170 | warn "Could not query businessSystem maximum file descriptor limit: $MAX_FD_LIMIT"
171 | fi
172 | fi
173 | fi
174 |
175 | # Fix the cygwin agent issue
176 | AGENT_GRAILS_HOME=$GRAILS_HOME
177 | if $cygwin ; then
178 | [ -n "$GRAILS_HOME" ] &&
179 | AGENT_GRAILS_HOME=`cygpath --windows "$GRAILS_HOME"`
180 | fi
181 |
182 | if [ -z "$GRAILS_AGENT_CACHE_DIR" ]; then
183 | GRAILS_AGENT_CACHE_DIR=~/.grails/2.2.1/
184 | fi
185 | SPRINGLOADED_PARAMS=profile=grails;cacheDir=$GRAILS_AGENT_CACHE_DIR
186 | if [ ! -d "$GRAILS_AGENT_CACHE_DIR" ]; then
187 | mkdir -p "$GRAILS_AGENT_CACHE_DIR"
188 | fi
189 |
190 | # Process JVM args
191 | AGENT_STRING="-javaagent:wrapper/springloaded-core-1.1.1.jar -noverify -Dspringloaded=\"$SPRINGLOADED_PARAMS\""
192 | CMD_LINE_ARGS=""
193 | DISABLE_RELOADING=false
194 |
195 | while true; do
196 | if [ "$1" = "-cp" ] || [ "$1" = "-classpath" ]; then
197 | CP=$2
198 | shift 2
199 | break
200 | fi
201 |
202 | if [ "$1" = "-reloading" ]; then
203 | AGENT=$AGENT_STRING
204 | shift
205 | break
206 | fi
207 |
208 | if [ "$1" = "-noreloading" ]; then
209 | DISABLE_RELOADING=true
210 | shift
211 | break
212 | fi
213 |
214 | if [ "$1" = "-debug" ]; then
215 | JAVA_OPTS="$JAVA_OPTS -Xdebug -Xnoagent -Dgrails.full.stacktrace=true -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005"
216 | shift
217 | break
218 | fi
219 |
220 | if [ "$1" != -* ]; then
221 | break
222 | fi
223 |
224 | CMD_LINE_ARGS="$CMD_LINE_ARGS $1"
225 | shift
226 | done
227 |
228 | # Enable agent-based reloading for the 'run-app' command.
229 | if ! $DISABLE_RELOADING; then
230 | for a in "$@"; do
231 | if [ "$a" = "run-app" ]; then
232 | AGENT=$AGENT_STRING
233 | fi
234 | done
235 |
236 | if [ $# = 0 ]; then
237 | AGENT=$AGENT_STRING
238 | fi
239 | fi
240 |
241 | ARGUMENTS="$CMD_LINE_ARGS $@"
242 |
243 | # Setup Profiler
244 | useprofiler=false
245 | if [ "x$PROFILER" != "x" ]; then
246 | if [ -r "$PROFILER" ]; then
247 | . $PROFILER
248 | useprofiler=true
249 | else
250 | die "Profiler file not found: $PROFILER"
251 | fi
252 | fi
253 |
254 | # For Darwin, use classes.jar for TOOLS_JAR
255 | TOOLS_JAR="$JAVA_HOME/lib/tools.jar"
256 | if $darwin; then
257 | JAVA_OPTS="-Xdock:name=Grails -Xdock:icon=$GRAILS_HOME/media/icons/grails.icns $JAVA_OPTS"
258 | # TOOLS_JAR="/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Classes/classes.jar"
259 | fi
260 |
261 | # For Cygwin, switch paths to Windows format before running java
262 | if $cygwin; then
263 | GRAILS_HOME=`cygpath --path --mixed "$GRAILS_HOME"`
264 | JAVA_HOME=`cygpath --path --mixed "$JAVA_HOME"`
265 | STARTER_CONF=`cygpath --path --mixed "$STARTER_CONF"`
266 | CP=`cygpath --path --mixed "$CP"`
267 | TOOLS_JAR=`cygpath --path --mixed "$TOOLS_JAR"`
268 | STARTER_CLASSPATH=`cygpath --path --mixed "$STARTER_CLASSPATH"`
269 | # We build the pattern for arguments to be converted via cygpath
270 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
271 | SEP=""
272 | for dir in $ROOTDIRSRAW; do
273 | ROOTDIRS="$ROOTDIRS$SEP$dir"
274 | SEP="|"
275 | done
276 | OURCYGPATTERN="(^($ROOTDIRS))"
277 | # Add a user-defined pattern to the cygpath arguments
278 | if [ "$GROOVY_CYGPATTERN" != "" ] ; then
279 | OURCYGPATTERN="$OURCYGPATTERN|($GROOVY_CYGPATTERN)"
280 | fi
281 | # Now convert the arguments
282 | ARGUMENTS=""
283 | for arg in "$@" ; do
284 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
285 | if [ $CHECK -ne 0 ] ; then
286 | convArg=`cygpath --path --ignore --mixed "$arg"`
287 | else
288 | convArg=$arg
289 | fi
290 | ARGUMENTS="$ARGUMENTS $convArg"
291 | done
292 | fi
293 |
294 | STARTER_MAIN_CLASS=org.grails.wrapper.GrailsWrapper
295 |
296 | lateInit
297 |
298 | startGrails() {
299 | CLASS=$1
300 | shift
301 | if [ -n "$GRAILS_OPTS" ]
302 | then
303 | GRAILS_OPTS="$GRAILS_OPTS"
304 | else
305 | GRAILS_OPTS="-server -Xmx768M -Xms64M -XX:PermSize=32m -XX:MaxPermSize=256m -Dfile.encoding=UTF-8"
306 | fi
307 | JAVA_OPTS="$GRAILS_OPTS $JAVA_OPTS $AGENT"
308 | # Start the Profiler or the JVM
309 | if $useprofiler; then
310 | runProfiler
311 | else
312 | if [ $# -eq 0 ] ; then # no argument given
313 | exec "$JAVACMD" $JAVA_OPTS \
314 | -classpath "$STARTER_CLASSPATH" \
315 | -Dgrails.home="$GRAILS_HOME" \
316 | -Dtools.jar="$TOOLS_JAR" \
317 | $STARTER_MAIN_CLASS \
318 | --main $CLASS \
319 | --conf "$STARTER_CONF" \
320 | --classpath "$CP"
321 | else
322 | exec "$JAVACMD" $JAVA_OPTS \
323 | -classpath "$STARTER_CLASSPATH" \
324 | -Dgrails.home="$GRAILS_HOME" \
325 | -Dtools.jar="$TOOLS_JAR" \
326 | $STARTER_MAIN_CLASS \
327 | --main $CLASS \
328 | --conf "$STARTER_CONF" \
329 | --classpath "$CP" \
330 | "${ARGUMENTS}"
331 | fi
332 | fi
333 | }
334 |
335 | startGrails $STARTER_MAIN_CLASS "$@"
--------------------------------------------------------------------------------
/grailsw.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem ##
4 | @rem Grails JVM Bootstrap for Windows ##
5 | @rem ##
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set CLASS=org.grails.wrapper.GrailsWrapper
12 |
13 | if exist "%USERPROFILE%/.groovy/preinit.bat" call "%USERPROFILE%/.groovy/preinit.bat"
14 |
15 | @rem Determine the command interpreter to execute the "CD" later
16 | set COMMAND_COM="cmd.exe"
17 | if exist "%SystemRoot%\system32\cmd.exe" set COMMAND_COM="%SystemRoot%\system32\cmd.exe"
18 | if exist "%SystemRoot%\command.com" set COMMAND_COM="%SystemRoot%\command.com"
19 |
20 | @rem Use explicit find.exe to prevent cygwin and others find.exe from being used
21 | set FIND_EXE="find.exe"
22 | if exist "%SystemRoot%\system32\find.exe" set FIND_EXE="%SystemRoot%\system32\find.exe"
23 | if exist "%SystemRoot%\command\find.exe" set FIND_EXE="%SystemRoot%\command\find.exe"
24 |
25 | :check_JAVA_HOME
26 | @rem Make sure we have a valid JAVA_HOME
27 | if not "%JAVA_HOME%" == "" goto have_JAVA_HOME
28 |
29 | echo.
30 | echo ERROR: Environment variable JAVA_HOME has not been set.
31 | echo.
32 | echo Please set the JAVA_HOME variable in your environment to match the
33 | echo location of your Java installation.
34 | echo.
35 | goto end
36 |
37 | :have_JAVA_HOME
38 | @rem Remove trailing slash from JAVA_HOME if found
39 | if "%JAVA_HOME:~-1%"=="\" SET JAVA_HOME=%JAVA_HOME:~0,-1%
40 |
41 | @rem Validate JAVA_HOME
42 | %COMMAND_COM% /C DIR "%JAVA_HOME%" 2>&1 | %FIND_EXE% /I /C "%JAVA_HOME%" >nul
43 | if not errorlevel 1 goto check_GRAILS_HOME
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 | echo.
51 | goto end
52 |
53 | :check_GRAILS_HOME
54 | @rem Define GRAILS_HOME if not set
55 | if "%GRAILS_HOME%" == "" set GRAILS_HOME=%DIRNAME%..
56 |
57 | @rem Remove trailing slash from GRAILS_HOME if found
58 | if "%GRAILS_HOME:~-1%"=="\" SET GRAILS_HOME=%GRAILS_HOME:~0,-1%
59 |
60 | :init
61 |
62 | for %%x in ("%HOMEPATH%") do set SHORTHOME=%%~fsx
63 | if "x%GRAILS_AGENT_CACHE_DIR%" == "x" set GRAILS_AGENT_CACHE_DIR=%SHORTHOME%/.grails/2.2.1/
64 | set SPRINGLOADED_PARAMS="profile=grails;cacheDir=%GRAILS_AGENT_CACHE_DIR%"
65 | if not exist "%GRAILS_AGENT_CACHE_DIR%" mkdir "%GRAILS_AGENT_CACHE_DIR%"
66 |
67 | set AGENT_STRING=-javaagent:wrapper/springloaded-core-1.1.1.jar -noverify -Dspringloaded=\"%SPRINGLOADED_PARAMS%\"
68 | set DISABLE_RELOADING=
69 | if "%GRAILS_OPTS%" == "" set GRAILS_OPTS=-server -Xmx768M -Xms64M -XX:PermSize=32m -XX:MaxPermSize=256m -Dfile.encoding=UTF-8
70 |
71 | @rem Get command-line arguments, handling Windows variants
72 | if "%@eval[2+2]" == "4" goto 4NT_args
73 |
74 | @rem Slurp the command line arguments.
75 | set CMD_LINE_ARGS=
76 | set CP=
77 | set INTERACTIVE=true
78 |
79 | :win9xME_args_slurp
80 | if "x%~1" == "x" goto execute
81 | set CURR_ARG=%~1
82 | if "%CURR_ARG:~0,2%" == "-D" (
83 | set GRAILS_OPTS=%GRAILS_OPTS% %~1=%~2
84 | shift
85 | shift
86 | goto win9xME_args_slurp
87 | )
88 | if "x%~1" == "x-cp" (
89 | set CP=%~2
90 | shift
91 | shift
92 | goto win9xME_args_slurp
93 | )
94 | if "x%~1" == "x-debug" (
95 | set JAVA_OPTS=%JAVA_OPTS% -Xdebug -Xnoagent -Dgrails.full.stacktrace=true -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005
96 | shift
97 | goto win9xME_args_slurp
98 | )
99 | if "x%~1" == "x-classpath" (
100 | set CP=%~2
101 | shift
102 | shift
103 | goto win9xME_args_slurp
104 | )
105 | if "x%~1" == "x-reloading" (
106 | set AGENT=%AGENT_STRING%
107 | shift
108 | goto win9xME_args_slurp
109 | )
110 | if "x%~1" == "xrun-app" (
111 | set AGENT=%AGENT_STRING%
112 | set INTERACTIVE=
113 | set CMD_LINE_ARGS=%CMD_LINE_ARGS% %1
114 | shift
115 | goto win9xME_args_slurp
116 | )
117 | if "x%~1" == "x-noreloading" (
118 | set DISABLE_RELOADING=true
119 | shift
120 | goto win9xME_args_slurp
121 | )
122 | set INTERACTIVE=
123 | set CMD_LINE_ARGS=%CMD_LINE_ARGS% %1
124 | shift
125 | goto win9xME_args_slurp
126 |
127 | :4NT_args
128 | @rem Get arguments from the 4NT Shell from JP Software
129 | set CMD_LINE_ARGS=%$
130 |
131 | :execute
132 | @rem Setup the command line
133 | set STARTER_CLASSPATH=wrapper/grails-wrapper-runtime-2.2.1.jar;wrapper;.
134 |
135 | if exist "%USERPROFILE%/.groovy/init.bat" call "%USERPROFILE%/.groovy/init.bat"
136 |
137 | @rem Setting a classpath using the -cp or -classpath option means not to use
138 | @rem the global classpath. Groovy behaves then the same as the java interpreter
139 |
140 | if "x" == "x%CLASSPATH%" goto after_classpath
141 | set CP=%CP%;%CLASSPATH%
142 | :after_classpath
143 |
144 | if "x%DISABLE_RELOADING%" == "xtrue" (
145 | set AGENT=
146 | ) else (
147 | if "x%INTERACTIVE%" == "xtrue" (
148 | set AGENT=%AGENT_STRING%
149 | )
150 | )
151 |
152 | set STARTER_MAIN_CLASS=org.grails.wrapper.GrailsWrapper
153 | set STARTER_CONF=%GRAILS_HOME%\conf\groovy-starter.conf
154 |
155 | set JAVA_EXE=%JAVA_HOME%\bin\java.exe
156 | set TOOLS_JAR=%JAVA_HOME%\lib\tools.jar
157 |
158 | set JAVA_OPTS=%GRAILS_OPTS% %JAVA_OPTS% %AGENT%
159 |
160 | set JAVA_OPTS=%JAVA_OPTS% -Dprogram.name="%PROGNAME%"
161 | set JAVA_OPTS=%JAVA_OPTS% -Dgrails.home="%GRAILS_HOME%"
162 | set JAVA_OPTS=%JAVA_OPTS% -Dgrails.version=2.2.1
163 | set JAVA_OPTS=%JAVA_OPTS% -Dbase.dir=.
164 | set JAVA_OPTS=%JAVA_OPTS% -Dtools.jar="%TOOLS_JAR%"
165 | set JAVA_OPTS=%JAVA_OPTS% -Dgroovy.starter.conf="%STARTER_CONF%"
166 |
167 | if exist "%USERPROFILE%/.groovy/postinit.bat" call "%USERPROFILE%/.groovy/postinit.bat"
168 |
169 | @rem Execute Grails
170 | CALL "%JAVA_EXE%" %JAVA_OPTS% -classpath "%STARTER_CLASSPATH%" %STARTER_MAIN_CLASS% --main %CLASS% --conf "%STARTER_CONF%" --classpath "%CP%" "%CMD_LINE_ARGS%"
171 | :end
172 | @rem End local scope for the variables with windows NT shell
173 | if "%OS%"=="Windows_NT" endlocal
174 |
175 | @rem Optional pause the batch file
176 | if "%GROOVY_BATCH_PAUSE%" == "on" pause
--------------------------------------------------------------------------------
/src/java/com/brandseye/cors/CorsCompatibleBasicAuthenticationEntryPoint.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 BrandsEye (http://www.brandseye.com/)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.brandseye.cors;
18 |
19 | import org.springframework.security.core.AuthenticationException;
20 | import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint;
21 |
22 | import javax.servlet.ServletException;
23 | import javax.servlet.http.HttpServletRequest;
24 | import javax.servlet.http.HttpServletResponse;
25 | import java.io.IOException;
26 |
27 | /**
28 | * Prevents authentication of HTTP OPTIONS preflight requests which would otherwise break CORS in most browsers.
29 | */
30 | public class CorsCompatibleBasicAuthenticationEntryPoint extends BasicAuthenticationEntryPoint {
31 |
32 | public CorsCompatibleBasicAuthenticationEntryPoint() {
33 | super();
34 | }
35 |
36 | @Override
37 | public void commence(final HttpServletRequest request, final HttpServletResponse response, final AuthenticationException authException) throws IOException, ServletException {
38 | if(!request.getMethod().equals("OPTIONS")) {
39 | super.commence(request, response, authException);
40 | }
41 | }
42 |
43 | }
--------------------------------------------------------------------------------
/src/java/com/brandseye/cors/CorsFilter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 BrandsEye (http://www.brandseye.com/)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.brandseye.cors;
18 |
19 | import javax.servlet.*;
20 | import javax.servlet.http.HttpServletRequest;
21 | import javax.servlet.http.HttpServletResponse;
22 | import java.io.IOException;
23 | import java.util.Enumeration;
24 | import java.util.LinkedHashMap;
25 | import java.util.Map;
26 | import java.util.regex.Pattern;
27 | import org.slf4j.LoggerFactory;
28 | import org.slf4j.Logger;
29 |
30 | /**
31 | * Adds CORS headers to requests to enable cross-site AJAX calls.
32 | */
33 | public class CorsFilter implements Filter {
34 |
35 | private static final String ACCESS_CONTROL_ALLOW_HEADERS = "Access-Control-Allow-Headers";
36 | private static Logger logger = LoggerFactory.getLogger(CorsFilter.class);
37 |
38 | private final Map optionsHeaders = new LinkedHashMap();
39 |
40 | private Pattern allowOriginRegex;
41 | private String allowOrigin;
42 | private String exposeHeaders;
43 | private boolean enableLogging;
44 |
45 | public void init(FilterConfig cfg) throws ServletException {
46 | optionsHeaders.put(ACCESS_CONTROL_ALLOW_HEADERS, "origin, authorization, accept, content-type, x-requested-with");
47 | optionsHeaders.put("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS");
48 | optionsHeaders.put("Access-Control-Max-Age", "3600");
49 | for (Enumeration i = cfg.getInitParameterNames(); i.hasMoreElements(); ) {
50 | String name = i.nextElement();
51 | if (name.startsWith("header:")) {
52 | optionsHeaders.put(name.substring(7), cfg.getInitParameter(name));
53 | }
54 | }
55 |
56 | String regex = cfg.getInitParameter("allow.origin.regex");
57 | if (regex != null) {
58 | allowOriginRegex = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
59 | }
60 | allowOrigin = optionsHeaders.remove("Access-Control-Allow-Origin");
61 |
62 | exposeHeaders = cfg.getInitParameter("expose.headers");
63 |
64 | String logging = cfg.getInitParameter("enable.logging");
65 | if(logging != null && logging.equalsIgnoreCase("true")){
66 | enableLogging = true;
67 | } else {
68 | enableLogging = false;
69 | }
70 | }
71 |
72 | public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
73 | throws IOException, ServletException {
74 | if (request instanceof HttpServletRequest && response instanceof HttpServletResponse) {
75 | HttpServletRequest req = (HttpServletRequest)request;
76 | HttpServletResponse resp = (HttpServletResponse)response;
77 | if ("OPTIONS".equals(req.getMethod())) {
78 | if (checkOrigin(req, resp)) {
79 | String accessControlRequestHeaders = req.getHeader("Access-Control-Request-Headers");
80 | for (Map.Entry e : optionsHeaders.entrySet()) {
81 | if(ACCESS_CONTROL_ALLOW_HEADERS.equals(e.getKey()) && "*".equals(e.getValue()) && accessControlRequestHeaders != null){
82 | resp.addHeader(ACCESS_CONTROL_ALLOW_HEADERS, accessControlRequestHeaders);
83 | } else {
84 | resp.addHeader(e.getKey(), e.getValue());
85 | }
86 | }
87 |
88 | // We need to return here since we don't want the chain to further process
89 | // a preflight request since this can lead to unexpected processing of the preflighted
90 | // request or a 405 - method not allowed in Grails 2.3
91 | return;
92 |
93 | }
94 | } else if (checkOrigin(req, resp)) {
95 | if (exposeHeaders != null) {
96 | resp.addHeader("Access-Control-Expose-Headers", exposeHeaders);
97 | }
98 | }
99 | }
100 | filterChain.doFilter(request, response);
101 | }
102 |
103 | private boolean checkOrigin(HttpServletRequest req, HttpServletResponse resp) {
104 | String origin = req.getHeader("Origin");
105 | if (origin == null) {
106 | //no origin; per W3C spec, terminate further processing for both preflight and actual requests
107 | if(enableLogging){
108 | logger.error("No origin header for request");
109 | logger.error(requestToString(req));
110 | }
111 | return false;
112 | }
113 |
114 | boolean matches;
115 | if (allowOriginRegex != null) {
116 | matches = allowOriginRegex.matcher(origin).matches();
117 | } else {
118 | matches = allowOrigin == null || allowOrigin.equals("*") || allowOrigin.equals(origin);
119 | }
120 |
121 | if (matches) {
122 | // if no 'Access-Control-Allow-Origin' specified in cors.headers then echo back Origin
123 | resp.addHeader("Access-Control-Allow-Origin", allowOrigin == null ? origin : allowOrigin);
124 | resp.addHeader("Access-Control-Allow-Credentials", "true");
125 | return true;
126 | } else {
127 | if(enableLogging){
128 | logger.error("Origin header is present but does not match to allowed origin");
129 | logger.error(requestToString(req));
130 | }
131 | return false;
132 | }
133 | }
134 |
135 | private String requestToString(HttpServletRequest req){
136 | String reqStr = "==================Request details start=================\r\n";
137 | reqStr += requestUrl(req);
138 | reqStr += requestHeaders(req);
139 | reqStr += "==================Request details end=================\n";
140 | return reqStr;
141 | }
142 |
143 | private String requestUrl(HttpServletRequest req) {
144 | String reqUrl = req.getRequestURL().toString();
145 | String queryString = req.getQueryString();
146 | if (queryString != null) {
147 | reqUrl += "?"+queryString;
148 | }
149 | return "Url: " + reqUrl + "\r\n";
150 | }
151 |
152 | private String requestHeaders(HttpServletRequest req) {
153 | String reqHeaders = "Headers:\n";
154 | Enumeration headerNames = req.getHeaderNames();
155 | while (headerNames.hasMoreElements()) {
156 | String headerName = headerNames.nextElement();
157 | reqHeaders += "\t" + headerName + ": ";
158 | String headerValue = req.getHeader(headerName);
159 | reqHeaders += headerValue + "\r\n";
160 | }
161 | return reqHeaders;
162 | }
163 |
164 | public void destroy() {
165 | }
166 | }
167 |
--------------------------------------------------------------------------------
/test/unit/com/brandseye/cors/CorsCompatibleBasicAuthenticationEntryPointTests.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 BrandsEye (http://www.brandseye.com/)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.brandseye.cors
18 |
19 | import groovy.mock.interceptor.MockFor
20 | import org.springframework.security.core.AuthenticationException
21 |
22 | import javax.servlet.http.HttpServletRequest
23 | import javax.servlet.http.HttpServletResponse
24 |
25 | class CorsCompatibleBasicAuthenticationEntryPointTests extends GroovyTestCase {
26 |
27 |
28 | public void testOptionsPreflight() {
29 | // given
30 | def baEntryPoint = new CorsCompatibleBasicAuthenticationEntryPoint()
31 | def mockRequestContext = new MockFor(HttpServletRequest)
32 | mockRequestContext.demand.getMethod { "OPTIONS" }
33 | def mockRequest = mockRequestContext.proxyDelegateInstance()
34 | def mockResponseContext = new MockFor(HttpServletResponse)
35 | mockResponseContext.demand.sendError(0)
36 | def mockResponse = mockResponseContext.proxyDelegateInstance()
37 |
38 | // when
39 | baEntryPoint.commence(mockRequest, mockResponse, null)
40 |
41 | // then
42 | mockRequestContext.verify mockRequest
43 | mockResponseContext.verify mockResponse
44 | }
45 |
46 | public void testAllOthersNeedAuth() {
47 | methodTester("GET")
48 | methodTester("PUT")
49 | methodTester("DELETE")
50 | methodTester("POST")
51 | }
52 |
53 | private void methodTester(String method) {
54 | // given
55 | def baEntryPoint = new CorsCompatibleBasicAuthenticationEntryPoint()
56 | baEntryPoint.setRealmName("Test Realm")
57 | def mockRequestContext = new MockFor(HttpServletRequest)
58 | mockRequestContext.demand.getMethod { method }
59 | def mockRequest = mockRequestContext.proxyDelegateInstance()
60 | def mockResponseContext = new MockFor(HttpServletResponse)
61 | mockResponseContext.demand.addHeader(1) { String name, String value -> }
62 | mockResponseContext.demand.sendError(1) { int error, String msg ->
63 | assert error == HttpServletResponse.SC_UNAUTHORIZED
64 | }
65 | def mockResponse = mockResponseContext.proxyDelegateInstance()
66 | def authExceptionContext = new MockFor(MockedAuthException)
67 | def mockAuthException = authExceptionContext.proxyDelegateInstance()
68 |
69 | // when
70 | baEntryPoint.commence(mockRequest, mockResponse, mockAuthException)
71 |
72 | // then
73 | mockRequestContext.verify mockRequest
74 | mockResponseContext.verify mockResponse
75 | }
76 |
77 | }
78 |
79 | class MockedAuthException extends AuthenticationException {
80 | public MockedAuthException() {
81 | super("MockedAuthException")
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/test/unit/com/brandseye/cors/CorsFilterTest.groovy:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 BrandsEye (http://www.brandseye.com/)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.brandseye.cors
18 |
19 | import org.springframework.mock.web.MockFilterChain
20 | import org.springframework.mock.web.MockFilterConfig
21 | import org.springframework.mock.web.MockHttpServletRequest
22 | import org.codehaus.groovy.grails.plugins.testing.GrailsMockHttpServletResponse
23 |
24 | import javax.servlet.http.HttpServletRequest
25 | import javax.servlet.http.HttpServletResponse
26 |
27 | /**
28 | * @author Peter Schneider-Manzell
29 | */
30 | @SuppressWarnings("GroovyUnusedDeclaration")
31 | class CorsFilterTest extends GroovyTestCase {
32 |
33 | CorsFilter underTest;
34 | def minimalHeadersForAllAllowedRequests = ["Access-Control-Allow-Origin","Access-Control-Allow-Credentials"]
35 | def defaultHeadersForAllowedOptionsRequest = [
36 | "Access-Control-Allow-Origin",
37 | "Access-Control-Allow-Credentials",
38 | "Access-Control-Allow-Headers",
39 | "Access-Control-Allow-Methods",
40 | "Access-Control-Max-Age"
41 | ]
42 |
43 | public void testDefaultHeadersForOptionsRequestsWithoutOriginRegexp(){
44 | def expectedHeadersForHttpMethods = [
45 | 'OPTIONS':defaultHeadersForAllowedOptionsRequest,
46 | 'GET':minimalHeadersForAllAllowedRequests,
47 | 'PUT':minimalHeadersForAllAllowedRequests,
48 | 'DELETE':minimalHeadersForAllAllowedRequests,
49 | 'POST':minimalHeadersForAllAllowedRequests
50 | ]
51 | String origin = "http://www.grails.org"
52 | executeTest([:],expectedHeadersForHttpMethods,origin)
53 | }
54 |
55 | public void testDefaultHeadersForOptionsRequestsWithOriginRegexpAndAllowedOrigin(){
56 | def expectedHeadersForHttpMethods = [
57 | 'OPTIONS':defaultHeadersForAllowedOptionsRequest,
58 | 'GET':minimalHeadersForAllAllowedRequests,
59 | 'PUT':minimalHeadersForAllAllowedRequests,
60 | 'DELETE':minimalHeadersForAllAllowedRequests,
61 | 'POST':minimalHeadersForAllAllowedRequests
62 | ]
63 | def filterInitParams = [:]
64 | filterInitParams['allow.origin.regex'] = '.*grails.*'
65 | String origin = "http://www.grails.org"
66 | executeTest(filterInitParams,expectedHeadersForHttpMethods,origin)
67 | }
68 |
69 | public void testDefaultHeadersForOptionsRequestsWithOriginRegexpAndNotAllowedOrigin(){
70 | def expectedHeadersForHttpMethods = [
71 | 'OPTIONS':[],
72 | 'GET':[],
73 | 'PUT':[],
74 | 'DELETE':[],
75 | 'POST':[]
76 | ]
77 | def filterInitParams = [:]
78 | filterInitParams['allow.origin.regex'] = '.*grails.*'
79 | String origin = "http://www.google.com"
80 | executeTest(filterInitParams,expectedHeadersForHttpMethods,origin)
81 | }
82 |
83 | public void testDefaultHeadersForOptionsRequestsWithoutOriginRegexpAndNotAllowedOrigin(){
84 | def expectedHeadersForHttpMethods = [
85 | 'OPTIONS':[],
86 | 'GET':[],
87 | 'PUT':[],
88 | 'DELETE':[],
89 | 'POST':[]
90 | ]
91 | def filterInitParams = [:]
92 | filterInitParams['header:Access-Control-Allow-Origin'] = 'http://www.grails.org'
93 | String origin = "http://www.google.com"
94 | executeTest(filterInitParams,expectedHeadersForHttpMethods,origin)
95 | }
96 |
97 | public void testExposedHeaders() {
98 | def headers = ["Access-Control-Allow-Origin","Access-Control-Allow-Credentials","Access-Control-Expose-Headers"]
99 | def expectedHeadersForHttpMethods = [
100 | 'OPTIONS':defaultHeadersForAllowedOptionsRequest,
101 | 'GET':headers,
102 | 'PUT':headers,
103 | 'DELETE':headers,
104 | 'POST':headers
105 | ]
106 | def filterInitParams = [:]
107 | filterInitParams['allow.origin.regex'] = '.*grails.*'
108 | filterInitParams['expose.headers'] = "X-custom-header"
109 | String origin = "http://www.grails.org"
110 | executeTest(filterInitParams,expectedHeadersForHttpMethods,origin)
111 | }
112 |
113 | public void testAllowAllHeaders() {
114 | def headers = ["Access-Control-Allow-Origin","Access-Control-Allow-Credentials","Access-Control-Request-Headers"]
115 | def expectedHeadersForHttpMethods = [
116 | 'OPTIONS':defaultHeadersForAllowedOptionsRequest,
117 | 'GET':minimalHeadersForAllAllowedRequests,
118 | 'PUT':minimalHeadersForAllAllowedRequests,
119 | 'DELETE':minimalHeadersForAllAllowedRequests,
120 | 'POST':minimalHeadersForAllAllowedRequests
121 | ]
122 | def filterInitParams = [:]
123 | filterInitParams['header:Access-Control-Allow-Headers'] = '*'
124 | String origin = "http://www.grails.org"
125 | executeTest(filterInitParams,expectedHeadersForHttpMethods,origin, ['Access-Control-Request-Headers':'X-custom-header'])
126 | }
127 |
128 |
129 | public void testValueOfExposedHeaders() {
130 | def underTest = new CorsFilter()
131 | def expected = 'X-custom-header1,X-custom-header2'
132 | MockFilterConfig filterConfig = new MockFilterConfig()
133 | filterConfig.addInitParameter('allow.origin.regex','.*grails.*')
134 | filterConfig.addInitParameter('expose.headers',expected)
135 | underTest.init(filterConfig)
136 |
137 | HttpServletRequest req = new MockHttpServletRequest()
138 | req.setMethod("PUT")
139 | req.addHeader("Origin",'http://www.grails.org')
140 | HttpServletResponse res = new GrailsMockHttpServletResponse()
141 | underTest.doFilter(req,res,new MockFilterChain())
142 | assert res.getHeader('Access-Control-Expose-Headers').equals('X-custom-header1,X-custom-header2') : "Access-Control-Expose-Headers, expected ${expected}, got ${res.getHeader('Access-Control-Expose-Headers')}"
143 |
144 | }
145 |
146 | void testAllowOriginStar() {
147 | def underTest = new CorsFilter()
148 |
149 | MockFilterConfig filterConfig = new MockFilterConfig()
150 | filterConfig.addInitParameter('header:Access-Control-Allow-Origin','*')
151 | underTest.init(filterConfig)
152 |
153 | HttpServletRequest req = new MockHttpServletRequest()
154 | req.setMethod("PUT")
155 | req.addHeader("Origin",'http://www.grails.org')
156 | HttpServletResponse res = new GrailsMockHttpServletResponse()
157 | underTest.doFilter(req, res, new MockFilterChain())
158 | assert '*' == res.getHeader('Access-Control-Allow-Origin')
159 | }
160 |
161 | void testAllowOriginEchosOrigin() {
162 | def underTest = new CorsFilter()
163 |
164 | underTest.init(new MockFilterConfig())
165 |
166 | HttpServletRequest req = new MockHttpServletRequest()
167 | req.setMethod("PUT")
168 | req.addHeader("Origin",'http://www.grails.org')
169 | HttpServletResponse res = new GrailsMockHttpServletResponse()
170 | underTest.doFilter(req, res, new MockFilterChain())
171 | assert 'http://www.grails.org' == res.getHeader('Access-Control-Allow-Origin')
172 | }
173 |
174 | void testAllowOriginStarWorksWithRegex() {
175 | def underTest = new CorsFilter()
176 |
177 | MockFilterConfig filterConfig = new MockFilterConfig()
178 | filterConfig.addInitParameter('header:Access-Control-Allow-Origin', '*')
179 | filterConfig.addInitParameter('allow.origin.regex', '.*grails.*')
180 | underTest.init(filterConfig)
181 |
182 | HttpServletRequest req = new MockHttpServletRequest()
183 | req.setMethod("PUT")
184 | req.addHeader("Origin",'http://www.grails.org')
185 | HttpServletResponse res = new GrailsMockHttpServletResponse()
186 | underTest.doFilter(req, res, new MockFilterChain())
187 | assert '*' == res.getHeader('Access-Control-Allow-Origin')
188 |
189 | req = new MockHttpServletRequest()
190 | req.setMethod("PUT")
191 | req.addHeader("Origin",'http://www.wibble.com')
192 | res = new GrailsMockHttpServletResponse()
193 | underTest.doFilter(req, res, new MockFilterChain())
194 | assert null == res.getHeader('Access-Control-Allow-Origin')
195 | }
196 |
197 | private void executeTest(Map filterInitParams,Map> expectedHeadersForHttpMethods,String origin, Map requestHeaders = [:]){
198 | underTest = new CorsFilter()
199 | MockFilterConfig filterConfig = new MockFilterConfig()
200 | filterInitParams.entrySet().each {filterEntry->
201 | filterConfig.addInitParameter(filterEntry.key,filterEntry.value)
202 | }
203 | underTest.init(filterConfig)
204 |
205 |
206 | expectedHeadersForHttpMethods.entrySet().each {expectedHeadersForHttpMethod->
207 | String httpMethod = expectedHeadersForHttpMethod.key
208 | Set expectedHeaders = expectedHeadersForHttpMethod.value
209 | HttpServletRequest req = new MockHttpServletRequest()
210 | req.setMethod(httpMethod)
211 | req.addHeader("Origin",origin)
212 | requestHeaders.entrySet().each {
213 | req.addHeader(it.key, it.value)
214 | }
215 | HttpServletResponse res = new GrailsMockHttpServletResponse()
216 | underTest.doFilter(req,res,new MockFilterChain())
217 | assert res.getHeaderNames().containsAll(expectedHeaders) : "Not all expected headers for request $httpMethod and origin $origin could be found! (expected: $expectedHeaders, got: ${res.getHeaderNames()})"
218 | assert res.getHeaderNames().size() == expectedHeaders.size() : "Header count for request $httpMethod and origin $origin not the expected header count! (expected: $expectedHeaders, got: ${res.getHeaderNames()})"
219 |
220 | //check that the Access-Control-Allow-Credentials is always single-valued
221 | if (res.getHeaderNames().contains('Access-Control-Allow-Credentials')) {
222 | assert res.headers('Access-Control-Allow-Credentials').size() == 1
223 | }
224 | //check that the Access-Control-Allow-Origin is always single-valued,
225 | //since we always set Access-Control-Allow-Credentials
226 | if (res.getHeaderNames().contains('Access-Control-Allow-Origin')) {
227 | assert res.headers('Access-Control-Allow-Origin').size() == 1
228 | }
229 |
230 | }
231 | }
232 | }
233 |
--------------------------------------------------------------------------------
/wrapper/grails-wrapper-runtime-2.2.1.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davidtinker/grails-cors/e3367cdf51ac6b3480e645ac12b431911fe9b48e/wrapper/grails-wrapper-runtime-2.2.1.jar
--------------------------------------------------------------------------------
/wrapper/grails-wrapper.properties:
--------------------------------------------------------------------------------
1 | wrapper.dist.url=http://dist.springframework.org.s3.amazonaws.com/release/GRAILS/
--------------------------------------------------------------------------------
/wrapper/springloaded-core-1.1.1.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davidtinker/grails-cors/e3367cdf51ac6b3480e645ac12b431911fe9b48e/wrapper/springloaded-core-1.1.1.jar
--------------------------------------------------------------------------------