Skip to content
Snippets Groups Projects
Commit 36215832 authored by Rohith's avatar Rohith
Browse files

Merge pull request #24 from gambol99/cors_config

- cleaning up the cors options
parents 15d55b51 c48d52cd
No related branches found
No related tags found
No related merge requests found
...@@ -40,7 +40,7 @@ func newDefaultConfig() *Config { ...@@ -40,7 +40,7 @@ func newDefaultConfig() *Config {
TagData: make(map[string]string, 0), TagData: make(map[string]string, 0),
ClaimsMatch: make(map[string]string, 0), ClaimsMatch: make(map[string]string, 0),
Header: make(map[string]string, 0), Header: make(map[string]string, 0),
CORSConfig: &CORS{ CORS: &CORS{
Origins: []string{}, Origins: []string{},
Methods: []string{}, Methods: []string{},
Headers: []string{}, Headers: []string{},
...@@ -203,6 +203,24 @@ func readOptions(cx *cli.Context, config *Config) (err error) { ...@@ -203,6 +203,24 @@ func readOptions(cx *cli.Context, config *Config) (err error) {
if cx.IsSet("hostname") { if cx.IsSet("hostname") {
config.Hostnames = cx.StringSlice("hostname") config.Hostnames = cx.StringSlice("hostname")
} }
if cx.IsSet("cors-origins") {
config.CORS.Origins = cx.StringSlice("cors-origins")
}
if cx.IsSet("cors-methods") {
config.CORS.Methods = cx.StringSlice("cors-methods")
}
if cx.IsSet("cors-headers") {
config.CORS.Headers = cx.StringSlice("cors-headers")
}
if cx.IsSet("cors-exposed-headers") {
config.CORS.ExposedHeaders = cx.StringSlice("cors-exposed-headers")
}
if cx.IsSet("cors-max-age") {
config.CORS.MaxAge = cx.Duration("cors-max-age")
}
if cx.IsSet("cors-credentials") {
config.CORS.Credentials = cx.BoolT("cors-credentials")
}
if cx.IsSet("tag") { if cx.IsSet("tag") {
config.TagData, err = decodeKeyPairs(cx.StringSlice("tag")) config.TagData, err = decodeKeyPairs(cx.StringSlice("tag"))
if err != nil { if err != nil {
...@@ -221,18 +239,7 @@ func readOptions(cx *cli.Context, config *Config) (err error) { ...@@ -221,18 +239,7 @@ func readOptions(cx *cli.Context, config *Config) (err error) {
return err return err
} }
} }
if cx.IsSet("cors-origins") {
config.CORSConfig.Origins = cx.StringSlice("cors-origins")
}
if cx.IsSet("cors-methods") {
config.CORSConfig.Methods = cx.StringSlice("cors-methods")
}
if cx.IsSet("cors-headers") {
config.CORSConfig.Headers = cx.StringSlice("cors-headers")
}
if cx.IsSet("cors-max-age") {
config.CORSConfig.MaxAge = cx.Duration("cors-max-age")
}
if cx.IsSet("resource") { if cx.IsSet("resource") {
for _, x := range cx.StringSlice("resource") { for _, x := range cx.StringSlice("resource") {
resource, err := decodeResource(x) resource, err := decodeResource(x)
...@@ -355,18 +362,26 @@ func getOptions() []cli.Flag { ...@@ -355,18 +362,26 @@ func getOptions() []cli.Flag {
Name: "cors-origins", Name: "cors-origins",
Usage: "a set of origins to add to the CORS access control (Access-Control-Allow-Origin)", Usage: "a set of origins to add to the CORS access control (Access-Control-Allow-Origin)",
}, },
cli.StringSliceFlag{
Name: "cors-methods",
Usage: "the method permitted in the access control (Access-Control-Allow-Methods)",
},
cli.StringSliceFlag{ cli.StringSliceFlag{
Name: "cors-headers", Name: "cors-headers",
Usage: "a set of headers to add to the CORS access control (Access-Control-Allow-Headers)", Usage: "a set of headers to add to the CORS access control (Access-Control-Allow-Headers)",
}, },
cli.StringSliceFlag{ cli.StringSliceFlag{
Name: "cors-methods", Name: "cors-exposes-headers",
Usage: "the method permitted in the access control (Access-Control-Allow-Methods)", Usage: "set the expose cors headers access control (Access-Control-Expose-Headers)",
}, },
cli.DurationFlag{ cli.DurationFlag{
Name: "cors-max-age", Name: "cors-max-age",
Usage: "the max age applied to cors headers (Access-Control-Max-Age)", Usage: "the max age applied to cors headers (Access-Control-Max-Age)",
}, },
cli.BoolFlag{
Name: "cors-credentials",
Usage: "the credentials access control header (Access-Control-Allow-Credentials)",
},
cli.BoolFlag{ cli.BoolFlag{
Name: "skip-token-verification", Name: "skip-token-verification",
Usage: "testing purposes ONLY, the option allows you to bypass the token verification, expiration and roles are still enforced", Usage: "testing purposes ONLY, the option allows you to bypass the token verification, expiration and roles are still enforced",
......
...@@ -53,3 +53,19 @@ resources: ...@@ -53,3 +53,19 @@ resources:
roles: roles:
- openvpn:vpn-user - openvpn:vpn-user
- openvpn:prod-vpn - 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
...@@ -22,7 +22,7 @@ import ( ...@@ -22,7 +22,7 @@ import (
const ( const (
prog = "keycloak-proxy" prog = "keycloak-proxy"
version = "v1.0.0-rc1" version = "v1.0.0-rc2"
author = "Rohith" author = "Rohith"
email = "gambol99@gmail.com" email = "gambol99@gmail.com"
description = "is a proxy using the keycloak service for auth and authorization" description = "is a proxy using the keycloak service for auth and authorization"
...@@ -67,7 +67,7 @@ type Resource struct { ...@@ -67,7 +67,7 @@ type Resource struct {
Roles []string `json:"roles" yaml:"roles"` Roles []string `json:"roles" yaml:"roles"`
} }
// CORS controls // CORS access controls
type CORS struct { type CORS struct {
// Origins is a list of origins permitted // Origins is a list of origins permitted
Origins []string `json:"origins" yaml:"origins"` Origins []string `json:"origins" yaml:"origins"`
...@@ -75,6 +75,10 @@ type CORS struct { ...@@ -75,6 +75,10 @@ type CORS struct {
Methods []string `json:"methods" yaml:"methods"` Methods []string `json:"methods" yaml:"methods"`
// Headers is a set of cors headers // Headers is a set of cors headers
Headers []string `json:"headers" yaml:"headers"` Headers []string `json:"headers" yaml:"headers"`
// ExposedHeaders are the exposed header fields
ExposedHeaders []string `json:"exposed-headers" yaml:"exposed-headers"`
// Credentials set the creds flag
Credentials bool `json:"credentials" yaml:"credentials"`
// MaxAge is the age for CORS // MaxAge is the age for CORS
MaxAge time.Duration `json:"max-age" yaml:"max-age"` MaxAge time.Duration `json:"max-age" yaml:"max-age"`
} }
...@@ -116,7 +120,7 @@ type Config struct { ...@@ -116,7 +120,7 @@ type Config struct {
// TagData is passed to the templates // TagData is passed to the templates
TagData map[string]string `json:"TagData" yaml:"TagData"` TagData map[string]string `json:"TagData" yaml:"TagData"`
// CORS permits adding headers to the /oauth handlers // CORS permits adding headers to the /oauth handlers
CORSConfig *CORS `json:"cors" yaml:"cors"` CORS *CORS `json:"cors" yaml:"cors"`
// Header permits adding customs headers across the board // Header permits adding customs headers across the board
Header map[string]string `json:"headers" yaml:"headers"` Header map[string]string `json:"headers" yaml:"headers"`
// Scopes is a list of scope we should request // Scopes is a list of scope we should request
......
...@@ -16,7 +16,6 @@ limitations under the License. ...@@ -16,7 +16,6 @@ limitations under the License.
package main package main
import ( import (
"fmt"
"net/http" "net/http"
"path" "path"
"regexp" "regexp"
...@@ -452,9 +451,6 @@ func (r *KeycloakProxy) oauthAuthorizationHandler(cx *gin.Context) { ...@@ -452,9 +451,6 @@ func (r *KeycloakProxy) oauthAuthorizationHandler(cx *gin.Context) {
return return
} }
// step: add the cors headers
r.corsAccessHeaders(cx)
// step: get the redirection url // step: get the redirection url
r.redirectToURL(redirectionURL, cx) r.redirectToURL(redirectionURL, cx)
} }
...@@ -564,9 +560,6 @@ func (r *KeycloakProxy) oauthCallbackHandler(cx *gin.Context) { ...@@ -564,9 +560,6 @@ func (r *KeycloakProxy) oauthCallbackHandler(cx *gin.Context) {
} }
} }
// step: add the cors headers
r.corsAccessHeaders(cx)
r.redirectToURL(state, cx) r.redirectToURL(state, cx)
} }
...@@ -576,21 +569,3 @@ func (r *KeycloakProxy) oauthCallbackHandler(cx *gin.Context) { ...@@ -576,21 +569,3 @@ func (r *KeycloakProxy) oauthCallbackHandler(cx *gin.Context) {
func (r *KeycloakProxy) healthHandler(cx *gin.Context) { func (r *KeycloakProxy) healthHandler(cx *gin.Context) {
cx.String(http.StatusOK, "OK") cx.String(http.StatusOK, "OK")
} }
// corsAccessHeaders adds the cors access controls to the oauth responses
func (r *KeycloakProxy) corsAccessHeaders(cx *gin.Context) {
cors := r.config.CORSConfig
if len(cors.Origins) > 0 {
cx.Writer.Header().Set("Access-Control-Allow-Origin", strings.Join(cors.Origins, ","))
}
if len(cors.Methods) > 0 {
cx.Writer.Header().Set("Access-Control-Allow-Methods", strings.Join(cors.Methods, ","))
}
if len(cors.Headers) > 0 {
cx.Writer.Header().Set("Access-Control-Allow-Headers", strings.Join(cors.Headers, ","))
}
if cors.MaxAge > 0 {
cx.Writer.Header().Set("Access-Control-Max-Age",
fmt.Sprintf("%d", int(cors.MaxAge.Seconds())))
}
}
...@@ -29,6 +29,7 @@ import ( ...@@ -29,6 +29,7 @@ import (
log "github.com/Sirupsen/logrus" log "github.com/Sirupsen/logrus"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"strings"
) )
// KeycloakProxy is the server component // KeycloakProxy is the server component
...@@ -193,7 +194,7 @@ func (r *KeycloakProxy) Run() error { ...@@ -193,7 +194,7 @@ func (r *KeycloakProxy) Run() error {
// redirectToURL redirects the user and aborts the context // redirectToURL redirects the user and aborts the context
func (r KeycloakProxy) redirectToURL(url string, cx *gin.Context) { func (r KeycloakProxy) redirectToURL(url string, cx *gin.Context) {
// step: add the cors headers // step: add the cors headers
r.corsAccessHeaders(cx) r.injectCORSHeaders(cx)
cx.Redirect(http.StatusTemporaryRedirect, url) cx.Redirect(http.StatusTemporaryRedirect, url)
cx.Abort() cx.Abort()
...@@ -227,6 +228,29 @@ func (r KeycloakProxy) redirectToAuthorization(cx *gin.Context) { ...@@ -227,6 +228,29 @@ func (r KeycloakProxy) redirectToAuthorization(cx *gin.Context) {
r.redirectToURL(authorizationURL+authQuery, cx) r.redirectToURL(authorizationURL+authQuery, cx)
} }
// injectCORSHeaders adds the cors access controls to the oauth responses
func (r *KeycloakProxy) injectCORSHeaders(cx *gin.Context) {
c := r.config.CORS
if len(c.Origins) > 0 {
cx.Writer.Header().Set("Access-Control-Allow-Origin", strings.Join(c.Origins, ","))
}
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())))
}
}
// tryUpdateConnection attempt to upgrade the connection to a http pdy stream // tryUpdateConnection attempt to upgrade the connection to a http pdy stream
func (r *KeycloakProxy) tryUpdateConnection(cx *gin.Context) error { func (r *KeycloakProxy) tryUpdateConnection(cx *gin.Context) error {
// step: dial the endpoint // step: dial the endpoint
......
...@@ -94,7 +94,7 @@ func newFakeKeycloakProxy(t *testing.T) *KeycloakProxy { ...@@ -94,7 +94,7 @@ func newFakeKeycloakProxy(t *testing.T) *KeycloakProxy {
Roles: []string{}, Roles: []string{},
}, },
}, },
CORSConfig: &CORS{}, CORS: &CORS{},
}, },
proxy: new(fakeReverseProxy), proxy: new(fakeReverseProxy),
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment