github.com/Bytom/bytom@v1.1.2-0.20210127130405-ae40204c0b09/accesstoken/accesstoken.go (about) 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/bytom/bytom/crypto/sha3pool" 14 "github.com/bytom/bytom/errors" 15 dbm "github.com/bytom/bytom/database/leveldb" 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 }