diff --git a/config.go b/config.go index 972f76328e701c8126552134d66a4c421135bcb2..b367217dd447f5046e5b7ff6a82064882ebe7244 100644 --- a/config.go +++ b/config.go @@ -39,6 +39,11 @@ func newDefaultConfig() *Config { MaxSession: time.Duration(1) * time.Hour, TagData: make(map[string]string, 0), ClaimsMatch: make(map[string]string, 0), + CORSConfig: &CORS{ + Origins: []string{}, + Methods: []string{}, + Headers: []string{}, + }, } } @@ -209,6 +214,24 @@ func readOptions(cx *cli.Context, config *Config) (err error) { return err } } + if cx.IsSet("header") { + config.Header, err = decodeKeyPairs(cx.StringSlice("header")) + if err != nil { + 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") { for _, x := range cx.StringSlice("resource") { resource, err := decodeResource(x) @@ -327,6 +350,22 @@ func getOptions() []cli.Flag { Usage: "if refresh sessions are enabled we can limit their duration via this", Value: defaults.MaxSession, }, + cli.StringSliceFlag{ + Name: "cors-origins", + Usage: "a set of origins to add to the CORS access control (Access-Control-Allow-Origin)", + }, + cli.StringSliceFlag{ + Name: "cors-headers", + Usage: "a set of headers to add to the CORS access control (Access-Control-Allow-Headers)", + }, + cli.StringSliceFlag{ + Name: "cors-methods", + Usage: "the method permitted in the access control (Access-Control-Allow-Methods)", + }, + cli.DurationFlag{ + Name: "cors-max-age", + Usage: "the max age applied to cors headers (Access-Control-Max-Age)", + }, cli.BoolFlag{ Name: "skip-token-verification", Usage: "testing purposes ONLY, the option allows you to bypass the token verification, expiration and roles are still enforced", diff --git a/doc.go b/doc.go index 81bc3498c621f7a513e8f70177d6c17d2029f72b..fe5b679b359f3aec45755fdf55ce4ed43b28ebf7 100644 --- a/doc.go +++ b/doc.go @@ -67,6 +67,18 @@ type Resource struct { Roles []string `json:"roles" yaml:"roles"` } +// CORS controls +type CORS struct { + // Origins is a list of origins permitted + Origins []string `json:"origins" yaml:"origins"` + // Methods is a set of access control methods + Methods []string `json:"methods" yaml:"methods"` + // Headers is a set of cors headers + Headers []string `json:"headers" yaml:"headers"` + // MaxAge is the age for CORS + MaxAge time.Duration `json:"max-age" yaml:"max-age"` +} + // Config is the configuration for the proxy type Config struct { // LogRequests indicates if we should log all the requests @@ -103,6 +115,10 @@ type Config struct { Upstream string `json:"upstream" yaml:"upstream"` // TagData is passed to the templates TagData map[string]string `json:"TagData" yaml:"TagData"` + // CORS permits adding headers to the /oauth handlers + CORSConfig *CORS `json:"cors" yaml:"cors"` + // Header permits adding customs headers across the board + Header map[string]string `json:"headers" yaml:"headers"` // Scopes is a list of scope we should request Scopes []string `json:"scopes" yaml:"scopes"` // Resources is a list of protected resources diff --git a/handlers.go b/handlers.go index 8dc8c0b8396104c367bf2610100ce6d8b59fcd35..a469019ac8d18b6c6fc2603ceb69c81946ae7c91 100644 --- a/handlers.go +++ b/handlers.go @@ -22,6 +22,7 @@ import ( "strings" "time" + "fmt" log "github.com/Sirupsen/logrus" "github.com/gambol99/go-oidc/jose" "github.com/gambol99/go-oidc/oauth2" @@ -113,6 +114,11 @@ func (r *KeycloakProxy) entryPointHandler() gin.HandlerFunc { // step: pass into the authentication and admission handlers cx.Next() + // step: add a custom headers to the request + for k, v := range r.config.Header { + cx.Request.Header.Set(k, v) + } + // step: check the request has not been aborted and if not, proxy request if !cx.IsAborted() { r.proxyHandler(cx) @@ -446,6 +452,9 @@ func (r *KeycloakProxy) oauthAuthorizationHandler(cx *gin.Context) { return } + // step: add the cors headers + r.corsAccessHeaders(cx) + // step: get the redirection url r.redirectToURL(redirectionURL, cx) } @@ -555,6 +564,9 @@ func (r *KeycloakProxy) oauthCallbackHandler(cx *gin.Context) { } } + // step: add the cors headers + r.corsAccessHeaders(cx) + r.redirectToURL(state, cx) } @@ -564,3 +576,21 @@ func (r *KeycloakProxy) oauthCallbackHandler(cx *gin.Context) { func (r *KeycloakProxy) healthHandler(cx *gin.Context) { 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()))) + } +} diff --git a/oauth.yml b/oauth.yml deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/oauth_test.go b/oauth_test.go deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000