Merge pull request #265 from cgroschupp/feat/static-upstream
Add upstream with static response
This commit is contained in:
commit
68abf7b2d8
|
|
@ -15,6 +15,7 @@
|
||||||
- [#300](https://github.com/pusher/oauth2_proxy/pull/300) Added userinfo endpoint (@kbabuadze)
|
- [#300](https://github.com/pusher/oauth2_proxy/pull/300) Added userinfo endpoint (@kbabuadze)
|
||||||
- [#309](https://github.com/pusher/oauth2_proxy/pull/309) Added support for custom CA when connecting to Redis cache (@lleszczu)
|
- [#309](https://github.com/pusher/oauth2_proxy/pull/309) Added support for custom CA when connecting to Redis cache (@lleszczu)
|
||||||
- [#248](https://github.com/pusher/oauth2_proxy/pull/248) Fix issue with X-Auth-Request-Redirect header being ignored (@webnard)
|
- [#248](https://github.com/pusher/oauth2_proxy/pull/248) Fix issue with X-Auth-Request-Redirect header being ignored (@webnard)
|
||||||
|
- [#265](https://github.com/pusher/oauth2_proxy/pull/265) Add upstream with static response (@cgroschupp)
|
||||||
|
|
||||||
# v4.0.0
|
# v4.0.0
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,7 @@ An example [oauth2_proxy.cfg]({{ site.gitweb }}/contrib/oauth2_proxy.cfg.example
|
||||||
| `-standard-logging-format` | string | Template for standard log lines | see [Logging Configuration](#logging-configuration) |
|
| `-standard-logging-format` | string | Template for standard log lines | see [Logging Configuration](#logging-configuration) |
|
||||||
| `-tls-cert-file` | string | path to certificate file | |
|
| `-tls-cert-file` | string | path to certificate file | |
|
||||||
| `-tls-key-file` | string | path to private key file | |
|
| `-tls-key-file` | string | path to private key file | |
|
||||||
| `-upstream` | string \| list | the http url(s) of the upstream endpoint or `file://` paths for static files. Routing is based on the path | |
|
| `-upstream` | string \| list | the http url(s) of the upstream endpoint, file:// paths for static files or `static://<status_code>` for static response. Routing is based on the path | |
|
||||||
| `-validate-url` | string | Access token validation endpoint | |
|
| `-validate-url` | string | Access token validation endpoint | |
|
||||||
| `-version` | n/a | print version string | |
|
| `-version` | n/a | print version string | |
|
||||||
| `-whitelist-domain` | string \| list | allowed domains for redirection after authentication. Prefix domain with a `.` to allow subdomains (eg `.example.com`) | |
|
| `-whitelist-domain` | string \| list | allowed domains for redirection after authentication. Prefix domain with a `.` to allow subdomains (eg `.example.com`) | |
|
||||||
|
|
|
||||||
2
main.go
2
main.go
|
|
@ -37,7 +37,7 @@ func main() {
|
||||||
flagSet.String("tls-key-file", "", "path to private key file")
|
flagSet.String("tls-key-file", "", "path to private key file")
|
||||||
flagSet.String("redirect-url", "", "the OAuth Redirect URL. ie: \"https://internalapp.yourcompany.com/oauth2/callback\"")
|
flagSet.String("redirect-url", "", "the OAuth Redirect URL. ie: \"https://internalapp.yourcompany.com/oauth2/callback\"")
|
||||||
flagSet.Bool("set-xauthrequest", false, "set X-Auth-Request-User and X-Auth-Request-Email response headers (useful in Nginx auth_request mode)")
|
flagSet.Bool("set-xauthrequest", false, "set X-Auth-Request-User and X-Auth-Request-Email response headers (useful in Nginx auth_request mode)")
|
||||||
flagSet.Var(&upstreams, "upstream", "the http url(s) of the upstream endpoint or file:// paths for static files. Routing is based on the path")
|
flagSet.Var(&upstreams, "upstream", "the http url(s) of the upstream endpoint, file:// paths for static files or static://<status_code> for static response. Routing is based on the path")
|
||||||
flagSet.Bool("pass-basic-auth", true, "pass HTTP Basic Auth, X-Forwarded-User and X-Forwarded-Email information to upstream")
|
flagSet.Bool("pass-basic-auth", true, "pass HTTP Basic Auth, X-Forwarded-User and X-Forwarded-Email information to upstream")
|
||||||
flagSet.Bool("pass-user-headers", true, "pass X-Forwarded-User and X-Forwarded-Email information to upstream")
|
flagSet.Bool("pass-user-headers", true, "pass X-Forwarded-User and X-Forwarded-Email information to upstream")
|
||||||
flagSet.String("basic-auth-password", "", "the password to set when passing the HTTP Basic Auth header")
|
flagSet.String("basic-auth-password", "", "the password to set when passing the HTTP Basic Auth header")
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"net/http/httputil"
|
"net/http/httputil"
|
||||||
"net/url"
|
"net/url"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -203,12 +204,23 @@ func NewOAuthProxy(opts *Options, validator func(string) bool) *OAuthProxy {
|
||||||
}
|
}
|
||||||
for _, u := range opts.proxyURLs {
|
for _, u := range opts.proxyURLs {
|
||||||
path := u.Path
|
path := u.Path
|
||||||
|
host := u.Host
|
||||||
switch u.Scheme {
|
switch u.Scheme {
|
||||||
case httpScheme, httpsScheme:
|
case httpScheme, httpsScheme:
|
||||||
logger.Printf("mapping path %q => upstream %q", path, u)
|
logger.Printf("mapping path %q => upstream %q", path, u)
|
||||||
proxy := NewWebSocketOrRestReverseProxy(u, opts, auth)
|
proxy := NewWebSocketOrRestReverseProxy(u, opts, auth)
|
||||||
serveMux.Handle(path, proxy)
|
serveMux.Handle(path, proxy)
|
||||||
|
case "static":
|
||||||
|
responseCode, err := strconv.Atoi(host)
|
||||||
|
if err != nil {
|
||||||
|
logger.Printf("unable to convert %q to int, use default \"200\"", host)
|
||||||
|
responseCode = 200
|
||||||
|
}
|
||||||
|
|
||||||
|
serveMux.HandleFunc(path, func(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
rw.WriteHeader(responseCode)
|
||||||
|
fmt.Fprintf(rw, "Authenticated")
|
||||||
|
})
|
||||||
case "file":
|
case "file":
|
||||||
if u.Fragment != "" {
|
if u.Fragment != "" {
|
||||||
path = u.Fragment
|
path = u.Fragment
|
||||||
|
|
|
||||||
|
|
@ -365,6 +365,7 @@ type PassAccessTokenTest struct {
|
||||||
|
|
||||||
type PassAccessTokenTestOptions struct {
|
type PassAccessTokenTestOptions struct {
|
||||||
PassAccessToken bool
|
PassAccessToken bool
|
||||||
|
ProxyUpstream string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPassAccessTokenTest(opts PassAccessTokenTestOptions) *PassAccessTokenTest {
|
func NewPassAccessTokenTest(opts PassAccessTokenTestOptions) *PassAccessTokenTest {
|
||||||
|
|
@ -372,7 +373,6 @@ func NewPassAccessTokenTest(opts PassAccessTokenTestOptions) *PassAccessTokenTes
|
||||||
|
|
||||||
t.providerServer = httptest.NewServer(
|
t.providerServer = httptest.NewServer(
|
||||||
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
logger.Printf("%#v", r)
|
|
||||||
var payload string
|
var payload string
|
||||||
switch r.URL.Path {
|
switch r.URL.Path {
|
||||||
case "/oauth/token":
|
case "/oauth/token":
|
||||||
|
|
@ -389,6 +389,9 @@ func NewPassAccessTokenTest(opts PassAccessTokenTestOptions) *PassAccessTokenTes
|
||||||
|
|
||||||
t.opts = NewOptions()
|
t.opts = NewOptions()
|
||||||
t.opts.Upstreams = append(t.opts.Upstreams, t.providerServer.URL)
|
t.opts.Upstreams = append(t.opts.Upstreams, t.providerServer.URL)
|
||||||
|
if opts.ProxyUpstream != "" {
|
||||||
|
t.opts.Upstreams = append(t.opts.Upstreams, opts.ProxyUpstream)
|
||||||
|
}
|
||||||
// The CookieSecret must be 32 bytes in order to create the AES
|
// The CookieSecret must be 32 bytes in order to create the AES
|
||||||
// cipher.
|
// cipher.
|
||||||
t.opts.CookieSecret = "xyzzyplughxyzzyplughxyzzyplughxp"
|
t.opts.CookieSecret = "xyzzyplughxyzzyplughxyzzyplughxp"
|
||||||
|
|
@ -425,7 +428,9 @@ func (patTest *PassAccessTokenTest) getCallbackEndpoint() (httpCode int,
|
||||||
return rw.Code, rw.HeaderMap["Set-Cookie"][1]
|
return rw.Code, rw.HeaderMap["Set-Cookie"][1]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (patTest *PassAccessTokenTest) getRootEndpoint(cookie string) (httpCode int, accessToken string) {
|
// getEndpointWithCookie makes a requests againt the oauthproxy with passed requestPath
|
||||||
|
// and cookie and returns body and status code.
|
||||||
|
func (patTest *PassAccessTokenTest) getEndpointWithCookie(cookie string, endpoint string) (httpCode int, accessToken string) {
|
||||||
cookieName := patTest.proxy.CookieName
|
cookieName := patTest.proxy.CookieName
|
||||||
var value string
|
var value string
|
||||||
keyPrefix := cookieName + "="
|
keyPrefix := cookieName + "="
|
||||||
|
|
@ -442,7 +447,7 @@ func (patTest *PassAccessTokenTest) getRootEndpoint(cookie string) (httpCode int
|
||||||
return 0, ""
|
return 0, ""
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := http.NewRequest("GET", "/", strings.NewReader(""))
|
req, err := http.NewRequest("GET", endpoint, strings.NewReader(""))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, ""
|
return 0, ""
|
||||||
}
|
}
|
||||||
|
|
@ -475,13 +480,37 @@ func TestForwardAccessTokenUpstream(t *testing.T) {
|
||||||
// Now we make a regular request; the access_token from the cookie is
|
// Now we make a regular request; the access_token from the cookie is
|
||||||
// forwarded as the "X-Forwarded-Access-Token" header. The token is
|
// forwarded as the "X-Forwarded-Access-Token" header. The token is
|
||||||
// read by the test provider server and written in the response body.
|
// read by the test provider server and written in the response body.
|
||||||
code, payload := patTest.getRootEndpoint(cookie)
|
code, payload := patTest.getEndpointWithCookie(cookie, "/")
|
||||||
if code != 200 {
|
if code != 200 {
|
||||||
t.Fatalf("expected 200; got %d", code)
|
t.Fatalf("expected 200; got %d", code)
|
||||||
}
|
}
|
||||||
assert.Equal(t, "my_auth_token", payload)
|
assert.Equal(t, "my_auth_token", payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestStaticProxyUpstream(t *testing.T) {
|
||||||
|
patTest := NewPassAccessTokenTest(PassAccessTokenTestOptions{
|
||||||
|
PassAccessToken: true,
|
||||||
|
ProxyUpstream: "static://200/static-proxy",
|
||||||
|
})
|
||||||
|
|
||||||
|
defer patTest.Close()
|
||||||
|
|
||||||
|
// A successful validation will redirect and set the auth cookie.
|
||||||
|
code, cookie := patTest.getCallbackEndpoint()
|
||||||
|
if code != 302 {
|
||||||
|
t.Fatalf("expected 302; got %d", code)
|
||||||
|
}
|
||||||
|
assert.NotEqual(t, nil, cookie)
|
||||||
|
|
||||||
|
// Now we make a regular request againts the upstream proxy; And validate
|
||||||
|
// the returned status code through the static proxy.
|
||||||
|
code, payload := patTest.getEndpointWithCookie(cookie, "/static-proxy")
|
||||||
|
if code != 200 {
|
||||||
|
t.Fatalf("expected 200; got %d", code)
|
||||||
|
}
|
||||||
|
assert.Equal(t, "Authenticated", payload)
|
||||||
|
}
|
||||||
|
|
||||||
func TestDoNotForwardAccessTokenUpstream(t *testing.T) {
|
func TestDoNotForwardAccessTokenUpstream(t *testing.T) {
|
||||||
patTest := NewPassAccessTokenTest(PassAccessTokenTestOptions{
|
patTest := NewPassAccessTokenTest(PassAccessTokenTestOptions{
|
||||||
PassAccessToken: false,
|
PassAccessToken: false,
|
||||||
|
|
@ -497,7 +526,7 @@ func TestDoNotForwardAccessTokenUpstream(t *testing.T) {
|
||||||
|
|
||||||
// Now we make a regular request, but the access token header should
|
// Now we make a regular request, but the access token header should
|
||||||
// not be present.
|
// not be present.
|
||||||
code, payload := patTest.getRootEndpoint(cookie)
|
code, payload := patTest.getEndpointWithCookie(cookie, "/")
|
||||||
if code != 200 {
|
if code != 200 {
|
||||||
t.Fatalf("expected 200; got %d", code)
|
t.Fatalf("expected 200; got %d", code)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue