mirror of
https://github.com/pomerium/pomerium.git
synced 2025-05-10 07:37:33 +02:00
proxy: add unit tests (#42)
- Fixed pomerium.io site description - Updated kubernetes examples to use the latest docker images. - Use cryputil hash function for proxy hmac. - Add unit tests for proxy handlers.
This commit is contained in:
parent
c886b924e7
commit
cedf9922d3
7 changed files with 336 additions and 40 deletions
|
@ -1,7 +1,7 @@
|
||||||
// .vuepress/config.js
|
// .vuepress/config.js
|
||||||
module.exports = {
|
module.exports = {
|
||||||
title: "Pomerium",
|
title: "Pomerium",
|
||||||
description: "Just playing around",
|
description: "An open source identity-aware access proxy.",
|
||||||
|
|
||||||
themeConfig: {
|
themeConfig: {
|
||||||
repo: "pomerium/pomerium",
|
repo: "pomerium/pomerium",
|
||||||
|
|
|
@ -16,7 +16,7 @@ spec:
|
||||||
app: pomerium-authenticate
|
app: pomerium-authenticate
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- image: pomerium/pomerium:grpctest
|
- image: pomerium/pomerium:latest
|
||||||
name: pomerium-authenticate
|
name: pomerium-authenticate
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 443
|
- containerPort: 443
|
||||||
|
|
|
@ -16,7 +16,7 @@ spec:
|
||||||
app: pomerium-proxy
|
app: pomerium-proxy
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- image: pomerium/pomerium:grpctest
|
- image: pomerium/pomerium:latest
|
||||||
name: pomerium-proxy
|
name: pomerium-proxy
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 443
|
- containerPort: 443
|
||||||
|
|
|
@ -2,7 +2,6 @@ package middleware // import "github.com/pomerium/pomerium/internal/middleware"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/hmac"
|
"crypto/hmac"
|
||||||
"crypto/sha256"
|
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -11,6 +10,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/pomerium/pomerium/internal/cryptutil"
|
||||||
"github.com/pomerium/pomerium/internal/httputil"
|
"github.com/pomerium/pomerium/internal/httputil"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -192,12 +192,12 @@ func ValidSignature(redirectURI, sigVal, timestamp, secret string) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
localSig := redirectURLSignature(redirectURI, tm, secret)
|
localSig := redirectURLSignature(redirectURI, tm, secret)
|
||||||
|
|
||||||
return hmac.Equal(requestSig, localSig)
|
return hmac.Equal(requestSig, localSig)
|
||||||
}
|
}
|
||||||
|
|
||||||
func redirectURLSignature(rawRedirect string, timestamp time.Time, secret string) []byte {
|
func redirectURLSignature(rawRedirect string, timestamp time.Time, secret string) []byte {
|
||||||
h := hmac.New(sha256.New, []byte(secret))
|
data := []byte(fmt.Sprint(rawRedirect, timestamp.Unix()))
|
||||||
h.Write([]byte(rawRedirect))
|
h := cryptutil.Hash(secret, data)
|
||||||
h.Write([]byte(fmt.Sprint(timestamp.Unix())))
|
return h
|
||||||
return h.Sum(nil)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,7 +74,7 @@ func Test_redirectURLSignature(t *testing.T) {
|
||||||
secret string
|
secret string
|
||||||
want string
|
want string
|
||||||
}{
|
}{
|
||||||
{"good signature", "https://example.com/redirect", time.Unix(1546797901, 0), "41aOD7VNtQ1/KZDCGrkYpaHwB50JC1y6BDs2KPRVd2A=", "GIDyWKjrG_7MwXwIq1o51f2pDT_rH9aLHdsHxSBEwy8="},
|
{"good signature", "https://example.com/redirect", time.Unix(1546797901, 0), "K3yqsJPahIzu5CdfCVJlIK4N8Dc135-27Tg1ROuQdhc=", "XeVJC2Iysq7mRUwOL3FX_5vx1d_kZV2HONHNig9fcKk="},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
package proxy // import "github.com/pomerium/pomerium/proxy"
|
package proxy // import "github.com/pomerium/pomerium/proxy"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/hmac"
|
|
||||||
"crypto/sha256"
|
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -85,7 +83,7 @@ func (p *Proxy) RobotsTxt(w http.ResponseWriter, _ *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Favicon will proxy the request as usual if the user is already authenticated but responds
|
// Favicon will proxy the request as usual if the user is already authenticated but responds
|
||||||
// with a 404 otherwise, to avoid spurious and confusing IdentityProvider attempts when a browser
|
// with a 404 otherwise, to avoid spurious and confusing authenticate attempts when a browser
|
||||||
// automatically requests the favicon on an error page.
|
// automatically requests the favicon on an error page.
|
||||||
func (p *Proxy) Favicon(w http.ResponseWriter, r *http.Request) {
|
func (p *Proxy) Favicon(w http.ResponseWriter, r *http.Request) {
|
||||||
err := p.Authenticate(w, r)
|
err := p.Authenticate(w, r)
|
||||||
|
@ -108,7 +106,7 @@ func (p *Proxy) SignOut(w http.ResponseWriter, r *http.Request) {
|
||||||
http.Redirect(w, r, fullURL.String(), http.StatusFound)
|
http.Redirect(w, r, fullURL.String(), http.StatusFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OAuthStart begins the IdentityProvider flow, encrypting the redirect url
|
// OAuthStart begins the authenticate flow, encrypting the redirect url
|
||||||
// in a request to the provider's sign in endpoint.
|
// in a request to the provider's sign in endpoint.
|
||||||
func (p *Proxy) OAuthStart(w http.ResponseWriter, r *http.Request) {
|
func (p *Proxy) OAuthStart(w http.ResponseWriter, r *http.Request) {
|
||||||
requestURI := r.URL.String()
|
requestURI := r.URL.String()
|
||||||
|
@ -139,10 +137,8 @@ func (p *Proxy) OAuthStart(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
signinURL := p.GetSignInURL(p.AuthenticateURL, callbackURL, encryptedState)
|
signinURL := p.GetSignInURL(p.AuthenticateURL, callbackURL, encryptedState)
|
||||||
log.FromRequest(r).Info().
|
log.FromRequest(r).Info().Str("SigninURL", signinURL.String()).Msg("proxy: oauth start")
|
||||||
Str("SigninURL", signinURL.String()).
|
// redirect the user to the authenticate provider along with the encrypted state which
|
||||||
Msg("redirecting to begin auth flow")
|
|
||||||
// redirect the user to the IdentityProvider provider along with the encrypted state which
|
|
||||||
// contains a redirect uri pointing back to the proxy
|
// contains a redirect uri pointing back to the proxy
|
||||||
http.Redirect(w, r, signinURL.String(), http.StatusFound)
|
http.Redirect(w, r, signinURL.String(), http.StatusFound)
|
||||||
}
|
}
|
||||||
|
@ -239,10 +235,10 @@ func (p *Proxy) AuthenticateOnly(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Proxy authenticates a request, either proxying the request if it is authenticated,
|
// Proxy authenticates a request, either proxying the request if it is authenticated,
|
||||||
// or starting the IdentityProvider service for validation if not.
|
// or starting the authenticate service for validation if not.
|
||||||
func (p *Proxy) Proxy(w http.ResponseWriter, r *http.Request) {
|
func (p *Proxy) Proxy(w http.ResponseWriter, r *http.Request) {
|
||||||
err := p.Authenticate(w, r)
|
err := p.Authenticate(w, r)
|
||||||
// If the IdentityProvider is not successful we proceed to start the OAuth Flow with
|
// If the authenticate is not successful we proceed to start the OAuth Flow with
|
||||||
// OAuthStart. If successful, we proceed to proxy to the configured upstream.
|
// OAuthStart. If successful, we proceed to proxy to the configured upstream.
|
||||||
if err != nil {
|
if err != nil {
|
||||||
switch err {
|
switch err {
|
||||||
|
@ -307,10 +303,7 @@ func (p *Proxy) Authenticate(w http.ResponseWriter, r *http.Request) (err error)
|
||||||
}
|
}
|
||||||
session.AccessToken = accessToken
|
session.AccessToken = accessToken
|
||||||
session.RefreshDeadline = expiry
|
session.RefreshDeadline = expiry
|
||||||
log.FromRequest(r).Info().
|
log.FromRequest(r).Info().Msg("proxy.Authenticate: refresh success")
|
||||||
Str("RefreshToken", session.RefreshToken).
|
|
||||||
Str("AccessToken", session.AccessToken).
|
|
||||||
Msg("proxy.Authenticate: refresh success")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = p.sessionStore.SaveSession(w, r, session)
|
err = p.sessionStore.SaveSession(w, r, session)
|
||||||
|
@ -340,24 +333,20 @@ func (p *Proxy) router(r *http.Request) (http.Handler, bool) {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRedirectURL returns the redirect url for a given Proxy,
|
// GetRedirectURL returns the redirect url for a single reverse proxy host. HTTPS is set explicitly.
|
||||||
// setting the scheme to be https if CookieSecure is true.
|
|
||||||
func (p *Proxy) GetRedirectURL(host string) *url.URL {
|
func (p *Proxy) GetRedirectURL(host string) *url.URL {
|
||||||
u := p.redirectURL
|
u := p.redirectURL
|
||||||
// Build redirect URI from request host
|
|
||||||
if u.Scheme == "" {
|
|
||||||
u.Scheme = "https"
|
u.Scheme = "https"
|
||||||
}
|
|
||||||
u.Host = host
|
u.Host = host
|
||||||
return u
|
return u
|
||||||
}
|
}
|
||||||
|
|
||||||
// signRedirectURL signs the redirect url string, given a timestamp, and returns it
|
// signRedirectURL takes a redirect url string and timestamp and returns the base64
|
||||||
|
// encoded HMAC result.
|
||||||
func (p *Proxy) signRedirectURL(rawRedirect string, timestamp time.Time) string {
|
func (p *Proxy) signRedirectURL(rawRedirect string, timestamp time.Time) string {
|
||||||
h := hmac.New(sha256.New, []byte(p.SharedKey))
|
data := []byte(fmt.Sprint(rawRedirect, timestamp.Unix()))
|
||||||
h.Write([]byte(rawRedirect))
|
h := cryptutil.Hash(p.SharedKey, data)
|
||||||
h.Write([]byte(fmt.Sprint(timestamp.Unix())))
|
return base64.URLEncoding.EncodeToString(h)
|
||||||
return base64.URLEncoding.EncodeToString(h.Sum(nil))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSignInURL with typical oauth parameters
|
// GetSignInURL with typical oauth parameters
|
||||||
|
|
|
@ -4,18 +4,20 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
|
"net/url"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/pomerium/pomerium/internal/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestProxy_RobotsTxt(t *testing.T) {
|
func TestProxy_RobotsTxt(t *testing.T) {
|
||||||
proxy := Proxy{}
|
proxy := Proxy{}
|
||||||
req, err := http.NewRequest("GET", "/robots.txt", nil)
|
req := httptest.NewRequest("GET", "/robots.txt", nil)
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
rr := httptest.NewRecorder()
|
rr := httptest.NewRecorder()
|
||||||
handler := http.HandlerFunc(proxy.RobotsTxt)
|
proxy.RobotsTxt(rr, req)
|
||||||
handler.ServeHTTP(rr, req)
|
|
||||||
if status := rr.Code; status != http.StatusOK {
|
if status := rr.Code; status != http.StatusOK {
|
||||||
t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusOK)
|
t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusOK)
|
||||||
}
|
}
|
||||||
|
@ -24,3 +26,308 @@ func TestProxy_RobotsTxt(t *testing.T) {
|
||||||
t.Errorf("handler returned wrong body: got %v want %v", rr.Body.String(), expected)
|
t.Errorf("handler returned wrong body: got %v want %v", rr.Body.String(), expected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestProxy_GetRedirectURL(t *testing.T) {
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
host string
|
||||||
|
want *url.URL
|
||||||
|
}{
|
||||||
|
{"google", "google.com", &url.URL{Scheme: "https", Host: "google.com", Path: "/.pomerium/callback"}},
|
||||||
|
{"pomerium", "pomerium.io", &url.URL{Scheme: "https", Host: "pomerium.io", Path: "/.pomerium/callback"}},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
p := &Proxy{redirectURL: &url.URL{Path: "/.pomerium/callback"}}
|
||||||
|
|
||||||
|
if got := p.GetRedirectURL(tt.host); !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Errorf("Proxy.GetRedirectURL() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProxy_signRedirectURL(t *testing.T) {
|
||||||
|
fixedDate := time.Date(2009, 11, 17, 20, 34, 58, 651387237, time.UTC)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
rawRedirect string
|
||||||
|
timestamp time.Time
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{"pomerium", "https://pomerium.io/.pomerium/callback", fixedDate, "wq3rAjRGN96RXS8TAzH-uxQTD0XgY_8ZYEKMiOLD5P4="},
|
||||||
|
{"google", "https://google.com/.pomerium/callback", fixedDate, "7EYHZObq167CuyuPm5CqOtkU4zg5dFeUCs7W7QOrgNQ="},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
p := &Proxy{}
|
||||||
|
if got := p.signRedirectURL(tt.rawRedirect, tt.timestamp); got != tt.want {
|
||||||
|
t.Errorf("Proxy.signRedirectURL() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProxy_GetSignOutURL(t *testing.T) {
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
authenticate string
|
||||||
|
redirect string
|
||||||
|
wantPrefix string
|
||||||
|
}{
|
||||||
|
{"without scheme", "auth.corp.pomerium.io", "hello.corp.pomerium.io", "https://auth.corp.pomerium.io/sign_out?redirect_uri=https%3A%2F%2Fhello.corp.pomerium.io"},
|
||||||
|
{"with scheme", "https://auth.corp.pomerium.io", "https://hello.corp.pomerium.io", "https://auth.corp.pomerium.io/sign_out?redirect_uri=https%3A%2F%2Fhello.corp.pomerium.io"},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
authenticateURL, _ := urlParse(tt.authenticate)
|
||||||
|
redirectURL, _ := urlParse(tt.redirect)
|
||||||
|
|
||||||
|
p := &Proxy{}
|
||||||
|
// signature is ignored as it is tested above. Avoids testing time.Now
|
||||||
|
if got := p.GetSignOutURL(authenticateURL, redirectURL); !strings.HasPrefix(got.String(), tt.wantPrefix) {
|
||||||
|
t.Errorf("Proxy.GetSignOutURL() = %v, wantPrefix %v", got.String(), tt.wantPrefix)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProxy_GetSignInURL(t *testing.T) {
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
authenticate string
|
||||||
|
redirect string
|
||||||
|
state string
|
||||||
|
|
||||||
|
wantPrefix string
|
||||||
|
}{
|
||||||
|
{"without scheme", "auth.corp.pomerium.io", "hello.corp.pomerium.io", "example_state", "https://auth.corp.pomerium.io/sign_in?redirect_uri=https%3A%2F%2Fhello.corp.pomerium.io&response_type=code&shared_secret=shared-secret"},
|
||||||
|
{"with scheme", "https://auth.corp.pomerium.io", "https://hello.corp.pomerium.io", "example_state", "https://auth.corp.pomerium.io/sign_in?redirect_uri=https%3A%2F%2Fhello.corp.pomerium.io&response_type=code&shared_secret=shared-secret"},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
p := &Proxy{SharedKey: "shared-secret"}
|
||||||
|
authenticateURL, _ := urlParse(tt.authenticate)
|
||||||
|
redirectURL, _ := urlParse(tt.redirect)
|
||||||
|
|
||||||
|
if got := p.GetSignInURL(authenticateURL, redirectURL, tt.state); !strings.HasPrefix(got.String(), tt.wantPrefix) {
|
||||||
|
t.Errorf("Proxy.GetSignOutURL() = %v, wantPrefix %v", got.String(), tt.wantPrefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProxy_Favicon(t *testing.T) {
|
||||||
|
proxy, err := New(testOptions())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
req := httptest.NewRequest("GET", "/favicon.ico", nil)
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
proxy.Favicon(rr, req)
|
||||||
|
if status := rr.Code; status != http.StatusNotFound {
|
||||||
|
t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusNotFound)
|
||||||
|
}
|
||||||
|
// todo(bdd) : good way of mocking auth then serving a simple favicon?
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProxy_Signout(t *testing.T) {
|
||||||
|
proxy, err := New(testOptions())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
req := httptest.NewRequest("GET", "/.pomerium/sign_out", nil)
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
proxy.SignOut(rr, req)
|
||||||
|
if status := rr.Code; status != http.StatusFound {
|
||||||
|
t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusFound)
|
||||||
|
}
|
||||||
|
// todo(bdd) : good way of mocking auth then serving a simple favicon?
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProxy_OAuthStart(t *testing.T) {
|
||||||
|
proxy, err := New(testOptions())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
req := httptest.NewRequest("GET", "/oauth-start", nil)
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
proxy.OAuthStart(rr, req)
|
||||||
|
// expect oauth redirect
|
||||||
|
if status := rr.Code; status != http.StatusFound {
|
||||||
|
t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusFound)
|
||||||
|
}
|
||||||
|
// expected url
|
||||||
|
expected := `<a href="https://sso-auth.corp.beyondperimeter.com/sign_in`
|
||||||
|
body := rr.Body.String()
|
||||||
|
if !strings.HasPrefix(body, expected) {
|
||||||
|
t.Errorf("handler returned unexpected body: got %v want %v", body, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func TestProxy_Handler(t *testing.T) {
|
||||||
|
proxy, err := New(testOptions())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
h := proxy.Handler()
|
||||||
|
if h == nil {
|
||||||
|
t.Error("handler cannot be nil")
|
||||||
|
}
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
mux.Handle("/", h)
|
||||||
|
req := httptest.NewRequest("GET", "/ping", nil)
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
mux.ServeHTTP(rr, req)
|
||||||
|
expected := version.UserAgent()
|
||||||
|
body := rr.Body.String()
|
||||||
|
if body != expected {
|
||||||
|
t.Errorf("handler returned unexpected body: got %v want %v", body, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// func (p *Proxy) OAuthCallback(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// err := r.ParseForm()
|
||||||
|
// if err != nil {
|
||||||
|
// log.FromRequest(r).Error().Err(err).Msg("failed parsing request form")
|
||||||
|
// httputil.ErrorResponse(w, r, err.Error(), http.StatusInternalServerError)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// errorString := r.Form.Get("error")
|
||||||
|
// if errorString != "" {
|
||||||
|
// httputil.ErrorResponse(w, r, errorString, http.StatusForbidden)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// // We begin the process of redeeming the code for an access token.
|
||||||
|
// session, err := p.AuthenticateRedeem(r.Form.Get("code"))
|
||||||
|
// if err != nil {
|
||||||
|
// log.FromRequest(r).Error().Err(err).Msg("error redeeming authorization code")
|
||||||
|
// httputil.ErrorResponse(w, r, "Internal error", http.StatusInternalServerError)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
// encryptedState := r.Form.Get("state")
|
||||||
|
// stateParameter := &StateParameter{}
|
||||||
|
// err = p.cipher.Unmarshal(encryptedState, stateParameter)
|
||||||
|
// if err != nil {
|
||||||
|
// log.FromRequest(r).Error().Err(err).Msg("could not unmarshal state")
|
||||||
|
// httputil.ErrorResponse(w, r, "Internal error", http.StatusInternalServerError)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
// c, err := p.csrfStore.GetCSRF(r)
|
||||||
|
// if err != nil {
|
||||||
|
// log.FromRequest(r).Error().Err(err).Msg("failed parsing csrf cookie")
|
||||||
|
// httputil.ErrorResponse(w, r, err.Error(), http.StatusBadRequest)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// p.csrfStore.ClearCSRF(w, r)
|
||||||
|
|
||||||
|
// encryptedCSRF := c.Value
|
||||||
|
// csrfParameter := &StateParameter{}
|
||||||
|
// err = p.cipher.Unmarshal(encryptedCSRF, csrfParameter)
|
||||||
|
// if err != nil {
|
||||||
|
// log.FromRequest(r).Error().Err(err).Msg("couldn't unmarshal CSRF")
|
||||||
|
// httputil.ErrorResponse(w, r, "Internal error", http.StatusInternalServerError)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if encryptedState == encryptedCSRF {
|
||||||
|
// log.FromRequest(r).Error().Msg("encrypted state and CSRF should not be equal")
|
||||||
|
// httputil.ErrorResponse(w, r, "Bad request", http.StatusBadRequest)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if !reflect.DeepEqual(stateParameter, csrfParameter) {
|
||||||
|
// log.FromRequest(r).Error().Msg("state and CSRF should be equal")
|
||||||
|
// httputil.ErrorResponse(w, r, "Bad request", http.StatusBadRequest)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // We store the session in a cookie and redirect the user back to the application
|
||||||
|
// err = p.sessionStore.SaveSession(w, r, session)
|
||||||
|
// if err != nil {
|
||||||
|
// log.FromRequest(r).Error().Msg("error saving session")
|
||||||
|
// httputil.ErrorResponse(w, r, "Error saving session", http.StatusInternalServerError)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
// log.FromRequest(r).Info().
|
||||||
|
// Str("code", r.Form.Get("code")).
|
||||||
|
// Str("state", r.Form.Get("state")).
|
||||||
|
// Str("RefreshToken", session.RefreshToken).
|
||||||
|
// Str("session", session.AccessToken).
|
||||||
|
// Str("RedirectURI", stateParameter.RedirectURI).
|
||||||
|
// Msg("session")
|
||||||
|
|
||||||
|
// // This is the redirect back to the original requested application
|
||||||
|
// http.Redirect(w, r, stateParameter.RedirectURI, http.StatusFound)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func TestProxy_OAuthCallback2(t *testing.T) {
|
||||||
|
// proxy, err := New(testOptions())
|
||||||
|
// if err != nil {
|
||||||
|
// t.Fatal(err)
|
||||||
|
// }
|
||||||
|
// testError := url.Values{"error": []string{"There was a bad error to handle"}}
|
||||||
|
// req := httptest.NewRequest("GET", "/oauth-callback", strings.NewReader(testError.Encode()))
|
||||||
|
// if err != nil {
|
||||||
|
// t.Fatal(err)
|
||||||
|
// }
|
||||||
|
// rr := httptest.NewRecorder()
|
||||||
|
// proxy.OAuthCallback)
|
||||||
|
// // expect oauth redirect
|
||||||
|
// if status := rr.Code; status != http.StatusInternalServerError {
|
||||||
|
// t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusInternalServerError)
|
||||||
|
// }
|
||||||
|
// // expected url
|
||||||
|
// // expected := `<a href="https://sso-auth.corp.beyondperimeter.com/sign_in`
|
||||||
|
// // body := rr.Body.String()
|
||||||
|
// // if !strings.HasPrefix(body, expected) {
|
||||||
|
// // t.Errorf("handler returned unexpected body: got %v want %v", body, expected)
|
||||||
|
// // }
|
||||||
|
// }
|
||||||
|
|
||||||
|
func TestProxy_OAuthCallback(t *testing.T) {
|
||||||
|
proxy, err := New(testOptions())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
method string
|
||||||
|
params map[string]string
|
||||||
|
wantCode int
|
||||||
|
}{
|
||||||
|
{"nil", http.MethodPost, nil, http.StatusInternalServerError},
|
||||||
|
{"error", http.MethodPost, map[string]string{"error": "some error"}, http.StatusForbidden},
|
||||||
|
{"state", http.MethodPost, map[string]string{"code": "code"}, http.StatusInternalServerError},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
|
||||||
|
req := httptest.NewRequest(tt.method, "/.pomerium/callback", nil)
|
||||||
|
q := req.URL.Query()
|
||||||
|
for k, v := range tt.params {
|
||||||
|
q.Add(k, v)
|
||||||
|
}
|
||||||
|
req.URL.RawQuery = q.Encode()
|
||||||
|
fmt.Println("OK OK OK OK")
|
||||||
|
|
||||||
|
fmt.Println(req.URL.String())
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
proxy.OAuthCallback(w, req)
|
||||||
|
if status := w.Code; status != tt.wantCode {
|
||||||
|
t.Errorf("handler returned wrong status code: got %v want %v", status, tt.wantCode)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue