auth

Paddy 2015-03-21 Parent:d103a598548c Child:de5e09680f6b

149:8267e1c8bcd1 Go to Latest

auth/scope.go

Test our Postgres profileStore implementation. Update all our test cases to use time.Now().Round(time.Millisecond), because Go uses nanosecond precision on time values, but Postgres silently truncates that to millisecond precision. This caused our tests to report false failures that were just silent precision loss, not actual failures. Set up our authd server to use the Postgres store for profiles and automatically create a test scope when starting up. Log errors when creating Clients through the API, instead of just swallowing them and sending back cryptic act of god errors. Add a NewPostgres helper that returns a postgres profileStore from a connection string (passed through pq transparently). Add an Empty() bool helper to ProfileChange and BulkProfileChange types, so we can determine if there are any changes we need to act on easily. Log errors when creating Pofiles through the API, instead of just swalloing them and sending back cryptic act of god errors. Remove the ` quotes around field and table names, which are not supported in Postgres. This required adding a few functions/methods to pan. Detect situations where a profile was expected and not found, and return ErrProfileNotFound. Detect pq errors thrown when the profiles_pkey constraint is violated, and transform them to the ErrProfileAlreadyExists error. Detect empty ProfileChange and BulkProfileChange variables and abort the updateProfile and updateProfiles methods early, before invalid SQL is generated. Detect pq errors thrown when the logins_pkey constraint is violated, and transform them to the ErrLoginAlreadyExists error. Detect when removing a Login and no rows were affected, and return an ErrLoginNotFound. Create an sql dir with a postgres_init script that will initialize the schema of the tables expected in the database.

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