mirror of
https://github.com/pomerium/pomerium.git
synced 2025-04-30 19:06:33 +02:00
- all: prefer `FormValues` to `ParseForm` with subsequent `Form.Get`s - all: refactor authentication stack to be checked by middleware, and accessible via request context. - all: replace http.ServeMux with gorilla/mux’s router - all: replace custom CSRF checks with gorilla/csrf middleware - authenticate: extract callback path as constant. - internal/config: implement stringer interface for policy - internal/cryptutil: add helper func `NewBase64Key` - internal/cryptutil: rename `GenerateKey` to `NewKey` - internal/cryptutil: rename `GenerateRandomString` to `NewRandomStringN` - internal/middleware: removed alice in favor of gorilla/mux - internal/sessions: remove unused `ValidateRedirectURI` and `ValidateClientSecret` - internal/sessions: replace custom CSRF with gorilla/csrf fork that supports custom handler protection - internal/urlutil: add `SignedRedirectURL` to create hmac'd URLs - internal/urlutil: add `ValidateURL` helper to parse URL options - internal/urlutil: add `GetAbsoluteURL` which takes a request and returns its absolute URL. - proxy: remove holdover state verification checks; we no longer are setting sessions in any proxy routes so we don’t need them. - proxy: replace un-named http.ServeMux with named domain routes. Signed-off-by: Bobby DeSimone <bobbydesimone@gmail.com>
203 lines
6.6 KiB
Go
203 lines
6.6 KiB
Go
package sessions
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"fmt"
|
|
"reflect"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/pomerium/pomerium/internal/cryptutil"
|
|
)
|
|
|
|
func TestStateSerialization(t *testing.T) {
|
|
secret := cryptutil.NewKey()
|
|
c, err := cryptutil.NewCipher(secret)
|
|
if err != nil {
|
|
t.Fatalf("expected to be able to create cipher: %v", err)
|
|
}
|
|
|
|
want := &State{
|
|
AccessToken: "token1234",
|
|
RefreshToken: "refresh4321",
|
|
RefreshDeadline: time.Now().Add(1 * time.Hour).Truncate(time.Second).UTC(),
|
|
Email: "user@domain.com",
|
|
User: "user",
|
|
}
|
|
|
|
ciphertext, err := MarshalSession(want, c)
|
|
if err != nil {
|
|
t.Fatalf("expected to be encode session: %v", err)
|
|
}
|
|
|
|
got, err := UnmarshalSession(ciphertext, c)
|
|
if err != nil {
|
|
t.Fatalf("expected to be decode session: %v", err)
|
|
}
|
|
|
|
if !reflect.DeepEqual(want, got) {
|
|
t.Logf("want: %#v", want)
|
|
t.Logf(" got: %#v", got)
|
|
t.Errorf("encoding and decoding session resulted in unexpected output")
|
|
}
|
|
}
|
|
|
|
func TestStateExpirations(t *testing.T) {
|
|
session := &State{
|
|
AccessToken: "token1234",
|
|
RefreshToken: "refresh4321",
|
|
RefreshDeadline: time.Now().Add(-1 * time.Hour),
|
|
Email: "user@domain.com",
|
|
User: "user",
|
|
}
|
|
if !session.Expired() {
|
|
t.Errorf("expected lifetime period to be expired")
|
|
}
|
|
|
|
}
|
|
|
|
func TestState_IssuedAt(t *testing.T) {
|
|
t.Parallel()
|
|
tests := []struct {
|
|
name string
|
|
IDToken string
|
|
want time.Time
|
|
wantErr bool
|
|
}{
|
|
{"simple parse", "eyJhbGciOiJSUzI1NiIsImtpZCI6IjA3YTA4MjgzOWYyZTcxYTliZjZjNTk2OTk2Yjk0NzM5Nzg1YWZkYzMiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJhenAiOiI4NTE4NzcwODIwNTktYmZna3BqMDlub29nN2FzM2dwYzN0N3I2bjlzamJnczYuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJhdWQiOiI4NTE4NzcwODIwNTktYmZna3BqMDlub29nN2FzM2dwYzN0N3I2bjlzamJnczYuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJzdWIiOiIxMTE0MzI2NTU5NzcyNzMxNTAzMDgiLCJoZCI6InBvbWVyaXVtLmlvIiwiZW1haWwiOiJiZGRAcG9tZXJpdW0uaW8iLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiYXRfaGFzaCI6IlkzYm1qV3R4US16OW1fM1RLb0dtRWciLCJuYW1lIjoiQm9iYnkgRGVTaW1vbmUiLCJwaWN0dXJlIjoiaHR0cHM6Ly9saDMuZ29vZ2xldXNlcmNvbnRlbnQuY29tLy1PX1BzRTlILTgzRS9BQUFBQUFBQUFBSS9BQUFBQUFBQUFBQS9BQ0hpM3JjQ0U0SFRLVDBhQk1pUFVfOEZfVXFOQ3F6RTBRL3M5Ni1jL3Bob3RvLmpwZyIsImdpdmVuX25hbWUiOiJCb2JieSIsImZhbWlseV9uYW1lIjoiRGVTaW1vbmUiLCJsb2NhbGUiOiJlbiIsImlhdCI6MTU1ODY3MjY4NywiZXhwIjoxNTU4Njc2Mjg3fQ.a4g8W94E7iVJhiIUmsNMwJssfx3Evi8sXeiXgXMC7kHNvftQ2CFU_LJ-dqZ5Jf61OXcrp26r7lUcTNENXuen9tyUWAiHvxk6OHTxZusdywTCY5xowpSZBO9PDWYrmmdvfhRbaKO6QVAUMkbKr1Tr8xqfoaYVXNZhERXhcVReDznI0ccbwCGrNx5oeqiL4eRdZY9eqFXi4Yfee0mkef9oyVPc2HvnpwcpM0eckYa_l_ZQChGjXVGBFIus_Ao33GbWDuc9gs-_Vp2ev4KUT2qWb7AXMCGDLx0tWI9umm7mCBi_7xnaanGKUYcVwcSrv45arllAAwzuNxO0BVw3oRWa5Q", time.Unix(1558672687, 0), false},
|
|
{"bad jwt", "x.x.x-x-x", time.Time{}, true},
|
|
{"malformed jwt", "x", time.Time{}, true},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
s := &State{IDToken: tt.IDToken}
|
|
got, err := s.IssuedAt()
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("State.IssuedAt() error = %v, wantErr %v", err, tt.wantErr)
|
|
return
|
|
}
|
|
if !reflect.DeepEqual(got, tt.want) {
|
|
t.Errorf("State.IssuedAt() = %v, want %v", got.Format(time.RFC3339), tt.want.Format(time.RFC3339))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestState_Impersonating(t *testing.T) {
|
|
t.Parallel()
|
|
tests := []struct {
|
|
name string
|
|
Email string
|
|
Groups []string
|
|
ImpersonateEmail string
|
|
ImpersonateGroups []string
|
|
want bool
|
|
wantResponseEmail string
|
|
wantResponseGroups string
|
|
}{
|
|
{"impersonating", "actual@user.com", []string{"actual-group"}, "impersonating@user.com", []string{"impersonating-group"}, true, "impersonating@user.com", "impersonating-group"},
|
|
{"not impersonating", "actual@user.com", []string{"actual-group"}, "", []string{}, false, "actual@user.com", "actual-group"},
|
|
{"impersonating user only", "actual@user.com", []string{"actual-group"}, "impersonating@user.com", []string{}, true, "impersonating@user.com", "actual-group"},
|
|
{"impersonating group only", "actual@user.com", []string{"actual-group"}, "", []string{"impersonating-group"}, true, "actual@user.com", "impersonating-group"},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
s := &State{
|
|
Email: tt.Email,
|
|
Groups: tt.Groups,
|
|
ImpersonateEmail: tt.ImpersonateEmail,
|
|
ImpersonateGroups: tt.ImpersonateGroups,
|
|
}
|
|
if got := s.Impersonating(); got != tt.want {
|
|
t.Errorf("State.Impersonating() = %v, want %v", got, tt.want)
|
|
}
|
|
if gotEmail := s.RequestEmail(); gotEmail != tt.wantResponseEmail {
|
|
t.Errorf("State.RequestEmail() = %v, want %v", gotEmail, tt.wantResponseEmail)
|
|
}
|
|
if gotGroups := s.RequestGroups(); gotGroups != tt.wantResponseGroups {
|
|
t.Errorf("State.v() = %v, want %v", gotGroups, tt.wantResponseGroups)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMarshalSession(t *testing.T) {
|
|
secret := cryptutil.NewKey()
|
|
c, err := cryptutil.NewCipher(secret)
|
|
if err != nil {
|
|
t.Fatalf("expected to be able to create cipher: %v", err)
|
|
}
|
|
hugeString := make([]byte, 4097)
|
|
if _, err := rand.Read(hugeString); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
s *State
|
|
wantErr bool
|
|
}{
|
|
{"simple", &State{}, false},
|
|
{"too big", &State{AccessToken: fmt.Sprintf("%x", hugeString)}, false},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
in, err := MarshalSession(tt.s, c)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("MarshalSession() error = %v, wantErr %v", err, tt.wantErr)
|
|
return
|
|
}
|
|
if err == nil {
|
|
out, err := UnmarshalSession(in, c)
|
|
if err != nil {
|
|
t.Fatalf("expected to be decode session: %v", err)
|
|
}
|
|
if diff := cmp.Diff(tt.s, out); diff != "" {
|
|
t.Errorf("MarshalSession() = %s", diff)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestState_Valid(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
name string
|
|
RefreshDeadline time.Time
|
|
wantErr bool
|
|
}{
|
|
{" good", time.Now().Add(10 * time.Second), false},
|
|
{" expired", time.Now().Add(-10 * time.Second), true},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
s := &State{
|
|
RefreshDeadline: tt.RefreshDeadline,
|
|
}
|
|
if err := s.Valid(); (err != nil) != tt.wantErr {
|
|
t.Errorf("State.Valid() error = %v, wantErr %v", err, tt.wantErr)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestState_ForceRefresh(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
RefreshDeadline time.Time
|
|
}{
|
|
{"good", time.Now().Truncate(time.Second)},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
s := &State{
|
|
RefreshDeadline: tt.RefreshDeadline,
|
|
}
|
|
s.ForceRefresh()
|
|
if s.RefreshDeadline != tt.RefreshDeadline {
|
|
t.Errorf("refresh deadline not updated")
|
|
}
|
|
})
|
|
}
|
|
}
|