2014-12-01 01:12:33 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2015-07-24 09:17:43 +00:00
|
|
|
"encoding/base64"
|
2015-05-21 06:50:21 +00:00
|
|
|
"github.com/bitly/oauth2_proxy/providers"
|
2015-04-03 00:57:17 +00:00
|
|
|
"github.com/bmizerany/assert"
|
2014-12-01 01:12:33 +00:00
|
|
|
"io/ioutil"
|
2015-05-21 03:23:48 +00:00
|
|
|
"log"
|
2014-12-01 01:12:33 +00:00
|
|
|
"net"
|
|
|
|
"net/http"
|
|
|
|
"net/http/httptest"
|
|
|
|
"net/url"
|
2015-04-07 02:10:03 +00:00
|
|
|
"regexp"
|
2015-04-03 00:57:17 +00:00
|
|
|
"strings"
|
2014-12-01 01:12:33 +00:00
|
|
|
"testing"
|
2015-04-03 00:57:17 +00:00
|
|
|
"time"
|
2014-12-01 01:12:33 +00:00
|
|
|
)
|
|
|
|
|
2015-05-21 03:23:48 +00:00
|
|
|
func init() {
|
|
|
|
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2014-12-01 01:12:33 +00:00
|
|
|
func TestNewReverseProxy(t *testing.T) {
|
|
|
|
backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
w.WriteHeader(200)
|
2015-03-17 19:15:15 +00:00
|
|
|
hostname, _, _ := net.SplitHostPort(r.Host)
|
2014-12-01 01:12:33 +00:00
|
|
|
w.Write([]byte(hostname))
|
|
|
|
}))
|
|
|
|
defer backend.Close()
|
|
|
|
|
|
|
|
backendURL, _ := url.Parse(backend.URL)
|
2015-04-03 01:06:37 +00:00
|
|
|
backendHostname, backendPort, _ := net.SplitHostPort(backendURL.Host)
|
2014-12-01 01:12:33 +00:00
|
|
|
backendHost := net.JoinHostPort(backendHostname, backendPort)
|
|
|
|
proxyURL, _ := url.Parse(backendURL.Scheme + "://" + backendHost + "/")
|
|
|
|
|
|
|
|
proxyHandler := NewReverseProxy(proxyURL)
|
2015-03-17 19:15:15 +00:00
|
|
|
setProxyUpstreamHostHeader(proxyHandler, proxyURL)
|
2014-12-01 01:12:33 +00:00
|
|
|
frontend := httptest.NewServer(proxyHandler)
|
|
|
|
defer frontend.Close()
|
|
|
|
|
|
|
|
getReq, _ := http.NewRequest("GET", frontend.URL, nil)
|
|
|
|
res, _ := http.DefaultClient.Do(getReq)
|
|
|
|
bodyBytes, _ := ioutil.ReadAll(res.Body)
|
|
|
|
if g, e := string(bodyBytes), backendHostname; g != e {
|
|
|
|
t.Errorf("got body %q; expected %q", g, e)
|
|
|
|
}
|
|
|
|
}
|
2015-03-17 21:17:40 +00:00
|
|
|
|
|
|
|
func TestEncodedSlashes(t *testing.T) {
|
|
|
|
var seen string
|
|
|
|
backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
w.WriteHeader(200)
|
|
|
|
seen = r.RequestURI
|
|
|
|
}))
|
|
|
|
defer backend.Close()
|
|
|
|
|
|
|
|
b, _ := url.Parse(backend.URL)
|
|
|
|
proxyHandler := NewReverseProxy(b)
|
|
|
|
setProxyDirector(proxyHandler)
|
|
|
|
frontend := httptest.NewServer(proxyHandler)
|
|
|
|
defer frontend.Close()
|
|
|
|
|
|
|
|
f, _ := url.Parse(frontend.URL)
|
2015-03-21 19:29:07 +00:00
|
|
|
encodedPath := "/a%2Fb/?c=1"
|
2015-03-17 21:17:40 +00:00
|
|
|
getReq := &http.Request{URL: &url.URL{Scheme: "http", Host: f.Host, Opaque: encodedPath}}
|
|
|
|
_, err := http.DefaultClient.Do(getReq)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err %s", err)
|
|
|
|
}
|
2015-03-21 19:29:07 +00:00
|
|
|
if seen != encodedPath {
|
|
|
|
t.Errorf("got bad request %q expected %q", seen, encodedPath)
|
2015-03-17 21:17:40 +00:00
|
|
|
}
|
|
|
|
}
|
2015-04-03 00:57:17 +00:00
|
|
|
|
2015-05-10 19:15:52 +00:00
|
|
|
func TestRobotsTxt(t *testing.T) {
|
|
|
|
opts := NewOptions()
|
|
|
|
opts.ClientID = "bazquux"
|
|
|
|
opts.ClientSecret = "foobar"
|
|
|
|
opts.CookieSecret = "xyzzyplugh"
|
|
|
|
opts.Validate()
|
|
|
|
|
2015-11-08 23:57:01 +00:00
|
|
|
proxy := NewOAuthProxy(opts, func(string) bool { return true })
|
2015-05-10 19:15:52 +00:00
|
|
|
rw := httptest.NewRecorder()
|
|
|
|
req, _ := http.NewRequest("GET", "/robots.txt", nil)
|
|
|
|
proxy.ServeHTTP(rw, req)
|
|
|
|
assert.Equal(t, 200, rw.Code)
|
|
|
|
assert.Equal(t, "User-agent: *\nDisallow: /", rw.Body.String())
|
|
|
|
}
|
|
|
|
|
2015-07-24 09:17:43 +00:00
|
|
|
func TestBasicAuthPassword(t *testing.T) {
|
|
|
|
provider_server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
log.Printf("%#v", r)
|
|
|
|
url := r.URL
|
|
|
|
payload := ""
|
|
|
|
switch url.Path {
|
|
|
|
case "/oauth/token":
|
|
|
|
payload = `{"access_token": "my_auth_token"}`
|
|
|
|
default:
|
|
|
|
payload = r.Header.Get("Authorization")
|
|
|
|
if payload == "" {
|
|
|
|
payload = "No Authorization header found."
|
|
|
|
}
|
|
|
|
}
|
|
|
|
w.WriteHeader(200)
|
|
|
|
w.Write([]byte(payload))
|
|
|
|
}))
|
|
|
|
opts := NewOptions()
|
|
|
|
opts.Upstreams = append(opts.Upstreams, provider_server.URL)
|
|
|
|
// The CookieSecret must be 32 bytes in order to create the AES
|
|
|
|
// cipher.
|
|
|
|
opts.CookieSecret = "xyzzyplughxyzzyplughxyzzyplughxp"
|
|
|
|
opts.ClientID = "bazquux"
|
|
|
|
opts.ClientSecret = "foobar"
|
|
|
|
opts.CookieSecure = false
|
|
|
|
opts.PassBasicAuth = true
|
|
|
|
opts.BasicAuthPassword = "This is a secure password"
|
|
|
|
opts.Validate()
|
|
|
|
|
|
|
|
provider_url, _ := url.Parse(provider_server.URL)
|
|
|
|
const email_address = "michael.bland@gsa.gov"
|
|
|
|
const user_name = "michael.bland"
|
|
|
|
|
|
|
|
opts.provider = &TestProvider{
|
|
|
|
ProviderData: &providers.ProviderData{
|
|
|
|
ProviderName: "Test Provider",
|
2015-11-08 23:47:44 +00:00
|
|
|
LoginURL: &url.URL{
|
2015-07-24 09:17:43 +00:00
|
|
|
Scheme: "http",
|
|
|
|
Host: provider_url.Host,
|
|
|
|
Path: "/oauth/authorize",
|
|
|
|
},
|
2015-11-08 23:47:44 +00:00
|
|
|
RedeemURL: &url.URL{
|
2015-07-24 09:17:43 +00:00
|
|
|
Scheme: "http",
|
|
|
|
Host: provider_url.Host,
|
|
|
|
Path: "/oauth/token",
|
|
|
|
},
|
2015-11-08 23:47:44 +00:00
|
|
|
ProfileURL: &url.URL{
|
2015-07-24 09:17:43 +00:00
|
|
|
Scheme: "http",
|
|
|
|
Host: provider_url.Host,
|
|
|
|
Path: "/api/v1/profile",
|
|
|
|
},
|
|
|
|
Scope: "profile.email",
|
|
|
|
},
|
|
|
|
EmailAddress: email_address,
|
|
|
|
}
|
|
|
|
|
2015-11-08 23:57:01 +00:00
|
|
|
proxy := NewOAuthProxy(opts, func(email string) bool {
|
2015-07-24 09:17:43 +00:00
|
|
|
return email == email_address
|
|
|
|
})
|
|
|
|
|
|
|
|
rw := httptest.NewRecorder()
|
|
|
|
req, _ := http.NewRequest("GET", "/oauth2/callback?code=callback_code",
|
|
|
|
strings.NewReader(""))
|
|
|
|
proxy.ServeHTTP(rw, req)
|
|
|
|
cookie := rw.HeaderMap["Set-Cookie"][0]
|
|
|
|
|
|
|
|
cookieName := proxy.CookieName
|
|
|
|
var value string
|
|
|
|
key_prefix := cookieName + "="
|
|
|
|
|
|
|
|
for _, field := range strings.Split(cookie, "; ") {
|
|
|
|
value = strings.TrimPrefix(field, key_prefix)
|
|
|
|
if value != field {
|
|
|
|
break
|
|
|
|
} else {
|
|
|
|
value = ""
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
req, _ = http.NewRequest("GET", "/", strings.NewReader(""))
|
|
|
|
req.AddCookie(&http.Cookie{
|
|
|
|
Name: cookieName,
|
|
|
|
Value: value,
|
|
|
|
Path: "/",
|
|
|
|
Expires: time.Now().Add(time.Duration(24)),
|
|
|
|
HttpOnly: true,
|
|
|
|
})
|
|
|
|
|
|
|
|
rw = httptest.NewRecorder()
|
|
|
|
proxy.ServeHTTP(rw, req)
|
|
|
|
expectedHeader := "Basic " + base64.StdEncoding.EncodeToString([]byte(user_name+":"+opts.BasicAuthPassword))
|
|
|
|
assert.Equal(t, expectedHeader, rw.Body.String())
|
|
|
|
provider_server.Close()
|
|
|
|
}
|
|
|
|
|
2015-04-03 00:57:17 +00:00
|
|
|
type TestProvider struct {
|
|
|
|
*providers.ProviderData
|
|
|
|
EmailAddress string
|
2015-05-13 01:48:13 +00:00
|
|
|
ValidToken bool
|
2015-04-03 00:57:17 +00:00
|
|
|
}
|
|
|
|
|
2015-06-23 11:23:39 +00:00
|
|
|
func (tp *TestProvider) GetEmailAddress(session *providers.SessionState) (string, error) {
|
2015-04-03 00:57:17 +00:00
|
|
|
return tp.EmailAddress, nil
|
|
|
|
}
|
|
|
|
|
2015-06-23 11:23:39 +00:00
|
|
|
func (tp *TestProvider) ValidateSessionState(session *providers.SessionState) bool {
|
2015-05-13 01:48:13 +00:00
|
|
|
return tp.ValidToken
|
|
|
|
}
|
|
|
|
|
2015-04-03 00:57:17 +00:00
|
|
|
type PassAccessTokenTest struct {
|
|
|
|
provider_server *httptest.Server
|
2015-11-08 23:57:01 +00:00
|
|
|
proxy *OAuthProxy
|
2015-04-03 00:57:17 +00:00
|
|
|
opts *Options
|
|
|
|
}
|
|
|
|
|
|
|
|
type PassAccessTokenTestOptions struct {
|
|
|
|
PassAccessToken bool
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewPassAccessTokenTest(opts PassAccessTokenTestOptions) *PassAccessTokenTest {
|
|
|
|
t := &PassAccessTokenTest{}
|
|
|
|
|
|
|
|
t.provider_server = httptest.NewServer(
|
|
|
|
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
2015-05-21 03:23:48 +00:00
|
|
|
log.Printf("%#v", r)
|
2015-04-03 00:57:17 +00:00
|
|
|
url := r.URL
|
|
|
|
payload := ""
|
|
|
|
switch url.Path {
|
|
|
|
case "/oauth/token":
|
|
|
|
payload = `{"access_token": "my_auth_token"}`
|
|
|
|
default:
|
2015-05-21 03:23:48 +00:00
|
|
|
payload = r.Header.Get("X-Forwarded-Access-Token")
|
|
|
|
if payload == "" {
|
2015-04-03 00:57:17 +00:00
|
|
|
payload = "No access token found."
|
|
|
|
}
|
|
|
|
}
|
|
|
|
w.WriteHeader(200)
|
|
|
|
w.Write([]byte(payload))
|
|
|
|
}))
|
|
|
|
|
|
|
|
t.opts = NewOptions()
|
|
|
|
t.opts.Upstreams = append(t.opts.Upstreams, t.provider_server.URL)
|
|
|
|
// The CookieSecret must be 32 bytes in order to create the AES
|
|
|
|
// cipher.
|
|
|
|
t.opts.CookieSecret = "xyzzyplughxyzzyplughxyzzyplughxp"
|
|
|
|
t.opts.ClientID = "bazquux"
|
|
|
|
t.opts.ClientSecret = "foobar"
|
|
|
|
t.opts.CookieSecure = false
|
|
|
|
t.opts.PassAccessToken = opts.PassAccessToken
|
|
|
|
t.opts.Validate()
|
|
|
|
|
|
|
|
provider_url, _ := url.Parse(t.provider_server.URL)
|
|
|
|
const email_address = "michael.bland@gsa.gov"
|
|
|
|
|
|
|
|
t.opts.provider = &TestProvider{
|
|
|
|
ProviderData: &providers.ProviderData{
|
|
|
|
ProviderName: "Test Provider",
|
2015-11-08 23:47:44 +00:00
|
|
|
LoginURL: &url.URL{
|
2015-04-03 00:57:17 +00:00
|
|
|
Scheme: "http",
|
|
|
|
Host: provider_url.Host,
|
|
|
|
Path: "/oauth/authorize",
|
|
|
|
},
|
2015-11-08 23:47:44 +00:00
|
|
|
RedeemURL: &url.URL{
|
2015-04-03 00:57:17 +00:00
|
|
|
Scheme: "http",
|
|
|
|
Host: provider_url.Host,
|
|
|
|
Path: "/oauth/token",
|
|
|
|
},
|
2015-11-08 23:47:44 +00:00
|
|
|
ProfileURL: &url.URL{
|
2015-04-03 00:57:17 +00:00
|
|
|
Scheme: "http",
|
|
|
|
Host: provider_url.Host,
|
|
|
|
Path: "/api/v1/profile",
|
|
|
|
},
|
|
|
|
Scope: "profile.email",
|
|
|
|
},
|
|
|
|
EmailAddress: email_address,
|
|
|
|
}
|
|
|
|
|
2015-11-08 23:57:01 +00:00
|
|
|
t.proxy = NewOAuthProxy(t.opts, func(email string) bool {
|
2015-04-03 00:57:17 +00:00
|
|
|
return email == email_address
|
|
|
|
})
|
|
|
|
return t
|
|
|
|
}
|
|
|
|
|
2015-04-07 01:35:58 +00:00
|
|
|
func (pat_test *PassAccessTokenTest) Close() {
|
|
|
|
pat_test.provider_server.Close()
|
2015-04-03 00:57:17 +00:00
|
|
|
}
|
|
|
|
|
2015-04-07 01:35:58 +00:00
|
|
|
func (pat_test *PassAccessTokenTest) getCallbackEndpoint() (http_code int,
|
|
|
|
cookie string) {
|
2015-04-03 00:57:17 +00:00
|
|
|
rw := httptest.NewRecorder()
|
|
|
|
req, err := http.NewRequest("GET", "/oauth2/callback?code=callback_code",
|
|
|
|
strings.NewReader(""))
|
|
|
|
if err != nil {
|
|
|
|
return 0, ""
|
|
|
|
}
|
2015-04-07 01:35:58 +00:00
|
|
|
pat_test.proxy.ServeHTTP(rw, req)
|
2015-04-03 00:57:17 +00:00
|
|
|
return rw.Code, rw.HeaderMap["Set-Cookie"][0]
|
|
|
|
}
|
|
|
|
|
2015-05-21 03:23:48 +00:00
|
|
|
func (pat_test *PassAccessTokenTest) getRootEndpoint(cookie string) (http_code int, access_token string) {
|
2015-06-08 03:52:28 +00:00
|
|
|
cookieName := pat_test.proxy.CookieName
|
2015-04-03 00:57:17 +00:00
|
|
|
var value string
|
2015-06-08 03:52:28 +00:00
|
|
|
key_prefix := cookieName + "="
|
2015-04-03 00:57:17 +00:00
|
|
|
|
|
|
|
for _, field := range strings.Split(cookie, "; ") {
|
|
|
|
value = strings.TrimPrefix(field, key_prefix)
|
|
|
|
if value != field {
|
|
|
|
break
|
|
|
|
} else {
|
|
|
|
value = ""
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if value == "" {
|
|
|
|
return 0, ""
|
|
|
|
}
|
|
|
|
|
|
|
|
req, err := http.NewRequest("GET", "/", strings.NewReader(""))
|
|
|
|
if err != nil {
|
|
|
|
return 0, ""
|
|
|
|
}
|
|
|
|
req.AddCookie(&http.Cookie{
|
2015-06-08 03:52:28 +00:00
|
|
|
Name: cookieName,
|
2015-04-03 00:57:17 +00:00
|
|
|
Value: value,
|
|
|
|
Path: "/",
|
|
|
|
Expires: time.Now().Add(time.Duration(24)),
|
|
|
|
HttpOnly: true,
|
|
|
|
})
|
|
|
|
|
|
|
|
rw := httptest.NewRecorder()
|
2015-04-07 01:35:58 +00:00
|
|
|
pat_test.proxy.ServeHTTP(rw, req)
|
2015-04-03 00:57:17 +00:00
|
|
|
return rw.Code, rw.Body.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestForwardAccessTokenUpstream(t *testing.T) {
|
2015-04-07 01:35:58 +00:00
|
|
|
pat_test := NewPassAccessTokenTest(PassAccessTokenTestOptions{
|
2015-04-03 00:57:17 +00:00
|
|
|
PassAccessToken: true,
|
|
|
|
})
|
2015-04-07 01:35:58 +00:00
|
|
|
defer pat_test.Close()
|
2015-04-03 00:57:17 +00:00
|
|
|
|
|
|
|
// A successful validation will redirect and set the auth cookie.
|
2015-04-07 01:35:58 +00:00
|
|
|
code, cookie := pat_test.getCallbackEndpoint()
|
2015-04-03 00:57:17 +00:00
|
|
|
assert.Equal(t, 302, code)
|
|
|
|
assert.NotEqual(t, nil, cookie)
|
|
|
|
|
|
|
|
// Now we make a regular request; the access_token from the cookie is
|
|
|
|
// forwarded as the "X-Forwarded-Access-Token" header. The token is
|
|
|
|
// read by the test provider server and written in the response body.
|
2015-04-07 01:35:58 +00:00
|
|
|
code, payload := pat_test.getRootEndpoint(cookie)
|
2015-04-03 00:57:17 +00:00
|
|
|
assert.Equal(t, 200, code)
|
|
|
|
assert.Equal(t, "my_auth_token", payload)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestDoNotForwardAccessTokenUpstream(t *testing.T) {
|
2015-04-07 01:35:58 +00:00
|
|
|
pat_test := NewPassAccessTokenTest(PassAccessTokenTestOptions{
|
2015-04-03 00:57:17 +00:00
|
|
|
PassAccessToken: false,
|
|
|
|
})
|
2015-04-07 01:35:58 +00:00
|
|
|
defer pat_test.Close()
|
2015-04-03 00:57:17 +00:00
|
|
|
|
|
|
|
// A successful validation will redirect and set the auth cookie.
|
2015-04-07 01:35:58 +00:00
|
|
|
code, cookie := pat_test.getCallbackEndpoint()
|
2015-04-03 00:57:17 +00:00
|
|
|
assert.Equal(t, 302, code)
|
|
|
|
assert.NotEqual(t, nil, cookie)
|
|
|
|
|
|
|
|
// Now we make a regular request, but the access token header should
|
|
|
|
// not be present.
|
2015-04-07 01:35:58 +00:00
|
|
|
code, payload := pat_test.getRootEndpoint(cookie)
|
2015-04-03 00:57:17 +00:00
|
|
|
assert.Equal(t, 200, code)
|
|
|
|
assert.Equal(t, "No access token found.", payload)
|
|
|
|
}
|
2015-04-07 02:10:03 +00:00
|
|
|
|
|
|
|
type SignInPageTest struct {
|
|
|
|
opts *Options
|
2015-11-08 23:57:01 +00:00
|
|
|
proxy *OAuthProxy
|
2015-04-07 02:10:03 +00:00
|
|
|
sign_in_regexp *regexp.Regexp
|
|
|
|
}
|
|
|
|
|
|
|
|
const signInRedirectPattern = `<input type="hidden" name="rd" value="(.*)">`
|
|
|
|
|
|
|
|
func NewSignInPageTest() *SignInPageTest {
|
|
|
|
var sip_test SignInPageTest
|
|
|
|
|
|
|
|
sip_test.opts = NewOptions()
|
|
|
|
sip_test.opts.CookieSecret = "foobar"
|
|
|
|
sip_test.opts.ClientID = "bazquux"
|
|
|
|
sip_test.opts.ClientSecret = "xyzzyplugh"
|
|
|
|
sip_test.opts.Validate()
|
|
|
|
|
2015-11-08 23:57:01 +00:00
|
|
|
sip_test.proxy = NewOAuthProxy(sip_test.opts, func(email string) bool {
|
2015-04-07 02:10:03 +00:00
|
|
|
return true
|
|
|
|
})
|
|
|
|
sip_test.sign_in_regexp = regexp.MustCompile(signInRedirectPattern)
|
|
|
|
|
|
|
|
return &sip_test
|
|
|
|
}
|
|
|
|
|
|
|
|
func (sip_test *SignInPageTest) GetEndpoint(endpoint string) (int, string) {
|
|
|
|
rw := httptest.NewRecorder()
|
|
|
|
req, _ := http.NewRequest("GET", endpoint, strings.NewReader(""))
|
|
|
|
sip_test.proxy.ServeHTTP(rw, req)
|
|
|
|
return rw.Code, rw.Body.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestSignInPageIncludesTargetRedirect(t *testing.T) {
|
|
|
|
sip_test := NewSignInPageTest()
|
|
|
|
const endpoint = "/some/random/endpoint"
|
|
|
|
|
|
|
|
code, body := sip_test.GetEndpoint(endpoint)
|
|
|
|
assert.Equal(t, 403, code)
|
|
|
|
|
|
|
|
match := sip_test.sign_in_regexp.FindStringSubmatch(body)
|
|
|
|
if match == nil {
|
|
|
|
t.Fatal("Did not find pattern in body: " +
|
|
|
|
signInRedirectPattern + "\nBody:\n" + body)
|
|
|
|
}
|
|
|
|
if match[1] != endpoint {
|
|
|
|
t.Fatal(`expected redirect to "` + endpoint +
|
|
|
|
`", but was "` + match[1] + `"`)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestSignInPageDirectAccessRedirectsToRoot(t *testing.T) {
|
|
|
|
sip_test := NewSignInPageTest()
|
|
|
|
code, body := sip_test.GetEndpoint("/oauth2/sign_in")
|
|
|
|
assert.Equal(t, 200, code)
|
|
|
|
|
|
|
|
match := sip_test.sign_in_regexp.FindStringSubmatch(body)
|
|
|
|
if match == nil {
|
|
|
|
t.Fatal("Did not find pattern in body: " +
|
|
|
|
signInRedirectPattern + "\nBody:\n" + body)
|
|
|
|
}
|
|
|
|
if match[1] != "/" {
|
|
|
|
t.Fatal(`expected redirect to "/", but was "` + match[1] + `"`)
|
|
|
|
}
|
|
|
|
}
|
2015-05-08 15:52:03 +00:00
|
|
|
|
|
|
|
type ProcessCookieTest struct {
|
2015-05-09 19:09:31 +00:00
|
|
|
opts *Options
|
2015-11-08 23:57:01 +00:00
|
|
|
proxy *OAuthProxy
|
2015-05-09 19:09:31 +00:00
|
|
|
rw *httptest.ResponseRecorder
|
|
|
|
req *http.Request
|
2015-05-13 01:48:13 +00:00
|
|
|
provider TestProvider
|
2015-05-09 19:09:31 +00:00
|
|
|
response_code int
|
2015-05-09 20:48:39 +00:00
|
|
|
validate_user bool
|
2015-05-08 15:52:03 +00:00
|
|
|
}
|
|
|
|
|
2015-05-13 01:48:13 +00:00
|
|
|
type ProcessCookieTestOpts struct {
|
|
|
|
provider_validate_cookie_response bool
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewProcessCookieTest(opts ProcessCookieTestOpts) *ProcessCookieTest {
|
2015-05-08 15:52:03 +00:00
|
|
|
var pc_test ProcessCookieTest
|
|
|
|
|
|
|
|
pc_test.opts = NewOptions()
|
|
|
|
pc_test.opts.ClientID = "bazquux"
|
|
|
|
pc_test.opts.ClientSecret = "xyzzyplugh"
|
2015-05-09 19:09:31 +00:00
|
|
|
pc_test.opts.CookieSecret = "0123456789abcdef"
|
2015-05-09 20:08:55 +00:00
|
|
|
// First, set the CookieRefresh option so proxy.AesCipher is created,
|
|
|
|
// needed to encrypt the access_token.
|
2015-06-22 19:10:08 +00:00
|
|
|
pc_test.opts.CookieRefresh = time.Hour
|
2015-05-08 15:52:03 +00:00
|
|
|
pc_test.opts.Validate()
|
|
|
|
|
2015-11-08 23:57:01 +00:00
|
|
|
pc_test.proxy = NewOAuthProxy(pc_test.opts, func(email string) bool {
|
2015-05-09 20:48:39 +00:00
|
|
|
return pc_test.validate_user
|
2015-05-08 15:52:03 +00:00
|
|
|
})
|
2015-05-13 01:48:13 +00:00
|
|
|
pc_test.proxy.provider = &TestProvider{
|
|
|
|
ValidToken: opts.provider_validate_cookie_response,
|
|
|
|
}
|
2015-05-08 15:52:03 +00:00
|
|
|
|
2015-05-09 20:08:55 +00:00
|
|
|
// Now, zero-out proxy.CookieRefresh for the cases that don't involve
|
|
|
|
// access_token validation.
|
|
|
|
pc_test.proxy.CookieRefresh = time.Duration(0)
|
2015-05-08 15:52:03 +00:00
|
|
|
pc_test.rw = httptest.NewRecorder()
|
|
|
|
pc_test.req, _ = http.NewRequest("GET", "/", strings.NewReader(""))
|
2015-05-09 20:48:39 +00:00
|
|
|
pc_test.validate_user = true
|
2015-05-08 15:52:03 +00:00
|
|
|
return &pc_test
|
|
|
|
}
|
|
|
|
|
2015-05-13 01:48:13 +00:00
|
|
|
func NewProcessCookieTestWithDefaults() *ProcessCookieTest {
|
|
|
|
return NewProcessCookieTest(ProcessCookieTestOpts{
|
|
|
|
provider_validate_cookie_response: true,
|
|
|
|
})
|
2015-05-09 19:09:31 +00:00
|
|
|
}
|
|
|
|
|
2015-06-23 11:23:39 +00:00
|
|
|
func (p *ProcessCookieTest) MakeCookie(value string, ref time.Time) *http.Cookie {
|
|
|
|
return p.proxy.MakeCookie(p.req, value, p.opts.CookieExpire, ref)
|
2015-05-08 15:52:03 +00:00
|
|
|
}
|
|
|
|
|
2015-06-23 11:23:39 +00:00
|
|
|
func (p *ProcessCookieTest) SaveSession(s *providers.SessionState, ref time.Time) error {
|
|
|
|
value, err := p.proxy.provider.CookieForSession(s, p.proxy.CookieCipher)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
p.req.AddCookie(p.proxy.MakeCookie(p.req, value, p.proxy.CookieExpire, ref))
|
|
|
|
return nil
|
2015-05-08 15:52:03 +00:00
|
|
|
}
|
|
|
|
|
2015-06-23 11:23:39 +00:00
|
|
|
func (p *ProcessCookieTest) LoadCookiedSession() (*providers.SessionState, time.Duration, error) {
|
|
|
|
return p.proxy.LoadCookiedSession(p.req)
|
2015-05-08 15:52:03 +00:00
|
|
|
}
|
|
|
|
|
2015-06-23 11:23:39 +00:00
|
|
|
func TestLoadCookiedSession(t *testing.T) {
|
2015-05-13 01:48:13 +00:00
|
|
|
pc_test := NewProcessCookieTestWithDefaults()
|
2015-05-09 19:09:31 +00:00
|
|
|
|
2015-06-23 11:23:39 +00:00
|
|
|
startSession := &providers.SessionState{Email: "michael.bland@gsa.gov", AccessToken: "my_access_token"}
|
|
|
|
pc_test.SaveSession(startSession, time.Now())
|
|
|
|
|
|
|
|
session, _, err := pc_test.LoadCookiedSession()
|
|
|
|
assert.Equal(t, nil, err)
|
|
|
|
assert.Equal(t, startSession.Email, session.Email)
|
|
|
|
assert.Equal(t, "michael.bland", session.User)
|
|
|
|
assert.Equal(t, startSession.AccessToken, session.AccessToken)
|
2015-05-08 15:52:03 +00:00
|
|
|
}
|
|
|
|
|
2015-05-08 14:00:57 +00:00
|
|
|
func TestProcessCookieNoCookieError(t *testing.T) {
|
2015-05-13 01:48:13 +00:00
|
|
|
pc_test := NewProcessCookieTestWithDefaults()
|
2015-05-08 14:00:57 +00:00
|
|
|
|
2015-06-23 11:23:39 +00:00
|
|
|
session, _, err := pc_test.LoadCookiedSession()
|
|
|
|
assert.Equal(t, "Cookie \"_oauth2_proxy\" not present", err.Error())
|
|
|
|
if session != nil {
|
|
|
|
t.Errorf("expected nil session. got %#v", session)
|
|
|
|
}
|
2015-05-09 20:31:18 +00:00
|
|
|
}
|
|
|
|
|
2015-05-08 14:00:57 +00:00
|
|
|
func TestProcessCookieRefreshNotSet(t *testing.T) {
|
2015-05-13 01:48:13 +00:00
|
|
|
pc_test := NewProcessCookieTestWithDefaults()
|
2015-05-10 04:11:26 +00:00
|
|
|
pc_test.proxy.CookieExpire = time.Duration(23) * time.Hour
|
2015-06-22 19:10:08 +00:00
|
|
|
reference := time.Now().Add(time.Duration(-2) * time.Hour)
|
2015-05-08 14:00:57 +00:00
|
|
|
|
2015-06-23 11:23:39 +00:00
|
|
|
startSession := &providers.SessionState{Email: "michael.bland@gsa.gov", AccessToken: "my_access_token"}
|
|
|
|
pc_test.SaveSession(startSession, reference)
|
2015-05-09 19:09:31 +00:00
|
|
|
|
2015-06-23 11:23:39 +00:00
|
|
|
session, age, err := pc_test.LoadCookiedSession()
|
|
|
|
assert.Equal(t, nil, err)
|
|
|
|
if age < time.Duration(-2)*time.Hour {
|
|
|
|
t.Errorf("cookie too young %v", age)
|
|
|
|
}
|
|
|
|
assert.Equal(t, startSession.Email, session.Email)
|
2015-05-10 04:11:26 +00:00
|
|
|
}
|
|
|
|
|
2015-06-22 19:10:08 +00:00
|
|
|
func TestProcessCookieFailIfCookieExpired(t *testing.T) {
|
|
|
|
pc_test := NewProcessCookieTestWithDefaults()
|
|
|
|
pc_test.proxy.CookieExpire = time.Duration(24) * time.Hour
|
|
|
|
reference := time.Now().Add(time.Duration(25) * time.Hour * -1)
|
2015-06-23 11:23:39 +00:00
|
|
|
startSession := &providers.SessionState{Email: "michael.bland@gsa.gov", AccessToken: "my_access_token"}
|
|
|
|
pc_test.SaveSession(startSession, reference)
|
2015-06-22 19:10:08 +00:00
|
|
|
|
2015-06-23 11:23:39 +00:00
|
|
|
session, _, err := pc_test.LoadCookiedSession()
|
|
|
|
assert.NotEqual(t, nil, err)
|
|
|
|
if session != nil {
|
|
|
|
t.Errorf("expected nil session %#v", session)
|
2015-06-22 19:10:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestProcessCookieFailIfRefreshSetAndCookieExpired(t *testing.T) {
|
|
|
|
pc_test := NewProcessCookieTestWithDefaults()
|
|
|
|
pc_test.proxy.CookieExpire = time.Duration(24) * time.Hour
|
|
|
|
reference := time.Now().Add(time.Duration(25) * time.Hour * -1)
|
2015-06-23 11:23:39 +00:00
|
|
|
startSession := &providers.SessionState{Email: "michael.bland@gsa.gov", AccessToken: "my_access_token"}
|
|
|
|
pc_test.SaveSession(startSession, reference)
|
2015-06-22 19:10:08 +00:00
|
|
|
|
|
|
|
pc_test.proxy.CookieRefresh = time.Hour
|
2015-06-23 11:23:39 +00:00
|
|
|
session, _, err := pc_test.LoadCookiedSession()
|
|
|
|
assert.NotEqual(t, nil, err)
|
|
|
|
if session != nil {
|
|
|
|
t.Errorf("expected nil session %#v", session)
|
2015-06-22 19:10:08 +00:00
|
|
|
}
|
|
|
|
}
|
2015-10-08 13:27:00 +00:00
|
|
|
|
|
|
|
func NewAuthOnlyEndpointTest() *ProcessCookieTest {
|
|
|
|
pc_test := NewProcessCookieTestWithDefaults()
|
|
|
|
pc_test.req, _ = http.NewRequest("GET",
|
|
|
|
pc_test.opts.ProxyPrefix + "/auth", nil)
|
|
|
|
return pc_test
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestAuthOnlyEndpointAccepted(t *testing.T) {
|
|
|
|
test := NewAuthOnlyEndpointTest()
|
|
|
|
startSession := &providers.SessionState{
|
|
|
|
Email: "michael.bland@gsa.gov", AccessToken: "my_access_token"}
|
|
|
|
test.SaveSession(startSession, time.Now())
|
|
|
|
|
|
|
|
test.proxy.ServeHTTP(test.rw, test.req)
|
|
|
|
assert.Equal(t, http.StatusAccepted, test.rw.Code)
|
|
|
|
bodyBytes, _ := ioutil.ReadAll(test.rw.Body)
|
|
|
|
assert.Equal(t, "", string(bodyBytes))
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestAuthOnlyEndpointUnauthorizedOnNoCookieSetError(t *testing.T) {
|
|
|
|
test := NewAuthOnlyEndpointTest()
|
|
|
|
|
|
|
|
test.proxy.ServeHTTP(test.rw, test.req)
|
|
|
|
assert.Equal(t, http.StatusUnauthorized, test.rw.Code)
|
|
|
|
bodyBytes, _ := ioutil.ReadAll(test.rw.Body)
|
|
|
|
assert.Equal(t, "unauthorized request\n", string(bodyBytes))
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestAuthOnlyEndpointUnauthorizedOnExpiration(t *testing.T) {
|
|
|
|
test := NewAuthOnlyEndpointTest()
|
|
|
|
test.proxy.CookieExpire = time.Duration(24) * time.Hour
|
|
|
|
reference := time.Now().Add(time.Duration(25) * time.Hour * -1)
|
|
|
|
startSession := &providers.SessionState{
|
|
|
|
Email: "michael.bland@gsa.gov", AccessToken: "my_access_token"}
|
|
|
|
test.SaveSession(startSession, reference)
|
|
|
|
|
|
|
|
test.proxy.ServeHTTP(test.rw, test.req)
|
|
|
|
assert.Equal(t, http.StatusUnauthorized, test.rw.Code)
|
|
|
|
bodyBytes, _ := ioutil.ReadAll(test.rw.Body)
|
|
|
|
assert.Equal(t, "unauthorized request\n", string(bodyBytes))
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestAuthOnlyEndpointUnauthorizedOnEmailValidationFailure(t *testing.T) {
|
|
|
|
test := NewAuthOnlyEndpointTest()
|
|
|
|
startSession := &providers.SessionState{
|
|
|
|
Email: "michael.bland@gsa.gov", AccessToken: "my_access_token"}
|
|
|
|
test.SaveSession(startSession, time.Now())
|
|
|
|
test.validate_user = false
|
|
|
|
|
|
|
|
test.proxy.ServeHTTP(test.rw, test.req)
|
|
|
|
assert.Equal(t, http.StatusUnauthorized, test.rw.Code)
|
|
|
|
bodyBytes, _ := ioutil.ReadAll(test.rw.Body)
|
|
|
|
assert.Equal(t, "unauthorized request\n", string(bodyBytes))
|
|
|
|
}
|