feat/create_new_token #2

Merged
daniel.w merged 10 commits from feat/create_new_token into main 2024-08-12 14:20:15 +00:00
8 changed files with 170 additions and 81 deletions
Showing only changes of commit b111cc1210 - Show all commits

View File

@ -1,20 +1,57 @@
package domain
import (
mts "ark-permission/internal/lib/metric"
ers "code.30cm.net/wanderland/library-go/errors"
"code.30cm.net/wanderland/library-go/errors/code"
)
// Decimal: 120314
// 12 represents Scope
// 03 represents Category
// 14 represents Detail error code
// 100 represents Category
// 9 represents Detail error code
// full code 12009 只會有 系統以及錯誤碼category 是給系統判定用的
// 目前 Scope 以及分類要系統共用,係向的錯誤各自服務實作就好
const (
TokenUnexpectedSigning = 1
TokenUnexpectedSigningErrorCode = iota + 1
TokenValidateErrorCode
TokenClaimErrorCode
)
// TokenUnexpectedSigningErr 031011
const (
RedisDelErrorCode = iota + 20
RedisPipLineErrorCode
)
// TokenUnexpectedSigningErr 30001 Token 簽名錯誤
func TokenUnexpectedSigningErr(msg string) *ers.Err {
return ers.NewErr(code.CloudEPPermission, code.CatInput, code.InvalidFormat, msg)
mts.AppErrorMetrics.AddFailure("token", "token_unexpected_sign")
return ers.NewErr(code.CloudEPPermission, code.CatInput, TokenUnexpectedSigningErrorCode, msg)
}
// TokenTokenValidateErr 30002 Token 驗證錯誤
func TokenTokenValidateErr(msg string) *ers.Err {
mts.AppErrorMetrics.AddFailure("token", "token_validate_ilegal")
return ers.NewErr(code.CloudEPPermission, code.CatInput, TokenValidateErrorCode, msg)
}
// TokenClaimError 30003 Token 驗證錯誤
func TokenClaimError(msg string) *ers.Err {
mts.AppErrorMetrics.AddFailure("token", "token_claim_error")
return ers.NewErr(code.CloudEPPermission, code.CatInput, TokenClaimErrorCode, msg)
}
// RedisDelError 30020 Redis 刪除錯誤
func RedisDelError(msg string) *ers.Err {
// 看需要建立哪些 Metrics
mts.AppErrorMetrics.AddFailure("redis", "del_error")
return ers.NewErr(code.CloudEPPermission, code.CatDB, RedisDelErrorCode, msg)
}
// RedisPipLineError 30021 Redis PipLine 錯誤
func RedisPipLineError(msg string) *ers.Err {
// 看需要建立哪些 Metrics
mts.AppErrorMetrics.AddFailure("redis", "pip_line_error")
return ers.NewErr(code.CloudEPPermission, code.CatInput, TokenClaimErrorCode, msg)
}

View File

@ -0,0 +1,30 @@
package metric
import (
"github.com/zeromicro/go-zero/core/metric"
)
var AppErrorMetrics = NewAppErrMetrics()
type appErrMetrics struct {
metric.CounterVec
}
type Metrics interface {
AddFailure(source, reason string)
}
// NewAppErrMetrics initiate metrics and register to prometheus
func NewAppErrMetrics() Metrics {
return &appErrMetrics{metric.NewCounterVec(&metric.CounterVecOpts{
Namespace: "ark",
Subsystem: "permission",
Name: "permission_app_error_total",
Help: "App defined failure total.",
Labels: []string{"source", "reason"},
})}
}
func (m *appErrMetrics) AddFailure(source, reason string) {
m.Inc(source, reason)
}

View File

@ -0,0 +1 @@
package metric

View File

