OSDN Git Service

640f4aed36d17f02127bd3915b026865055a7a0d
[bytom/vapor.git] / accesstoken / accesstoken.go
1 // Package accesstoken provides storage and validation of Chain Core
2 // credentials.
3 package accesstoken
4
5 import (
6         "crypto/rand"
7         "encoding/json"
8         "fmt"
9         "regexp"
10         "strings"
11         "time"
12
13         "github.com/vapor/crypto/sha3pool"
14         dbm "github.com/vapor/database/leveldb"
15         "github.com/vapor/errors"
16 )
17
18 const tokenSize = 32
19
20 var (
21         // ErrBadID is returned when Create is called on an invalid id string.
22         ErrBadID = errors.New("invalid id")
23         // ErrDuplicateID is returned when Create is called on an existing ID.
24         ErrDuplicateID = errors.New("duplicate access token ID")
25         // ErrBadType is returned when Create is called with a bad type.
26         ErrBadType = errors.New("type must be client or network")
27         // ErrNoMatchID is returned when Delete is called on nonexisting ID.
28         ErrNoMatchID = errors.New("nonexisting access token ID")
29         // ErrInvalidToken is returned when Check is called on invalid token
30         ErrInvalidToken = errors.New("invalid token")
31
32         // validIDRegexp checks that all characters are alphumeric, _ or -.
33         // It also must have a length of at least 1.
34         validIDRegexp = regexp.MustCompile(`^[\w-]+$`)
35 )
36
37 // Token describe the access token.
38 type Token struct {
39         ID      string    `json:"id"`
40         Token   string    `json:"token,omitempty"`
41         Type    string    `json:"type,omitempty"`
42         Created time.Time `json:"created_at"`
43 }
44
45 // CredentialStore store user access credential.
46 type CredentialStore struct {
47         DB dbm.DB
48 }
49
50 // NewStore creates and returns a new Store object.
51 func NewStore(db dbm.DB) *CredentialStore {
52         return &CredentialStore{
53                 DB: db,
54         }
55 }
56
57 // Create generates a new access token with the given ID.
58 func (cs *CredentialStore) Create(id, typ string) (*Token, error) {
59         if !validIDRegexp.MatchString(id) {
60                 return nil, errors.WithDetailf(ErrBadID, "invalid id %q", id)
61         }
62
63         key := []byte(id)
64         if cs.DB.Get(key) != nil {
65                 return nil, errors.WithDetailf(ErrDuplicateID, "id %q already in use", id)
66         }
67
68         secret := make([]byte, tokenSize)
69         if _, err := rand.Read(secret); err != nil {
70                 return nil, err
71         }
72
73         hashedSecret := make([]byte, tokenSize)
74         sha3pool.Sum256(hashedSecret, secret)
75         token := &Token{
76                 ID:      id,
77                 Token:   fmt.Sprintf("%s:%x", id, hashedSecret),
78                 Type:    typ,
79                 Created: time.Now(),
80         }
81
82         value, err := json.Marshal(token)
83         if err != nil {
84                 return nil, err
85         }
86
87         cs.DB.Set(key, value)
88         return token, nil
89 }
90
91 // Check returns whether or not an id-secret pair is a valid access token.
92 func (cs *CredentialStore) Check(id string, secret string) error {
93         if !validIDRegexp.MatchString(id) {
94                 return errors.WithDetailf(ErrBadID, "invalid id %q", id)
95         }
96
97         value := cs.DB.Get([]byte(id))
98         if value == nil {
99                 return errors.WithDetailf(ErrNoMatchID, "check id %q nonexisting", id)
100         }
101
102         token := &Token{}
103         if err := json.Unmarshal(value, token); err != nil {
104                 return err
105         }
106
107         if strings.Split(token.Token, ":")[1] == secret {
108                 return nil
109         }
110
111         return ErrInvalidToken
112 }
113
114 // List lists all access tokens.
115 func (cs *CredentialStore) List() ([]*Token, error) {
116         tokens := make([]*Token, 0)
117         iter := cs.DB.Iterator()
118         defer iter.Release()
119
120         for iter.Next() {
121                 token := &Token{}
122                 if err := json.Unmarshal(iter.Value(), token); err != nil {
123                         return nil, err
124                 }
125                 tokens = append(tokens, token)
126         }
127         return tokens, nil
128 }
129
130 // Delete deletes an access token by id.
131 func (cs *CredentialStore) Delete(id string) error {
132         if !validIDRegexp.MatchString(id) {
133                 return errors.WithDetailf(ErrBadID, "invalid id %q", id)
134         }
135
136         if value := cs.DB.Get([]byte(id)); value == nil {
137                 return errors.WithDetailf(ErrNoMatchID, "check id %q", id)
138         }
139
140         cs.DB.Delete([]byte(id))
141         return nil
142 }