<commit_before>// Package accesstoken provides storage and validation of Chain Core
// credentials.
package accesstoken

import (
	"context"
	"crypto/rand"
	"encoding/json"
	"fmt"
	"regexp"
	"strings"
	"time"

	dbm "github.com/tendermint/tmlibs/db"

	"github.com/bytom/crypto/sha3pool"
	"github.com/bytom/errors"
)

const tokenSize = 32

var (
	// ErrBadID is returned when Create is called on an invalid id string.
	ErrBadID = errors.New("invalid id")
	// ErrDuplicateID is returned when Create is called on an existing ID.
	ErrDuplicateID = errors.New("duplicate access token ID")
	// ErrBadType is returned when Create is called with a bad type.
	ErrBadType = errors.New("type must be client or network")
	// ErrNoMatchID is returned when Delete is called on nonexisting ID.
	ErrNoMatchID = errors.New("nonexisting access token ID")

	// validIDRegexp checks that all characters are alphumeric, _ or -.
	// It also must have a length of at least 1.
	validIDRegexp = regexp.MustCompile(`^[\w-]+$`)
)

// Token describe the access token.
type Token struct {
	ID      string    `json:"id"`
	Token   string    `json:"token,omitempty"`
	Type    string    `json:"type,omitempty"`
	Created time.Time `json:"created_at"`
}

// CredentialStore store user access credential.
type CredentialStore struct {
	DB dbm.DB
}

// NewStore creates and returns a new Store object.
func NewStore(db dbm.DB) *CredentialStore {
	return &CredentialStore{
		DB: db,
	}
}

// Create generates a new access token with the given ID.
func (cs *CredentialStore) Create(ctx context.Context, id, typ string) (*string, error) {
	if !validIDRegexp.MatchString(id) {
		return nil, errors.WithDetailf(ErrBadID, "invalid id %q", id)
	}
	k, err := json.Marshal(id)
	if v := cs.DB.Get(k); v != nil {
		return nil, errors.WithDetailf(ErrDuplicateID, "id %q already in use", id)
	}
	var secret [tokenSize]byte
	v, err := rand.Read(secret[:])
	if err != nil || v != tokenSize {
		return nil, err
	}

	var hashedSecret [tokenSize]byte
	sha3pool.Sum256(hashedSecret[:], secret[:])
	created := time.Now()

	token := &Token{
		ID:      id,
		Token:   fmt.Sprintf("%s:%x", id, hashedSecret),
		Type:    typ,
		Created: created,
	}

	key, err := json.Marshal(id)
	if err != nil {
		return nil, err
	}
	value, err := json.Marshal(token)
	if err != nil {
		return nil, err
	}
	cs.DB.Set(key, value)
	hexsec := fmt.Sprintf("%s:%x", id, secret)
	return &hexsec, nil
}

// Check returns whether or not an id-secret pair is a valid access token.
func (cs *CredentialStore) Check(ctx context.Context, id string, secret []byte) (bool, error) {
	if !validIDRegexp.MatchString(id) {
		return false, errors.WithDetailf(ErrBadID, "invalid id %q", id)
	}

	var toHash [tokenSize]byte
	var hashed [tokenSize]byte
	copy(toHash[:], secret)
	sha3pool.Sum256(hashed[:], toHash[:])
	inToken := fmt.Sprintf("%s:%x", id, hashed[:])

	var value []byte
	token := &Token{}
	k, err := json.Marshal(id)
	if err != nil {<commit_msg>refactor accesstoken create function (#250)<commit_after>// Package accesstoken provides storage and validation of Chain Core
// credentials.
package accesstoken

import (
	"context"
	"crypto/rand"
	"encoding/json"
	"fmt"
	"regexp"
	"strings"
	"time"

	dbm "github.com/tendermint/tmlibs/db"

	"github.com/bytom/crypto/sha3pool"
	"github.com/bytom/errors"
)

const tokenSize = 32

var (
	// ErrBadID is returned when Create is called on an invalid id string.
	ErrBadID = errors.New("invalid id")
	// ErrDuplicateID is returned when Create is called on an existing ID.
	ErrDuplicateID = errors.New("duplicate access token ID")
	// ErrBadType is returned when Create is called with a bad type.
	ErrBadType = errors.New("type must be client or network")
	// ErrNoMatchID is returned when Delete is called on nonexisting ID.
	ErrNoMatchID = errors.New("nonexisting access token ID")

	// validIDRegexp checks that all characters are alphumeric, _ or -.
	// It also must have a length of at least 1.
	validIDRegexp = regexp.MustCompile(`^[\w-]+$`)
)

// Token describe the access token.
type Token struct {
	ID      string    `json:"id"`
	Token   string    `json:"token,omitempty"`
	Type    string    `json:"type,omitempty"`
	Created time.Time `json:"created_at"`
}

// CredentialStore store user access credential.
type CredentialStore struct {
	DB dbm.DB
}

// NewStore creates and returns a new Store object.
func NewStore(db dbm.DB) *CredentialStore {
	return &CredentialStore{
		DB: db,
	}
}

// Create generates a new access token with the given ID.
func (cs *CredentialStore) Create(ctx context.Context, id, typ string) (*string, error) {
	if !validIDRegexp.MatchString(id) {
		return nil, errors.WithDetailf(ErrBadID, "invalid id %q", id)
	}
	key, err := json.Marshal(id)
	if err != nil {
		return nil, err
	}

	if v := cs.DB.Get(key); v != nil {
		return nil, errors.WithDetailf(ErrDuplicateID, "id %q already in use", id)
	}
	var secret [tokenSize]byte
	v, err := rand.Read(secret[:])
	if err != nil || v != tokenSize {
		return nil, err
	}

	var hashedSecret [tokenSize]byte
	sha3pool.Sum256(hashedSecret[:], secret[:])
	created := time.Now()

	token := &Token{
		ID:      id,
		Token:   fmt.Sprintf("%s:%x", id, hashedSecret),
		Type:    typ,
		Created: created,
	}

	value, err := json.Marshal(token)
	if err != nil {
		return nil, err
	}
	cs.DB.Set(key, value)
	hexsec := fmt.Sprintf("%s:%x", id, secret)
	return &hexsec, nil
}

// Check returns whether or not an id-secret pair is a valid access token.
func (cs *CredentialStore) Check(ctx context.Context, id string, secret []byte) (bool, error) {
	if !validIDRegexp.MatchString(id) {
		return false, errors.WithDetailf(ErrBadID, "invalid id %q", id)
	}

	var toHash [tokenSize]byte
	var hashed [tokenSize]byte
	copy(toHash[:], secret)
	sha3pool.Sum256(hashed[:], toHash[:])
	inToken := fmt.Sprintf("%s:%x", id, hashed[:])

	var value []byte
	token := &Token{}
	k, err := json.Marshal(id)
	if err != nil {
{'fraction_non_alphanumeric': 0.13248945147679325, 'fraction_numerical': 0.004556962025316456, 'mean_word_length': 3.9219269102990033, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7470623', 'n_tokens_mistral': 2103, 'n_tokens_neox': 2037, 'n_words': 647}