auth
auth/profile_test.go
Implement postgres clientStore. Stop requiring the client ID be passed to clientStore.addEndpoints and context.AddEndpoints. The Endpoints themselves contain the client ID. When using the authd server, set the log flags to include the file path and line number. Add an ErrEndpointAlreadyExists error, to return when creating an endpoint and its ID already exists in the database. Add a Deleted property to Clients and remove the clientStore.deleteClient and context.DeleteClient methods. We're not going to actually remove that data, and we want to be able to restore it, so include it in the ClientChange type and call it using UpdateClient. Create a ClientChange.Empty helper method that will return whether the ClientChange has any changes to perform. Return ErrClientNotFound from clientStore.getClient if the Client's Deleted property is set to true. This also requires us to ignore ErrClientNotFound errors when calling memstore.listClientsByOwner, as they should just be skipped instead of returning an error. Add the postgres type methods needed to implement clientStore. Include postgres as a clientStore if the testing.Short() flag is not set. Generate a new ID for the Client on every run in the tests, now that we can't actually remove it from the database/memstore in code. We really just need a *Store.Reset() function that erases all the data and starts over again, to give the tests a clean execution environment (and so they can clean up after themselves). Add the CREATE TABLE statements for the Clients table and the Endpoints table to sql/postgres_init.sql.
| paddy@38 | 1 package auth |
| paddy@38 | 2 |
| paddy@38 | 3 import ( |
| paddy@38 | 4 "fmt" |
| paddy@38 | 5 "testing" |
| paddy@38 | 6 "time" |
| paddy@38 | 7 |
| paddy@107 | 8 "code.secondbit.org/uuid.hg" |
| paddy@38 | 9 ) |
| paddy@38 | 10 |
| paddy@38 | 11 const ( |
| paddy@38 | 12 profileChangeName = 1 << iota |
| paddy@38 | 13 profileChangePassphrase |
| paddy@38 | 14 profileChangeIterations |
| paddy@38 | 15 profileChangeSalt |
| paddy@38 | 16 profileChangePassphraseScheme |
| paddy@38 | 17 profileChangeCompromised |
| paddy@38 | 18 profileChangeLockedUntil |
| paddy@38 | 19 profileChangePassphraseReset |
| paddy@38 | 20 profileChangePassphraseResetCreated |
| paddy@38 | 21 profileChangeLastSeen |
| paddy@38 | 22 ) |
| paddy@38 | 23 |
| paddy@149 | 24 func init() { |
| paddy@149 | 25 p, err := NewPostgres("dbname=testdb sslmode=disable") |
| paddy@149 | 26 if err != nil { |
| paddy@149 | 27 panic(err) |
| paddy@149 | 28 } |
| paddy@149 | 29 if !testing.Short() { |
| paddy@149 | 30 profileStores = append(profileStores, &p) |
| paddy@149 | 31 } |
| paddy@149 | 32 } |
| paddy@149 | 33 |
| paddy@57 | 34 var profileStores = []profileStore{NewMemstore()} |
| paddy@38 | 35 |
| paddy@38 | 36 func compareProfiles(profile1, profile2 Profile) (success bool, field string, val1, val2 interface{}) { |
| paddy@38 | 37 if !profile1.ID.Equal(profile2.ID) { |
| paddy@38 | 38 return false, "ID", profile1.ID, profile2.ID |
| paddy@38 | 39 } |
| paddy@38 | 40 if profile1.Name != profile2.Name { |
| paddy@38 | 41 return false, "name", profile1.Name, profile2.Name |
| paddy@38 | 42 } |
| paddy@38 | 43 if profile1.Passphrase != profile2.Passphrase { |
| paddy@38 | 44 return false, "passphrase", profile1.Passphrase, profile2.Passphrase |
| paddy@38 | 45 } |
| paddy@38 | 46 if profile1.Iterations != profile2.Iterations { |
| paddy@38 | 47 return false, "iterations", profile1.Iterations, profile2.Iterations |
| paddy@38 | 48 } |
| paddy@38 | 49 if profile1.Salt != profile2.Salt { |
| paddy@38 | 50 return false, "salt", profile1.Salt, profile2.Salt |
| paddy@38 | 51 } |
| paddy@38 | 52 if profile1.PassphraseScheme != profile2.PassphraseScheme { |
| paddy@38 | 53 return false, "passphrase scheme", profile1.PassphraseScheme, profile2.PassphraseScheme |
| paddy@38 | 54 } |
| paddy@38 | 55 if profile1.Compromised != profile2.Compromised { |
| paddy@38 | 56 return false, "compromised", profile1.Compromised, profile2.Compromised |
| paddy@38 | 57 } |
| paddy@38 | 58 if !profile1.LockedUntil.Equal(profile2.LockedUntil) { |
| paddy@38 | 59 return false, "locked until", profile1.LockedUntil, profile2.LockedUntil |
| paddy@38 | 60 } |
| paddy@38 | 61 if profile1.PassphraseReset != profile2.PassphraseReset { |
| paddy@38 | 62 return false, "passphrase reset", profile1.PassphraseReset, profile2.PassphraseReset |
| paddy@38 | 63 } |
| paddy@38 | 64 if !profile1.PassphraseResetCreated.Equal(profile2.PassphraseResetCreated) { |
| paddy@38 | 65 return false, "passphrase reset created", profile1.PassphraseResetCreated, profile2.PassphraseResetCreated |
| paddy@38 | 66 } |
| paddy@38 | 67 if !profile1.Created.Equal(profile2.Created) { |
| paddy@38 | 68 return false, "created", profile1.Created, profile2.Created |
| paddy@38 | 69 } |
| paddy@38 | 70 if !profile1.LastSeen.Equal(profile2.LastSeen) { |
| paddy@38 | 71 return false, "last seen", profile1.LastSeen, profile2.LastSeen |
| paddy@38 | 72 } |
| paddy@148 | 73 if profile1.Deleted != profile2.Deleted { |
| paddy@148 | 74 return false, "deleted", profile1.Deleted, profile2.Deleted |
| paddy@148 | 75 } |
| paddy@38 | 76 return true, "", nil, nil |
| paddy@38 | 77 } |
| paddy@38 | 78 |
| paddy@46 | 79 func compareLogins(login1, login2 Login) (success bool, field string, val1, val2 interface{}) { |
| paddy@46 | 80 if login1.Type != login2.Type { |
| paddy@46 | 81 return false, "Type", login1.Type, login2.Type |
| paddy@46 | 82 } |
| paddy@46 | 83 if login1.Value != login2.Value { |
| paddy@46 | 84 return false, "Value", login1.Value, login2.Value |
| paddy@46 | 85 } |
| paddy@46 | 86 if !login1.ProfileID.Equal(login2.ProfileID) { |
| paddy@46 | 87 return false, "ProfileID", login1.ProfileID, login2.ProfileID |
| paddy@46 | 88 } |
| paddy@46 | 89 if !login1.Created.Equal(login2.Created) { |
| paddy@46 | 90 return false, "Created", login1.Created, login2.Created |
| paddy@46 | 91 } |
| paddy@46 | 92 if !login1.LastUsed.Equal(login2.LastUsed) { |
| paddy@46 | 93 return false, "LastUsed", login1.LastUsed, login2.LastUsed |
| paddy@46 | 94 } |
| paddy@46 | 95 return true, "", nil, nil |
| paddy@46 | 96 } |
| paddy@46 | 97 |
| paddy@38 | 98 func TestProfileStoreSuccess(t *testing.T) { |
| paddy@38 | 99 t.Parallel() |
| paddy@38 | 100 profile := Profile{ |
| paddy@38 | 101 ID: uuid.NewID(), |
| paddy@38 | 102 Name: "name", |
| paddy@38 | 103 Passphrase: "passphrase", |
| paddy@38 | 104 Iterations: 10000, |
| paddy@38 | 105 Salt: "salt", |
| paddy@38 | 106 PassphraseScheme: 1, |
| paddy@38 | 107 Compromised: false, |
| paddy@149 | 108 LockedUntil: time.Now().Add(time.Hour).Round(time.Millisecond), |
| paddy@38 | 109 PassphraseReset: "passphrase reset", |
| paddy@149 | 110 PassphraseResetCreated: time.Now().Round(time.Millisecond), |
| paddy@149 | 111 Created: time.Now().Round(time.Millisecond), |
| paddy@149 | 112 LastSeen: time.Now().Round(time.Millisecond), |
| paddy@38 | 113 } |
| paddy@38 | 114 for _, store := range profileStores { |
| paddy@116 | 115 context := Context{profiles: store} |
| paddy@116 | 116 err := context.SaveProfile(profile) |
| paddy@38 | 117 if err != nil { |
| paddy@38 | 118 t.Errorf("Error saving profile to %T: %s", store, err) |
| paddy@38 | 119 } |
| paddy@116 | 120 err = context.SaveProfile(profile) |
| paddy@38 | 121 if err != ErrProfileAlreadyExists { |
| paddy@38 | 122 t.Errorf("Expected ErrProfileAlreadyExists from %T, got %+v", store, err) |
| paddy@38 | 123 } |
| paddy@116 | 124 retrieved, err := context.GetProfileByID(profile.ID) |
| paddy@38 | 125 if err != nil { |
| paddy@38 | 126 t.Errorf("Error retrieving profile from %T: %s", store, err) |
| paddy@38 | 127 } |
| paddy@38 | 128 match, field, expectation, result := compareProfiles(profile, retrieved) |
| paddy@38 | 129 if !match { |
| paddy@38 | 130 t.Errorf("Expected `%v` in the `%s` field of profile retrieved from %T, got `%v`", expectation, field, store, result) |
| paddy@38 | 131 } |
| paddy@148 | 132 deleted := true |
| paddy@148 | 133 err = context.UpdateProfile(profile.ID, ProfileChange{Deleted: &deleted}) |
| paddy@38 | 134 if err != nil { |
| paddy@38 | 135 t.Errorf("Error removing profile from %T: %s", store, err) |
| paddy@38 | 136 } |
| paddy@116 | 137 retrieved, err = context.GetProfileByID(profile.ID) |
| paddy@38 | 138 if err != ErrProfileNotFound { |
| paddy@38 | 139 t.Errorf("Expected ErrProfileNotFound from %T, got %+v and %+v", store, retrieved, err) |
| paddy@38 | 140 } |
| paddy@38 | 141 } |
| paddy@38 | 142 } |
| paddy@38 | 143 |
| paddy@38 | 144 func TestProfileUpdates(t *testing.T) { |
| paddy@38 | 145 t.Parallel() |
| paddy@38 | 146 variations := 1 << 10 |
| paddy@38 | 147 profile := Profile{ |
| paddy@38 | 148 ID: uuid.NewID(), |
| paddy@38 | 149 Name: "name", |
| paddy@38 | 150 Passphrase: "passphrase", |
| paddy@38 | 151 Iterations: 10000, |
| paddy@38 | 152 Salt: "salt", |
| paddy@38 | 153 PassphraseScheme: 1, |
| paddy@38 | 154 Compromised: false, |
| paddy@149 | 155 LockedUntil: time.Now().Add(time.Hour).Round(time.Millisecond), |
| paddy@38 | 156 PassphraseReset: "passphrase reset", |
| paddy@149 | 157 PassphraseResetCreated: time.Now().Round(time.Millisecond), |
| paddy@149 | 158 Created: time.Now().Round(time.Millisecond), |
| paddy@149 | 159 LastSeen: time.Now().Round(time.Millisecond), |
| paddy@38 | 160 } |
| paddy@38 | 161 for i := 0; i < variations; i++ { |
| paddy@38 | 162 var name, passphrase, salt, passphraseReset string |
| paddy@69 | 163 var iterations int |
| paddy@38 | 164 var lockedUntil, passphraseResetCreated, lastSeen time.Time |
| paddy@38 | 165 var passphraseScheme int |
| paddy@38 | 166 var compromised bool |
| paddy@38 | 167 |
| paddy@148 | 168 profile.ID = uuid.NewID() |
| paddy@38 | 169 change := ProfileChange{} |
| paddy@38 | 170 expectation := profile |
| paddy@38 | 171 result := profile |
| paddy@38 | 172 if i&profileChangeName != 0 { |
| paddy@38 | 173 name = fmt.Sprintf("name-%d", i) |
| paddy@38 | 174 change.Name = &name |
| paddy@38 | 175 expectation.Name = name |
| paddy@38 | 176 } |
| paddy@38 | 177 if i&profileChangePassphrase != 0 { |
| paddy@38 | 178 passphrase = fmt.Sprintf("passphrase-%d", i) |
| paddy@38 | 179 change.Passphrase = &passphrase |
| paddy@38 | 180 expectation.Passphrase = passphrase |
| paddy@38 | 181 } |
| paddy@38 | 182 if i&profileChangeIterations != 0 { |
| paddy@69 | 183 iterations = i |
| paddy@38 | 184 change.Iterations = &iterations |
| paddy@38 | 185 expectation.Iterations = iterations |
| paddy@38 | 186 } |
| paddy@38 | 187 if i&profileChangeSalt != 0 { |
| paddy@38 | 188 salt = fmt.Sprintf("salt-%d", i) |
| paddy@38 | 189 change.Salt = &salt |
| paddy@38 | 190 expectation.Salt = salt |
| paddy@38 | 191 } |
| paddy@38 | 192 if i&profileChangePassphraseScheme != 0 { |
| paddy@38 | 193 passphraseScheme = i |
| paddy@38 | 194 change.PassphraseScheme = &passphraseScheme |
| paddy@38 | 195 expectation.PassphraseScheme = passphraseScheme |
| paddy@38 | 196 } |
| paddy@38 | 197 if i&profileChangeCompromised != 0 { |
| paddy@38 | 198 compromised = i%2 != 0 |
| paddy@38 | 199 change.Compromised = &compromised |
| paddy@38 | 200 expectation.Compromised = compromised |
| paddy@38 | 201 } |
| paddy@38 | 202 if i&profileChangeLockedUntil != 0 { |
| paddy@149 | 203 lockedUntil = time.Now().Round(time.Millisecond) |
| paddy@38 | 204 change.LockedUntil = &lockedUntil |
| paddy@38 | 205 expectation.LockedUntil = lockedUntil |
| paddy@38 | 206 } |
| paddy@38 | 207 if i&profileChangePassphraseReset != 0 { |
| paddy@38 | 208 passphraseReset = fmt.Sprintf("passphraseReset-%d", i) |
| paddy@38 | 209 change.PassphraseReset = &passphraseReset |
| paddy@38 | 210 expectation.PassphraseReset = passphraseReset |
| paddy@38 | 211 } |
| paddy@38 | 212 if i&profileChangePassphraseResetCreated != 0 { |
| paddy@149 | 213 passphraseResetCreated = time.Now().Round(time.Millisecond) |
| paddy@38 | 214 change.PassphraseResetCreated = &passphraseResetCreated |
| paddy@38 | 215 expectation.PassphraseResetCreated = passphraseResetCreated |
| paddy@38 | 216 } |
| paddy@38 | 217 if i&profileChangeLastSeen != 0 { |
| paddy@149 | 218 lastSeen = time.Now().Round(time.Millisecond) |
| paddy@38 | 219 change.LastSeen = &lastSeen |
| paddy@38 | 220 expectation.LastSeen = lastSeen |
| paddy@38 | 221 } |
| paddy@38 | 222 result.ApplyChange(change) |
| paddy@38 | 223 match, field, expected, got := compareProfiles(expectation, result) |
| paddy@38 | 224 if !match { |
| paddy@38 | 225 t.Errorf("Expected field `%s` to be `%v`, got `%v`", field, expected, got) |
| paddy@38 | 226 } |
| paddy@38 | 227 for _, store := range profileStores { |
| paddy@116 | 228 context := Context{profiles: store} |
| paddy@116 | 229 err := context.SaveProfile(profile) |
| paddy@38 | 230 if err != nil { |
| paddy@38 | 231 t.Errorf("Error saving profile in %T: %s", store, err) |
| paddy@38 | 232 } |
| paddy@116 | 233 err = context.UpdateProfile(profile.ID, change) |
| paddy@38 | 234 if err != nil { |
| paddy@38 | 235 t.Errorf("Error updating profile in %T: %s", store, err) |
| paddy@38 | 236 } |
| paddy@116 | 237 retrieved, err := context.GetProfileByID(profile.ID) |
| paddy@38 | 238 if err != nil { |
| paddy@38 | 239 t.Errorf("Error getting profile from %T: %s", store, err) |
| paddy@38 | 240 } |
| paddy@38 | 241 match, field, expected, got = compareProfiles(expectation, retrieved) |
| paddy@38 | 242 if !match { |
| paddy@38 | 243 t.Errorf("Expected field `%s` to be `%v`, got `%v` from %T", field, expected, got, store) |
| paddy@38 | 244 } |
| paddy@38 | 245 } |
| paddy@38 | 246 } |
| paddy@38 | 247 } |
| paddy@45 | 248 |
| paddy@45 | 249 func TestProfilesUpdates(t *testing.T) { |
| paddy@45 | 250 profile1 := Profile{ |
| paddy@45 | 251 ID: uuid.NewID(), |
| paddy@45 | 252 } |
| paddy@45 | 253 profile2 := Profile{ |
| paddy@45 | 254 ID: uuid.NewID(), |
| paddy@45 | 255 } |
| paddy@45 | 256 profile3 := Profile{ |
| paddy@45 | 257 ID: uuid.NewID(), |
| paddy@45 | 258 } |
| paddy@45 | 259 truth := true |
| paddy@45 | 260 change := BulkProfileChange{ |
| paddy@45 | 261 Compromised: &truth, |
| paddy@45 | 262 } |
| paddy@45 | 263 for _, store := range profileStores { |
| paddy@116 | 264 context := Context{profiles: store} |
| paddy@116 | 265 err := context.SaveProfile(profile1) |
| paddy@45 | 266 if err != nil { |
| paddy@45 | 267 t.Errorf("Error saving profile in %T: %s", store, err) |
| paddy@45 | 268 } |
| paddy@116 | 269 err = context.SaveProfile(profile2) |
| paddy@45 | 270 if err != nil { |
| paddy@45 | 271 t.Errorf("Error saving profile in %T: %s", store, err) |
| paddy@45 | 272 } |
| paddy@116 | 273 err = context.SaveProfile(profile3) |
| paddy@45 | 274 if err != nil { |
| paddy@45 | 275 t.Errorf("Error saving profile in %T: %s", store, err) |
| paddy@45 | 276 } |
| paddy@116 | 277 err = context.UpdateProfiles([]uuid.ID{profile1.ID, profile3.ID}, change) |
| paddy@45 | 278 if err != nil { |
| paddy@45 | 279 t.Errorf("Error updating profile in %T: %s", store, err) |
| paddy@45 | 280 } |
| paddy@45 | 281 profile1.Compromised = truth |
| paddy@45 | 282 profile3.Compromised = truth |
| paddy@116 | 283 retrieved, err := context.GetProfileByID(profile1.ID) |
| paddy@45 | 284 if err != nil { |
| paddy@45 | 285 t.Errorf("Error getting profile from %T: %s", store, err) |
| paddy@45 | 286 } |
| paddy@45 | 287 match, field, expected, got := compareProfiles(profile1, retrieved) |
| paddy@45 | 288 if !match { |
| paddy@45 | 289 t.Errorf("Expected field `%s` to be `%v`, got `%v` from %T", field, expected, got, store) |
| paddy@45 | 290 } |
| paddy@116 | 291 retrieved, err = context.GetProfileByID(profile2.ID) |
| paddy@45 | 292 if err != nil { |
| paddy@45 | 293 t.Errorf("Error getting profile from %T: %s", store, err) |
| paddy@45 | 294 } |
| paddy@45 | 295 match, field, expected, got = compareProfiles(profile2, retrieved) |
| paddy@45 | 296 if !match { |
| paddy@45 | 297 t.Errorf("Expected field `%s` to be `%v`, got `%v` from %T", field, expected, got, store) |
| paddy@45 | 298 } |
| paddy@116 | 299 retrieved, err = context.GetProfileByID(profile3.ID) |
| paddy@45 | 300 if err != nil { |
| paddy@45 | 301 t.Errorf("Error getting profile from %T: %s", store, err) |
| paddy@45 | 302 } |
| paddy@45 | 303 match, field, expected, got = compareProfiles(profile3, retrieved) |
| paddy@45 | 304 if !match { |
| paddy@45 | 305 t.Errorf("Expected field `%s` to be `%v`, got `%v` from %T", field, expected, got, store) |
| paddy@45 | 306 } |
| paddy@45 | 307 } |
| paddy@45 | 308 } |
| paddy@46 | 309 |
| paddy@46 | 310 func TestProfileStoreLoginSuccess(t *testing.T) { |
| paddy@46 | 311 t.Parallel() |
| paddy@46 | 312 login := Login{ |
| paddy@46 | 313 Type: "type", |
| paddy@46 | 314 Value: "value", |
| paddy@46 | 315 ProfileID: uuid.NewID(), |
| paddy@149 | 316 Created: time.Now().Add(-1 * time.Hour).Round(time.Millisecond), |
| paddy@149 | 317 LastUsed: time.Now().Add(-1 * time.Minute).Round(time.Millisecond), |
| paddy@46 | 318 } |
| paddy@46 | 319 for _, store := range profileStores { |
| paddy@116 | 320 context := Context{profiles: store} |
| paddy@116 | 321 err := context.AddLogin(login) |
| paddy@46 | 322 if err != nil { |
| paddy@46 | 323 t.Errorf("Error adding login to %T: %s", store, err) |
| paddy@46 | 324 } |
| paddy@116 | 325 err = context.AddLogin(login) |
| paddy@46 | 326 if err != ErrLoginAlreadyExists { |
| paddy@46 | 327 t.Errorf("Expected ErrLoginAlreadyExists from %T, got %+v", store, err) |
| paddy@46 | 328 } |
| paddy@116 | 329 retrieved, err := context.ListLogins(login.ProfileID, 10, 0) |
| paddy@46 | 330 if err != nil { |
| paddy@46 | 331 t.Errorf("Error retrieving logins from %T: %s", store, err) |
| paddy@46 | 332 } |
| paddy@46 | 333 if len(retrieved) != 1 { |
| paddy@46 | 334 t.Errorf("Expected 1 login result from %T, got %d", store, len(retrieved)) |
| paddy@46 | 335 } |
| paddy@46 | 336 match, field, expectation, result := compareLogins(login, retrieved[0]) |
| paddy@46 | 337 if !match { |
| paddy@46 | 338 t.Errorf("Expected `%v` in the `%s` field of login retrieved from %T, got `%v`", expectation, field, store, result) |
| paddy@46 | 339 } |
| paddy@149 | 340 lastUsed := time.Now().Round(time.Millisecond) |
| paddy@116 | 341 err = context.RecordLoginUse(login.Value, lastUsed) |
| paddy@46 | 342 if err != nil { |
| paddy@46 | 343 t.Errorf("Error recording use of login to %T: %s", store, err) |
| paddy@46 | 344 } |
| paddy@46 | 345 login.LastUsed = lastUsed |
| paddy@116 | 346 retrieved, err = context.ListLogins(login.ProfileID, 10, 0) |
| paddy@46 | 347 if err != nil { |
| paddy@46 | 348 t.Errorf("Error retrieving logins from %T: %s", store, err) |
| paddy@46 | 349 } |
| paddy@46 | 350 if len(retrieved) != 1 { |
| paddy@46 | 351 t.Errorf("Expected 1 login result from %T, got %d", store, len(retrieved)) |
| paddy@46 | 352 } |
| paddy@46 | 353 match, field, expectation, result = compareLogins(login, retrieved[0]) |
| paddy@46 | 354 if !match { |
| paddy@46 | 355 t.Errorf("Expected `%v` in the `%s` field of login retrieved from %T, got `%v`", expectation, field, store, result) |
| paddy@46 | 356 } |
| paddy@116 | 357 err = context.RemoveLogin(login.Value, login.ProfileID) |
| paddy@46 | 358 if err != nil { |
| paddy@46 | 359 t.Errorf("Error removing login from %T: %s", store, err) |
| paddy@46 | 360 } |
| paddy@116 | 361 retrieved, err = context.ListLogins(login.ProfileID, 10, 0) |
| paddy@46 | 362 if len(retrieved) != 0 { |
| paddy@46 | 363 t.Errorf("Expected 0 login results from %T, got %d: %+v", store, len(retrieved), retrieved) |
| paddy@46 | 364 } |
| paddy@116 | 365 err = context.RemoveLogin(login.Value, login.ProfileID) |
| paddy@46 | 366 if err != ErrLoginNotFound { |
| paddy@46 | 367 t.Errorf("Expected ErrLoginNotFound from %T, got %+v", store, err) |
| paddy@46 | 368 } |
| paddy@46 | 369 } |
| paddy@46 | 370 } |
| paddy@47 | 371 |
| paddy@47 | 372 func TestProfileStoreLoginRetrieval(t *testing.T) { |
| paddy@47 | 373 t.Parallel() |
| paddy@47 | 374 profile := Profile{ |
| paddy@47 | 375 ID: uuid.NewID(), |
| paddy@47 | 376 Name: "name", |
| paddy@47 | 377 Passphrase: "passphrase", |
| paddy@47 | 378 Iterations: 10000, |
| paddy@47 | 379 Salt: "salt", |
| paddy@47 | 380 PassphraseScheme: 1, |
| paddy@47 | 381 Compromised: false, |
| paddy@149 | 382 LockedUntil: time.Now().Add(time.Hour).Round(time.Millisecond), |
| paddy@47 | 383 PassphraseReset: "passphrase reset", |
| paddy@149 | 384 PassphraseResetCreated: time.Now().Round(time.Millisecond), |
| paddy@149 | 385 Created: time.Now().Round(time.Millisecond), |
| paddy@149 | 386 LastSeen: time.Now().Round(time.Millisecond), |
| paddy@47 | 387 } |
| paddy@47 | 388 login := Login{ |
| paddy@47 | 389 Type: "type", |
| paddy@47 | 390 Value: "value", |
| paddy@47 | 391 ProfileID: profile.ID, |
| paddy@149 | 392 Created: time.Now().Add(-1 * time.Hour).Round(time.Millisecond), |
| paddy@149 | 393 LastUsed: time.Now().Add(-1 * time.Minute).Round(time.Millisecond), |
| paddy@47 | 394 } |
| paddy@47 | 395 for _, store := range profileStores { |
| paddy@116 | 396 context := Context{profiles: store} |
| paddy@116 | 397 err := context.SaveProfile(profile) |
| paddy@47 | 398 if err != nil { |
| paddy@47 | 399 t.Errorf("Error saving profile in %T: %s", store, err) |
| paddy@47 | 400 } |
| paddy@116 | 401 err = context.AddLogin(login) |
| paddy@47 | 402 if err != nil { |
| paddy@47 | 403 t.Errorf("Error storing login in %T: %s", store, err) |
| paddy@47 | 404 } |
| paddy@116 | 405 retrieved, err := context.GetProfileByLogin(login.Value) |
| paddy@47 | 406 if err != nil { |
| paddy@47 | 407 t.Errorf("Error retrieving profile by login from %T: %s", store, err) |
| paddy@47 | 408 } |
| paddy@47 | 409 match, field, expectation, result := compareProfiles(profile, retrieved) |
| paddy@47 | 410 if !match { |
| paddy@47 | 411 t.Errorf("Expected `%v` in the `%s` field of profile retrieved from %T, got `%v`", expectation, field, store, result) |
| paddy@47 | 412 } |
| paddy@47 | 413 } |
| paddy@47 | 414 } |
| paddy@48 | 415 |
| paddy@48 | 416 func TestProfileChangeValidation(t *testing.T) { |
| paddy@48 | 417 t.Parallel() |
| paddy@48 | 418 passphraseScheme := 1 |
| paddy@48 | 419 passphraseReset := "reset" |
| paddy@48 | 420 salt := "salt" |
| paddy@69 | 421 iterations := 100 |
| paddy@48 | 422 emptyName := "" |
| paddy@48 | 423 enteredName := "Paddy" |
| paddy@48 | 424 okPassphrase := "this is a decent passphrase" |
| paddy@48 | 425 compromised := true |
| paddy@149 | 426 lockedUntil := time.Now().Round(time.Millisecond) |
| paddy@149 | 427 resetCreated := time.Now().Round(time.Millisecond) |
| paddy@149 | 428 lastSeen := time.Now().Round(time.Millisecond) |
| paddy@48 | 429 changes := map[*ProfileChange]error{ |
| paddy@48 | 430 &ProfileChange{}: ErrEmptyChange, |
| paddy@48 | 431 &ProfileChange{PassphraseScheme: &passphraseScheme}: ErrMissingPassphrase, |
| paddy@48 | 432 &ProfileChange{PassphraseScheme: &passphraseScheme, Passphrase: &okPassphrase}: nil, |
| paddy@48 | 433 &ProfileChange{PassphraseReset: &passphraseReset}: ErrMissingPassphraseResetCreated, |
| paddy@48 | 434 &ProfileChange{PassphraseReset: &passphraseReset, PassphraseResetCreated: &resetCreated}: nil, |
| paddy@48 | 435 &ProfileChange{Salt: &salt}: ErrMissingPassphrase, |
| paddy@48 | 436 &ProfileChange{Salt: &salt, Passphrase: &okPassphrase}: nil, |
| paddy@48 | 437 &ProfileChange{Iterations: &iterations}: ErrMissingPassphrase, |
| paddy@48 | 438 &ProfileChange{Iterations: &iterations, Passphrase: &okPassphrase}: nil, |
| paddy@48 | 439 &ProfileChange{Passphrase: &okPassphrase}: nil, |
| paddy@48 | 440 &ProfileChange{Name: &emptyName}: nil, |
| paddy@48 | 441 &ProfileChange{Name: &enteredName}: nil, |
| paddy@48 | 442 &ProfileChange{Compromised: &compromised}: nil, |
| paddy@48 | 443 &ProfileChange{LockedUntil: &lockedUntil}: nil, |
| paddy@48 | 444 &ProfileChange{LastSeen: &lastSeen}: nil, |
| paddy@48 | 445 &ProfileChange{PassphraseResetCreated: &resetCreated}: ErrMissingPassphraseReset, |
| paddy@48 | 446 } |
| paddy@48 | 447 for change, expectedErr := range changes { |
| paddy@48 | 448 if err := change.Validate(); err != expectedErr { |
| paddy@48 | 449 t.Errorf("Expected %+v to give an error of %v, gave %v", change, expectedErr, err) |
| paddy@48 | 450 } |
| paddy@48 | 451 } |
| paddy@48 | 452 } |
| paddy@48 | 453 |
| paddy@48 | 454 func TestBulkProfileChangeValidation(t *testing.T) { |
| paddy@48 | 455 t.Parallel() |
| paddy@48 | 456 compromised := true |
| paddy@48 | 457 changes := map[*BulkProfileChange]error{ |
| paddy@48 | 458 &BulkProfileChange{}: ErrEmptyChange, |
| paddy@48 | 459 &BulkProfileChange{Compromised: &compromised}: nil, |
| paddy@48 | 460 } |
| paddy@48 | 461 for change, expectedErr := range changes { |
| paddy@48 | 462 if err := change.Validate(); err != expectedErr { |
| paddy@48 | 463 t.Errorf("Expected %+v to give an error of %v, gave %v", change, expectedErr, err) |
| paddy@48 | 464 } |
| paddy@48 | 465 } |
| paddy@48 | 466 } |
| paddy@128 | 467 |
| paddy@128 | 468 // BUG(paddy): We need to test the validateNewProfileRequest helper. |
| paddy@128 | 469 // BUG(paddy): We need to test the CreateProfileHandler. |
| paddy@148 | 470 // BUG(paddy): We need to test that deleting works as we expect. |