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  }