auth

Paddy 2014-09-01 Child:3a6a65ed380c

28:75cf37088852 Go to Latest

auth/token.go

Rough out tokens and begin the memstore. Rough out the Token type for working with OAuth2 access and refresh tokens. Rough out the TokenStore interface that dictates how Tokens will be stored and retrieved. Write tests for the successful (in the working-as-intended sense) calls to TokenStore. Begin a Memstore type that stores data in memory. Implement the TokenStore interface for Memstore.

History
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 }