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