auth
auth/scope.go
Randomly generate codes. We've been using our IDs for auth codes. But our IDs may at some point be non-random, for the purpose of optimising database performance, or some other perfectly valid reason. Auth codes we always want to be random, and have no relation to IDs, so why conflate them? Instead, we pull 16 random bytes out of crypto/rand.Reader and hex encode them.
| paddy@134 | 1 package auth |
| paddy@134 | 2 |
| paddy@134 | 3 import ( |
| paddy@134 | 4 "errors" |
| paddy@134 | 5 "fmt" |
| paddy@134 | 6 "sort" |
| paddy@134 | 7 ) |
| paddy@134 | 8 |
| paddy@134 | 9 var ( |
| paddy@134 | 10 ErrNoScopeStore = errors.New("scopeStore not set in Context") |
| paddy@134 | 11 ) |
| paddy@134 | 12 |
| paddy@134 | 13 type ErrScopeNotFound struct { |
| paddy@134 | 14 Pos int |
| paddy@134 | 15 ID string |
| paddy@134 | 16 } |
| paddy@134 | 17 |
| paddy@134 | 18 func (e ErrScopeNotFound) Error() string { |
| paddy@134 | 19 return fmt.Sprintf("scope %s couldn't be found", e.ID) |
| paddy@134 | 20 } |
| paddy@134 | 21 |
| paddy@134 | 22 type ErrScopeAlreadyExists struct { |
| paddy@134 | 23 Pos int |
| paddy@134 | 24 ID string |
| paddy@134 | 25 } |
| paddy@134 | 26 |
| paddy@134 | 27 func (e ErrScopeAlreadyExists) Error() string { |
| paddy@134 | 28 return fmt.Sprintf("scope %s already exists", e.ID) |
| paddy@134 | 29 } |
| paddy@134 | 30 |
| paddy@134 | 31 // Scope represents a limit on the access that a grant provides. |
| paddy@134 | 32 type Scope struct { |
| paddy@134 | 33 ID string |
| paddy@134 | 34 Name string |
| paddy@134 | 35 Description string |
| paddy@134 | 36 } |
| paddy@134 | 37 |
| paddy@134 | 38 func (s *Scope) ApplyChange(change ScopeChange) { |
| paddy@134 | 39 if change.Name != nil { |
| paddy@134 | 40 s.Name = *change.Name |
| paddy@134 | 41 } |
| paddy@134 | 42 if change.Description != nil { |
| paddy@134 | 43 s.Description = *change.Description |
| paddy@134 | 44 } |
| paddy@134 | 45 } |
| paddy@134 | 46 |
| paddy@134 | 47 type sortedScopes []Scope |
| paddy@134 | 48 |
| paddy@134 | 49 func (s sortedScopes) Len() int { |
| paddy@134 | 50 return len(s) |
| paddy@134 | 51 } |
| paddy@134 | 52 |
| paddy@134 | 53 func (s sortedScopes) Swap(i, j int) { |
| paddy@134 | 54 s[i], s[j] = s[j], s[i] |
| paddy@134 | 55 } |
| paddy@134 | 56 |
| paddy@134 | 57 func (s sortedScopes) Less(i, j int) bool { |
| paddy@134 | 58 return s[i].ID < s[j].ID |
| paddy@134 | 59 } |
| paddy@134 | 60 |
| paddy@134 | 61 // ScopeChange represents a change to a Scope. |
| paddy@134 | 62 type ScopeChange struct { |
| paddy@134 | 63 ID string |
| paddy@134 | 64 Name *string |
| paddy@134 | 65 Description *string |
| paddy@134 | 66 } |
| paddy@134 | 67 |
| paddy@134 | 68 type scopeStore interface { |
| paddy@134 | 69 createScopes(scopes []Scope) error |
| paddy@134 | 70 getScopes(ids []string) ([]Scope, error) |
| paddy@134 | 71 updateScopes(changes []ScopeChange) ([]Scope, error) |
| paddy@134 | 72 removeScopes(ids []string) error |
| paddy@134 | 73 listScopes() ([]Scope, error) |
| paddy@134 | 74 } |
| paddy@134 | 75 |
| paddy@134 | 76 func (m *memstore) createScopes(scopes []Scope) error { |
| paddy@134 | 77 m.scopeLock.Lock() |
| paddy@134 | 78 defer m.scopeLock.Unlock() |
| paddy@134 | 79 |
| paddy@134 | 80 for pos, scope := range scopes { |
| paddy@134 | 81 if _, ok := m.scopes[scope.ID]; ok { |
| paddy@134 | 82 return ErrScopeAlreadyExists{Pos: pos, ID: scope.ID} |
| paddy@134 | 83 } |
| paddy@134 | 84 } |
| paddy@134 | 85 for _, scope := range scopes { |
| paddy@134 | 86 m.scopes[scope.ID] = scope |
| paddy@134 | 87 } |
| paddy@134 | 88 return nil |
| paddy@134 | 89 } |
| paddy@134 | 90 |
| paddy@134 | 91 func (m *memstore) getScopes(ids []string) ([]Scope, error) { |
| paddy@134 | 92 m.scopeLock.RLock() |
| paddy@134 | 93 defer m.scopeLock.RUnlock() |
| paddy@134 | 94 |
| paddy@134 | 95 scopes := []Scope{} |
| paddy@134 | 96 for pos, id := range ids { |
| paddy@134 | 97 scope, ok := m.scopes[id] |
| paddy@134 | 98 if !ok { |
| paddy@134 | 99 return []Scope{}, ErrScopeNotFound{ID: id, Pos: pos} |
| paddy@134 | 100 } |
| paddy@134 | 101 scopes = append(scopes, scope) |
| paddy@134 | 102 } |
| paddy@134 | 103 sorted := sortedScopes(scopes) |
| paddy@134 | 104 sort.Sort(sorted) |
| paddy@134 | 105 scopes = sorted |
| paddy@134 | 106 return scopes, nil |
| paddy@134 | 107 } |
| paddy@134 | 108 |
| paddy@134 | 109 func (m *memstore) updateScopes(changes []ScopeChange) ([]Scope, error) { |
| paddy@134 | 110 m.scopeLock.Lock() |
| paddy@134 | 111 defer m.scopeLock.Unlock() |
| paddy@134 | 112 |
| paddy@134 | 113 scopes := []Scope{} |
| paddy@134 | 114 |
| paddy@134 | 115 for pos, change := range changes { |
| paddy@134 | 116 if _, ok := m.scopes[change.ID]; !ok { |
| paddy@134 | 117 return []Scope{}, ErrScopeNotFound{Pos: pos, ID: change.ID} |
| paddy@134 | 118 } |
| paddy@134 | 119 } |
| paddy@134 | 120 for _, change := range changes { |
| paddy@134 | 121 scope := m.scopes[change.ID] |
| paddy@134 | 122 scope.ApplyChange(change) |
| paddy@134 | 123 m.scopes[change.ID] = scope |
| paddy@134 | 124 scopes = append(scopes, scope) |
| paddy@134 | 125 } |
| paddy@134 | 126 sorted := sortedScopes(scopes) |
| paddy@134 | 127 sort.Sort(sorted) |
| paddy@134 | 128 scopes = sorted |
| paddy@134 | 129 return scopes, nil |
| paddy@134 | 130 } |
| paddy@134 | 131 |
| paddy@134 | 132 func (m *memstore) removeScopes(ids []string) error { |
| paddy@134 | 133 m.scopeLock.Lock() |
| paddy@134 | 134 defer m.scopeLock.Unlock() |
| paddy@134 | 135 |
| paddy@134 | 136 for pos, id := range ids { |
| paddy@134 | 137 if _, ok := m.scopes[id]; !ok { |
| paddy@134 | 138 return ErrScopeNotFound{Pos: pos, ID: id} |
| paddy@134 | 139 } |
| paddy@134 | 140 } |
| paddy@134 | 141 for _, id := range ids { |
| paddy@134 | 142 delete(m.scopes, id) |
| paddy@134 | 143 } |
| paddy@134 | 144 return nil |
| paddy@134 | 145 } |
| paddy@134 | 146 |
| paddy@134 | 147 func (m *memstore) listScopes() ([]Scope, error) { |
| paddy@134 | 148 m.scopeLock.RLock() |
| paddy@134 | 149 defer m.scopeLock.RUnlock() |
| paddy@134 | 150 |
| paddy@134 | 151 scopes := []Scope{} |
| paddy@134 | 152 for _, scope := range m.scopes { |
| paddy@134 | 153 scopes = append(scopes, scope) |
| paddy@134 | 154 } |
| paddy@134 | 155 sorted := sortedScopes(scopes) |
| paddy@134 | 156 sort.Sort(sorted) |
| paddy@134 | 157 scopes = sorted |
| paddy@134 | 158 return scopes, nil |
| paddy@134 | 159 } |