auth
auth/client.go
Add more tests for ClientStores. Test that deleting a client that doesn't exist throws an ErrClientNotFound. Test that adding a client twice returns an ErrClientAlreadyExists. Test that clients retrieve have properties that all match the client that was stored. Remove the unused error return from the internal MemoryStore helper for getting a list of clients by their OwnerID. If a profile doesn't exist, return an empty list of clients, as we're technically returning any client that has that OwnerID. There's no guarantee that that OwnerID is actually valid.
| 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@31 | 27 type ClientChange struct { |
| paddy@31 | 28 Secret *string |
| paddy@31 | 29 RedirectURI *string |
| paddy@31 | 30 OwnerID uuid.ID |
| paddy@31 | 31 Name *string |
| paddy@31 | 32 Logo *string |
| paddy@31 | 33 Website *string |
| paddy@31 | 34 } |
| paddy@31 | 35 |
| paddy@25 | 36 // ClientStore abstracts the storage interface for |
| paddy@25 | 37 // storing and retrieving Clients. |
| paddy@25 | 38 type ClientStore interface { |
| paddy@25 | 39 GetClient(id uuid.ID) (Client, error) |
| paddy@25 | 40 SaveClient(client Client) error |
| paddy@31 | 41 UpdateClient(id uuid.ID, change ClientChange) error |
| paddy@25 | 42 DeleteClient(id uuid.ID) error |
| paddy@31 | 43 ListClientsByOwner(ownerID uuid.ID, num, offset int) ([]Client, error) |
| paddy@0 | 44 } |
| paddy@31 | 45 |
| paddy@31 | 46 func (m *Memstore) GetClient(id uuid.ID) (Client, error) { |
| paddy@31 | 47 m.clientLock.RLock() |
| paddy@31 | 48 defer m.clientLock.RUnlock() |
| paddy@31 | 49 c, ok := m.clients[id.String()] |
| paddy@31 | 50 if !ok { |
| paddy@31 | 51 return Client{}, ErrClientNotFound |
| paddy@31 | 52 } |
| paddy@31 | 53 return c, nil |
| paddy@31 | 54 } |
| paddy@31 | 55 |
| paddy@31 | 56 func (m *Memstore) SaveClient(client Client) error { |
| paddy@31 | 57 m.clientLock.Lock() |
| paddy@31 | 58 defer m.clientLock.Unlock() |
| paddy@31 | 59 if _, ok := m.clients[client.ID.String()]; ok { |
| paddy@31 | 60 return ErrClientAlreadyExists |
| paddy@31 | 61 } |
| paddy@31 | 62 m.clients[client.ID.String()] = client |
| paddy@31 | 63 m.profileClientLookup[client.OwnerID.String()] = append(m.profileClientLookup[client.OwnerID.String()], client.ID) |
| paddy@31 | 64 return nil |
| paddy@31 | 65 } |
| paddy@31 | 66 |
| paddy@31 | 67 func (m *Memstore) UpdateClient(id uuid.ID, change ClientChange) error { |
| paddy@31 | 68 return nil |
| paddy@31 | 69 } |
| paddy@31 | 70 |
| paddy@31 | 71 func (m *Memstore) DeleteClient(id uuid.ID) error { |
| paddy@31 | 72 client, err := m.GetClient(id) |
| paddy@31 | 73 if err != nil { |
| paddy@31 | 74 return err |
| paddy@31 | 75 } |
| paddy@31 | 76 m.clientLock.Lock() |
| paddy@31 | 77 defer m.clientLock.Unlock() |
| paddy@31 | 78 delete(m.clients, id.String()) |
| paddy@31 | 79 pos := -1 |
| paddy@31 | 80 for p, item := range m.profileClientLookup[client.OwnerID.String()] { |
| paddy@31 | 81 if item.Equal(id) { |
| paddy@31 | 82 pos = p |
| paddy@31 | 83 break |
| paddy@31 | 84 } |
| paddy@31 | 85 } |
| paddy@31 | 86 if pos >= 0 { |
| paddy@31 | 87 m.profileClientLookup[client.OwnerID.String()] = append(m.profileClientLookup[client.OwnerID.String()][:pos], m.profileClientLookup[client.OwnerID.String()][pos+1:]...) |
| paddy@31 | 88 } |
| paddy@31 | 89 return nil |
| paddy@31 | 90 } |
| paddy@31 | 91 |
| paddy@31 | 92 func (m *Memstore) ListClientsByOwner(ownerID uuid.ID, num, offset int) ([]Client, error) { |
| paddy@33 | 93 ids := m.lookupClientsByProfileID(ownerID.String()) |
| paddy@31 | 94 if len(ids) > num+offset { |
| paddy@31 | 95 ids = ids[offset : num+offset] |
| paddy@31 | 96 } else if len(ids) > offset { |
| paddy@31 | 97 ids = ids[offset:] |
| paddy@31 | 98 } else { |
| paddy@31 | 99 return []Client{}, nil |
| paddy@31 | 100 } |
| paddy@31 | 101 clients := []Client{} |
| paddy@31 | 102 for _, id := range ids { |
| paddy@31 | 103 client, err := m.GetClient(id) |
| paddy@31 | 104 if err != nil { |
| paddy@31 | 105 return []Client{}, err |
| paddy@31 | 106 } |
| paddy@31 | 107 clients = append(clients, client) |
| paddy@31 | 108 } |
| paddy@31 | 109 return clients, nil |
| paddy@31 | 110 } |