auth

Paddy 2014-10-15 Parent:73a9f7a6af54 Child:e45bfa2abc00

52:aff6863e3cb3 Go to Latest

auth/profile.go

Move HTTP tests to http_test.go, rename the GetGrant test. Rename the GetGrant test to something that makes it more obvious, and indicate that we're only testing for successful responses in this function (e.g., responses that should be successfully handled). Another function will deal with failure modes. Move the function to a new http_test.go file. The model files are shouldn't have information about how the models are being represented.

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