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

Prefix #326

Merged
merged 1 commit into from
Mar 14, 2018
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ FEATURES:
* Added spelling check to the tests [#PR322](https://github.com/gambol99/keycloak-proxy/pull/322)
* Added the X-Auth-Audience to the upstream headers [#PR319](https://github.com/gambol99/keycloak-proxy/pull/319)
* Added the ability to control the timeout on the initial openid configuration from .well-known/openid-configuration [#PR315](https://github.com/gambol99/keycloak-proxy/pull/315)
* Added the feature to customize the oauth prefix (defaults to /oauth) [#PR326](https://github.com/gambol99/keycloak-proxy/pull/326)
* Added a `enable-logout-redirect` which redirects the /oauth/logout to the provider [#PR327](https://github.com/gambol99/keycloak-proxy/pull/327)
* Adding additional metrics covering provider request latency, token breakdown [#PR324](https://github.com/gambol99/keycloak-proxy/pull/324)
* Added environment variables alternatives for the forwarding username and password [#PR329]https://github.com/gambol99/keycloak-proxy/pull/329)
Expand Down
6 changes: 6 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ func newDefaultConfig() *Config {
Headers: make(map[string]string),
LetsEncryptCacheDir: "./cache/",
MatchClaims: make(map[string]string),
OAuthURI: "/oauth",
OpenIDProviderTimeout: 30 * time.Second,
SecureCookie: true,
ServerIdleTimeout: 120 * time.Second,
Expand All @@ -54,6 +55,11 @@ func newDefaultConfig() *Config {
}
}

// WithOAuthURI returns the oauth uri
func (r *Config) WithOAuthURI(uri string) string {
return fmt.Sprintf("%s/%s", r.OAuthURI, uri)
}

// isValid validates if the config is valid
func (r *Config) isValid() error {
if r.Listen == "" {
Expand Down
3 changes: 2 additions & 1 deletion doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ const (
httpSchema = "http"
versionHeader = "X-Auth-Proxy-Version"

oauthURL = "/oauth"
authorizationURL = "/authorize"
callbackURL = "/callback"
expiredURL = "/expired"
Expand Down Expand Up @@ -165,6 +164,8 @@ type Config struct {
OpenIDProviderProxy string `json:"openid-provider-proxy" yaml:"openid-provider-proxy" usage:"proxy for communication with the openid provider"`
// OpenIDProviderTimeout is the timeout used to pulling the openid configuration from the provider
OpenIDProviderTimeout time.Duration `json:"openid-provider-timeout" yaml:"openid-provider-timeout" usage:"timeout for openid configuration on .well-known/openid-configuration"`
// OAuthURI is the uri for the oauth endpoints for the proxy
OAuthURI string `json:"oauth-uri" yaml:"oauth-uri" usage:"the uri for proxy oauth endpoints" env:"OAUTH_URI"`
// Scopes is a list of scope we should request
Scopes []string `json:"scopes" yaml:"scopes" usage:"list of scopes requested when authenticating the user"`
// Upstream is the upstream endpoint i.e whom were proxying to
Expand Down
2 changes: 1 addition & 1 deletion handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ func (r *oauthProxy) getRedirectionURL(w http.ResponseWriter, req *http.Request)
redirect = r.config.RedirectionURL
}

return fmt.Sprintf("%s/oauth/callback", redirect)
return fmt.Sprintf("%s%s", redirect, r.config.WithOAuthURI("callback"))
}

// oauthAuthorizationHandler is responsible for performing the redirection to oauth provider
Expand Down
42 changes: 23 additions & 19 deletions handlers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ func TestDebugHandler(t *testing.T) {
}

func TestExpirationHandler(t *testing.T) {
uri := oauthURL + expiredURL
cfg := newFakeKeycloakConfig()
uri := cfg.WithOAuthURI(expiredURL)
requests := []fakeRequest{
{
URI: uri,
Expand Down Expand Up @@ -78,8 +79,8 @@ func TestLoginHandlerDisabled(t *testing.T) {
c := newFakeKeycloakConfig()
c.EnableLoginHandler = false
requests := []fakeRequest{
{URI: oauthURL + loginURL, Method: http.MethodPost, ExpectedCode: http.StatusNotImplemented},
{URI: oauthURL + loginURL, ExpectedCode: http.StatusMethodNotAllowed},
{URI: c.WithOAuthURI(loginURL), Method: http.MethodPost, ExpectedCode: http.StatusNotImplemented},
{URI: c.WithOAuthURI(loginURL), ExpectedCode: http.StatusMethodNotAllowed},
}
newFakeProxy(c).RunTests(t, requests)
}
Expand All @@ -94,7 +95,7 @@ func TestLoginHandlerNotDisabled(t *testing.T) {
}

func TestLoginHandler(t *testing.T) {
uri := oauthURL + loginURL
uri := newFakeKeycloakConfig().WithOAuthURI(loginURL)
requests := []fakeRequest{
{
URI: uri,
Expand Down Expand Up @@ -137,25 +138,26 @@ func TestLoginHandler(t *testing.T) {

func TestLogoutHandlerBadRequest(t *testing.T) {
requests := []fakeRequest{
{URI: oauthURL + logoutURL, ExpectedCode: http.StatusBadRequest},
{URI: newFakeKeycloakConfig().WithOAuthURI(logoutURL), ExpectedCode: http.StatusBadRequest},
}
newFakeProxy(nil).RunTests(t, requests)
}

func TestLogoutHandlerBadToken(t *testing.T) {
c := newFakeKeycloakConfig()
requests := []fakeRequest{
{
URI: oauthURL + logoutURL,
URI: c.WithOAuthURI(logoutURL),
ExpectedCode: http.StatusBadRequest,
},
{
URI: oauthURL + logoutURL,
URI: c.WithOAuthURI(logoutURL),
HasCookieToken: true,
RawToken: "this.is.a.bad.token",
ExpectedCode: http.StatusBadRequest,
},
{
URI: oauthURL + logoutURL,
URI: c.WithOAuthURI(logoutURL),
RawToken: "this.is.a.bad.token",
ExpectedCode: http.StatusBadRequest,
},
Expand All @@ -164,14 +166,15 @@ func TestLogoutHandlerBadToken(t *testing.T) {
}

func TestLogoutHandlerGood(t *testing.T) {
c := newFakeKeycloakConfig()
requests := []fakeRequest{
{
URI: oauthURL + logoutURL,
URI: c.WithOAuthURI(logoutURL),
HasToken: true,
ExpectedCode: http.StatusOK,
},
{
URI: oauthURL + logoutURL + "?redirect=http://example.com",
URI: c.WithOAuthURI(logoutURL) + "?redirect=http://example.com",
HasToken: true,
ExpectedCode: http.StatusTemporaryRedirect,
ExpectedLocation: "http://example.com",
Expand All @@ -181,7 +184,7 @@ func TestLogoutHandlerGood(t *testing.T) {
}

func TestTokenHandler(t *testing.T) {
uri := oauthURL + tokenURL
uri := newFakeKeycloakConfig().WithOAuthURI(tokenURL)
requests := []fakeRequest{
{
URI: uri,
Expand Down Expand Up @@ -228,7 +231,7 @@ func TestAuthorizationURLWithSkipToken(t *testing.T) {
c.SkipTokenVerification = true
newFakeProxy(c).RunTests(t, []fakeRequest{
{
URI: oauthURL + authorizationURL,
URI: c.WithOAuthURI(authorizationURL),
ExpectedCode: http.StatusNotAcceptable,
},
})
Expand Down Expand Up @@ -278,28 +281,28 @@ func TestCallbackURL(t *testing.T) {
cfg := newFakeKeycloakConfig()
requests := []fakeRequest{
{
URI: oauthURL + callbackURL,
URI: cfg.WithOAuthURI(callbackURL),
Method: http.MethodPost,
ExpectedCode: http.StatusMethodNotAllowed,
},
{
URI: oauthURL + callbackURL,
URI: cfg.WithOAuthURI(callbackURL),
ExpectedCode: http.StatusBadRequest,
},
{
URI: oauthURL + callbackURL + "?code=fake",
URI: cfg.WithOAuthURI(callbackURL) + "?code=fake",
ExpectedCookies: map[string]string{cfg.CookieAccessName: ""},
ExpectedLocation: "/",
ExpectedCode: http.StatusTemporaryRedirect,
},
{
URI: oauthURL + callbackURL + "?code=fake&state=/admin",
URI: cfg.WithOAuthURI(callbackURL) + "?code=fake&state=/admin",
ExpectedCookies: map[string]string{cfg.CookieAccessName: ""},
ExpectedLocation: "/",
ExpectedCode: http.StatusTemporaryRedirect,
},
{
URI: oauthURL + callbackURL + "?code=fake&state=L2FkbWlu",
URI: cfg.WithOAuthURI(callbackURL) + "?code=fake&state=L2FkbWlu",
ExpectedCookies: map[string]string{cfg.CookieAccessName: ""},
ExpectedLocation: "/admin",
ExpectedCode: http.StatusTemporaryRedirect,
Expand All @@ -309,14 +312,15 @@ func TestCallbackURL(t *testing.T) {
}

func TestHealthHandler(t *testing.T) {
c := newFakeKeycloakConfig()
requests := []fakeRequest{
{
URI: oauthURL + healthURL,
URI: c.WithOAuthURI(healthURL),
ExpectedCode: http.StatusOK,
ExpectedContent: "OK\n",
},
{
URI: oauthURL + healthURL,
URI: c.WithOAuthURI(healthURL),
Method: http.MethodHead,
ExpectedCode: http.StatusMethodNotAllowed,
},
Expand Down
4 changes: 2 additions & 2 deletions middleware_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -296,12 +296,12 @@ func TestMetricsMiddleware(t *testing.T) {
cfg.LocalhostMetrics = true
requests := []fakeRequest{
{
URI: oauthURL + metricsURL,
URI: cfg.WithOAuthURI(metricsURL),
ExpectedCode: http.StatusOK,
ExpectedContentContains: "proxy_request_status_total",
},
{
URI: oauthURL + metricsURL,
URI: cfg.WithOAuthURI(metricsURL),
Headers: map[string]string{
"X-Forwarded-For": "10.0.0.1",
},
Expand Down
2 changes: 1 addition & 1 deletion misc.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ func (r *oauthProxy) redirectToAuthorization(w http.ResponseWriter, req *http.Re
w.WriteHeader(http.StatusForbidden)
return r.revokeProxy(w, req)
}
r.redirectToURL(oauthURL+authorizationURL+authQuery, w, req)
r.redirectToURL(r.config.WithOAuthURI(authorizationURL+authQuery), w, req)

return r.revokeProxy(w, req)
}
Expand Down
3 changes: 0 additions & 3 deletions resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,6 @@ func (r *Resource) valid() error {
if r.Roles == nil {
r.Roles = make([]string, 0)
}
if strings.HasPrefix(r.URL, oauthURL) {
return errors.New("this is used by the oauth handlers")
}
if r.URL == "" {
return errors.New("resource does not have url")
}
Expand Down
4 changes: 2 additions & 2 deletions server.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ func (r *oauthProxy) createReverseProxy() error {
r.router = engine

// step: add the routing for oauth
engine.With(proxyDenyMiddleware).Route(oauthURL, func(e chi.Router) {
engine.With(proxyDenyMiddleware).Route(r.config.OAuthURI, func(e chi.Router) {
e.MethodNotAllowed(methodNotAllowHandlder)
e.Get(authorizationURL, r.oauthAuthorizationHandler)
e.Get(callbackURL, r.oauthCallbackHandler)
Expand All @@ -197,7 +197,7 @@ func (r *oauthProxy) createReverseProxy() error {
e.Get(tokenURL, r.tokenHandler)
e.Post(loginURL, r.loginHandler)
if r.config.EnableMetrics {
r.log.Info("enabled the service metrics middleware, available on", zap.String("path", fmt.Sprintf("%s%s", oauthURL, metricsURL)))
r.log.Info("enabled the service metrics middleware", zap.String("path", r.config.WithOAuthURI(metricsURL)))
e.Get(metricsURL, r.proxyMetricsHandler)
}
})
Expand Down
9 changes: 5 additions & 4 deletions server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ func TestAuthorizationTemplate(t *testing.T) {
}
requests := []fakeRequest{
{
URI: oauthURL + authorizationURL,
URI: cfg.WithOAuthURI(authorizationURL),
Redirects: true,
ExpectedCode: http.StatusOK,
ExpectedContentContains: "Sign In",
Expand Down Expand Up @@ -447,15 +447,16 @@ func newFakeKeycloakConfig() *Config {
CookieRefreshName: "kc-state",
DisableAllLogging: true,
DiscoveryURL: "127.0.0.1:0",
OpenIDProviderTimeout: time.Second * 5,
EnableAuthorizationHeader: true,
EnableAuthorizationCookies: true,
EnableAuthorizationHeader: true,
EnableLogging: false,
EnableLoginHandler: true,
EnableTokenHeader: true,
Listen: "127.0.0.1:0",
OAuthURI: "/oauth",
OpenIDProviderTimeout: time.Second * 5,
Scopes: []string{},
Verbose: true,
Verbose: false,
Resources: []*Resource{
{
URL: fakeAdminRoleURL,
Expand Down