|
|
|
|
@ -4,6 +4,9 @@ package conformance
|
|
|
|
|
|
|
|
|
|
import ( |
|
|
|
|
"testing" |
|
|
|
|
"time" |
|
|
|
|
|
|
|
|
|
"golang.org/x/crypto/bcrypt" |
|
|
|
|
|
|
|
|
|
"github.com/coreos/dex/storage" |
|
|
|
|
) |
|
|
|
|
@ -17,7 +20,10 @@ import (
|
|
|
|
|
// conformance.
|
|
|
|
|
func RunTransactionTests(t *testing.T, newStorage func() storage.Storage) { |
|
|
|
|
runTests(t, newStorage, []subTest{ |
|
|
|
|
{"AuthRequestConcurrentUpdate", testAuthRequestConcurrentUpdate}, |
|
|
|
|
{"ClientConcurrentUpdate", testClientConcurrentUpdate}, |
|
|
|
|
{"PasswordConcurrentUpdate", testPasswordConcurrentUpdate}, |
|
|
|
|
{"KeysConcurrentUpdate", testKeysConcurrentUpdate}, |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -45,10 +51,124 @@ func testClientConcurrentUpdate(t *testing.T, s storage.Storage) {
|
|
|
|
|
return old, nil |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
t.Logf("update1: %v", err1) |
|
|
|
|
t.Logf("update2: %v", err2) |
|
|
|
|
if (err1 == nil) == (err2 == nil) { |
|
|
|
|
t.Errorf("update client:\nupdate1: %v\nupdate2: %v\n", err1, err2) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func testAuthRequestConcurrentUpdate(t *testing.T, s storage.Storage) { |
|
|
|
|
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, |
|
|
|
|
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"}, |
|
|
|
|
}, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if err := s.CreateAuthRequest(a); err != nil { |
|
|
|
|
t.Fatalf("failed creating auth request: %v", err) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var err1, err2 error |
|
|
|
|
|
|
|
|
|
err1 = s.UpdateAuthRequest(a.ID, func(old storage.AuthRequest) (storage.AuthRequest, error) { |
|
|
|
|
old.State = "state 1" |
|
|
|
|
err2 = s.UpdateAuthRequest(a.ID, func(old storage.AuthRequest) (storage.AuthRequest, error) { |
|
|
|
|
old.State = "state 2" |
|
|
|
|
return old, nil |
|
|
|
|
}) |
|
|
|
|
return old, nil |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
if (err1 == nil) == (err2 == nil) { |
|
|
|
|
t.Errorf("update auth request:\nupdate1: %v\nupdate2: %v\n", err1, err2) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func testPasswordConcurrentUpdate(t *testing.T, s storage.Storage) { |
|
|
|
|
// Use bcrypt.MinCost to keep the tests short.
|
|
|
|
|
passwordHash, err := bcrypt.GenerateFromPassword([]byte("secret"), bcrypt.MinCost) |
|
|
|
|
if err != nil { |
|
|
|
|
t.Fatal(err) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
password := storage.Password{ |
|
|
|
|
Email: "jane@example.com", |
|
|
|
|
Hash: passwordHash, |
|
|
|
|
Username: "jane", |
|
|
|
|
UserID: "foobar", |
|
|
|
|
} |
|
|
|
|
if err := s.CreatePassword(password); err != nil { |
|
|
|
|
t.Fatalf("create password token: %v", err) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var err1, err2 error |
|
|
|
|
|
|
|
|
|
err1 = s.UpdatePassword(password.Email, func(old storage.Password) (storage.Password, error) { |
|
|
|
|
old.Username = "user 1" |
|
|
|
|
err2 = s.UpdatePassword(password.Email, func(old storage.Password) (storage.Password, error) { |
|
|
|
|
old.Username = "user 2" |
|
|
|
|
return old, nil |
|
|
|
|
}) |
|
|
|
|
return old, nil |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
if (err1 == nil) == (err2 == nil) { |
|
|
|
|
t.Errorf("update password: concurrent updates both returned no error") |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func testKeysConcurrentUpdate(t *testing.T, s storage.Storage) { |
|
|
|
|
// Test twice. Once for a create, once for an update.
|
|
|
|
|
for i := 0; i < 2; i++ { |
|
|
|
|
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), |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var err1, err2 error |
|
|
|
|
|
|
|
|
|
err1 = s.UpdateKeys(func(old storage.Keys) (storage.Keys, error) { |
|
|
|
|
err2 = s.UpdateKeys(func(old storage.Keys) (storage.Keys, error) { |
|
|
|
|
return keys1, nil |
|
|
|
|
}) |
|
|
|
|
return keys2, nil |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
if err1 == nil && err2 == nil { |
|
|
|
|
t.Errorf("update client: concurrent updates both returned no error") |
|
|
|
|
if (err1 == nil) == (err2 == nil) { |
|
|
|
|
t.Errorf("update keys: concurrent updates both returned no error") |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|