auth

Paddy 2014-09-18 Parent:607708cd8829 Child:113ccb15b919

39:690561c6619a Go to Latest

auth/client.go

Include client updates and tests. Flesh out updating clients, and include unit tests to ensure that clientstores actually update appropriately. Add TODO comments where functionality is planned but stubbed out.

History
paddy@6 1 package auth
paddy@0 2
paddy@0 3 import (
paddy@31 4 "errors"
paddy@31 5
paddy@0 6 "secondbit.org/uuid"
paddy@0 7 )
paddy@0 8
paddy@31 9 var (
paddy@31 10 ErrClientNotFound = errors.New("Client not found in ClientStore.")
paddy@31 11 ErrClientAlreadyExists = errors.New("Client already exists in ClientStore.")
paddy@31 12 )
paddy@31 13
paddy@25 14 // Client represents a client that grants access
paddy@25 15 // to the auth server, exchanging grants for tokens,
paddy@25 16 // and tokens for access.
paddy@0 17 type Client struct {
paddy@0 18 ID uuid.ID
paddy@0 19 Secret string
paddy@0 20 RedirectURI string
paddy@0 21 OwnerID uuid.ID
paddy@0 22 Name string
paddy@0 23 Logo string
paddy@25 24 Website string
paddy@0 25 }
paddy@0 26
paddy@39 27 func (c *Client) ApplyChange(change ClientChange) {
paddy@39 28 if change.Secret != nil {
paddy@39 29 c.Secret = *change.Secret
paddy@39 30 }
paddy@39 31 if change.RedirectURI != nil {
paddy@39 32 c.RedirectURI = *change.RedirectURI
paddy@39 33 }
paddy@39 34 if change.OwnerID != nil {
paddy@39 35 c.OwnerID = change.OwnerID
paddy@39 36 }
paddy@39 37 if change.Name != nil {
paddy@39 38 c.Name = *change.Name
paddy@39 39 }
paddy@39 40 if change.Logo != nil {
paddy@39 41 c.Logo = *change.Logo
paddy@39 42 }
paddy@39 43 if change.Website != nil {
paddy@39 44 c.Website = *change.Website
paddy@39 45 }
paddy@39 46 }
paddy@39 47
paddy@31 48 type ClientChange struct {
paddy@31 49 Secret *string
paddy@31 50 RedirectURI *string
paddy@31 51 OwnerID uuid.ID
paddy@31 52 Name *string
paddy@31 53 Logo *string
paddy@31 54 Website *string
paddy@31 55 }
paddy@31 56
paddy@39 57 func (c ClientChange) Validate() error {
paddy@39 58 // TODO: validate client changes
paddy@39 59 return nil
paddy@39 60 }
paddy@39 61
paddy@25 62 // ClientStore abstracts the storage interface for
paddy@25 63 // storing and retrieving Clients.
paddy@25 64 type ClientStore interface {
paddy@25 65 GetClient(id uuid.ID) (Client, error)
paddy@25 66 SaveClient(client Client) error
paddy@31 67 UpdateClient(id uuid.ID, change ClientChange) error
paddy@25 68 DeleteClient(id uuid.ID) error
paddy@31 69 ListClientsByOwner(ownerID uuid.ID, num, offset int) ([]Client, error)
paddy@0 70 }
paddy@31 71
paddy@31 72 func (m *Memstore) GetClient(id uuid.ID) (Client, error) {
paddy@31 73 m.clientLock.RLock()
paddy@31 74 defer m.clientLock.RUnlock()
paddy@31 75 c, ok := m.clients[id.String()]
paddy@31 76 if !ok {
paddy@31 77 return Client{}, ErrClientNotFound
paddy@31 78 }
paddy@31 79 return c, nil
paddy@31 80 }
paddy@31 81
paddy@31 82 func (m *Memstore) SaveClient(client Client) error {
paddy@31 83 m.clientLock.Lock()
paddy@31 84 defer m.clientLock.Unlock()
paddy@31 85 if _, ok := m.clients[client.ID.String()]; ok {
paddy@31 86 return ErrClientAlreadyExists
paddy@31 87 }
paddy@31 88 m.clients[client.ID.String()] = client
paddy@31 89 m.profileClientLookup[client.OwnerID.String()] = append(m.profileClientLookup[client.OwnerID.String()], client.ID)
paddy@31 90 return nil
paddy@31 91 }
paddy@31 92
paddy@31 93 func (m *Memstore) UpdateClient(id uuid.ID, change ClientChange) error {
paddy@39 94 m.clientLock.Lock()
paddy@39 95 defer m.clientLock.Unlock()
paddy@39 96 c, ok := m.clients[id.String()]
paddy@39 97 if !ok {
paddy@39 98 return ErrClientNotFound
paddy@39 99 }
paddy@39 100 c.ApplyChange(change)
paddy@39 101 m.clients[id.String()] = c
paddy@31 102 return nil
paddy@31 103 }
paddy@31 104
paddy@31 105 func (m *Memstore) DeleteClient(id uuid.ID) error {
paddy@31 106 client, err := m.GetClient(id)
paddy@31 107 if err != nil {
paddy@31 108 return err
paddy@31 109 }
paddy@31 110 m.clientLock.Lock()
paddy@31 111 defer m.clientLock.Unlock()
paddy@31 112 delete(m.clients, id.String())
paddy@31 113 pos := -1
paddy@31 114 for p, item := range m.profileClientLookup[client.OwnerID.String()] {
paddy@31 115 if item.Equal(id) {
paddy@31 116 pos = p
paddy@31 117 break
paddy@31 118 }
paddy@31 119 }
paddy@31 120 if pos >= 0 {
paddy@31 121 m.profileClientLookup[client.OwnerID.String()] = append(m.profileClientLookup[client.OwnerID.String()][:pos], m.profileClientLookup[client.OwnerID.String()][pos+1:]...)
paddy@31 122 }
paddy@31 123 return nil
paddy@31 124 }
paddy@31 125
paddy@31 126 func (m *Memstore) ListClientsByOwner(ownerID uuid.ID, num, offset int) ([]Client, error) {
paddy@33 127 ids := m.lookupClientsByProfileID(ownerID.String())
paddy@31 128 if len(ids) > num+offset {
paddy@31 129 ids = ids[offset : num+offset]
paddy@31 130 } else if len(ids) > offset {
paddy@31 131 ids = ids[offset:]
paddy@31 132 } else {
paddy@31 133 return []Client{}, nil
paddy@31 134 }
paddy@31 135 clients := []Client{}
paddy@31 136 for _, id := range ids {
paddy@31 137 client, err := m.GetClient(id)
paddy@31 138 if err != nil {
paddy@31 139 return []Client{}, err
paddy@31 140 }
paddy@31 141 clients = append(clients, client)
paddy@31 142 }
paddy@31 143 return clients, nil
paddy@31 144 }