1 // Package accesstoken provides storage and validation of Chain Core
14 dbm "github.com/tendermint/tmlibs/db"
16 "github.com/bytom/crypto/sha3pool"
17 "github.com/bytom/errors"
23 // ErrBadID is returned when Create is called on an invalid id string.
24 ErrBadID = errors.New("invalid id")
25 // ErrDuplicateID is returned when Create is called on an existing ID.
26 ErrDuplicateID = errors.New("duplicate access token ID")
27 // ErrBadType is returned when Create is called with a bad type.
28 ErrBadType = errors.New("type must be client or network")
29 // ErrNoMatchID is returned when Delete is called on nonexisting ID.
30 ErrNoMatchID = errors.New("nonexisting access token ID")
31 // ErrInvalidToken is returned when Check is called on invalid token
32 ErrInvalidToken = errors.New("invalid token")
34 // validIDRegexp checks that all characters are alphumeric, _ or -.
35 // It also must have a length of at least 1.
36 validIDRegexp = regexp.MustCompile(`^[\w-]+$`)
39 // Token describe the access token.
42 Token string `json:"token,omitempty"`
43 Type string `json:"type,omitempty"`
44 Created time.Time `json:"created_at"`
47 // CredentialStore store user access credential.
48 type CredentialStore struct {
52 // NewStore creates and returns a new Store object.
53 func NewStore(db dbm.DB) *CredentialStore {
54 return &CredentialStore{
59 // Create generates a new access token with the given ID.
60 func (cs *CredentialStore) Create(ctx context.Context, id, typ string) (*Token, error) {
61 if !validIDRegexp.MatchString(id) {
62 return nil, errors.WithDetailf(ErrBadID, "invalid id %q", id)
66 if cs.DB.Get(key) != nil {
67 return nil, errors.WithDetailf(ErrDuplicateID, "id %q already in use", id)
70 secret := make([]byte, tokenSize)
71 if _, err := rand.Read(secret); err != nil {
75 hashedSecret := make([]byte, tokenSize)
76 sha3pool.Sum256(hashedSecret, secret)
80 Token: fmt.Sprintf("%s:%x", id, hashedSecret),
85 value, err := json.Marshal(token)
94 // Check returns whether or not an id-secret pair is a valid access token.
95 func (cs *CredentialStore) Check(ctx context.Context, id string, secret string) error {
96 if !validIDRegexp.MatchString(id) {
97 return errors.WithDetailf(ErrBadID, "invalid id %q", id)
103 if value = cs.DB.Get([]byte(id)); value == nil {
104 return errors.WithDetailf(ErrNoMatchID, "check id %q nonexisting", id)
106 if err := json.Unmarshal(value, token); err != nil {
110 if strings.Split(token.Token, ":")[1] == secret {
114 return ErrInvalidToken
117 // List lists all access tokens.
118 func (cs *CredentialStore) List(ctx context.Context) ([]*Token, error) {
119 tokens := make([]*Token, 0)
120 iter := cs.DB.Iterator()
125 if err := json.Unmarshal(iter.Value(), token); err != nil {
128 tokens = append(tokens, token)
133 // Delete deletes an access token by id.
134 func (cs *CredentialStore) Delete(ctx context.Context, id string) error {
135 if !validIDRegexp.MatchString(id) {
136 return errors.WithDetailf(ErrBadID, "invalid id %q", id)
139 if value := cs.DB.Get([]byte(id)); value == nil {
140 return errors.WithDetailf(ErrNoMatchID, "check id %q", id)
143 cs.DB.Delete([]byte(id))