auth

Paddy 2014-10-15 Parent:d78418fb9f56 Child:e45bfa2abc00

49:73a9f7a6af54 Go to Latest

auth/profile.go

Update error strings, add ErrNo*Store errors. Update error strings to consistently begin with a lowercase letter and end without punctuation, as per the Go style guide. See https://code.google.com/p/go-wiki/wiki/CodeReviewComments#Error_Strings For each type of Store, add an ErrNo*Store error (e.g., ErrNoTokenStore) variable, to prepare for our Context type, which will throw these errors when a Store is used without being set.

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