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