auth
auth/memstore.go
Parse the redirect URI early, add new failure modes to tests. When obtaining a grant code, parse the redirect_uri early as a URL, so we can fail without even hitting the database, if possible. Add a test to cover the scenario where the client doesn't specify an endpoint, and the client has no endpoints registered. Add a test to cover the scenario where the client has two endpoints registered, but doesn't specify an endpoint. Fix the test that tested for invalid URLs by actually using an invalid URL. Apparently, "not a URL" is a valid URL. Go figure.
| paddy@28 | 1 package auth |
| paddy@28 | 2 |
| paddy@31 | 3 import ( |
| paddy@31 | 4 "sync" |
| paddy@31 | 5 |
| paddy@45 | 6 "code.secondbit.org/uuid" |
| paddy@31 | 7 ) |
| paddy@28 | 8 |
| paddy@57 | 9 type memstore struct { |
| paddy@28 | 10 tokens map[string]Token |
| paddy@28 | 11 refreshTokenLookup map[string]string |
| paddy@28 | 12 profileTokenLookup map[string][]string |
| paddy@28 | 13 tokenLock sync.RWMutex |
| paddy@29 | 14 |
| paddy@29 | 15 grants map[string]Grant |
| paddy@29 | 16 grantLock sync.RWMutex |
| paddy@31 | 17 |
| paddy@31 | 18 clients map[string]Client |
| paddy@31 | 19 profileClientLookup map[string][]uuid.ID |
| paddy@31 | 20 clientLock sync.RWMutex |
| paddy@38 | 21 |
| paddy@41 | 22 endpoints map[string][]Endpoint |
| paddy@41 | 23 endpointLock sync.RWMutex |
| paddy@41 | 24 |
| paddy@38 | 25 profiles map[string]Profile |
| paddy@38 | 26 profileLock sync.RWMutex |
| paddy@44 | 27 |
| paddy@44 | 28 logins map[string]Login |
| paddy@44 | 29 profileLoginLookup map[string][]string |
| paddy@44 | 30 loginLock sync.RWMutex |
| paddy@28 | 31 } |
| paddy@28 | 32 |
| paddy@57 | 33 // NewMemstore returns an in-memory version of our datastores, |
| paddy@57 | 34 // which is handy for tests. Though the implementation is concurrency-safe, |
| paddy@57 | 35 // if makes no attempt to persist the data, and therefore it is inadvisable |
| paddy@57 | 36 // to use it in a production setting. |
| paddy@57 | 37 func NewMemstore() *memstore { |
| paddy@57 | 38 return &memstore{ |
| paddy@31 | 39 tokens: map[string]Token{}, |
| paddy@31 | 40 refreshTokenLookup: map[string]string{}, |
| paddy@31 | 41 profileTokenLookup: map[string][]string{}, |
| paddy@31 | 42 grants: map[string]Grant{}, |
| paddy@31 | 43 clients: map[string]Client{}, |
| paddy@31 | 44 profileClientLookup: map[string][]uuid.ID{}, |
| paddy@41 | 45 endpoints: map[string][]Endpoint{}, |
| paddy@38 | 46 profiles: map[string]Profile{}, |
| paddy@44 | 47 logins: map[string]Login{}, |
| paddy@44 | 48 profileLoginLookup: map[string][]string{}, |
| paddy@28 | 49 } |
| paddy@28 | 50 } |
| paddy@28 | 51 |
| paddy@57 | 52 func (m *memstore) lookupTokenByRefresh(token string) (string, error) { |
| paddy@28 | 53 m.tokenLock.RLock() |
| paddy@28 | 54 defer m.tokenLock.RUnlock() |
| paddy@28 | 55 t, ok := m.refreshTokenLookup[token] |
| paddy@28 | 56 if !ok { |
| paddy@28 | 57 return "", ErrTokenNotFound |
| paddy@28 | 58 } |
| paddy@28 | 59 return t, nil |
| paddy@28 | 60 } |
| paddy@28 | 61 |
| paddy@57 | 62 func (m *memstore) lookupTokensByProfileID(id string) ([]string, error) { |
| paddy@28 | 63 m.tokenLock.RLock() |
| paddy@28 | 64 defer m.tokenLock.RUnlock() |
| paddy@28 | 65 return m.profileTokenLookup[id], nil |
| paddy@28 | 66 } |
| paddy@31 | 67 |
| paddy@57 | 68 func (m *memstore) lookupClientsByProfileID(id string) []uuid.ID { |
| paddy@31 | 69 m.clientLock.RLock() |
| paddy@31 | 70 defer m.clientLock.RUnlock() |
| paddy@33 | 71 c, ok := m.profileClientLookup[id] |
| paddy@33 | 72 if !ok { |
| paddy@33 | 73 return []uuid.ID{} |
| paddy@33 | 74 } |
| paddy@33 | 75 return c |
| paddy@31 | 76 } |