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