@ -3,6 +3,7 @@ package logic
import (
"ark-permission/gen_result/pb/permission"
"ark-permission/internal/svc"
ers "code.30cm.net/wanderland/library-go/errors"
"context"
"github.com/zeromicro/go-zero/core/logx"
)
@ -27,34 +28,38 @@ type cancelTokenReq struct {
// CancelToken 取消 Token也包含他裡面的 One Time Toke
func (l *CancelTokenLogic) CancelToken(in *permission.CancelTokenReq) (*permission.OKResp, error) {
// // 驗證所需
// if err := l.svcCtx.Validate.ValidateAll(&cancelTokenReq{
// Token: in.GetToken(),
// }); err != nil {
// return nil, ers.InvalidFormat(err.Error())
// }
// 驗證所需
if err := l.svcCtx.Validate.ValidateAll(&cancelTokenReq{
Token: in.GetToken(),
}); err != nil {
return nil, ers.InvalidFormat(err.Error())
}
// claims, err := uc.parseClaims(accessToken)
// if err != nil {
// return err
// }
//
// token, err := uc.TokenRepository.GetByAccess(ctx, claims.ID())
// if err != nil {
// if errors.Is(err, repository.ErrRecordNotFound) {
// return usecase.TokenError{Msg: "token not found"}
// }
//
// return usecase.InternalError{Err: fmt.Errorf("tokenRepository.GetByAccess error: %w", err)}
// }
//
// if err := uc.TokenRepository.Delete(ctx, token); err != nil {
// if errors.Is(err, repository.ErrRecordNotFound) {
// return nil, usecase.TokenError{Msg: "token not found"}
// }
//
// return nil, err
// }
claims, err := parseClaims(l.ctx, in.GetToken(), l.svcCtx.Config.Token.Secret)
if err != nil {
logx.WithCallerSkip(1).WithFields(
logx.Field("func", "parseClaims"),
).Error(err.Error())
return nil, err
}
token, err := l.svcCtx.TokenRedisRepo.GetByAccess(l.ctx, claims.ID())
if err != nil {
logx.WithCallerSkip(1).WithFields(
logx.Field("func", "TokenRedisRepo.GetByAccess"),
logx.Field("claims", claims),
).Error(err.Error())
return nil, err
}
err = l.svcCtx.TokenRedisRepo.Delete(l.ctx, token)
if err != nil {
logx.WithCallerSkip(1).WithFields(
logx.Field("func", "TokenRedisRepo.Delete"),
logx.Field("req", token),
).Error(err.Error())
return nil, err
}
return &permission.OKResp{}, nil
}

View File

@ -7,7 +7,6 @@ import (
"ark-permission/internal/svc"
ers "code.30cm.net/wanderland/library-go/errors"
"context"
"fmt"
"github.com/google/uuid"
"time"
@ -88,7 +87,11 @@ func (l *NewTokenLogic) NewToken(in *permission.AuthorizationReq) (*permission.T
var err error
token.AccessToken, err = generateAccessTokenFunc(token, claims, l.svcCtx.Config.Token.Secret)
if err != nil {
return nil, ers.ArkInternal(fmt.Errorf("accessGenerate token error: %w", err).Error())
logx.WithCallerSkip(1).WithFields(
logx.Field("func", "generateAccessTokenFunc"),
logx.Field("claims", claims),
).Error(err.Error())
return nil, err
}
if in.GetIsRefreshToken() {
@ -97,7 +100,11 @@ func (l *NewTokenLogic) NewToken(in *permission.AuthorizationReq) (*permission.T
err = l.svcCtx.TokenRedisRepo.Create(l.ctx, token)
if err != nil {
return nil, ers.ArkInternal(fmt.Errorf("tokenRepository.Create error: %w", err).Error())
logx.WithCallerSkip(1).WithFields(
logx.Field("func", "TokenRedisRepo.Create"),
logx.Field("token", token),
).Error(err.Error())
return nil, err
}
return &permission.TokenResp{

View File

@ -1,13 +1,9 @@
package logic
import (
"ark-permission/internal/domain"
"context"
"fmt"
"strconv"
"ark-permission/gen_result/pb/permission"
"ark-permission/internal/svc"
"context"
"github.com/zeromicro/go-zero/core/logx"
)
@ -29,8 +25,6 @@ func NewRefreshTokenLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Refr
// RefreshToken 更新目前的token 以及裡面包含的一次性 Token
func (l *RefreshTokenLogic) RefreshToken(in *permission.RefreshTokenReq) (*permission.RefreshTokenResp, error) {
// todo: add your logic here and delete this line
e := domain.TokenUnexpectedSigningErr("gg88g88")
fmt.Printf(strconv.Itoa(int(e.Code())), e.Category(), e.Scope(), e.FullCode(), e.Error())
return &permission.RefreshTokenResp{}, nil
}

View File

@ -1,8 +1,10 @@
package logic
import (
"ark-permission/internal/domain"
"ark-permission/internal/entity"
"bytes"
"context"
"crypto/sha256"
"encoding/hex"
"fmt"
@ -23,7 +25,7 @@ func generateAccessToken(token entity.Token, data any, sign string) (string, err
accessToken, err := jwt.NewWithClaims(jwt.SigningMethodHS256, claim).
SignedString([]byte(sign))
if err != nil {
return "", err
return "", domain.TokenClaimError(err.Error())
}
return accessToken, nil
@ -37,40 +39,54 @@ func generateRefreshToken(accessToken string) string {
return hex.EncodeToString(h.Sum(nil))
}
func parseClaims(accessToken string) (claims, error) {
claimMap, err := parseToken(accessToken)
func parseClaims(ctx context.Context, accessToken string, secret string) (claims, error) {
claimMap, err := parseToken(ctx, accessToken, secret)
if err != nil {
return claims{}, err
}
claims, ok := claimMap["data"].(map[string]string)
claims, ok := claimMap["data"].(map[string]any)
if ok {
return claims, nil
return convertMap(claims), nil
}
return nil, fmt.Errorf("get data from claim map error")
return nil, domain.TokenClaimError("get data from claim map error")
}
func parseToken(accessToken string) (jwt.MapClaims, error) {
// token, err := jwt.Parse(accessToken, func(token *jwt.Token) (interface{}, error) {
// if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
// return nil, domain.TokenUnexpectedSigningErr(fmt.Sprintf("token unexpected signing method: %v", token.Header["alg"]))
// }
//
// return []byte(uc.Config.CustomConfig.Token.Secret), nil
// })
//
// if err != nil {
// ers.FromCode()
// return jwt.MapClaims{}, usecase.TokenError{Msg: fmt.Sprintf("parse token error: %s token: %s", err.Error(), accessToken)}
// }
//
// claims, ok := token.Claims.(jwt.MapClaims)
//
// if !(ok && token.Valid) {
// return jwt.MapClaims{}, usecase.TokenError{Msg: "token valid error"}
// }
//
// return claims, nil
return nil, nil
func parseToken(ctx context.Context, accessToken string, secret string) (jwt.MapClaims, error) {
token, err := jwt.Parse(accessToken, func(token *jwt.Token) (any, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, domain.TokenUnexpectedSigningErr(fmt.Sprintf("token unexpected signing method: %v", token.Header["alg"]))
}
return []byte(secret), nil
})
if err != nil {
return jwt.MapClaims{}, err
}
claims, ok := token.Claims.(jwt.MapClaims)
if !(ok && token.Valid) {
return jwt.MapClaims{}, domain.TokenTokenValidateErr("token valid error")
}
return claims, nil
}
func convertMap(input map[string]interface{}) map[string]string {
output := make(map[string]string)
for key, value := range input {
switch v := value.(type) {
case string:
output[key] = v
case fmt.Stringer:
output[key] = v.String()
default:
output[key] = fmt.Sprintf("%v", value)
}
}
return output
}

View File

@ -5,7 +5,6 @@ import (
"ark-permission/internal/domain/repository"
"ark-permission/internal/entity"
ers "code.30cm.net/wanderland/library-go/errors"
"context"
"encoding/json"
"errors"
@ -32,7 +31,7 @@ func NewTokenRepository(param TokenRepositoryParam) repository.TokenRepository {
func (t *tokenRepository) Create(ctx context.Context, token entity.Token) error {
body, err := json.Marshal(token)
if err != nil {
return wrapError("json.Marshal token error", err)
return ers.ArkInternal("json.Marshal token error", err.Error())
}
err = t.store.Pipelined(func(tx redis.Pipeliner) error {
@ -53,11 +52,11 @@ func (t *tokenRepository) Create(ctx context.Context, token entity.Token) error
return nil
})
if err != nil {
return wrapError("store.Pipelined error", err)
return domain.RedisPipLineError(err.Error())
}
if err := t.SetUIDToken(token); err != nil {
return wrapError("SetUIDToken error", err)
return ers.ArkInternal("SetUIDToken error", err.Error())
}
return nil
@ -76,7 +75,7 @@ func (t *tokenRepository) Delete(ctx context.Context, token entity.Token) error
for _, key := range keys {
if err := tx.Del(ctx, key).Err(); err != nil {
return fmt.Errorf("store.Del key error: %w", err)
return domain.RedisDelError(fmt.Sprintf("store.Del key error: %v", err))
}
}
@ -84,7 +83,7 @@ func (t *tokenRepository) Delete(ctx context.Context, token entity.Token) error
key := domain.DeviceTokenRedisKey.With(token.UID).ToString()
_, err := t.store.Hdel(key, token.DeviceID)
if err != nil {
return fmt.Errorf("store.HDel deviceKey error: %w", err)
return domain.RedisDelError(fmt.Sprintf("store.HDel deviceKey error: %v", err))
}
}
@ -92,7 +91,7 @@ func (t *tokenRepository) Delete(ctx context.Context, token entity.Token) error
})
if err != nil {
return fmt.Errorf("store.Pipelined error: %w", err)
return domain.RedisPipLineError(fmt.Sprintf("store.Pipelined error: %v", err))
}
return nil
@ -100,17 +99,17 @@ func (t *tokenRepository) Delete(ctx context.Context, token entity.Token) error
func (t *tokenRepository) get(key string) (entity.Token, error) {
body, err := t.store.Get(key)
if errors.Is(err, redis.Nil) {
if errors.Is(err, redis.Nil) || body == "" {
return entity.Token{}, ers.ResourceNotFound("token key not found in redis", key)
}
if err != nil {
return entity.Token{}, fmt.Errorf("store.Get tokenTag error: %w", err)
return entity.Token{}, ers.ArkInternal(fmt.Sprintf("store.Get tokenTag error: %v", err))
}
var token entity.Token
if err := json.Unmarshal([]byte(body), &token); err != nil {
return entity.Token{}, fmt.Errorf("json.Unmarshal token error: %w", err)
return entity.Token{}, ers.ArkInternal(fmt.Sprintf("json.Unmarshal token error: %w", err))
}
return token, nil