auth
auth/scope.go
Test our GetClientHandler function, add isAuthError helper. Add a helper that identifies whether the error passed to it is an authentication error or is some other type of error. This is useful fo checking whether or not an internal error occurred while authenticating users. Update all instances where we call our authentication helper to make them use the new error helper. All tests continue to pass. Add a new test case for retrieving a client as an unauthenticated user. This clears the client's secret from the response before sending it. Update the GetClientHandler function to return the secret when the owner of the client used Basic Auth in the request. Add a new test case for retrieving a client as an authenticated user, both the owner and a non-owner user. This makes sure the secret is divulged only in the appropriate cases.
| 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 } |