util for password and configs
This commit is contained in:
parent
eb4e6b786a
commit
6b340d7195
32
util/config.go
Normal file
32
util/config.go
Normal file
@ -0,0 +1,32 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
DBDriver string `mapstructure:"DB_TYPE"`
|
||||
DBSource string `mapstructure:"DB_SOURCE"`
|
||||
ServerAddress string `mapstructure:"SERVER_ADDRESS"`
|
||||
TokenSymmetricKey string `mapstructure:"TOKEN_SYMMETRIC_KEY"`
|
||||
TokenDuration time.Duration `mapstructure:"TOKEN_DURATION"`
|
||||
RefreshTokenDuration time.Duration `mapstructure:"REFRESH_TOKEN_DURATION"`
|
||||
}
|
||||
|
||||
func LoadConfig(path string) (config Config, err error) {
|
||||
viper.AddConfigPath(path)
|
||||
viper.SetConfigName("dev")
|
||||
viper.SetConfigType("env")
|
||||
|
||||
viper.AutomaticEnv()
|
||||
|
||||
err = viper.ReadInConfig()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = viper.Unmarshal(&config)
|
||||
return
|
||||
}
|
19
util/password.go
Normal file
19
util/password.go
Normal file
@ -0,0 +1,19 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
func HashPassword(password string) (string, error) {
|
||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Failed to hash password: %w", err)
|
||||
}
|
||||
return string(hashedPassword), nil
|
||||
}
|
||||
|
||||
func CheckPassword(password string, hashedPassword string) error {
|
||||
return bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password))
|
||||
}
|
23
util/password_test.go
Normal file
23
util/password_test.go
Normal file
@ -0,0 +1,23 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
func TestPasswordUtil(t *testing.T) {
|
||||
password := RandomString(10)
|
||||
|
||||
hashedPassword, err := HashPassword(password)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, hashedPassword)
|
||||
|
||||
err = CheckPassword(password, hashedPassword)
|
||||
require.NoError(t, err)
|
||||
|
||||
wrongPassword := RandomString(5)
|
||||
err = CheckPassword(wrongPassword, hashedPassword)
|
||||
require.EqualError(t, err, bcrypt.ErrMismatchedHashAndPassword.Error())
|
||||
}
|
41
util/random.go
Normal file
41
util/random.go
Normal file
@ -0,0 +1,41 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const alphabet = "abcdefghijklmnopqrstuvwxyz"
|
||||
|
||||
func init() {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
}
|
||||
|
||||
// RandomInt generates a random integer between min and max
|
||||
func RandomInt(min, max int64) int64 {
|
||||
return min + rand.Int63n(max-min+1)
|
||||
}
|
||||
|
||||
// RandomString generates a random string of length n
|
||||
func RandomString(n int) string {
|
||||
var sb strings.Builder
|
||||
k := len(alphabet)
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
c := alphabet[rand.Intn(k)]
|
||||
sb.WriteByte(c)
|
||||
}
|
||||
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func RandomEmail() string {
|
||||
return fmt.Sprintf("%s@mail.com", RandomString(5))
|
||||
}
|
||||
|
||||
func RandomTransactionCode(prefix string, merchant_index int64) string {
|
||||
time_now := time.Now().Unix()
|
||||
return fmt.Sprintf("%s%d%d", prefix, merchant_index, time_now)
|
||||
}
|
8
util/token/maker.go
Normal file
8
util/token/maker.go
Normal file
@ -0,0 +1,8 @@
|
||||
package token
|
||||
|
||||
import "time"
|
||||
|
||||
type Maker interface {
|
||||
CreateToken(email string, userID int, duration time.Duration) (string, *Payload, error)
|
||||
VerifyToken(token string) (*Payload, error)
|
||||
}
|
53
util/token/paseto.go
Normal file
53
util/token/paseto.go
Normal file
@ -0,0 +1,53 @@
|
||||
package token
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/aead/chacha20poly1305"
|
||||
"github.com/o1egl/paseto"
|
||||
)
|
||||
|
||||
type PasetoMaker struct {
|
||||
paseto *paseto.V2
|
||||
symmetricKey []byte
|
||||
}
|
||||
|
||||
func NewPasetoMaker(symmetricKey string) (Maker, error) {
|
||||
if len(symmetricKey) != chacha20poly1305.KeySize {
|
||||
return nil, fmt.Errorf("invalid key size: must be exactly %d characters", chacha20poly1305.KeySize)
|
||||
}
|
||||
|
||||
maker := &PasetoMaker{
|
||||
paseto: paseto.NewV2(),
|
||||
symmetricKey: []byte(symmetricKey),
|
||||
}
|
||||
|
||||
return maker, nil
|
||||
}
|
||||
|
||||
func (maker *PasetoMaker) CreateToken(email string, UserID int, duration time.Duration) (string, *Payload, error) {
|
||||
payload, err := NewPayload(email, UserID, duration)
|
||||
if err != nil {
|
||||
return "", payload, err
|
||||
}
|
||||
|
||||
token, err := maker.paseto.Encrypt(maker.symmetricKey, payload, nil)
|
||||
return token, payload, err
|
||||
}
|
||||
|
||||
func (maker *PasetoMaker) VerifyToken(token string) (*Payload, error) {
|
||||
payload := &Payload{}
|
||||
|
||||
err := maker.paseto.Decrypt(token, maker.symmetricKey, payload, nil)
|
||||
if err != nil {
|
||||
return nil, ErrInvalidToken
|
||||
}
|
||||
|
||||
err = payload.Valid()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return payload, nil
|
||||
}
|
52
util/token/paseto_test.go
Normal file
52
util/token/paseto_test.go
Normal file
@ -0,0 +1,52 @@
|
||||
package token
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.nochill.in/nochill/hiling_go/util"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var userID = rand.Intn(10)
|
||||
|
||||
func TestPasetoMaker(t *testing.T) {
|
||||
maker, err := NewPasetoMaker(util.RandomString(32))
|
||||
require.NoError(t, err)
|
||||
|
||||
email := util.RandomEmail()
|
||||
duration := time.Minute
|
||||
|
||||
issuedAt := time.Now()
|
||||
expiredAt := issuedAt.Add(duration)
|
||||
|
||||
token, payload, err := maker.CreateToken(email, userID, duration)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, token)
|
||||
require.NotEmpty(t, payload)
|
||||
|
||||
payload, err = maker.VerifyToken(token)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, payload)
|
||||
|
||||
// require.NotZero(t, payload.ID)
|
||||
require.Equal(t, email, payload.Email)
|
||||
require.WithinDuration(t, issuedAt, payload.IssuedAt, time.Second)
|
||||
require.WithinDuration(t, expiredAt, payload.ExpiredAt, time.Second)
|
||||
}
|
||||
|
||||
func TestExpiredPasetoToken(t *testing.T) {
|
||||
maker, err := NewPasetoMaker(util.RandomString(32))
|
||||
require.NoError(t, err)
|
||||
|
||||
token, payload, err := maker.CreateToken(util.RandomEmail(), userID, -time.Minute)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, token)
|
||||
require.NotEmpty(t, payload)
|
||||
|
||||
payload, err = maker.VerifyToken(token)
|
||||
require.Error(t, err)
|
||||
require.EqualError(t, err, ErrExpiredToken.Error())
|
||||
require.Nil(t, payload)
|
||||
}
|
36
util/token/payload.go
Normal file
36
util/token/payload.go
Normal file
@ -0,0 +1,36 @@
|
||||
package token
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrExpiredToken = errors.New("token has expired")
|
||||
ErrInvalidToken = errors.New("token is invalid")
|
||||
)
|
||||
|
||||
type Payload struct {
|
||||
Email string `json:"email"`
|
||||
UserID int `json:"user_id"`
|
||||
IssuedAt time.Time `json:"issued_at"`
|
||||
ExpiredAt time.Time `json:"expired_at"`
|
||||
}
|
||||
|
||||
func NewPayload(email string, user_id int, duration time.Duration) (*Payload, error) {
|
||||
payload := &Payload{
|
||||
Email: email,
|
||||
UserID: user_id,
|
||||
IssuedAt: time.Now(),
|
||||
ExpiredAt: time.Now().Add(duration),
|
||||
}
|
||||
|
||||
return payload, nil
|
||||
}
|
||||
|
||||
func (payload *Payload) Valid() error {
|
||||
if time.Now().After(payload.ExpiredAt) {
|
||||
return ErrExpiredToken
|
||||
}
|
||||
return nil
|
||||
}
|
Loading…
Reference in New Issue
Block a user