auth
auth/token.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.
1 package auth
3 import (
4 "errors"
5 "time"
7 "secondbit.org/uuid"
8 )
10 var (
11 ErrTokenNotFound = errors.New("Token not found in TokenStore.")
12 ErrTokenAlreadyExists = errors.New("Token already exists in TokenStore.")
13 )
15 type Token struct {
16 AccessToken string
17 RefreshToken string
18 Created time.Time
19 ExpiresIn int32
20 TokenType string
21 Scope string
22 ProfileID uuid.ID
23 }
25 type TokenStore interface {
26 GetToken(token string, refresh bool) (Token, error)
27 SaveToken(token Token) error
28 RemoveToken(token string) error
29 GetTokensByProfileID(profileID uuid.ID, num, offset int) ([]Token, error)
30 }
32 func (m *Memstore) GetToken(token string, refresh bool) (Token, error) {
33 if refresh {
34 t, err := m.lookupTokenByRefresh(token)
35 if err != nil {
36 return Token{}, err
37 }
38 token = t
39 }
40 m.tokenLock.RLock()
41 defer m.tokenLock.RUnlock()
42 result, ok := m.tokens[token]
43 if !ok {
44 return Token{}, ErrTokenNotFound
45 }
46 return result, nil
47 }
49 func (m *Memstore) SaveToken(token Token) error {
50 m.tokenLock.Lock()
51 defer m.tokenLock.Unlock()
52 _, ok := m.tokens[token.AccessToken]
53 if ok {
54 return ErrTokenAlreadyExists
55 }
56 m.tokens[token.AccessToken] = token
57 if token.RefreshToken != "" {
58 m.refreshTokenLookup[token.RefreshToken] = token.AccessToken
59 }
60 if _, ok = m.profileTokenLookup[token.ProfileID.String()]; ok {
61 m.profileTokenLookup[token.ProfileID.String()] = append(m.profileTokenLookup[token.ProfileID.String()], token.AccessToken)
62 } else {
63 m.profileTokenLookup[token.ProfileID.String()] = []string{token.AccessToken}
64 }
65 return nil
66 }
68 func (m *Memstore) RemoveToken(token string) error {
69 m.tokenLock.Lock()
70 defer m.tokenLock.Unlock()
71 t, ok := m.tokens[token]
72 if !ok {
73 return ErrTokenNotFound
74 }
75 delete(m.tokens, token)
76 if t.RefreshToken != "" {
77 delete(m.refreshTokenLookup, t.RefreshToken)
78 }
79 pos := -1
80 for p, item := range m.profileTokenLookup[t.ProfileID.String()] {
81 if item == token {
82 pos = p
83 break
84 }
85 }
86 if pos >= 0 {
87 m.profileTokenLookup[t.ProfileID.String()] = append(m.profileTokenLookup[t.ProfileID.String()][:pos], m.profileTokenLookup[t.ProfileID.String()][pos+1:]...)
88 }
89 return nil
90 }
92 func (m *Memstore) GetTokensByProfileID(profileID uuid.ID, num, offset int) ([]Token, error) {
93 ids, err := m.lookupTokensByProfileID(profileID.String())
94 if err != nil {
95 return []Token{}, err
96 }
97 if len(ids) > num+offset {
98 ids = ids[offset : num+offset]
99 } else if len(ids) > offset {
100 ids = ids[offset:]
101 } else {
102 return []Token{}, nil
103 }
104 tokens := []Token{}
105 for _, id := range ids {
106 token, err := m.GetToken(id, false)
107 if err != nil {
108 return []Token{}, err
109 }
110 tokens = append(tokens, token)
111 }
112 return tokens, nil
113 }