OSDN Git Service

Merge pull request #201 from Bytom/v0.1
[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
76         token := &Token{
77                 ID:      id,
78                 Token:   fmt.Sprintf("%s:%x", id, hashedSecret),
79                 Type:    typ,
80                 Created: time.Now(),
81         }
82
83         value, err := json.Marshal(token)
84         if err != nil {
85                 return nil, err
86         }
87         cs.DB.Set(key, value)
88
89         return token, nil
90 }
91
92 // Check returns whether or not an id-secret pair is a valid access token.
93 func (cs *CredentialStore) Check(id string, secret string) error {
94         if !validIDRegexp.MatchString(id) {
95                 return errors.WithDetailf(ErrBadID, "invalid id %q", id)
96         }
97
98         var value []byte
99         token := &Token{}
100
101         if value = cs.DB.Get([]byte(id)); value == nil {
102                 return errors.WithDetailf(ErrNoMatchID, "check id %q nonexisting", id)
103         }
104         if err := json.Unmarshal(value, token); err != nil {
105                 return err
106         }
107
108         if strings.Split(token.Token, ":")[1] == secret {
109                 return nil
110         }
111
112         return ErrInvalidToken
113 }
114
115 // List lists all access tokens.
116 func (cs *CredentialStore) List() ([]*Token, error) {
117         tokens := make([]*Token, 0)
118         iter := cs.DB.Iterator()
119         defer iter.Release()
120
121         for iter.Next() {
122                 token := &Token{}
123                 if err := json.Unmarshal(iter.Value(), token); err != nil {
124                         return nil, err
125                 }
126                 tokens = append(tokens, token)
127         }
128         return tokens, nil
129 }
130
131 // Delete deletes an access token by id.
132 func (cs *CredentialStore) Delete(id string) error {
133         if !validIDRegexp.MatchString(id) {
134                 return errors.WithDetailf(ErrBadID, "invalid id %q", id)
135         }
136
137         if value := cs.DB.Get([]byte(id)); value == nil {
138                 return errors.WithDetailf(ErrNoMatchID, "check id %q", id)
139         }
140
141         cs.DB.Delete([]byte(id))
142         return nil
143 }