auth
auth/memstore.go
Attach our Scope type to AuthCodes and Tokens. When obtaining an AuthorizationCode or Token, attach a slice of strings, each one a Scope ID, instead of just attaching the encoded string the user passes in. This will allow us to change our Scope encoding down the line, and is more conceptually faithful. Also, if an authorization request is made with an invalid scope, return the invalid_scope error.
| paddy@28 | 1 package auth |
| paddy@28 | 2 |
| paddy@31 | 3 import ( |
| paddy@31 | 4 "sync" |
| paddy@31 | 5 |
| paddy@107 | 6 "code.secondbit.org/uuid.hg" |
| 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@87 | 15 authCodes map[string]AuthorizationCode |
| paddy@87 | 16 authCodeLock 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@77 | 31 |
| paddy@77 | 32 sessions map[string]Session |
| paddy@77 | 33 sessionLock sync.RWMutex |
| paddy@134 | 34 |
| paddy@134 | 35 scopes map[string]Scope |
| paddy@134 | 36 scopeLock sync.RWMutex |
| paddy@28 | 37 } |
| paddy@28 | 38 |
| paddy@57 | 39 // NewMemstore returns an in-memory version of our datastores, |
| paddy@57 | 40 // which is handy for tests. Though the implementation is concurrency-safe, |
| paddy@57 | 41 // if makes no attempt to persist the data, and therefore it is inadvisable |
| paddy@57 | 42 // to use it in a production setting. |
| paddy@57 | 43 func NewMemstore() *memstore { |
| paddy@57 | 44 return &memstore{ |
| paddy@31 | 45 tokens: map[string]Token{}, |
| paddy@31 | 46 refreshTokenLookup: map[string]string{}, |
| paddy@31 | 47 profileTokenLookup: map[string][]string{}, |
| paddy@87 | 48 authCodes: map[string]AuthorizationCode{}, |
| paddy@31 | 49 clients: map[string]Client{}, |
| paddy@31 | 50 profileClientLookup: map[string][]uuid.ID{}, |
| paddy@41 | 51 endpoints: map[string][]Endpoint{}, |
| paddy@38 | 52 profiles: map[string]Profile{}, |
| paddy@44 | 53 logins: map[string]Login{}, |
| paddy@44 | 54 profileLoginLookup: map[string][]string{}, |
| paddy@77 | 55 sessions: map[string]Session{}, |
| paddy@134 | 56 scopes: map[string]Scope{}, |
| paddy@28 | 57 } |
| paddy@28 | 58 } |
| paddy@28 | 59 |
| paddy@57 | 60 func (m *memstore) lookupTokenByRefresh(token string) (string, error) { |
| paddy@28 | 61 m.tokenLock.RLock() |
| paddy@28 | 62 defer m.tokenLock.RUnlock() |
| paddy@28 | 63 t, ok := m.refreshTokenLookup[token] |
| paddy@28 | 64 if !ok { |
| paddy@28 | 65 return "", ErrTokenNotFound |
| paddy@28 | 66 } |
| paddy@28 | 67 return t, nil |
| paddy@28 | 68 } |
| paddy@28 | 69 |
| paddy@57 | 70 func (m *memstore) lookupTokensByProfileID(id string) ([]string, error) { |
| paddy@28 | 71 m.tokenLock.RLock() |
| paddy@28 | 72 defer m.tokenLock.RUnlock() |
| paddy@28 | 73 return m.profileTokenLookup[id], nil |
| paddy@28 | 74 } |
| paddy@31 | 75 |
| paddy@57 | 76 func (m *memstore) lookupClientsByProfileID(id string) []uuid.ID { |
| paddy@31 | 77 m.clientLock.RLock() |
| paddy@31 | 78 defer m.clientLock.RUnlock() |
| paddy@33 | 79 c, ok := m.profileClientLookup[id] |
| paddy@33 | 80 if !ok { |
| paddy@33 | 81 return []uuid.ID{} |
| paddy@33 | 82 } |
| paddy@33 | 83 return c |
| paddy@31 | 84 } |