auth
2015-02-20
Child:de5e09680f6b
auth/scope.go
Introduced scopes. Created a Scope type and a scopeStore interface, along with the memstore methods for the scopeStore. This will allow applications to define access with granularity, so users can grant access to some data, not _all_ data. We're operating on the assumption that there won't be an unreasonable number of scopes defined, so there is no paging operation included for the ListScopes method. This is a decision that may have to be revisited in the future, depending on usecases.
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/scope.go Fri Feb 20 22:34:43 2015 -0500 1.3 @@ -0,0 +1,159 @@ 1.4 +package auth 1.5 + 1.6 +import ( 1.7 + "errors" 1.8 + "fmt" 1.9 + "sort" 1.10 +) 1.11 + 1.12 +var ( 1.13 + ErrNoScopeStore = errors.New("scopeStore not set in Context") 1.14 +) 1.15 + 1.16 +type ErrScopeNotFound struct { 1.17 + Pos int 1.18 + ID string 1.19 +} 1.20 + 1.21 +func (e ErrScopeNotFound) Error() string { 1.22 + return fmt.Sprintf("scope %s couldn't be found", e.ID) 1.23 +} 1.24 + 1.25 +type ErrScopeAlreadyExists struct { 1.26 + Pos int 1.27 + ID string 1.28 +} 1.29 + 1.30 +func (e ErrScopeAlreadyExists) Error() string { 1.31 + return fmt.Sprintf("scope %s already exists", e.ID) 1.32 +} 1.33 + 1.34 +// Scope represents a limit on the access that a grant provides. 1.35 +type Scope struct { 1.36 + ID string 1.37 + Name string 1.38 + Description string 1.39 +} 1.40 + 1.41 +func (s *Scope) ApplyChange(change ScopeChange) { 1.42 + if change.Name != nil { 1.43 + s.Name = *change.Name 1.44 + } 1.45 + if change.Description != nil { 1.46 + s.Description = *change.Description 1.47 + } 1.48 +} 1.49 + 1.50 +type sortedScopes []Scope 1.51 + 1.52 +func (s sortedScopes) Len() int { 1.53 + return len(s) 1.54 +} 1.55 + 1.56 +func (s sortedScopes) Swap(i, j int) { 1.57 + s[i], s[j] = s[j], s[i] 1.58 +} 1.59 + 1.60 +func (s sortedScopes) Less(i, j int) bool { 1.61 + return s[i].ID < s[j].ID 1.62 +} 1.63 + 1.64 +// ScopeChange represents a change to a Scope. 1.65 +type ScopeChange struct { 1.66 + ID string 1.67 + Name *string 1.68 + Description *string 1.69 +} 1.70 + 1.71 +type scopeStore interface { 1.72 + createScopes(scopes []Scope) error 1.73 + getScopes(ids []string) ([]Scope, error) 1.74 + updateScopes(changes []ScopeChange) ([]Scope, error) 1.75 + removeScopes(ids []string) error 1.76 + listScopes() ([]Scope, error) 1.77 +} 1.78 + 1.79 +func (m *memstore) createScopes(scopes []Scope) error { 1.80 + m.scopeLock.Lock() 1.81 + defer m.scopeLock.Unlock() 1.82 + 1.83 + for pos, scope := range scopes { 1.84 + if _, ok := m.scopes[scope.ID]; ok { 1.85 + return ErrScopeAlreadyExists{Pos: pos, ID: scope.ID} 1.86 + } 1.87 + } 1.88 + for _, scope := range scopes { 1.89 + m.scopes[scope.ID] = scope 1.90 + } 1.91 + return nil 1.92 +} 1.93 + 1.94 +func (m *memstore) getScopes(ids []string) ([]Scope, error) { 1.95 + m.scopeLock.RLock() 1.96 + defer m.scopeLock.RUnlock() 1.97 + 1.98 + scopes := []Scope{} 1.99 + for pos, id := range ids { 1.100 + scope, ok := m.scopes[id] 1.101 + if !ok { 1.102 + return []Scope{}, ErrScopeNotFound{ID: id, Pos: pos} 1.103 + } 1.104 + scopes = append(scopes, scope) 1.105 + } 1.106 + sorted := sortedScopes(scopes) 1.107 + sort.Sort(sorted) 1.108 + scopes = sorted 1.109 + return scopes, nil 1.110 +} 1.111 + 1.112 +func (m *memstore) updateScopes(changes []ScopeChange) ([]Scope, error) { 1.113 + m.scopeLock.Lock() 1.114 + defer m.scopeLock.Unlock() 1.115 + 1.116 + scopes := []Scope{} 1.117 + 1.118 + for pos, change := range changes { 1.119 + if _, ok := m.scopes[change.ID]; !ok { 1.120 + return []Scope{}, ErrScopeNotFound{Pos: pos, ID: change.ID} 1.121 + } 1.122 + } 1.123 + for _, change := range changes { 1.124 + scope := m.scopes[change.ID] 1.125 + scope.ApplyChange(change) 1.126 + m.scopes[change.ID] = scope 1.127 + scopes = append(scopes, scope) 1.128 + } 1.129 + sorted := sortedScopes(scopes) 1.130 + sort.Sort(sorted) 1.131 + scopes = sorted 1.132 + return scopes, nil 1.133 +} 1.134 + 1.135 +func (m *memstore) removeScopes(ids []string) error { 1.136 + m.scopeLock.Lock() 1.137 + defer m.scopeLock.Unlock() 1.138 + 1.139 + for pos, id := range ids { 1.140 + if _, ok := m.scopes[id]; !ok { 1.141 + return ErrScopeNotFound{Pos: pos, ID: id} 1.142 + } 1.143 + } 1.144 + for _, id := range ids { 1.145 + delete(m.scopes, id) 1.146 + } 1.147 + return nil 1.148 +} 1.149 + 1.150 +func (m *memstore) listScopes() ([]Scope, error) { 1.151 + m.scopeLock.RLock() 1.152 + defer m.scopeLock.RUnlock() 1.153 + 1.154 + scopes := []Scope{} 1.155 + for _, scope := range m.scopes { 1.156 + scopes = append(scopes, scope) 1.157 + } 1.158 + sorted := sortedScopes(scopes) 1.159 + sort.Sort(sorted) 1.160 + scopes = sorted 1.161 + return scopes, nil 1.162 +}