auth
auth/token.go
Move HTTP tests to http_test.go, rename the GetGrant test. Rename the GetGrant test to something that makes it more obvious, and indicate that we're only testing for successful responses in this function (e.g., responses that should be successfully handled). Another function will deal with failure modes. Move the function to a new http_test.go file. The model files are shouldn't have information about how the models are being represented.
1 package auth
3 import (
4 "errors"
5 "time"
7 "code.secondbit.org/uuid"
8 )
10 var (
11 ErrNoTokenStore = errors.New("no TokenStore was specified for the Context")
12 ErrTokenNotFound = errors.New("token not found in TokenStore")
13 ErrTokenAlreadyExists = errors.New("token already exists in TokenStore")
14 )
16 type Token struct {
17 AccessToken string
18 RefreshToken string
19 Created time.Time
20 ExpiresIn int32
21 TokenType string
22 Scope string
23 ProfileID uuid.ID
24 }
26 type TokenStore interface {
27 GetToken(token string, refresh bool) (Token, error)
28 SaveToken(token Token) error
29 RemoveToken(token string) error
30 GetTokensByProfileID(profileID uuid.ID, num, offset int) ([]Token, error)
31 }
33 func (m *Memstore) GetToken(token string, refresh bool) (Token, error) {
34 if refresh {
35 t, err := m.lookupTokenByRefresh(token)
36 if err != nil {
37 return Token{}, err
38 }
39 token = t
40 }
41 m.tokenLock.RLock()
42 defer m.tokenLock.RUnlock()
43 result, ok := m.tokens[token]
44 if !ok {
45 return Token{}, ErrTokenNotFound
46 }
47 return result, nil
48 }
50 func (m *Memstore) SaveToken(token Token) error {
51 m.tokenLock.Lock()
52 defer m.tokenLock.Unlock()
53 _, ok := m.tokens[token.AccessToken]
54 if ok {
55 return ErrTokenAlreadyExists
56 }
57 m.tokens[token.AccessToken] = token
58 if token.RefreshToken != "" {
59 m.refreshTokenLookup[token.RefreshToken] = token.AccessToken
60 }
61 if _, ok = m.profileTokenLookup[token.ProfileID.String()]; ok {
62 m.profileTokenLookup[token.ProfileID.String()] = append(m.profileTokenLookup[token.ProfileID.String()], token.AccessToken)
63 } else {
64 m.profileTokenLookup[token.ProfileID.String()] = []string{token.AccessToken}
65 }
66 return nil
67 }
69 func (m *Memstore) RemoveToken(token string) error {
70 m.tokenLock.Lock()
71 defer m.tokenLock.Unlock()
72 t, ok := m.tokens[token]
73 if !ok {
74 return ErrTokenNotFound
75 }
76 delete(m.tokens, token)
77 if t.RefreshToken != "" {
78 delete(m.refreshTokenLookup, t.RefreshToken)
79 }
80 pos := -1
81 for p, item := range m.profileTokenLookup[t.ProfileID.String()] {
82 if item == token {
83 pos = p
84 break
85 }
86 }
87 if pos >= 0 {
88 m.profileTokenLookup[t.ProfileID.String()] = append(m.profileTokenLookup[t.ProfileID.String()][:pos], m.profileTokenLookup[t.ProfileID.String()][pos+1:]...)
89 }
90 return nil
91 }
93 func (m *Memstore) GetTokensByProfileID(profileID uuid.ID, num, offset int) ([]Token, error) {
94 ids, err := m.lookupTokensByProfileID(profileID.String())
95 if err != nil {
96 return []Token{}, err
97 }
98 if len(ids) > num+offset {
99 ids = ids[offset : num+offset]
100 } else if len(ids) > offset {
101 ids = ids[offset:]
102 } else {
103 return []Token{}, nil
104 }
105 tokens := []Token{}
106 for _, id := range ids {
107 token, err := m.GetToken(id, false)
108 if err != nil {
109 return []Token{}, err
110 }
111 tokens = append(tokens, token)
112 }
113 return tokens, nil
114 }