Skip to content
This repository has been archived by the owner on Dec 7, 2020. It is now read-only.

CORS Headers #174

Merged
merged 1 commit into from
Jan 9, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@

#### **2.0.2**

FEATURES:
* adding the --enable-cors-global to switch on CORs header injects into every response

#### **2.0.1**

BUGS:
Expand Down
11 changes: 5 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -441,12 +441,11 @@ You are permitted to add CORS following headers into the /oauth uri namespace
Either from the config file:

```YAML
cors:
origins:
- '*'
methods:
- GET
- POST
cors-origins:
- '*'
cors-methods:
- GET
- POST
```

or via the command line arguments
Expand Down
29 changes: 14 additions & 15 deletions config_sample.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@


# is the url for retrieve the openid configuration - normally the <server>/auth/realm/<realm_name>
discovery-url: https://keycloak.example.com/auth/realms/commons
# the client id for the 'client' application
Expand Down Expand Up @@ -74,17 +73,17 @@ resources:
- openvpn:vpn-user
- openvpn:prod-vpn

# set the cross origin resource sharing headers
cors:
# an array of origins (Access-Control-Allow-Origin)
origins: []
# an array of headers to apply (Access-Control-Allow-Headers)
headers: []
# an array of expose headers (Access-Control-Expose-Headers)
exposed-headers: []
# an array of methods (Access-Control-Allow-Methods)
methods: []
# the credentials flag (Access-Control-Allow-Credentials)
credentials: true|false
# the max age (Access-Control-Max-Age)
max-age: 1h
# indicates you want cors headers injects for ALL responses
enable-cors-global: false
# an array of origins (Access-Control-Allow-Origin)
cors-origins: []
# an array of headers to apply (Access-Control-Allow-Headers)
cors-headers: []
# an array of expose headers (Access-Control-Expose-Headers)
cors-exposed-headers: []
# an array of methods (Access-Control-Allow-Methods)
cors-methods: []
# the credentials flag (Access-Control-Allow-Credentials)
cors-credentials: true|false
# the max age (Access-Control-Max-Age)
cors-max-age: 1h
2 changes: 2 additions & 0 deletions doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@ type Config struct {
// Headers permits adding customs headers across the board
Headers map[string]string `json:"headers" yaml:"headers" usage:"custom headers to the upstream request, key=value"`

// EnableCorsGlobal enables the CORs header in all response headers
EnableCorsGlobal bool `json:"enable-cors-global" yaml:"enable-cors-global" usage:"inject the CORs headers into all responses" env:"ENABLE_CORS_GLOBAL"`
// EnableForwarding enables the forwarding proxy
EnableForwarding bool `json:"enable-forwarding" yaml:"enable-forwarding" usage:"enables the forwarding proxy mode, signing outbound request"`
// EnableSecurityFilter enabled the security handler
Expand Down
14 changes: 6 additions & 8 deletions middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -366,26 +366,24 @@ func (r *oauthProxy) admissionMiddleware() gin.HandlerFunc {
}
}

//
// corsMiddleware injects the CORS headers, if set, for request made to /oauth
//
func (r *oauthProxy) corsMiddleware(c Cors) gin.HandlerFunc {
return func(cx *gin.Context) {
if len(c.Origins) > 0 {
cx.Writer.Header().Set("Access-Control-Allow-Origin", strings.Join(c.Origins, ","))
}
if c.Credentials {
cx.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
}
if len(c.ExposedHeaders) > 0 {
cx.Writer.Header().Set("Access-Control-Expose-Headers", strings.Join(c.ExposedHeaders, ","))
}
if len(c.Methods) > 0 {
cx.Writer.Header().Set("Access-Control-Allow-Methods", strings.Join(c.Methods, ","))
}
if len(c.Headers) > 0 {
cx.Writer.Header().Set("Access-Control-Allow-Headers", strings.Join(c.Headers, ","))
}
if len(c.ExposedHeaders) > 0 {
cx.Writer.Header().Set("Access-Control-Expose-Headers", strings.Join(c.ExposedHeaders, ","))
}
if c.Credentials {
cx.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
}
if c.MaxAge > 0 {
cx.Writer.Header().Set("Access-Control-Max-Age", fmt.Sprintf("%d", int(c.MaxAge.Seconds())))
}
Expand Down
22 changes: 19 additions & 3 deletions middleware_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,10 +215,14 @@ func TestCrossSiteHandler(t *testing.T) {
p, _, _ := newTestProxyService(nil)

cases := []struct {
Method string
URI string
Cors Cors
Headers map[string]string
}{
{
Method: http.MethodGet,
URI: "/oauth/test",
Cors: Cors{
Origins: []string{"*"},
},
Expand All @@ -227,21 +231,33 @@ func TestCrossSiteHandler(t *testing.T) {
},
},
{
Method: http.MethodGet,
URI: "/oauth/test",
Cors: Cors{
Origins: []string{"*", "https://examples.com"},
Methods: []string{"GET"},
},
Headers: map[string]string{
"Access-Control-Allow-Origin": "*,https://examples.com",
},
},
{
Method: http.MethodGet,
URI: "/foo",
Cors: Cors{
Origins: []string{"*", "https://examples.com"},
Methods: []string{"GET", "POST"},
},
Headers: map[string]string{
"Access-Control-Allow-Origin": "*,https://examples.com",
"Access-Control-Allow-Methods": "GET",
"Access-Control-Allow-Methods": "GET,POST",
},
},
}

for i, c := range cases {
handler := p.corsMiddleware(c.Cors)
// call the handler and check the responses
context := newFakeGinContext("GET", "/oauth/test")
context := newFakeGinContext(c.Method, c.URI)
handler(context)
// step: check the headers
for k, v := range c.Headers {
Expand Down
16 changes: 12 additions & 4 deletions server.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,16 +166,24 @@ func (r *oauthProxy) createReverseProxy() error {
if r.config.EnableSecurityFilter {
engine.Use(r.securityMiddleware())
}

// step: add the routing and cors middleware
oauth := engine.Group(oauthURL).Use(r.corsMiddleware(Cors{
cors := Cors{
Origins: r.config.CorsOrigins,
Methods: r.config.CorsMethods,
Headers: r.config.CorsHeaders,
ExposedHeaders: r.config.CorsExposedHeaders,
Credentials: r.config.CorsCredentials,
MaxAge: r.config.CorsMaxAge,
}))
}
// step: enabling globaling?
if r.config.EnableCorsGlobal {
log.Info("enabling CORs header injection globally")
engine.Use(r.corsMiddleware(cors))
}
// step: add the routing and cors middleware
oauth := engine.Group(oauthURL)
if !r.config.EnableCorsGlobal {
oauth.Use(r.corsMiddleware(cors))
}
oauth.GET(authorizationURL, r.oauthAuthorizationHandler)
oauth.GET(callbackURL, r.oauthCallbackHandler)
oauth.GET(healthURL, r.healthHandler)
Expand Down