OSDN Git Service

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