auth

Paddy 2015-06-29 Parent:8ecb60d29b0d

176:fc68085eb40d Go to Latest

auth/profile_test.go

Add kubernetes definitions. Define a replication controller that will spin up authd servers (using Ducky right now--other instances should rename the ducky parts appropriately). Also, my understanding of which labels go where may be shaky, which is probably evidenced by the fact that all of these things share the same lables. _Whatever_. It also hooks the generated pods up to the JWT secret volume, so they can properly read the JWT secret. Also, created a LoadBalancer Service that will route traffic to the pods created by the Replication Controller.

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