auth

Paddy 2015-02-20 Child:de5e09680f6b

134:d103a598548c Go to Latest

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.

History
     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 +}