1 // Package accesstoken provides storage and validation of Chain Core
12 "github.com/jinzhu/gorm"
14 "github.com/vapor/database/orm"
16 "github.com/vapor/crypto/sha3pool"
17 dbm "github.com/vapor/database/db"
18 "github.com/vapor/errors"
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")
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-]+$`)
40 // Token describe the access token.
43 Token string `json:"token,omitempty"`
44 Type string `json:"type,omitempty"`
45 Created time.Time `json:"created_at"`
48 func tokenFromOrmToken(ac orm.AccessToken) *Token {
57 // CredentialStore store user access credential.
58 type CredentialStore struct {
62 // NewStore creates and returns a new Store object.
63 func NewStore(db dbm.SQLDB) *CredentialStore {
64 return &CredentialStore{
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)
75 accessToken := orm.AccessToken{ID: id}
77 if err := cs.DB.Db().Where(&accessToken).Find(&accessToken).Error; err != nil {
78 if err != gorm.ErrRecordNotFound {
81 secret := make([]byte, tokenSize)
82 if _, err := rand.Read(secret); err != nil {
85 hashedSecret := make([]byte, tokenSize)
86 sha3pool.Sum256(hashedSecret, secret)
87 accessToken = orm.AccessToken{
89 Token: fmt.Sprintf("%s:%x", id, hashedSecret),
93 if err = cs.DB.Db().Create(&accessToken).Error; err != nil {
96 return tokenFromOrmToken(accessToken), nil
98 return nil, errors.WithDetailf(ErrDuplicateID, "id %q already in use", id)
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)
106 accessToken := orm.AccessToken{ID: id}
108 if err := cs.DB.Db().Where(&accessToken).Find(&accessToken).Error; err != nil {
112 if strings.Split(accessToken.Token, ":")[1] == secret {
116 return ErrInvalidToken
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()
127 accessToken := orm.AccessToken{}
128 if err := rows.Scan(&accessToken.ID, &accessToken.Token, &accessToken.Type, &accessToken.Created); err != nil {
131 tokens = append(tokens, tokenFromOrmToken(accessToken))
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)
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)