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

Commit

Permalink
Proxy Prefix (#326)
Browse files Browse the repository at this point in the history
- adding the ability to control the prefix
  • Loading branch information
gambol99 committed Mar 14, 2018
1 parent d6af522 commit 557b5f1
Show file tree
Hide file tree
Showing 10 changed files with 43 additions and 33 deletions.
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

0 comments on commit 557b5f1

Please sign in to comment.