auth

Paddy 2014-10-15 Parent:3a6a65ed380c Child:0f80a3e391b8

49:73a9f7a6af54 Go to Latest

auth/client.go

Update error strings, add ErrNo*Store errors. Update error strings to consistently begin with a lowercase letter and end without punctuation, as per the Go style guide. See https://code.google.com/p/go-wiki/wiki/CodeReviewComments#Error_Strings For each type of Store, add an ErrNo*Store error (e.g., ErrNoTokenStore) variable, to prepare for our Context type, which will throw these errors when a Store is used without being set.

History
paddy@6 1 package auth
paddy@0 2
paddy@0 3 import (
paddy@31 4 "errors"
paddy@41 5 "net/url"
paddy@49 6 "strings"
paddy@41 7 "time"
paddy@31 8
paddy@45 9 "code.secondbit.org/uuid"
paddy@0 10 )
paddy@0 11
paddy@31 12 var (
paddy@49 13 ErrNoClientStore = errors.New("no ClientStore was specified for the Context")
paddy@49 14 ErrClientNotFound = errors.New("client not found in ClientStore")
paddy@49 15 ErrClientAlreadyExists = errors.New("client already exists in ClientStore")
paddy@41 16
paddy@49 17 ErrEmptyChange = errors.New("change must have at least one change in it")
paddy@49 18 ErrClientNameTooShort = errors.New("client name must be at least 2 characters")
paddy@49 19 ErrClientNameTooLong = errors.New("client name must be at most 32 characters")
paddy@49 20 ErrClientLogoTooLong = errors.New("client logo must be at most 1024 characters")
paddy@49 21 ErrClientLogoNotURL = errors.New("client logo must be a valid absolute URL")
paddy@49 22 ErrClientWebsiteTooLong = errors.New("client website must be at most 1024 characters")
paddy@49 23 ErrClientWebsiteNotURL = errors.New("client website must be a valid absolute URL")
paddy@31 24 )
paddy@31 25
paddy@25 26 // Client represents a client that grants access
paddy@25 27 // to the auth server, exchanging grants for tokens,
paddy@25 28 // and tokens for access.
paddy@0 29 type Client struct {
paddy@41 30 ID uuid.ID
paddy@41 31 Secret string
paddy@41 32 OwnerID uuid.ID
paddy@41 33 Name string
paddy@41 34 Logo string
paddy@41 35 Website string
paddy@41 36 Type string
paddy@0 37 }
paddy@0 38
paddy@39 39 func (c *Client) ApplyChange(change ClientChange) {
paddy@39 40 if change.Secret != nil {
paddy@39 41 c.Secret = *change.Secret
paddy@39 42 }
paddy@39 43 if change.OwnerID != nil {
paddy@39 44 c.OwnerID = change.OwnerID
paddy@39 45 }
paddy@39 46 if change.Name != nil {
paddy@39 47 c.Name = *change.Name
paddy@39 48 }
paddy@39 49 if change.Logo != nil {
paddy@39 50 c.Logo = *change.Logo
paddy@39 51 }
paddy@39 52 if change.Website != nil {
paddy@39 53 c.Website = *change.Website
paddy@39 54 }
paddy@39 55 }
paddy@39 56
paddy@31 57 type ClientChange struct {
paddy@41 58 Secret *string
paddy@41 59 OwnerID uuid.ID
paddy@41 60 Name *string
paddy@41 61 Logo *string
paddy@41 62 Website *string
paddy@31 63 }
paddy@31 64
paddy@39 65 func (c ClientChange) Validate() error {
paddy@42 66 if c.Secret == nil && c.OwnerID == nil && c.Name == nil && c.Logo == nil && c.Website == nil {
paddy@42 67 return ErrEmptyChange
paddy@42 68 }
paddy@41 69 if c.Name != nil && len(*c.Name) < 2 {
paddy@41 70 return ErrClientNameTooShort
paddy@41 71 }
paddy@41 72 if c.Name != nil && len(*c.Name) > 32 {
paddy@41 73 return ErrClientNameTooLong
paddy@41 74 }
paddy@42 75 if c.Logo != nil && *c.Logo != "" {
paddy@42 76 if len(*c.Logo) > 1024 {
paddy@42 77 return ErrClientLogoTooLong
paddy@42 78 }
paddy@42 79 u, err := url.Parse(*c.Logo)
paddy@42 80 if err != nil || !u.IsAbs() {
paddy@42 81 return ErrClientLogoNotURL
paddy@42 82 }
paddy@41 83 }
paddy@42 84 if c.Website != nil && *c.Website != "" {
paddy@42 85 if len(*c.Website) > 140 {
paddy@42 86 return ErrClientWebsiteTooLong
paddy@42 87 }
paddy@42 88 u, err := url.Parse(*c.Website)
paddy@42 89 if err != nil || !u.IsAbs() {
paddy@42 90 return ErrClientWebsiteNotURL
paddy@42 91 }
paddy@41 92 }
paddy@39 93 return nil
paddy@39 94 }
paddy@39 95
paddy@41 96 type Endpoint struct {
paddy@41 97 ID uuid.ID
paddy@41 98 ClientID uuid.ID
paddy@41 99 URI url.URL
paddy@41 100 Added time.Time
paddy@41 101 }
paddy@41 102
paddy@41 103 type sortedEndpoints []Endpoint
paddy@41 104
paddy@41 105 func (s sortedEndpoints) Len() int {
paddy@41 106 return len(s)
paddy@41 107 }
paddy@41 108
paddy@41 109 func (s sortedEndpoints) Less(i, j int) bool {
paddy@41 110 return s[i].Added.Before(s[j].Added)
paddy@41 111 }
paddy@41 112
paddy@41 113 func (s sortedEndpoints) Swap(i, j int) {
paddy@41 114 s[i], s[j] = s[j], s[i]
paddy@41 115 }
paddy@41 116
paddy@25 117 // ClientStore abstracts the storage interface for
paddy@25 118 // storing and retrieving Clients.
paddy@25 119 type ClientStore interface {
paddy@25 120 GetClient(id uuid.ID) (Client, error)
paddy@25 121 SaveClient(client Client) error
paddy@31 122 UpdateClient(id uuid.ID, change ClientChange) error
paddy@25 123 DeleteClient(id uuid.ID) error
paddy@31 124 ListClientsByOwner(ownerID uuid.ID, num, offset int) ([]Client, error)
paddy@41 125
paddy@41 126 AddEndpoint(client uuid.ID, endpoint Endpoint) error
paddy@41 127 RemoveEndpoint(client, endpoint uuid.ID) error
paddy@41 128 CheckEndpoint(client uuid.ID, endpoint string) (bool, error)
paddy@41 129 ListEndpoints(client uuid.ID, num, offset int) ([]Endpoint, error)
paddy@0 130 }
paddy@31 131
paddy@31 132 func (m *Memstore) GetClient(id uuid.ID) (Client, error) {
paddy@31 133 m.clientLock.RLock()
paddy@31 134 defer m.clientLock.RUnlock()
paddy@31 135 c, ok := m.clients[id.String()]
paddy@31 136 if !ok {
paddy@31 137 return Client{}, ErrClientNotFound
paddy@31 138 }
paddy@31 139 return c, nil
paddy@31 140 }
paddy@31 141
paddy@31 142 func (m *Memstore) SaveClient(client Client) error {
paddy@31 143 m.clientLock.Lock()
paddy@31 144 defer m.clientLock.Unlock()
paddy@31 145 if _, ok := m.clients[client.ID.String()]; ok {
paddy@31 146 return ErrClientAlreadyExists
paddy@31 147 }
paddy@31 148 m.clients[client.ID.String()] = client
paddy@31 149 m.profileClientLookup[client.OwnerID.String()] = append(m.profileClientLookup[client.OwnerID.String()], client.ID)
paddy@31 150 return nil
paddy@31 151 }
paddy@31 152
paddy@31 153 func (m *Memstore) UpdateClient(id uuid.ID, change ClientChange) error {
paddy@39 154 m.clientLock.Lock()
paddy@39 155 defer m.clientLock.Unlock()
paddy@39 156 c, ok := m.clients[id.String()]
paddy@39 157 if !ok {
paddy@39 158 return ErrClientNotFound
paddy@39 159 }
paddy@39 160 c.ApplyChange(change)
paddy@39 161 m.clients[id.String()] = c
paddy@31 162 return nil
paddy@31 163 }
paddy@31 164
paddy@31 165 func (m *Memstore) DeleteClient(id uuid.ID) error {
paddy@31 166 client, err := m.GetClient(id)
paddy@31 167 if err != nil {
paddy@31 168 return err
paddy@31 169 }
paddy@31 170 m.clientLock.Lock()
paddy@31 171 defer m.clientLock.Unlock()
paddy@31 172 delete(m.clients, id.String())
paddy@31 173 pos := -1
paddy@31 174 for p, item := range m.profileClientLookup[client.OwnerID.String()] {
paddy@31 175 if item.Equal(id) {
paddy@31 176 pos = p
paddy@31 177 break
paddy@31 178 }
paddy@31 179 }
paddy@31 180 if pos >= 0 {
paddy@31 181 m.profileClientLookup[client.OwnerID.String()] = append(m.profileClientLookup[client.OwnerID.String()][:pos], m.profileClientLookup[client.OwnerID.String()][pos+1:]...)
paddy@31 182 }
paddy@31 183 return nil
paddy@31 184 }
paddy@31 185
paddy@31 186 func (m *Memstore) ListClientsByOwner(ownerID uuid.ID, num, offset int) ([]Client, error) {
paddy@33 187 ids := m.lookupClientsByProfileID(ownerID.String())
paddy@31 188 if len(ids) > num+offset {
paddy@31 189 ids = ids[offset : num+offset]
paddy@31 190 } else if len(ids) > offset {
paddy@31 191 ids = ids[offset:]
paddy@31 192 } else {
paddy@31 193 return []Client{}, nil
paddy@31 194 }
paddy@31 195 clients := []Client{}
paddy@31 196 for _, id := range ids {
paddy@31 197 client, err := m.GetClient(id)
paddy@31 198 if err != nil {
paddy@31 199 return []Client{}, err
paddy@31 200 }
paddy@31 201 clients = append(clients, client)
paddy@31 202 }
paddy@31 203 return clients, nil
paddy@31 204 }
paddy@41 205
paddy@41 206 func (m *Memstore) AddEndpoint(client uuid.ID, endpoint Endpoint) error {
paddy@41 207 m.endpointLock.Lock()
paddy@41 208 defer m.endpointLock.Unlock()
paddy@41 209 m.endpoints[client.String()] = append(m.endpoints[client.String()], endpoint)
paddy@41 210 return nil
paddy@41 211 }
paddy@41 212
paddy@41 213 func (m *Memstore) RemoveEndpoint(client, endpoint uuid.ID) error {
paddy@41 214 m.endpointLock.Lock()
paddy@41 215 defer m.endpointLock.Unlock()
paddy@41 216 pos := -1
paddy@41 217 for p, item := range m.endpoints[client.String()] {
paddy@41 218 if item.ID.Equal(endpoint) {
paddy@41 219 pos = p
paddy@41 220 break
paddy@41 221 }
paddy@41 222 }
paddy@41 223 if pos >= 0 {
paddy@41 224 m.endpoints[client.String()] = append(m.endpoints[client.String()][:pos], m.endpoints[client.String()][pos+1:]...)
paddy@41 225 }
paddy@41 226 return nil
paddy@41 227 }
paddy@41 228
paddy@41 229 func (m *Memstore) CheckEndpoint(client uuid.ID, endpoint string) (bool, error) {
paddy@41 230 m.endpointLock.RLock()
paddy@41 231 defer m.endpointLock.RUnlock()
paddy@41 232 for _, candidate := range m.endpoints[client.String()] {
paddy@41 233 if strings.HasPrefix(endpoint, candidate.URI.String()) {
paddy@41 234 return true, nil
paddy@41 235 }
paddy@41 236 }
paddy@41 237 return false, nil
paddy@41 238 }
paddy@41 239
paddy@41 240 func (m *Memstore) ListEndpoints(client uuid.ID, num, offset int) ([]Endpoint, error) {
paddy@41 241 m.endpointLock.RLock()
paddy@41 242 defer m.endpointLock.RUnlock()
paddy@41 243 return m.endpoints[client.String()], nil
paddy@41 244 }