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  }