OSDN Git Service

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