auth

Paddy 2014-09-27 Parent:fb827644bfd8 Child:d78418fb9f56

45:3a6a65ed380c Go to Latest

auth/profile.go

Update uuid import path, test for multiple profile updates. Test updating multiple profiles in one request (e.g., when profiles become compromised.) Update the uuid import path to use the new code.secondbit.org/uuid import path.

History
paddy@27 1 package auth
paddy@27 2
paddy@27 3 import (
paddy@38 4 "errors"
paddy@27 5 "time"
paddy@27 6
paddy@45 7 "code.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@44 13 ErrLoginAlreadyExists = errors.New("login already exists in ProfileStore")
paddy@44 14 ErrLoginNotFound = errors.New("login not found in ProfileStore")
paddy@38 15 )
paddy@38 16
paddy@27 17 type Profile struct {
paddy@38 18 ID uuid.ID
paddy@38 19 Name string
paddy@38 20 Passphrase string
paddy@38 21 Iterations int64
paddy@38 22 Salt string
paddy@38 23 PassphraseScheme int
paddy@38 24 Compromised bool
paddy@38 25 LockedUntil time.Time
paddy@38 26 PassphraseReset string
paddy@38 27 PassphraseResetCreated time.Time
paddy@38 28 Created time.Time
paddy@38 29 LastSeen time.Time
paddy@38 30 }
paddy@38 31
paddy@38 32 func (p *Profile) ApplyChange(change ProfileChange) {
paddy@38 33 if change.Name != nil {
paddy@38 34 p.Name = *change.Name
paddy@38 35 }
paddy@38 36 if change.Passphrase != nil {
paddy@38 37 p.Passphrase = *change.Passphrase
paddy@38 38 }
paddy@38 39 if change.Iterations != nil {
paddy@38 40 p.Iterations = *change.Iterations
paddy@38 41 }
paddy@38 42 if change.Salt != nil {
paddy@38 43 p.Salt = *change.Salt
paddy@38 44 }
paddy@38 45 if change.PassphraseScheme != nil {
paddy@38 46 p.PassphraseScheme = *change.PassphraseScheme
paddy@38 47 }
paddy@38 48 if change.Compromised != nil {
paddy@38 49 p.Compromised = *change.Compromised
paddy@38 50 }
paddy@38 51 if change.LockedUntil != nil {
paddy@38 52 p.LockedUntil = *change.LockedUntil
paddy@38 53 }
paddy@38 54 if change.PassphraseReset != nil {
paddy@38 55 p.PassphraseReset = *change.PassphraseReset
paddy@38 56 }
paddy@38 57 if change.PassphraseResetCreated != nil {
paddy@38 58 p.PassphraseResetCreated = *change.PassphraseResetCreated
paddy@38 59 }
paddy@38 60 if change.LastSeen != nil {
paddy@38 61 p.LastSeen = *change.LastSeen
paddy@38 62 }
paddy@38 63 }
paddy@38 64
paddy@44 65 func (p *Profile) ApplyBulkChange(change BulkProfileChange) {
paddy@44 66 if change.Compromised != nil {
paddy@44 67 p.Compromised = *change.Compromised
paddy@44 68 }
paddy@44 69 }
paddy@44 70
paddy@38 71 type ProfileChange struct {
paddy@38 72 Name *string
paddy@38 73 Passphrase *string
paddy@38 74 Iterations *int64
paddy@38 75 Salt *string
paddy@38 76 PassphraseScheme *int
paddy@38 77 Compromised *bool
paddy@38 78 LockedUntil *time.Time
paddy@38 79 PassphraseReset *string
paddy@38 80 PassphraseResetCreated *time.Time
paddy@38 81 LastSeen *time.Time
paddy@38 82 }
paddy@38 83
paddy@38 84 func (c ProfileChange) Validate() error {
paddy@40 85 // TODO: validate profile changes
paddy@38 86 return nil
paddy@27 87 }
paddy@27 88
paddy@44 89 type BulkProfileChange struct {
paddy@44 90 Compromised *bool
paddy@44 91 }
paddy@44 92
paddy@44 93 func (b BulkProfileChange) Validate() error {
paddy@44 94 // TODO: validate bulk profile changs
paddy@44 95 return nil
paddy@44 96 }
paddy@44 97
paddy@27 98 type Login struct {
paddy@27 99 Type string
paddy@27 100 Value string
paddy@27 101 ProfileID uuid.ID
paddy@27 102 Created time.Time
paddy@27 103 LastUsed time.Time
paddy@27 104 }
paddy@27 105
paddy@27 106 type ProfileStore interface {
paddy@27 107 GetProfileByID(id uuid.ID) (Profile, error)
paddy@44 108 GetProfileByLogin(loginType, value string) (Profile, error)
paddy@38 109 SaveProfile(profile Profile) error
paddy@38 110 UpdateProfile(id uuid.ID, change ProfileChange) error
paddy@44 111 UpdateProfiles(ids []uuid.ID, change BulkProfileChange) error
paddy@27 112 DeleteProfile(id uuid.ID) error
paddy@44 113
paddy@44 114 AddLogin(login Login) error
paddy@44 115 RemoveLogin(loginType, value string, profile uuid.ID) error
paddy@44 116 RecordLoginUse(loginType, value string, when time.Time) error
paddy@44 117 ListLogins(profile uuid.ID, num, offset int) ([]Login, error)
paddy@38 118 }
paddy@27 119
paddy@38 120 func (m *Memstore) GetProfileByID(id uuid.ID) (Profile, error) {
paddy@38 121 m.profileLock.RLock()
paddy@38 122 defer m.profileLock.RUnlock()
paddy@38 123 p, ok := m.profiles[id.String()]
paddy@38 124 if !ok {
paddy@38 125 return Profile{}, ErrProfileNotFound
paddy@38 126 }
paddy@38 127 return p, nil
paddy@27 128 }
paddy@38 129
paddy@44 130 func (m *Memstore) GetProfileByLogin(loginType, value string) (Profile, error) {
paddy@44 131 m.loginLock.RLock()
paddy@44 132 defer m.loginLock.RUnlock()
paddy@44 133 login, ok := m.logins[loginType+":"+value]
paddy@44 134 if !ok {
paddy@44 135 return Profile{}, ErrLoginNotFound
paddy@44 136 }
paddy@44 137 m.profileLock.RLock()
paddy@44 138 defer m.profileLock.RUnlock()
paddy@44 139 profile, ok := m.profiles[login.ProfileID.String()]
paddy@44 140 if !ok {
paddy@44 141 return Profile{}, ErrProfileNotFound
paddy@44 142 }
paddy@44 143 return profile, nil
paddy@38 144 }
paddy@38 145
paddy@38 146 func (m *Memstore) SaveProfile(profile Profile) error {
paddy@38 147 m.profileLock.Lock()
paddy@38 148 defer m.profileLock.Unlock()
paddy@38 149 _, ok := m.profiles[profile.ID.String()]
paddy@38 150 if ok {
paddy@38 151 return ErrProfileAlreadyExists
paddy@38 152 }
paddy@38 153 m.profiles[profile.ID.String()] = profile
paddy@38 154 return nil
paddy@38 155 }
paddy@38 156
paddy@38 157 func (m *Memstore) UpdateProfile(id uuid.ID, change ProfileChange) error {
paddy@38 158 m.profileLock.Lock()
paddy@38 159 defer m.profileLock.Unlock()
paddy@38 160 p, ok := m.profiles[id.String()]
paddy@38 161 if !ok {
paddy@38 162 return ErrProfileNotFound
paddy@38 163 }
paddy@38 164 p.ApplyChange(change)
paddy@38 165 m.profiles[id.String()] = p
paddy@38 166 return nil
paddy@38 167 }
paddy@38 168
paddy@44 169 func (m *Memstore) UpdateProfiles(ids []uuid.ID, change BulkProfileChange) error {
paddy@44 170 m.profileLock.Lock()
paddy@44 171 defer m.profileLock.Unlock()
paddy@44 172 for id, profile := range m.profiles {
paddy@44 173 for _, i := range ids {
paddy@44 174 if id == i.String() {
paddy@44 175 profile.ApplyBulkChange(change)
paddy@44 176 m.profiles[id] = profile
paddy@44 177 break
paddy@44 178 }
paddy@44 179 }
paddy@44 180 }
paddy@44 181 return nil
paddy@44 182 }
paddy@44 183
paddy@38 184 func (m *Memstore) DeleteProfile(id uuid.ID) error {
paddy@38 185 m.profileLock.Lock()
paddy@38 186 defer m.profileLock.Unlock()
paddy@38 187 _, ok := m.profiles[id.String()]
paddy@38 188 if !ok {
paddy@38 189 return ErrProfileNotFound
paddy@38 190 }
paddy@38 191 delete(m.profiles, id.String())
paddy@38 192 return nil
paddy@38 193 }
paddy@40 194
paddy@44 195 func (m *Memstore) AddLogin(login Login) error {
paddy@44 196 m.loginLock.Lock()
paddy@44 197 defer m.loginLock.Unlock()
paddy@44 198 _, ok := m.logins[login.Type+":"+login.Value]
paddy@44 199 if ok {
paddy@44 200 return ErrLoginAlreadyExists
paddy@44 201 }
paddy@44 202 m.logins[login.Type+":"+login.Value] = login
paddy@44 203 m.profileLoginLookup[login.ProfileID.String()] = append(m.profileLoginLookup[login.ProfileID.String()], login.Type+":"+login.Value)
paddy@44 204 return nil
paddy@44 205 }
paddy@44 206
paddy@44 207 func (m *Memstore) RemoveLogin(loginType, value string, profile uuid.ID) error {
paddy@44 208 m.loginLock.Lock()
paddy@44 209 defer m.loginLock.Unlock()
paddy@44 210 l, ok := m.logins[loginType+":"+value]
paddy@44 211 if !ok {
paddy@44 212 return ErrLoginNotFound
paddy@44 213 }
paddy@44 214 if !l.ProfileID.Equal(profile) {
paddy@44 215 return ErrLoginNotFound
paddy@44 216 }
paddy@44 217 delete(m.logins, loginType+":"+value)
paddy@44 218 pos := -1
paddy@44 219 for p, id := range m.profileLoginLookup[profile.String()] {
paddy@44 220 if id == loginType+":"+value {
paddy@44 221 pos = p
paddy@44 222 break
paddy@44 223 }
paddy@44 224 }
paddy@44 225 if pos >= 0 {
paddy@44 226 m.profileLoginLookup[profile.String()] = append(m.profileLoginLookup[profile.String()][:pos], m.profileLoginLookup[profile.String()][pos+1:]...)
paddy@44 227 }
paddy@44 228 return nil
paddy@44 229 }
paddy@44 230
paddy@44 231 func (m *Memstore) RecordLoginUse(loginType, value string, when time.Time) error {
paddy@44 232 m.loginLock.Lock()
paddy@44 233 defer m.loginLock.Unlock()
paddy@44 234 l, ok := m.logins[loginType+":"+value]
paddy@44 235 if !ok {
paddy@44 236 return ErrLoginNotFound
paddy@44 237 }
paddy@44 238 l.LastUsed = when
paddy@44 239 m.logins[loginType+":"+value] = l
paddy@44 240 return nil
paddy@44 241 }
paddy@44 242
paddy@44 243 func (m *Memstore) ListLogins(profile uuid.ID, num, offset int) ([]Login, error) {
paddy@44 244 m.loginLock.RLock()
paddy@44 245 defer m.loginLock.RUnlock()
paddy@44 246 ids, ok := m.profileLoginLookup[profile.String()]
paddy@44 247 if !ok {
paddy@44 248 return []Login{}, nil
paddy@44 249 }
paddy@44 250 if len(ids) > num+offset {
paddy@44 251 ids = ids[offset : num+offset]
paddy@44 252 } else if len(ids) > offset {
paddy@44 253 ids = ids[offset:]
paddy@44 254 } else {
paddy@44 255 return []Login{}, nil
paddy@44 256 }
paddy@44 257 logins := []Login{}
paddy@44 258 for _, id := range ids {
paddy@44 259 login, ok := m.logins[id]
paddy@44 260 if !ok {
paddy@44 261 continue
paddy@44 262 }
paddy@44 263 logins = append(logins, login)
paddy@44 264 }
paddy@44 265 return logins, nil
paddy@44 266 }