auth
auth/profile.go
Added validation for clients, split endpoints out. Split endpoints out into their own type and added associated methods to the ClientStores, so now each client can have more than one redirect endpoint. Added unit testing for endpoint methods. Added validation code to validate client changes.
| paddy@27 | 1 package auth |
| paddy@27 | 2 |
| paddy@27 | 3 import ( |
| paddy@38 | 4 "errors" |
| paddy@27 | 5 "time" |
| paddy@27 | 6 |
| paddy@27 | 7 "secondbit.org/uuid" |
| paddy@27 | 8 ) |
| paddy@27 | 9 |
| paddy@38 | 10 var ( |
| paddy@38 | 11 ErrProfileAlreadyExists = errors.New("profile already exists in ProfileStore") |
| paddy@38 | 12 ErrProfileNotFound = errors.New("profile not found in ProfileStore") |
| paddy@38 | 13 ) |
| paddy@38 | 14 |
| paddy@27 | 15 type Profile struct { |
| paddy@38 | 16 ID uuid.ID |
| paddy@38 | 17 Name string |
| paddy@38 | 18 Passphrase string |
| paddy@38 | 19 Iterations int64 |
| paddy@38 | 20 Salt string |
| paddy@38 | 21 PassphraseScheme int |
| paddy@38 | 22 Compromised bool |
| paddy@38 | 23 LockedUntil time.Time |
| paddy@38 | 24 PassphraseReset string |
| paddy@38 | 25 PassphraseResetCreated time.Time |
| paddy@38 | 26 Created time.Time |
| paddy@38 | 27 LastSeen time.Time |
| paddy@38 | 28 } |
| paddy@38 | 29 |
| paddy@38 | 30 func (p *Profile) ApplyChange(change ProfileChange) { |
| paddy@38 | 31 if change.Name != nil { |
| paddy@38 | 32 p.Name = *change.Name |
| paddy@38 | 33 } |
| paddy@38 | 34 if change.Passphrase != nil { |
| paddy@38 | 35 p.Passphrase = *change.Passphrase |
| paddy@38 | 36 } |
| paddy@38 | 37 if change.Iterations != nil { |
| paddy@38 | 38 p.Iterations = *change.Iterations |
| paddy@38 | 39 } |
| paddy@38 | 40 if change.Salt != nil { |
| paddy@38 | 41 p.Salt = *change.Salt |
| paddy@38 | 42 } |
| paddy@38 | 43 if change.PassphraseScheme != nil { |
| paddy@38 | 44 p.PassphraseScheme = *change.PassphraseScheme |
| paddy@38 | 45 } |
| paddy@38 | 46 if change.Compromised != nil { |
| paddy@38 | 47 p.Compromised = *change.Compromised |
| paddy@38 | 48 } |
| paddy@38 | 49 if change.LockedUntil != nil { |
| paddy@38 | 50 p.LockedUntil = *change.LockedUntil |
| paddy@38 | 51 } |
| paddy@38 | 52 if change.PassphraseReset != nil { |
| paddy@38 | 53 p.PassphraseReset = *change.PassphraseReset |
| paddy@38 | 54 } |
| paddy@38 | 55 if change.PassphraseResetCreated != nil { |
| paddy@38 | 56 p.PassphraseResetCreated = *change.PassphraseResetCreated |
| paddy@38 | 57 } |
| paddy@38 | 58 if change.LastSeen != nil { |
| paddy@38 | 59 p.LastSeen = *change.LastSeen |
| paddy@38 | 60 } |
| paddy@38 | 61 } |
| paddy@38 | 62 |
| paddy@38 | 63 type ProfileChange struct { |
| paddy@38 | 64 Name *string |
| paddy@38 | 65 Passphrase *string |
| paddy@38 | 66 Iterations *int64 |
| paddy@38 | 67 Salt *string |
| paddy@38 | 68 PassphraseScheme *int |
| paddy@38 | 69 Compromised *bool |
| paddy@38 | 70 LockedUntil *time.Time |
| paddy@38 | 71 PassphraseReset *string |
| paddy@38 | 72 PassphraseResetCreated *time.Time |
| paddy@38 | 73 LastSeen *time.Time |
| paddy@38 | 74 } |
| paddy@38 | 75 |
| paddy@38 | 76 func (c ProfileChange) Validate() error { |
| paddy@40 | 77 // TODO: validate profile changes |
| paddy@38 | 78 return nil |
| paddy@27 | 79 } |
| paddy@27 | 80 |
| paddy@27 | 81 type Login struct { |
| paddy@27 | 82 Type string |
| paddy@27 | 83 Value string |
| paddy@27 | 84 ProfileID uuid.ID |
| paddy@27 | 85 Created time.Time |
| paddy@27 | 86 LastUsed time.Time |
| paddy@27 | 87 } |
| paddy@27 | 88 |
| paddy@27 | 89 type ProfileStore interface { |
| paddy@27 | 90 GetProfileByID(id uuid.ID) (Profile, error) |
| paddy@38 | 91 GetProfileByLogin(login Login) (Profile, error) |
| paddy@38 | 92 SaveProfile(profile Profile) error |
| paddy@38 | 93 UpdateProfile(id uuid.ID, change ProfileChange) error |
| paddy@27 | 94 DeleteProfile(id uuid.ID) error |
| paddy@38 | 95 } |
| paddy@27 | 96 |
| paddy@38 | 97 func (m *Memstore) GetProfileByID(id uuid.ID) (Profile, error) { |
| paddy@38 | 98 m.profileLock.RLock() |
| paddy@38 | 99 defer m.profileLock.RUnlock() |
| paddy@38 | 100 p, ok := m.profiles[id.String()] |
| paddy@38 | 101 if !ok { |
| paddy@38 | 102 return Profile{}, ErrProfileNotFound |
| paddy@38 | 103 } |
| paddy@38 | 104 return p, nil |
| paddy@27 | 105 } |
| paddy@38 | 106 |
| paddy@38 | 107 func (m *Memstore) GetProfileByLogin(login Login) (Profile, error) { |
| paddy@40 | 108 // TODO: get profile by login |
| paddy@38 | 109 return Profile{}, nil |
| paddy@38 | 110 } |
| paddy@38 | 111 |
| paddy@38 | 112 func (m *Memstore) SaveProfile(profile Profile) error { |
| paddy@38 | 113 m.profileLock.Lock() |
| paddy@38 | 114 defer m.profileLock.Unlock() |
| paddy@38 | 115 _, ok := m.profiles[profile.ID.String()] |
| paddy@38 | 116 if ok { |
| paddy@38 | 117 return ErrProfileAlreadyExists |
| paddy@38 | 118 } |
| paddy@38 | 119 m.profiles[profile.ID.String()] = profile |
| paddy@38 | 120 return nil |
| paddy@38 | 121 } |
| paddy@38 | 122 |
| paddy@38 | 123 func (m *Memstore) UpdateProfile(id uuid.ID, change ProfileChange) error { |
| paddy@38 | 124 m.profileLock.Lock() |
| paddy@38 | 125 defer m.profileLock.Unlock() |
| paddy@38 | 126 p, ok := m.profiles[id.String()] |
| paddy@38 | 127 if !ok { |
| paddy@38 | 128 return ErrProfileNotFound |
| paddy@38 | 129 } |
| paddy@38 | 130 p.ApplyChange(change) |
| paddy@38 | 131 m.profiles[id.String()] = p |
| paddy@38 | 132 return nil |
| paddy@38 | 133 } |
| paddy@38 | 134 |
| paddy@38 | 135 func (m *Memstore) DeleteProfile(id uuid.ID) error { |
| paddy@38 | 136 m.profileLock.Lock() |
| paddy@38 | 137 defer m.profileLock.Unlock() |
| paddy@38 | 138 _, ok := m.profiles[id.String()] |
| paddy@38 | 139 if !ok { |
| paddy@38 | 140 return ErrProfileNotFound |
| paddy@38 | 141 } |
| paddy@38 | 142 delete(m.profiles, id.String()) |
| paddy@38 | 143 return nil |
| paddy@38 | 144 } |
| paddy@40 | 145 |
| paddy@40 | 146 // TODO: login store |
| paddy@40 | 147 // TODO: login delete |
| paddy@40 | 148 // TODO: login update |