Add an endpoint to validate and register profiles.
Add a newProfileRequest object that defines the user-specified properties of a
new Profile.
Add a helper that validates a newProfileRequest and modifies it for
sanitization, mostly just removing leading and trailing whitespace.
Add MaxNameLength, MaxUsernameLength, and MaxEmailLength constants to hold the
maximum length for those properties.
Add errors to be returned when a users attempts to log in with a profile that is
compromised or locked.
Add the bare bones of a CreateProfileHandler that validates a profile
registration request adn uses it to create a Profile and at least one Login.
Create a requestError struct that is used for returning API errors, along with
constants for the slugs we'll use to signal those errors.
7 "code.secondbit.org/uuid"
10 var tokenStores = []tokenStore{NewMemstore()}
12 func compareTokens(token1, token2 Token) (success bool, field string, val1, val2 interface{}) {
13 if token1.AccessToken != token2.AccessToken {
14 return false, "access token", token1.AccessToken, token2.AccessToken
16 if token1.RefreshToken != token2.RefreshToken {
17 return false, "refresh token", token1.RefreshToken, token2.RefreshToken
19 if !token1.Created.Equal(token2.Created) {
20 return false, "created", token1.Created, token2.Created
22 if token1.CreatedFrom != token2.CreatedFrom {
23 return false, "created from", token1.CreatedFrom, token2.CreatedFrom
25 if token1.ExpiresIn != token2.ExpiresIn {
26 return false, "expires in", token1.ExpiresIn, token2.ExpiresIn
28 if token1.RefreshExpiresIn != token2.RefreshExpiresIn {
29 return false, "refresh expires in", token1.RefreshExpiresIn, token2.RefreshExpiresIn
31 if token1.TokenType != token2.TokenType {
32 return false, "token type", token1.TokenType, token2.TokenType
34 if token1.Scope != token2.Scope {
35 return false, "scope", token1.Scope, token2.Scope
37 if !token1.ProfileID.Equal(token2.ProfileID) {
38 return false, "profile ID", token1.ProfileID, token2.ProfileID
40 if token1.Revoked != token2.Revoked {
41 return false, "revoked", token1.Revoked, token2.Revoked
43 return true, "", nil, nil
46 func TestTokenStoreSuccess(t *testing.T) {
49 AccessToken: "access",
50 RefreshToken: "refresh",
55 ProfileID: uuid.NewID(),
57 for _, store := range tokenStores {
58 err := store.saveToken(token)
60 t.Errorf("Error saving token to %T: %s", store, err)
62 err = store.saveToken(token)
63 if err != ErrTokenAlreadyExists {
64 t.Errorf("Expected ErrTokenAlreadyExists from %T, got %s", store, err)
66 retrievedAccess, err := store.getToken(token.AccessToken, false)
68 t.Errorf("Error retrieving token from %T: %s", store, err)
70 success, field, expectation, result := compareTokens(token, retrievedAccess)
72 t.Errorf("Expected field %s to be %v, but got %v from %T", field, expectation, result, store)
74 retrievedRefresh, err := store.getToken(token.RefreshToken, true)
76 t.Errorf("Error retrieving refresh token from %T: %s", store, err)
78 success, field, expectation, result = compareTokens(token, retrievedRefresh)
80 t.Errorf("Expected field %s to be %v, but got %v from %T", field, expectation, result, store)
82 retrievedProfile, err := store.getTokensByProfileID(token.ProfileID, 25, 0)
84 t.Errorf("Error retrieving token by profile from %T: %s", store, err)
86 if len(retrievedProfile) != 1 {
87 t.Errorf("Expected 1 token retrieved by profile ID from %T, got %+v", store, retrievedProfile)
89 success, field, expectation, result = compareTokens(token, retrievedProfile[0])
91 t.Errorf("Expected field %s to be %v, but got %v from %T", field, expectation, result, store)
93 err = store.revokeToken(token.AccessToken, false)
95 t.Errorf("Error revoking token in %T: %s", store, err)
97 retrievedRevoked, err := store.getToken(token.AccessToken, false)
99 t.Errorf("Error retrieving token from %T: %s", store, err)
102 success, field, expectation, result = compareTokens(token, retrievedRevoked)
104 t.Errorf("Expected field %s to be %v, but got %v from %T", field, expectation, result, store)
106 // TODO(paddy): test revoking by refresh token.
107 err = store.removeToken(token.AccessToken)
109 t.Errorf("Error removing token from %T: %s", store, err)
111 _, err = store.getToken(token.AccessToken, false)
112 if err != ErrTokenNotFound {
113 t.Errorf("Expected ErrTokenNotFound from %T, got %s", store, err)
115 _, err = store.getToken(token.RefreshToken, true)
116 if err != ErrTokenNotFound {
117 t.Errorf("Expected ErrTokenNotFound from %T, got %s", store, err)
119 retrievedProfile, err = store.getTokensByProfileID(token.ProfileID, 25, 0)
121 t.Errorf("Error retrieving token by profile from %T: %s", store, err)
123 if len(retrievedProfile) != 0 {
124 t.Errorf("Expected list of 0 tokens from %T, got %+v", store, retrievedProfile)
126 err = store.removeToken(token.AccessToken)
127 if err != ErrTokenNotFound {
128 t.Errorf("Expected ErrTokenNotFound from %T, got %s", store, err)
130 err = store.revokeToken(token.AccessToken, false)
131 if err != ErrTokenNotFound {
132 t.Errorf("Expected ErrTokenNotFound from %T, got %s", store, err)
134 err = store.revokeToken(token.RefreshToken, true)
135 if err != ErrTokenNotFound {
136 t.Errorf("Expected ErrTokenNotFound from %T, got %s", store, err)