auth
2015-06-29
Parent:8ecb60d29b0d
auth/profile_test.go
Create Docker image for authd. Create a Dockerfile for authd, which will wrap the compiled Go binary up into a tiny little Docker image. Create an authd/build-docker.sh script that will build the statically-linked binary in a Docker container, so the authd Docker image can use it. We had to include ca-certificates.crt in the Dockerfile, as well, so we could communicate over SSL with things. A wrapper.sh file is included that will pull the JWT_SECRET environment variable out of a kubernetes secrets file, which is a handy wrapper to have. Finally, we added the authd/docker-authd binary to the .hgignore.
| 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@172 | 93 if login1.Verification != login2.Verification { |
| paddy@172 | 94 return false, "Verification", login1.Verification, login2.Verification |
| paddy@172 | 95 } |
| paddy@172 | 96 if login1.Verified != login2.Verified { |
| paddy@172 | 97 return false, "Verified", login1.Verified, login2.Verified |
| paddy@172 | 98 } |
| paddy@46 | 99 return true, "", nil, nil |
| paddy@46 | 100 } |
| paddy@46 | 101 |
| paddy@38 | 102 func TestProfileStoreSuccess(t *testing.T) { |
| paddy@38 | 103 t.Parallel() |
| paddy@38 | 104 profile := Profile{ |
| paddy@38 | 105 ID: uuid.NewID(), |
| paddy@38 | 106 Name: "name", |
| paddy@38 | 107 Passphrase: "passphrase", |
| paddy@38 | 108 Iterations: 10000, |
| paddy@38 | 109 Salt: "salt", |
| paddy@38 | 110 PassphraseScheme: 1, |
| paddy@38 | 111 Compromised: false, |
| paddy@149 | 112 LockedUntil: time.Now().Add(time.Hour).Round(time.Millisecond), |
| paddy@38 | 113 PassphraseReset: "passphrase reset", |
| paddy@149 | 114 PassphraseResetCreated: time.Now().Round(time.Millisecond), |
| paddy@149 | 115 Created: time.Now().Round(time.Millisecond), |
| paddy@149 | 116 LastSeen: time.Now().Round(time.Millisecond), |
| paddy@38 | 117 } |
| paddy@38 | 118 for _, store := range profileStores { |
| paddy@116 | 119 context := Context{profiles: store} |
| paddy@116 | 120 err := context.SaveProfile(profile) |
| paddy@38 | 121 if err != nil { |
| paddy@38 | 122 t.Errorf("Error saving profile to %T: %s", store, err) |
| paddy@38 | 123 } |
| paddy@116 | 124 err = context.SaveProfile(profile) |
| paddy@38 | 125 if err != ErrProfileAlreadyExists { |
| paddy@38 | 126 t.Errorf("Expected ErrProfileAlreadyExists from %T, got %+v", store, err) |
| paddy@38 | 127 } |
| paddy@116 | 128 retrieved, err := context.GetProfileByID(profile.ID) |
| paddy@38 | 129 if err != nil { |
| paddy@38 | 130 t.Errorf("Error retrieving profile from %T: %s", store, err) |
| paddy@38 | 131 } |
| paddy@38 | 132 match, field, expectation, result := compareProfiles(profile, retrieved) |
| paddy@38 | 133 if !match { |
| paddy@38 | 134 t.Errorf("Expected `%v` in the `%s` field of profile retrieved from %T, got `%v`", expectation, field, store, result) |
| paddy@38 | 135 } |
| paddy@161 | 136 err = context.DeleteProfile(profile.ID) |
| paddy@38 | 137 if err != nil { |
| paddy@38 | 138 t.Errorf("Error removing profile from %T: %s", store, err) |
| paddy@38 | 139 } |
| paddy@116 | 140 retrieved, err = context.GetProfileByID(profile.ID) |
| paddy@38 | 141 if err != ErrProfileNotFound { |
| paddy@38 | 142 t.Errorf("Expected ErrProfileNotFound from %T, got %+v and %+v", store, retrieved, err) |
| paddy@38 | 143 } |
| paddy@38 | 144 } |
| paddy@38 | 145 } |
| paddy@38 | 146 |
| paddy@38 | 147 func TestProfileUpdates(t *testing.T) { |
| paddy@38 | 148 t.Parallel() |
| paddy@38 | 149 variations := 1 << 10 |
| paddy@38 | 150 profile := Profile{ |
| paddy@38 | 151 ID: uuid.NewID(), |
| paddy@38 | 152 Name: "name", |
| paddy@38 | 153 Passphrase: "passphrase", |
| paddy@38 | 154 Iterations: 10000, |
| paddy@38 | 155 Salt: "salt", |
| paddy@38 | 156 PassphraseScheme: 1, |
| paddy@38 | 157 Compromised: false, |
| paddy@149 | 158 LockedUntil: time.Now().Add(time.Hour).Round(time.Millisecond), |
| paddy@38 | 159 PassphraseReset: "passphrase reset", |
| paddy@149 | 160 PassphraseResetCreated: time.Now().Round(time.Millisecond), |
| paddy@149 | 161 Created: time.Now().Round(time.Millisecond), |
| paddy@149 | 162 LastSeen: time.Now().Round(time.Millisecond), |
| paddy@38 | 163 } |
| paddy@38 | 164 for i := 0; i < variations; i++ { |
| paddy@38 | 165 var name, passphrase, salt, passphraseReset string |
| paddy@69 | 166 var iterations int |
| paddy@38 | 167 var lockedUntil, passphraseResetCreated, lastSeen time.Time |
| paddy@38 | 168 var passphraseScheme int |
| paddy@38 | 169 var compromised bool |
| paddy@38 | 170 |
| paddy@148 | 171 profile.ID = uuid.NewID() |
| paddy@38 | 172 change := ProfileChange{} |
| paddy@38 | 173 expectation := profile |
| paddy@38 | 174 result := profile |
| paddy@38 | 175 if i&profileChangeName != 0 { |
| paddy@38 | 176 name = fmt.Sprintf("name-%d", i) |
| paddy@38 | 177 change.Name = &name |
| paddy@38 | 178 expectation.Name = name |
| paddy@38 | 179 } |
| paddy@38 | 180 if i&profileChangePassphrase != 0 { |
| paddy@38 | 181 passphrase = fmt.Sprintf("passphrase-%d", i) |
| paddy@38 | 182 change.Passphrase = &passphrase |
| paddy@38 | 183 expectation.Passphrase = passphrase |
| paddy@38 | 184 } |
| paddy@38 | 185 if i&profileChangeIterations != 0 { |
| paddy@69 | 186 iterations = i |
| paddy@38 | 187 change.Iterations = &iterations |
| paddy@38 | 188 expectation.Iterations = iterations |
| paddy@38 | 189 } |
| paddy@38 | 190 if i&profileChangeSalt != 0 { |
| paddy@38 | 191 salt = fmt.Sprintf("salt-%d", i) |
| paddy@38 | 192 change.Salt = &salt |
| paddy@38 | 193 expectation.Salt = salt |
| paddy@38 | 194 } |
| paddy@38 | 195 if i&profileChangePassphraseScheme != 0 { |
| paddy@38 | 196 passphraseScheme = i |
| paddy@38 | 197 change.PassphraseScheme = &passphraseScheme |
| paddy@38 | 198 expectation.PassphraseScheme = passphraseScheme |
| paddy@38 | 199 } |
| paddy@38 | 200 if i&profileChangeCompromised != 0 { |
| paddy@38 | 201 compromised = i%2 != 0 |
| paddy@38 | 202 change.Compromised = &compromised |
| paddy@38 | 203 expectation.Compromised = compromised |
| paddy@38 | 204 } |
| paddy@38 | 205 if i&profileChangeLockedUntil != 0 { |
| paddy@149 | 206 lockedUntil = time.Now().Round(time.Millisecond) |
| paddy@38 | 207 change.LockedUntil = &lockedUntil |
| paddy@38 | 208 expectation.LockedUntil = lockedUntil |
| paddy@38 | 209 } |
| paddy@38 | 210 if i&profileChangePassphraseReset != 0 { |
| paddy@38 | 211 passphraseReset = fmt.Sprintf("passphraseReset-%d", i) |
| paddy@38 | 212 change.PassphraseReset = &passphraseReset |
| paddy@38 | 213 expectation.PassphraseReset = passphraseReset |
| paddy@38 | 214 } |
| paddy@38 | 215 if i&profileChangePassphraseResetCreated != 0 { |
| paddy@149 | 216 passphraseResetCreated = time.Now().Round(time.Millisecond) |
| paddy@38 | 217 change.PassphraseResetCreated = &passphraseResetCreated |
| paddy@38 | 218 expectation.PassphraseResetCreated = passphraseResetCreated |
| paddy@38 | 219 } |
| paddy@38 | 220 if i&profileChangeLastSeen != 0 { |
| paddy@149 | 221 lastSeen = time.Now().Round(time.Millisecond) |
| paddy@38 | 222 change.LastSeen = &lastSeen |
| paddy@38 | 223 expectation.LastSeen = lastSeen |
| paddy@38 | 224 } |
| paddy@38 | 225 result.ApplyChange(change) |
| paddy@38 | 226 match, field, expected, got := compareProfiles(expectation, result) |
| paddy@38 | 227 if !match { |
| paddy@38 | 228 t.Errorf("Expected field `%s` to be `%v`, got `%v`", field, expected, got) |
| paddy@38 | 229 } |
| paddy@38 | 230 for _, store := range profileStores { |
| paddy@116 | 231 context := Context{profiles: store} |
| paddy@116 | 232 err := context.SaveProfile(profile) |
| paddy@38 | 233 if err != nil { |
| paddy@38 | 234 t.Errorf("Error saving profile in %T: %s", store, err) |
| paddy@38 | 235 } |
| paddy@116 | 236 err = context.UpdateProfile(profile.ID, change) |
| paddy@38 | 237 if err != nil { |
| paddy@38 | 238 t.Errorf("Error updating profile in %T: %s", store, err) |
| paddy@38 | 239 } |
| paddy@116 | 240 retrieved, err := context.GetProfileByID(profile.ID) |
| paddy@38 | 241 if err != nil { |
| paddy@38 | 242 t.Errorf("Error getting profile from %T: %s", store, err) |
| paddy@38 | 243 } |
| paddy@38 | 244 match, field, expected, got = compareProfiles(expectation, retrieved) |
| paddy@38 | 245 if !match { |
| paddy@38 | 246 t.Errorf("Expected field `%s` to be `%v`, got `%v` from %T", field, expected, got, store) |
| paddy@38 | 247 } |
| paddy@38 | 248 } |
| paddy@38 | 249 } |
| paddy@38 | 250 } |
| paddy@45 | 251 |
| paddy@45 | 252 func TestProfilesUpdates(t *testing.T) { |
| paddy@45 | 253 profile1 := Profile{ |
| paddy@45 | 254 ID: uuid.NewID(), |
| paddy@45 | 255 } |
| paddy@45 | 256 profile2 := Profile{ |
| paddy@45 | 257 ID: uuid.NewID(), |
| paddy@45 | 258 } |
| paddy@45 | 259 profile3 := Profile{ |
| paddy@45 | 260 ID: uuid.NewID(), |
| paddy@45 | 261 } |
| paddy@45 | 262 truth := true |
| paddy@45 | 263 change := BulkProfileChange{ |
| paddy@45 | 264 Compromised: &truth, |
| paddy@45 | 265 } |
| paddy@45 | 266 for _, store := range profileStores { |
| paddy@116 | 267 context := Context{profiles: store} |
| paddy@116 | 268 err := context.SaveProfile(profile1) |
| paddy@45 | 269 if err != nil { |
| paddy@45 | 270 t.Errorf("Error saving profile in %T: %s", store, err) |
| paddy@45 | 271 } |
| paddy@116 | 272 err = context.SaveProfile(profile2) |
| paddy@45 | 273 if err != nil { |
| paddy@45 | 274 t.Errorf("Error saving profile in %T: %s", store, err) |
| paddy@45 | 275 } |
| paddy@116 | 276 err = context.SaveProfile(profile3) |
| paddy@45 | 277 if err != nil { |
| paddy@45 | 278 t.Errorf("Error saving profile in %T: %s", store, err) |
| paddy@45 | 279 } |
| paddy@116 | 280 err = context.UpdateProfiles([]uuid.ID{profile1.ID, profile3.ID}, change) |
| paddy@45 | 281 if err != nil { |
| paddy@45 | 282 t.Errorf("Error updating profile in %T: %s", store, err) |
| paddy@45 | 283 } |
| paddy@45 | 284 profile1.Compromised = truth |
| paddy@45 | 285 profile3.Compromised = truth |
| paddy@116 | 286 retrieved, err := context.GetProfileByID(profile1.ID) |
| paddy@45 | 287 if err != nil { |
| paddy@45 | 288 t.Errorf("Error getting profile from %T: %s", store, err) |
| paddy@45 | 289 } |
| paddy@45 | 290 match, field, expected, got := compareProfiles(profile1, retrieved) |
| paddy@45 | 291 if !match { |
| paddy@45 | 292 t.Errorf("Expected field `%s` to be `%v`, got `%v` from %T", field, expected, got, store) |
| paddy@45 | 293 } |
| paddy@116 | 294 retrieved, err = context.GetProfileByID(profile2.ID) |
| paddy@45 | 295 if err != nil { |
| paddy@45 | 296 t.Errorf("Error getting profile from %T: %s", store, err) |
| paddy@45 | 297 } |
| paddy@45 | 298 match, field, expected, got = compareProfiles(profile2, retrieved) |
| paddy@45 | 299 if !match { |
| paddy@45 | 300 t.Errorf("Expected field `%s` to be `%v`, got `%v` from %T", field, expected, got, store) |
| paddy@45 | 301 } |
| paddy@116 | 302 retrieved, err = context.GetProfileByID(profile3.ID) |
| paddy@45 | 303 if err != nil { |
| paddy@45 | 304 t.Errorf("Error getting profile from %T: %s", store, err) |
| paddy@45 | 305 } |
| paddy@45 | 306 match, field, expected, got = compareProfiles(profile3, retrieved) |
| paddy@45 | 307 if !match { |
| paddy@45 | 308 t.Errorf("Expected field `%s` to be `%v`, got `%v` from %T", field, expected, got, store) |
| paddy@45 | 309 } |
| paddy@45 | 310 } |
| paddy@45 | 311 } |
| paddy@46 | 312 |
| paddy@46 | 313 func TestProfileStoreLoginSuccess(t *testing.T) { |
| paddy@46 | 314 t.Parallel() |
| paddy@46 | 315 login := Login{ |
| paddy@46 | 316 Type: "type", |
| paddy@46 | 317 Value: "value", |
| paddy@46 | 318 ProfileID: uuid.NewID(), |
| paddy@149 | 319 Created: time.Now().Add(-1 * time.Hour).Round(time.Millisecond), |
| paddy@149 | 320 LastUsed: time.Now().Add(-1 * time.Minute).Round(time.Millisecond), |
| paddy@46 | 321 } |
| paddy@46 | 322 for _, store := range profileStores { |
| paddy@116 | 323 context := Context{profiles: store} |
| paddy@116 | 324 err := context.AddLogin(login) |
| paddy@46 | 325 if err != nil { |
| paddy@46 | 326 t.Errorf("Error adding login to %T: %s", store, err) |
| paddy@46 | 327 } |
| paddy@116 | 328 err = context.AddLogin(login) |
| paddy@46 | 329 if err != ErrLoginAlreadyExists { |
| paddy@46 | 330 t.Errorf("Expected ErrLoginAlreadyExists from %T, got %+v", store, err) |
| paddy@46 | 331 } |
| paddy@116 | 332 retrieved, err := context.ListLogins(login.ProfileID, 10, 0) |
| paddy@46 | 333 if err != nil { |
| paddy@46 | 334 t.Errorf("Error retrieving logins from %T: %s", store, err) |
| paddy@46 | 335 } |
| paddy@46 | 336 if len(retrieved) != 1 { |
| paddy@46 | 337 t.Errorf("Expected 1 login result from %T, got %d", store, len(retrieved)) |
| paddy@46 | 338 } |
| paddy@46 | 339 match, field, expectation, result := compareLogins(login, retrieved[0]) |
| paddy@46 | 340 if !match { |
| paddy@46 | 341 t.Errorf("Expected `%v` in the `%s` field of login retrieved from %T, got `%v`", expectation, field, store, result) |
| paddy@46 | 342 } |
| paddy@149 | 343 lastUsed := time.Now().Round(time.Millisecond) |
| paddy@116 | 344 err = context.RecordLoginUse(login.Value, lastUsed) |
| paddy@46 | 345 if err != nil { |
| paddy@46 | 346 t.Errorf("Error recording use of login to %T: %s", store, err) |
| paddy@46 | 347 } |
| paddy@46 | 348 login.LastUsed = lastUsed |
| paddy@116 | 349 retrieved, err = context.ListLogins(login.ProfileID, 10, 0) |
| paddy@46 | 350 if err != nil { |
| paddy@46 | 351 t.Errorf("Error retrieving logins from %T: %s", store, err) |
| paddy@46 | 352 } |
| paddy@46 | 353 if len(retrieved) != 1 { |
| paddy@46 | 354 t.Errorf("Expected 1 login result from %T, got %d", store, len(retrieved)) |
| paddy@46 | 355 } |
| paddy@46 | 356 match, field, expectation, result = compareLogins(login, retrieved[0]) |
| paddy@46 | 357 if !match { |
| paddy@46 | 358 t.Errorf("Expected `%v` in the `%s` field of login retrieved from %T, got `%v`", expectation, field, store, result) |
| paddy@46 | 359 } |
| paddy@116 | 360 err = context.RemoveLogin(login.Value, login.ProfileID) |
| paddy@46 | 361 if err != nil { |
| paddy@46 | 362 t.Errorf("Error removing login from %T: %s", store, err) |
| paddy@46 | 363 } |
| paddy@116 | 364 retrieved, err = context.ListLogins(login.ProfileID, 10, 0) |
| paddy@46 | 365 if len(retrieved) != 0 { |
| paddy@46 | 366 t.Errorf("Expected 0 login results from %T, got %d: %+v", store, len(retrieved), retrieved) |
| paddy@46 | 367 } |
| paddy@116 | 368 err = context.RemoveLogin(login.Value, login.ProfileID) |
| paddy@46 | 369 if err != ErrLoginNotFound { |
| paddy@46 | 370 t.Errorf("Expected ErrLoginNotFound from %T, got %+v", store, err) |
| paddy@46 | 371 } |
| paddy@46 | 372 } |
| paddy@46 | 373 } |
| paddy@47 | 374 |
| paddy@47 | 375 func TestProfileStoreLoginRetrieval(t *testing.T) { |
| paddy@47 | 376 t.Parallel() |
| paddy@47 | 377 profile := Profile{ |
| paddy@47 | 378 ID: uuid.NewID(), |
| paddy@47 | 379 Name: "name", |
| paddy@47 | 380 Passphrase: "passphrase", |
| paddy@47 | 381 Iterations: 10000, |
| paddy@47 | 382 Salt: "salt", |
| paddy@47 | 383 PassphraseScheme: 1, |
| paddy@47 | 384 Compromised: false, |
| paddy@149 | 385 LockedUntil: time.Now().Add(time.Hour).Round(time.Millisecond), |
| paddy@47 | 386 PassphraseReset: "passphrase reset", |
| paddy@149 | 387 PassphraseResetCreated: time.Now().Round(time.Millisecond), |
| paddy@149 | 388 Created: time.Now().Round(time.Millisecond), |
| paddy@149 | 389 LastSeen: time.Now().Round(time.Millisecond), |
| paddy@47 | 390 } |
| paddy@47 | 391 login := Login{ |
| paddy@47 | 392 Type: "type", |
| paddy@47 | 393 Value: "value", |
| paddy@47 | 394 ProfileID: profile.ID, |
| paddy@149 | 395 Created: time.Now().Add(-1 * time.Hour).Round(time.Millisecond), |
| paddy@149 | 396 LastUsed: time.Now().Add(-1 * time.Minute).Round(time.Millisecond), |
| paddy@47 | 397 } |
| paddy@47 | 398 for _, store := range profileStores { |
| paddy@116 | 399 context := Context{profiles: store} |
| paddy@116 | 400 err := context.SaveProfile(profile) |
| paddy@47 | 401 if err != nil { |
| paddy@47 | 402 t.Errorf("Error saving profile in %T: %s", store, err) |
| paddy@47 | 403 } |
| paddy@116 | 404 err = context.AddLogin(login) |
| paddy@47 | 405 if err != nil { |
| paddy@47 | 406 t.Errorf("Error storing login in %T: %s", store, err) |
| paddy@47 | 407 } |
| paddy@116 | 408 retrieved, err := context.GetProfileByLogin(login.Value) |
| paddy@47 | 409 if err != nil { |
| paddy@47 | 410 t.Errorf("Error retrieving profile by login from %T: %s", store, err) |
| paddy@47 | 411 } |
| paddy@47 | 412 match, field, expectation, result := compareProfiles(profile, retrieved) |
| paddy@47 | 413 if !match { |
| paddy@47 | 414 t.Errorf("Expected `%v` in the `%s` field of profile retrieved from %T, got `%v`", expectation, field, store, result) |
| paddy@47 | 415 } |
| paddy@47 | 416 } |
| paddy@47 | 417 } |
| paddy@48 | 418 |
| paddy@48 | 419 func TestProfileChangeValidation(t *testing.T) { |
| paddy@48 | 420 t.Parallel() |
| paddy@48 | 421 passphraseScheme := 1 |
| paddy@48 | 422 passphraseReset := "reset" |
| paddy@48 | 423 salt := "salt" |
| paddy@69 | 424 iterations := 100 |
| paddy@48 | 425 emptyName := "" |
| paddy@48 | 426 enteredName := "Paddy" |
| paddy@48 | 427 okPassphrase := "this is a decent passphrase" |
| paddy@48 | 428 compromised := true |
| paddy@149 | 429 lockedUntil := time.Now().Round(time.Millisecond) |
| paddy@149 | 430 resetCreated := time.Now().Round(time.Millisecond) |
| paddy@149 | 431 lastSeen := time.Now().Round(time.Millisecond) |
| paddy@48 | 432 changes := map[*ProfileChange]error{ |
| paddy@48 | 433 &ProfileChange{}: ErrEmptyChange, |
| paddy@48 | 434 &ProfileChange{PassphraseScheme: &passphraseScheme}: ErrMissingPassphrase, |
| paddy@48 | 435 &ProfileChange{PassphraseScheme: &passphraseScheme, Passphrase: &okPassphrase}: nil, |
| paddy@48 | 436 &ProfileChange{PassphraseReset: &passphraseReset}: ErrMissingPassphraseResetCreated, |
| paddy@48 | 437 &ProfileChange{PassphraseReset: &passphraseReset, PassphraseResetCreated: &resetCreated}: nil, |
| paddy@48 | 438 &ProfileChange{Salt: &salt}: ErrMissingPassphrase, |
| paddy@48 | 439 &ProfileChange{Salt: &salt, Passphrase: &okPassphrase}: nil, |
| paddy@48 | 440 &ProfileChange{Iterations: &iterations}: ErrMissingPassphrase, |
| paddy@48 | 441 &ProfileChange{Iterations: &iterations, Passphrase: &okPassphrase}: nil, |
| paddy@48 | 442 &ProfileChange{Passphrase: &okPassphrase}: nil, |
| paddy@48 | 443 &ProfileChange{Name: &emptyName}: nil, |
| paddy@48 | 444 &ProfileChange{Name: &enteredName}: nil, |
| paddy@48 | 445 &ProfileChange{Compromised: &compromised}: nil, |
| paddy@48 | 446 &ProfileChange{LockedUntil: &lockedUntil}: nil, |
| paddy@48 | 447 &ProfileChange{LastSeen: &lastSeen}: nil, |
| paddy@48 | 448 &ProfileChange{PassphraseResetCreated: &resetCreated}: ErrMissingPassphraseReset, |
| paddy@48 | 449 } |
| paddy@48 | 450 for change, expectedErr := range changes { |
| paddy@48 | 451 if err := change.Validate(); err != expectedErr { |
| paddy@48 | 452 t.Errorf("Expected %+v to give an error of %v, gave %v", change, expectedErr, err) |
| paddy@48 | 453 } |
| paddy@48 | 454 } |
| paddy@48 | 455 } |
| paddy@48 | 456 |
| paddy@48 | 457 func TestBulkProfileChangeValidation(t *testing.T) { |
| paddy@48 | 458 t.Parallel() |
| paddy@48 | 459 compromised := true |
| paddy@48 | 460 changes := map[*BulkProfileChange]error{ |
| paddy@48 | 461 &BulkProfileChange{}: ErrEmptyChange, |
| paddy@48 | 462 &BulkProfileChange{Compromised: &compromised}: nil, |
| paddy@48 | 463 } |
| paddy@48 | 464 for change, expectedErr := range changes { |
| paddy@48 | 465 if err := change.Validate(); err != expectedErr { |
| paddy@48 | 466 t.Errorf("Expected %+v to give an error of %v, gave %v", change, expectedErr, err) |
| paddy@48 | 467 } |
| paddy@48 | 468 } |
| paddy@48 | 469 } |
| paddy@128 | 470 |
| paddy@128 | 471 // BUG(paddy): We need to test the validateNewProfileRequest helper. |
| paddy@128 | 472 // BUG(paddy): We need to test the CreateProfileHandler. |
| paddy@148 | 473 // BUG(paddy): We need to test that deleting works as we expect. |