Newer
Older
// Package conformance provides conformance tests for storage implementations.
package conformance
jose "gopkg.in/square/go-jose.v2"
"golang.org/x/crypto/bcrypt"
"github.com/dexidp/dex/storage"
"github.com/kylelemons/godebug/pretty"
// ensure that values being tested on never expire.
var neverExpire = time.Now().UTC().Add(time.Hour * 24 * 365 * 100)
type subTest struct {
name string
run func(t *testing.T, s storage.Storage)
}
func runTests(t *testing.T, newStorage func() storage.Storage, tests []subTest) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
s := newStorage()
test.run(t, s)
s.Close()
})
}
}
// RunTests runs a set of conformance tests against a storage. newStorage should
// return an initialized but empty storage. The storage will be closed at the
// end of each test run.
func RunTests(t *testing.T, newStorage func() storage.Storage) {
runTests(t, newStorage, []subTest{
{"AuthCodeCRUD", testAuthCodeCRUD},
{"AuthRequestCRUD", testAuthRequestCRUD},
{"ClientCRUD", testClientCRUD},
{"RefreshTokenCRUD", testRefreshTokenCRUD},
{"PasswordCRUD", testPasswordCRUD},
{"KeysCRUD", testKeysCRUD},
{"OfflineSessionCRUD", testOfflineSessionCRUD},
{"ConnectorCRUD", testConnectorCRUD},
{"GarbageCollection", testGC},
{"TimezoneSupport", testTimezones},
func mustLoadJWK(b string) *jose.JSONWebKey {
var jwt jose.JSONWebKey
if err := jwt.UnmarshalJSON([]byte(b)); err != nil {
panic(err)
}
return &jwt
}
func mustBeErrNotFound(t *testing.T, kind string, err error) {
switch {
case err == nil:
t.Errorf("deleting non-existent %s should return an error", kind)
case err != storage.ErrNotFound:
t.Errorf("deleting %s expected storage.ErrNotFound, got %v", kind, err)
}
}
func mustBeErrAlreadyExists(t *testing.T, kind string, err error) {
switch {
case err == nil:
t.Errorf("attempting to create an existing %s should return an error", kind)
case err != storage.ErrAlreadyExists:
t.Errorf("creating an existing %s expected storage.ErrAlreadyExists, got %v", kind, err)
}
}
func testAuthRequestCRUD(t *testing.T, s storage.Storage) {
rithu john
committed
a1 := storage.AuthRequest{
rithu john
committed
ClientID: "client1",
ResponseTypes: []string{"code"},
Scopes: []string{"openid", "email"},
RedirectURI: "https://localhost:80/callback",
Nonce: "foo",
State: "bar",
ForceApprovalPrompt: true,
LoggedIn: true,
Expiry: neverExpire,
ConnectorID: "ldap",
ConnectorData: []byte(`{"some":"data"}`),
Claims: storage.Claims{
UserID: "1",
Username: "jane",
Email: "jane.doe@example.com",
EmailVerified: true,
Groups: []string{"a", "b"},
},
rithu john
committed
if err := s.CreateAuthRequest(a1); err != nil {
// Attempt to create same AuthRequest twice.
rithu john
committed
err := s.CreateAuthRequest(a1)
mustBeErrAlreadyExists(t, "auth request", err)
rithu john
committed
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
a2 := storage.AuthRequest{
ID: storage.NewID(),
ClientID: "client2",
ResponseTypes: []string{"code"},
Scopes: []string{"openid", "email"},
RedirectURI: "https://localhost:80/callback",
Nonce: "bar",
State: "foo",
ForceApprovalPrompt: true,
LoggedIn: true,
Expiry: neverExpire,
ConnectorID: "ldap",
ConnectorData: []byte(`{"some":"data"}`),
Claims: storage.Claims{
UserID: "2",
Username: "john",
Email: "john.doe@example.com",
EmailVerified: true,
Groups: []string{"a"},
},
}
if err := s.CreateAuthRequest(a2); err != nil {
t.Fatalf("failed creating auth request: %v", err)
}
if err := s.UpdateAuthRequest(a1.ID, func(old storage.AuthRequest) (storage.AuthRequest, error) {
old.Claims = identity
old.ConnectorID = "connID"
return old, nil
}); err != nil {
t.Fatalf("failed to update auth request: %v", err)
}
rithu john
committed
got, err := s.GetAuthRequest(a1.ID)
if err != nil {
t.Fatalf("failed to get auth req: %v", err)
}
if !reflect.DeepEqual(got.Claims, identity) {
t.Fatalf("update failed, wanted identity=%#v got %#v", identity, got.Claims)
rithu john
committed
if err := s.DeleteAuthRequest(a1.ID); err != nil {
t.Fatalf("failed to delete auth request: %v", err)
}
if err := s.DeleteAuthRequest(a2.ID); err != nil {
t.Fatalf("failed to delete auth request: %v", err)
}
func testAuthCodeCRUD(t *testing.T, s storage.Storage) {
rithu john
committed
a1 := storage.AuthCode{
rithu john
committed
ClientID: "client1",
RedirectURI: "https://localhost:80/callback",
Nonce: "foobar",
Scopes: []string{"openid", "email"},
Expiry: neverExpire,
ConnectorID: "ldap",
ConnectorData: []byte(`{"some":"data"}`),
Claims: storage.Claims{
UserID: "1",
Username: "jane",
Email: "jane.doe@example.com",
EmailVerified: true,
Groups: []string{"a", "b"},
},
}
rithu john
committed
if err := s.CreateAuthCode(a1); err != nil {
t.Fatalf("failed creating auth code: %v", err)
}
rithu john
committed
a2 := storage.AuthCode{
ID: storage.NewID(),
ClientID: "client2",
RedirectURI: "https://localhost:80/callback",
Nonce: "foobar",
Scopes: []string{"openid", "email"},
Expiry: neverExpire,
ConnectorID: "ldap",
ConnectorData: []byte(`{"some":"data"}`),
Claims: storage.Claims{
UserID: "2",
Username: "john",
Email: "john.doe@example.com",
EmailVerified: true,
Groups: []string{"a"},
},
}
// Attempt to create same AuthCode twice.
rithu john
committed
err := s.CreateAuthCode(a1)
mustBeErrAlreadyExists(t, "auth code", err)
rithu john
committed
if err := s.CreateAuthCode(a2); err != nil {
t.Fatalf("failed creating auth code: %v", err)
}
got, err := s.GetAuthCode(a1.ID)
if err != nil {
t.Fatalf("failed to get auth req: %v", err)
}
rithu john
committed
if a1.Expiry.Unix() != got.Expiry.Unix() {
t.Errorf("auth code expiry did not match want=%s vs got=%s", a1.Expiry, got.Expiry)
rithu john
committed
got.Expiry = a1.Expiry // time fields do not compare well
if diff := pretty.Compare(a1, got); diff != "" {
t.Errorf("auth code retrieved from storage did not match: %s", diff)
}
rithu john
committed
if err := s.DeleteAuthCode(a1.ID); err != nil {
t.Fatalf("delete auth code: %v", err)
}
if err := s.DeleteAuthCode(a2.ID); err != nil {
t.Fatalf("delete auth code: %v", err)
}
rithu john
committed
_, err = s.GetAuthCode(a1.ID)
mustBeErrNotFound(t, "auth code", err)
}
func testClientCRUD(t *testing.T, s storage.Storage) {
rithu john
committed
id1 := storage.NewID()
c1 := storage.Client{
ID: id1,
Secret: "foobar",
RedirectURIs: []string{"foo://bar.com/", "https://auth.example.com"},
Name: "dex client",
LogoURL: "https://goo.gl/JIyzIC",
}
rithu john
committed
err := s.DeleteClient(id1)
mustBeErrNotFound(t, "client", err)
rithu john
committed
if err := s.CreateClient(c1); err != nil {
t.Fatalf("create client: %v", err)
}
// Attempt to create same Client twice.
rithu john
committed
err = s.CreateClient(c1)
mustBeErrAlreadyExists(t, "client", err)
rithu john
committed
id2 := storage.NewID()
c2 := storage.Client{
ID: id2,
Secret: "barfoo",
RedirectURIs: []string{"foo://bar.com/", "https://auth.example.com"},
Name: "dex client",
LogoURL: "https://goo.gl/JIyzIC",
}
if err := s.CreateClient(c2); err != nil {
t.Fatalf("create client: %v", err)
}
getAndCompare := func(id string, want storage.Client) {
rithu john
committed
gc, err := s.GetClient(id1)
if err != nil {
t.Errorf("get client: %v", err)
return
}
if diff := pretty.Compare(want, gc); diff != "" {
t.Errorf("client retrieved from storage did not match: %s", diff)
}
}
rithu john
committed
getAndCompare(id1, c1)
rithu john
committed
err = s.UpdateClient(id1, func(old storage.Client) (storage.Client, error) {
old.Secret = newSecret
return old, nil
})
if err != nil {
t.Errorf("update client: %v", err)
}
rithu john
committed
c1.Secret = newSecret
getAndCompare(id1, c1)
if err := s.DeleteClient(id1); err != nil {
t.Fatalf("delete client: %v", err)
}
rithu john
committed
if err := s.DeleteClient(id2); err != nil {
t.Fatalf("delete client: %v", err)
}
rithu john
committed
_, err = s.GetClient(id1)
mustBeErrNotFound(t, "client", err)
}
func testRefreshTokenCRUD(t *testing.T, s storage.Storage) {
id := storage.NewID()
refresh := storage.RefreshToken{
ID: id,
Token: "bar",
Nonce: "foo",
ClientID: "client_id",
ConnectorID: "client_secret",
Scopes: []string{"openid", "email", "profile"},
CreatedAt: time.Now().UTC().Round(time.Millisecond),
LastUsed: time.Now().UTC().Round(time.Millisecond),
Claims: storage.Claims{
UserID: "1",
Username: "jane",
Email: "jane.doe@example.com",
EmailVerified: true,
Groups: []string{"a", "b"},
},
ConnectorData: []byte(`{"some":"data"}`),
}
if err := s.CreateRefresh(refresh); err != nil {
t.Fatalf("create refresh token: %v", err)
}
// Attempt to create same Refresh Token twice.
err := s.CreateRefresh(refresh)
mustBeErrAlreadyExists(t, "refresh token", err)
getAndCompare := func(id string, want storage.RefreshToken) {
gr, err := s.GetRefresh(id)
if err != nil {
t.Errorf("get refresh: %v", err)
return
}
if diff := pretty.Compare(gr.CreatedAt.UnixNano(), gr.CreatedAt.UnixNano()); diff != "" {
t.Errorf("refresh token created timestamp retrieved from storage did not match: %s", diff)
}
if diff := pretty.Compare(gr.LastUsed.UnixNano(), gr.LastUsed.UnixNano()); diff != "" {
t.Errorf("refresh token last used timestamp retrieved from storage did not match: %s", diff)
}
gr.CreatedAt = time.Time{}
gr.LastUsed = time.Time{}
want.CreatedAt = time.Time{}
want.LastUsed = time.Time{}
if diff := pretty.Compare(want, gr); diff != "" {
t.Errorf("refresh token retrieved from storage did not match: %s", diff)
}
getAndCompare(id, refresh)
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
id2 := storage.NewID()
refresh2 := storage.RefreshToken{
ID: id2,
Token: "bar_2",
Nonce: "foo_2",
ClientID: "client_id_2",
ConnectorID: "client_secret",
Scopes: []string{"openid", "email", "profile"},
CreatedAt: time.Now().UTC().Round(time.Millisecond),
LastUsed: time.Now().UTC().Round(time.Millisecond),
Claims: storage.Claims{
UserID: "2",
Username: "john",
Email: "john.doe@example.com",
EmailVerified: true,
Groups: []string{"a", "b"},
},
ConnectorData: []byte(`{"some":"data"}`),
}
if err := s.CreateRefresh(refresh2); err != nil {
t.Fatalf("create second refresh token: %v", err)
}
getAndCompare(id2, refresh2)
updatedAt := time.Now().UTC().Round(time.Millisecond)
updater := func(r storage.RefreshToken) (storage.RefreshToken, error) {
r.Token = "spam"
r.LastUsed = updatedAt
return r, nil
}
if err := s.UpdateRefreshToken(id, updater); err != nil {
t.Errorf("failed to udpate refresh token: %v", err)
}
refresh.Token = "spam"
refresh.LastUsed = updatedAt
getAndCompare(id, refresh)
// Ensure that updating the first token doesn't impact the second. Issue #847.
getAndCompare(id2, refresh2)
if err := s.DeleteRefresh(id); err != nil {
t.Fatalf("failed to delete refresh request: %v", err)
}
rithu john
committed
if err := s.DeleteRefresh(id2); err != nil {
t.Fatalf("failed to delete refresh request: %v", err)
}
_, err = s.GetRefresh(id)
mustBeErrNotFound(t, "refresh token", err)
type byEmail []storage.Password
func (n byEmail) Len() int { return len(n) }
func (n byEmail) Less(i, j int) bool { return n[i].Email < n[j].Email }
func (n byEmail) Swap(i, j int) { n[i], n[j] = n[j], n[i] }
func testPasswordCRUD(t *testing.T, s storage.Storage) {
// Use bcrypt.MinCost to keep the tests short.
rithu john
committed
passwordHash1, err := bcrypt.GenerateFromPassword([]byte("secret"), bcrypt.MinCost)
if err != nil {
t.Fatal(err)
}
rithu john
committed
password1 := storage.Password{
Email: "jane@example.com",
rithu john
committed
Hash: passwordHash1,
Username: "jane",
UserID: "foobar",
}
rithu john
committed
if err := s.CreatePassword(password1); err != nil {
t.Fatalf("create password token: %v", err)
}
// Attempt to create same Password twice.
rithu john
committed
err = s.CreatePassword(password1)
mustBeErrAlreadyExists(t, "password", err)
rithu john
committed
passwordHash2, err := bcrypt.GenerateFromPassword([]byte("password"), bcrypt.MinCost)
if err != nil {
t.Fatal(err)
}
password2 := storage.Password{
Email: "john@example.com",
Hash: passwordHash2,
Username: "john",
UserID: "barfoo",
}
if err := s.CreatePassword(password2); err != nil {
t.Fatalf("create password token: %v", err)
}
getAndCompare := func(id string, want storage.Password) {
gr, err := s.GetPassword(id)
if err != nil {
t.Errorf("get password %q: %v", id, err)
return
}
if diff := pretty.Compare(want, gr); diff != "" {
t.Errorf("password retrieved from storage did not match: %s", diff)
}
}
rithu john
committed
getAndCompare("jane@example.com", password1)
getAndCompare("JANE@example.com", password1) // Emails should be case insensitive
rithu john
committed
if err := s.UpdatePassword(password1.Email, func(old storage.Password) (storage.Password, error) {
old.Username = "jane doe"
return old, nil
}); err != nil {
t.Fatalf("failed to update auth request: %v", err)
}
rithu john
committed
password1.Username = "jane doe"
getAndCompare("jane@example.com", password1)
var passwordList []storage.Password
rithu john
committed
passwordList = append(passwordList, password1, password2)
listAndCompare := func(want []storage.Password) {
passwords, err := s.ListPasswords()
if err != nil {
t.Errorf("list password: %v", err)
return
}
sort.Sort(byEmail(want))
sort.Sort(byEmail(passwords))
if diff := pretty.Compare(want, passwords); diff != "" {
t.Errorf("password list retrieved from storage did not match: %s", diff)
}
}
listAndCompare(passwordList)
rithu john
committed
if err := s.DeletePassword(password1.Email); err != nil {
t.Fatalf("failed to delete password: %v", err)
}
if err := s.DeletePassword(password2.Email); err != nil {
t.Fatalf("failed to delete password: %v", err)
}
rithu john
committed
_, err = s.GetPassword(password1.Email)
mustBeErrNotFound(t, "password", err)
func testOfflineSessionCRUD(t *testing.T, s storage.Storage) {
rithu john
committed
userID1 := storage.NewID()
session1 := storage.OfflineSessions{
UserID: userID1,
ConnID: "Conn1",
Refresh: make(map[string]*storage.RefreshTokenRef),
}
// Creating an OfflineSession with an empty Refresh list to ensure that
// an empty map is translated as expected by the storage.
rithu john
committed
if err := s.CreateOfflineSessions(session1); err != nil {
t.Fatalf("create offline session with UserID = %s: %v", session1.UserID, err)
// Attempt to create same OfflineSession twice.
rithu john
committed
err := s.CreateOfflineSessions(session1)
mustBeErrAlreadyExists(t, "offline session", err)
rithu john
committed
userID2 := storage.NewID()
session2 := storage.OfflineSessions{
UserID: userID2,
ConnID: "Conn2",
Refresh: make(map[string]*storage.RefreshTokenRef),
}
if err := s.CreateOfflineSessions(session2); err != nil {
t.Fatalf("create offline session with UserID = %s: %v", session2.UserID, err)
}
getAndCompare := func(userID string, connID string, want storage.OfflineSessions) {
gr, err := s.GetOfflineSessions(userID, connID)
if err != nil {
t.Errorf("get offline session: %v", err)
return
}
if diff := pretty.Compare(want, gr); diff != "" {
t.Errorf("offline session retrieved from storage did not match: %s", diff)
}
}
rithu john
committed
getAndCompare(userID1, "Conn1", session1)
id := storage.NewID()
tokenRef := storage.RefreshTokenRef{
ID: id,
ClientID: "client_id",
CreatedAt: time.Now().UTC().Round(time.Millisecond),
LastUsed: time.Now().UTC().Round(time.Millisecond),
}
rithu john
committed
session1.Refresh[tokenRef.ClientID] = &tokenRef
rithu john
committed
if err := s.UpdateOfflineSessions(session1.UserID, session1.ConnID, func(old storage.OfflineSessions) (storage.OfflineSessions, error) {
old.Refresh[tokenRef.ClientID] = &tokenRef
return old, nil
}); err != nil {
t.Fatalf("failed to update offline session: %v", err)
}
rithu john
committed
getAndCompare(userID1, "Conn1", session1)
rithu john
committed
if err := s.DeleteOfflineSessions(session1.UserID, session1.ConnID); err != nil {
t.Fatalf("failed to delete offline session: %v", err)
}
rithu john
committed
if err := s.DeleteOfflineSessions(session2.UserID, session2.ConnID); err != nil {
t.Fatalf("failed to delete offline session: %v", err)
}
rithu john
committed
_, err = s.GetOfflineSessions(session1.UserID, session1.ConnID)
mustBeErrNotFound(t, "offline session", err)
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
func testConnectorCRUD(t *testing.T, s storage.Storage) {
id1 := storage.NewID()
config1 := []byte(`{"issuer": "https://accounts.google.com"}`)
c1 := storage.Connector{
ID: id1,
Type: "Default",
Name: "Default",
ResourceVersion: "1",
Config: config1,
}
if err := s.CreateConnector(c1); err != nil {
t.Fatalf("create connector with ID = %s: %v", c1.ID, err)
}
// Attempt to create same Connector twice.
err := s.CreateConnector(c1)
mustBeErrAlreadyExists(t, "connector", err)
id2 := storage.NewID()
config2 := []byte(`{"redirectURIi": "http://127.0.0.1:5556/dex/callback"}`)
c2 := storage.Connector{
ID: id2,
Type: "Mock",
Name: "Mock",
ResourceVersion: "2",
Config: config2,
}
if err := s.CreateConnector(c2); err != nil {
t.Fatalf("create connector with ID = %s: %v", c2.ID, err)
}
getAndCompare := func(id string, want storage.Connector) {
gr, err := s.GetConnector(id)
if err != nil {
t.Errorf("get connector: %v", err)
return
}
if diff := pretty.Compare(want, gr); diff != "" {
t.Errorf("connector retrieved from storage did not match: %s", diff)
}
}
getAndCompare(id1, c1)
if err := s.UpdateConnector(c1.ID, func(old storage.Connector) (storage.Connector, error) {
old.Type = "oidc"
return old, nil
}); err != nil {
t.Fatalf("failed to update Connector: %v", err)
}
c1.Type = "oidc"
getAndCompare(id1, c1)
connectorList := []storage.Connector{c1, c2}
listAndCompare := func(want []storage.Connector) {
connectors, err := s.ListConnectors()
if err != nil {
t.Errorf("list connectors: %v", err)
return
}
sort.Slice(connectors, func(i, j int) bool {
return connectors[i].Name < connectors[j].Name
})
if diff := pretty.Compare(want, connectors); diff != "" {
t.Errorf("password list retrieved from storage did not match: %s", diff)
}
if err := s.DeleteConnector(c1.ID); err != nil {
t.Fatalf("failed to delete connector: %v", err)
}
if err := s.DeleteConnector(c2.ID); err != nil {
t.Fatalf("failed to delete connector: %v", err)
}
_, err = s.GetConnector(c1.ID)
mustBeErrNotFound(t, "connector", err)
}
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
func testKeysCRUD(t *testing.T, s storage.Storage) {
updateAndCompare := func(k storage.Keys) {
err := s.UpdateKeys(func(oldKeys storage.Keys) (storage.Keys, error) {
return k, nil
})
if err != nil {
t.Errorf("failed to update keys: %v", err)
return
}
if got, err := s.GetKeys(); err != nil {
t.Errorf("failed to get keys: %v", err)
} else {
got.NextRotation = got.NextRotation.UTC()
if diff := pretty.Compare(k, got); diff != "" {
t.Errorf("got keys did not equal expected: %s", diff)
}
}
}
// Postgres isn't as accurate with nano seconds as we'd like
n := time.Now().UTC().Round(time.Second)
keys1 := storage.Keys{
SigningKey: jsonWebKeys[0].Private,
SigningKeyPub: jsonWebKeys[0].Public,
NextRotation: n,
}
keys2 := storage.Keys{
SigningKey: jsonWebKeys[2].Private,
SigningKeyPub: jsonWebKeys[2].Public,
NextRotation: n.Add(time.Hour),
VerificationKeys: []storage.VerificationKey{
{
PublicKey: jsonWebKeys[0].Public,
Expiry: n.Add(time.Hour),
},
{
PublicKey: jsonWebKeys[1].Public,
Expiry: n.Add(time.Hour * 2),
},
},
}
updateAndCompare(keys1)
updateAndCompare(keys2)
}
func testGC(t *testing.T, s storage.Storage) {
est, err := time.LoadLocation("America/New_York")
if err != nil {
t.Fatal(err)
}
pst, err := time.LoadLocation("America/Los_Angeles")
if err != nil {
t.Fatal(err)
}
expiry := time.Now().In(est)
c := storage.AuthCode{
ID: storage.NewID(),
ClientID: "foobar",
RedirectURI: "https://localhost:80/callback",
Nonce: "foobar",
Scopes: []string{"openid", "email"},
ConnectorID: "ldap",
ConnectorData: []byte(`{"some":"data"}`),
Claims: storage.Claims{
UserID: "1",
Username: "jane",
Email: "jane.doe@example.com",
EmailVerified: true,
Groups: []string{"a", "b"},
},
}
if err := s.CreateAuthCode(c); err != nil {
t.Fatalf("failed creating auth code: %v", err)
}
for _, tz := range []*time.Location{time.UTC, est, pst} {
result, err := s.GarbageCollect(expiry.Add(-time.Hour).In(tz))
if err != nil {
t.Errorf("garbage collection failed: %v", err)
} else {
if result.AuthCodes != 0 || result.AuthRequests != 0 {
t.Errorf("expected no garbage collection results, got %#v", result)
}
}
if _, err := s.GetAuthCode(c.ID); err != nil {
t.Errorf("expected to be able to get auth code after GC: %v", err)
}
if r, err := s.GarbageCollect(expiry.Add(time.Hour)); err != nil {
t.Errorf("garbage collection failed: %v", err)
} else if r.AuthCodes != 1 {
t.Errorf("expected to garbage collect 1 objects, got %d", r.AuthCodes)
}
if _, err := s.GetAuthCode(c.ID); err == nil {
t.Errorf("expected auth code to be GC'd")
} else if err != storage.ErrNotFound {
t.Errorf("expected storage.ErrNotFound, got %v", err)
}
a := storage.AuthRequest{
ID: storage.NewID(),
ClientID: "foobar",
ResponseTypes: []string{"code"},
Scopes: []string{"openid", "email"},
RedirectURI: "https://localhost:80/callback",
Nonce: "foo",
State: "bar",
ForceApprovalPrompt: true,
LoggedIn: true,
ConnectorID: "ldap",
ConnectorData: []byte(`{"some":"data"}`),
Claims: storage.Claims{
UserID: "1",
Username: "jane",
Email: "jane.doe@example.com",
EmailVerified: true,
Groups: []string{"a", "b"},
},
}
if err := s.CreateAuthRequest(a); err != nil {
t.Fatalf("failed creating auth request: %v", err)
}
for _, tz := range []*time.Location{time.UTC, est, pst} {
result, err := s.GarbageCollect(expiry.Add(-time.Hour).In(tz))
if err != nil {
t.Errorf("garbage collection failed: %v", err)
} else {
if result.AuthCodes != 0 || result.AuthRequests != 0 {
t.Errorf("expected no garbage collection results, got %#v", result)
}
}
if _, err := s.GetAuthRequest(a.ID); err != nil {
t.Errorf("expected to be able to get auth code after GC: %v", err)
}
if r, err := s.GarbageCollect(expiry.Add(time.Hour)); err != nil {
t.Errorf("garbage collection failed: %v", err)
} else if r.AuthRequests != 1 {
t.Errorf("expected to garbage collect 1 objects, got %d", r.AuthRequests)
}
if _, err := s.GetAuthRequest(a.ID); err == nil {
t.Errorf("expected auth code to be GC'd")
} else if err != storage.ErrNotFound {
t.Errorf("expected storage.ErrNotFound, got %v", err)
}
}
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
// testTimezones tests that backends either fully support timezones or
// do the correct standardization.
func testTimezones(t *testing.T, s storage.Storage) {
est, err := time.LoadLocation("America/New_York")
if err != nil {
t.Fatal(err)
}
// Create an expiry with timezone info. Only expect backends to be
// accurate to the millisecond
expiry := time.Now().In(est).Round(time.Millisecond)
c := storage.AuthCode{
ID: storage.NewID(),
ClientID: "foobar",
RedirectURI: "https://localhost:80/callback",
Nonce: "foobar",
Scopes: []string{"openid", "email"},
Expiry: expiry,
ConnectorID: "ldap",
ConnectorData: []byte(`{"some":"data"}`),
Claims: storage.Claims{
UserID: "1",
Username: "jane",
Email: "jane.doe@example.com",
EmailVerified: true,
Groups: []string{"a", "b"},
},
}
if err := s.CreateAuthCode(c); err != nil {
t.Fatalf("failed creating auth code: %v", err)
}
got, err := s.GetAuthCode(c.ID)
if err != nil {
t.Fatalf("failed to get auth code: %v", err)
}
// Ensure that if the resulting time is converted to the same
// timezone, it's the same value. We DO NOT expect timezones
// to be preserved.
gotTime := got.Expiry.In(est)
wantTime := expiry
if !gotTime.Equal(wantTime) {
t.Fatalf("expected expiry %v got %v", wantTime, gotTime)
}
}