fix: get profile user/staff using token rather than id

This commit is contained in:
ericprd 2025-02-28 12:18:47 +08:00
parent 5fb0764630
commit 6b858b99fb
15 changed files with 234 additions and 44 deletions

View File

@ -1,9 +1,5 @@
package config package config
import (
"legalgo-BE-go/internal/utilities/utils"
)
var ( var (
APP_PORT int APP_PORT int
GRACEFULL_TIMEOUT int GRACEFULL_TIMEOUT int
@ -13,16 +9,19 @@ var (
DB_USER, DB_USER,
DB_PASSWORD, DB_PASSWORD,
DB_NAME, DB_NAME,
DB_PORT string DB_PORT,
SALT_SECURITY string
) )
func InitEnv() { func InitEnv() {
DB_HOST = utils.GetOrDefault("DB_HOST", "localhost") DB_HOST = GetOrDefault("DB_HOST", "localhost")
DB_USER = utils.GetOrDefault("DB_USER", "") DB_USER = GetOrDefault("DB_USER", "")
DB_PASSWORD = utils.GetOrDefault("DB_PASSWORD", "") DB_PASSWORD = GetOrDefault("DB_PASSWORD", "")
DB_NAME = utils.GetOrDefault("DB_NAME", "") DB_NAME = GetOrDefault("DB_NAME", "")
DB_PORT = utils.GetOrDefault("DB_PORT", "") DB_PORT = GetOrDefault("DB_PORT", "")
APP_PORT = utils.GetOrDefault("APP_PORT", 3000) SALT_SECURITY = GetOrDefault("SALT_SECURITY", "legalgo")
GRACEFULL_TIMEOUT = utils.GetOrDefault("GRACEFULL_TIMEOUT", 10)
APP_PORT = GetOrDefault("APP_PORT", 3000)
GRACEFULL_TIMEOUT = GetOrDefault("GRACEFULL_TIMEOUT", 10)
} }

View File

@ -1,4 +1,4 @@
package utils package config
import ( import (
"os" "os"

View File

@ -0,0 +1,29 @@
package userrepository
import (
"errors"
authdomain "legalgo-BE-go/internal/domain/auth"
"gorm.io/gorm"
)
func (ur *UserRepository) GetUserProfile(email string) (*authdomain.UserProfile, error) {
var users []authdomain.UserProfile
if email == "" {
return nil, errors.New("email is empty")
}
if err := ur.DB.Table("users u").
Select("u.email, u.id, s.status as subscribe_status, sp.code as subscribe_plan_code, sp.name as subscribe_plan_name").
Joins("join subscribes s on s.id = u.subscribe_id").
Joins("join subscribe_plans sp on s.subscribe_plan_id = sp.id").
Scan(&users).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, errors.New("user not found")
}
return nil, err
}
return &users[0], nil
}

View File

@ -12,6 +12,7 @@ type UserRepository struct {
type UserIntf interface { type UserIntf interface {
GetUserByEmail(string) (*authdomain.User, error) GetUserByEmail(string) (*authdomain.User, error)
GetUserByID(string) (*authdomain.UserProfile, error) GetUserByID(string) (*authdomain.UserProfile, error)
GetUserProfile(string) (*authdomain.UserProfile, error)
CreateUser(*authdomain.User) (*authdomain.User, error) CreateUser(*authdomain.User) (*authdomain.User, error)
} }

View File

@ -4,7 +4,9 @@ import (
"errors" "errors"
authsvc "legalgo-BE-go/internal/services/auth" authsvc "legalgo-BE-go/internal/services/auth"
"legalgo-BE-go/internal/utilities/response" "legalgo-BE-go/internal/utilities/response"
"legalgo-BE-go/internal/utilities/utils"
"net/http" "net/http"
"strings"
"github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5"
) )
@ -13,14 +15,15 @@ func GetStaffProfile(
router chi.Router, router chi.Router,
authSvc authsvc.AuthIntf, authSvc authsvc.AuthIntf,
) { ) {
router.Get("/staff/{id}/profile", func(w http.ResponseWriter, r *http.Request) { router.Get("/staff/profile", func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context() ctx := r.Context()
id := chi.URLParam(r, "id") authHeader := r.Header.Get("Authorization")
if id == "" {
if authHeader == "" {
response.ResponseWithErrorCode( response.ResponseWithErrorCode(
ctx, ctx,
w, w,
errors.New("provided id is empty"), errors.New("provided auth is empty"),
response.ErrBadRequest.Code, response.ErrBadRequest.Code,
response.ErrBadRequest.HttpCode, response.ErrBadRequest.HttpCode,
"required params is not provided", "required params is not provided",
@ -28,7 +31,45 @@ func GetStaffProfile(
return return
} }
staffProfile, err := authSvc.GetStaffProfile(id) if !strings.HasPrefix(authHeader, "Bearer") {
response.ResponseWithErrorCode(
ctx,
w,
errors.New("invalid authorization token"),
response.ErrBadRequest.Code,
response.ErrBadRequest.HttpCode,
"invalid required token",
)
return
}
token := strings.Split(authHeader, " ")
if len(token) < 2 {
response.ResponseWithErrorCode(
ctx,
w,
errors.New("invalid authorization"),
response.ErrBadRequest.Code,
response.ErrBadRequest.HttpCode,
"invalid required token",
)
return
}
destructedToken, err := utils.DestructToken(token[1])
if err != nil {
response.ResponseWithErrorCode(
ctx,
w,
err,
response.ErrBadRequest.Code,
response.ErrBadRequest.HttpCode,
err.Error(),
)
return
}
staffProfile, err := authSvc.GetStaffProfile(destructedToken.Email)
if err != nil { if err != nil {
response.ResponseWithErrorCode( response.ResponseWithErrorCode(
ctx, ctx,
@ -49,14 +90,15 @@ func GetUserProfile(
router chi.Router, router chi.Router,
authSvc authsvc.AuthIntf, authSvc authsvc.AuthIntf,
) { ) {
router.Get("/user/{id}/profile", func(w http.ResponseWriter, r *http.Request) { router.Get("/user/profile", func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context() ctx := r.Context()
id := chi.URLParam(r, "id") authHeader := r.Header.Get("Authorization")
if id == "" {
if authHeader == "" {
response.ResponseWithErrorCode( response.ResponseWithErrorCode(
ctx, ctx,
w, w,
errors.New("provided id is empty"), errors.New("provided auth is empty"),
response.ErrBadRequest.Code, response.ErrBadRequest.Code,
response.ErrBadRequest.HttpCode, response.ErrBadRequest.HttpCode,
"required params is not provided", "required params is not provided",
@ -64,7 +106,45 @@ func GetUserProfile(
return return
} }
userProfile, err := authSvc.GetUserProfile(id) if !strings.HasPrefix(authHeader, "Bearer") {
response.ResponseWithErrorCode(
ctx,
w,
errors.New("invalid authorization token"),
response.ErrBadRequest.Code,
response.ErrBadRequest.HttpCode,
"invalid required token",
)
return
}
token := strings.Split(authHeader, " ")
if len(token) < 2 {
response.ResponseWithErrorCode(
ctx,
w,
errors.New("invalid authorization"),
response.ErrBadRequest.Code,
response.ErrBadRequest.HttpCode,
"invalid required token",
)
return
}
destructedToken, err := utils.DestructToken(token[1])
if err != nil {
response.ResponseWithErrorCode(
ctx,
w,
err,
response.ErrBadRequest.Code,
response.ErrBadRequest.HttpCode,
err.Error(),
)
return
}
userProfile, err := authSvc.GetUserProfile(destructedToken.Email)
if err != nil { if err != nil {
response.ResponseWithErrorCode( response.ResponseWithErrorCode(
ctx, ctx,

View File

@ -0,0 +1,6 @@
package authdomain
type AuthToken struct {
Email string
SessionID string
}

View File

@ -4,7 +4,7 @@ type JWTClaim string
const ( const (
TYPE JWTClaim = "type" TYPE JWTClaim = "type"
USERNAME JWTClaim = "username" EMAIL JWTClaim = "email"
EXPIRED_AT JWTClaim = "exp" EXPIRED_AT JWTClaim = "exp"
SESSION_ID JWTClaim = "sid" SESSION_ID JWTClaim = "sid"
ISSUED_AT JWTClaim = "iat" ISSUED_AT JWTClaim = "iat"

View File

@ -2,8 +2,8 @@ package authsvc
import authdomain "legalgo-BE-go/internal/domain/auth" import authdomain "legalgo-BE-go/internal/domain/auth"
func (as *AuthSvc) GetStaffProfile(id string) (*authdomain.StaffProfile, error) { func (as *AuthSvc) GetStaffProfile(email string) (*authdomain.StaffProfile, error) {
staff, err := as.staffRepo.GetStaffByID(id) staff, err := as.staffRepo.GetStaffByEmail(email)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -2,8 +2,8 @@ package authsvc
import authdomain "legalgo-BE-go/internal/domain/auth" import authdomain "legalgo-BE-go/internal/domain/auth"
func (as *AuthSvc) GetUserProfile(id string) (*authdomain.UserProfile, error) { func (as *AuthSvc) GetUserProfile(email string) (*authdomain.UserProfile, error) {
user, err := as.userRepo.GetUserByID(id) user, err := as.userRepo.GetUserProfile(email)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -5,6 +5,8 @@ import (
authdomain "legalgo-BE-go/internal/domain/auth" authdomain "legalgo-BE-go/internal/domain/auth"
"legalgo-BE-go/internal/utilities/utils" "legalgo-BE-go/internal/utilities/utils"
"github.com/google/uuid"
) )
func (sv *AuthSvc) LoginAsStaff(spec authdomain.LoginReq) (string, error) { func (sv *AuthSvc) LoginAsStaff(spec authdomain.LoginReq) (string, error) {
@ -18,7 +20,12 @@ func (sv *AuthSvc) LoginAsStaff(spec authdomain.LoginReq) (string, error) {
return "", errors.New("wrong password") return "", errors.New("wrong password")
} }
token, err := utils.GenerateToken(staff.Email) authToken := authdomain.AuthToken{
Email: staff.Email,
SessionID: uuid.NewString(),
}
token, err := utils.GenerateToken(authToken)
if err != nil { if err != nil {
return "", errors.New(err.Error()) return "", errors.New(err.Error())
} }

View File

@ -5,6 +5,8 @@ import (
authdomain "legalgo-BE-go/internal/domain/auth" authdomain "legalgo-BE-go/internal/domain/auth"
"legalgo-BE-go/internal/utilities/utils" "legalgo-BE-go/internal/utilities/utils"
"github.com/google/uuid"
) )
func (a *AuthSvc) LoginAsUser(spec authdomain.LoginReq) (string, error) { func (a *AuthSvc) LoginAsUser(spec authdomain.LoginReq) (string, error) {
@ -18,7 +20,12 @@ func (a *AuthSvc) LoginAsUser(spec authdomain.LoginReq) (string, error) {
return "", errors.New("wrong password") return "", errors.New("wrong password")
} }
token, err := utils.GenerateToken(user.Email) authToken := authdomain.AuthToken{
Email: user.Email,
SessionID: uuid.NewString(),
}
token, err := utils.GenerateToken(authToken)
if err != nil { if err != nil {
return "", errors.New(err.Error()) return "", errors.New(err.Error())
} }

View File

@ -19,19 +19,24 @@ func (a *AuthSvc) RegisterStaff(spec authdomain.RegisterStaffReq) (string, error
return "", err return "", err
} }
user := authdomain.Staff{ staff := authdomain.Staff{
ID: uuid.NewString(), ID: uuid.NewString(),
Email: spec.Email, Email: spec.Email,
Password: hashedPwd, Password: hashedPwd,
Username: spec.Username, Username: spec.Username,
} }
_, err = a.staffRepo.Create(&user) _, err = a.staffRepo.Create(&staff)
if err != nil { if err != nil {
return "", errors.New(err.Error()) return "", errors.New(err.Error())
} }
token, err := utils.GenerateToken(spec.Email) authToken := authdomain.AuthToken{
Email: staff.Email,
SessionID: uuid.NewString(),
}
token, err := utils.GenerateToken(authToken)
if err != nil { if err != nil {
return "", errors.New(err.Error()) return "", errors.New(err.Error())
} }

View File

@ -48,7 +48,12 @@ func (a *AuthSvc) RegisterUser(spec authdomain.RegisterUserReq) (string, error)
return "", errors.New(err.Error()) return "", errors.New(err.Error())
} }
token, err := utils.GenerateToken(spec.Email) authToken := authdomain.AuthToken{
Email: user.Email,
SessionID: uuid.NewString(),
}
token, err := utils.GenerateToken(authToken)
if err != nil { if err != nil {
return "", errors.New(err.Error()) return "", errors.New(err.Error())
} }

View File

@ -1,15 +1,18 @@
package utils package utils
import ( import (
"errors"
"fmt"
"time" "time"
"legalgo-BE-go/config"
authdomain "legalgo-BE-go/internal/domain/auth"
jwtclaimenum "legalgo-BE-go/internal/enums/jwt"
timeutils "legalgo-BE-go/internal/utilities/time_utils" timeutils "legalgo-BE-go/internal/utilities/time_utils"
"github.com/golang-jwt/jwt/v5" "github.com/golang-jwt/jwt/v5"
) )
var jwtSecret = []byte("secret jwt key") // TODO: change later from env
type ClaimOption func(options jwt.MapClaims) type ClaimOption func(options jwt.MapClaims)
// func GenerateToken(options ...ClaimOption) (string, error) { // func GenerateToken(options ...ClaimOption) (string, error) {
@ -28,12 +31,60 @@ type ClaimOption func(options jwt.MapClaims)
// return token.SignedString(jwtSecret) // return token.SignedString(jwtSecret)
// } // }
func GenerateToken(email string) (string, error) { func GenerateToken(data authdomain.AuthToken) (string, error) {
now := timeutils.Now() now := timeutils.Now()
token := jwt.New(jwt.SigningMethodHS256)
claims := token.Claims.(jwt.MapClaims)
claims["email"] = email
claims["exp"] = now.Add(time.Hour).Unix()
return token.SignedString(jwtSecret) claims := jwt.MapClaims{
"email": data.Email,
"session_id": data.SessionID,
"exp": now.Add(1 * time.Hour).Unix(),
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString([]byte(config.SALT_SECURITY))
}
func parseToken(s string) (*jwt.Token, error) {
return jwt.Parse(s, func(t *jwt.Token) (any, error) {
if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
errMsg := fmt.Sprintf("unexpected signing method: %v", t.Header["alg"])
return nil, errors.New(errMsg)
}
return []byte(config.SALT_SECURITY), nil
})
}
func DestructToken(s string) (authdomain.AuthToken, error) {
var data authdomain.AuthToken
token, err := parseToken(s)
if err != nil {
return data, err
}
claims, ok := token.Claims.(jwt.MapClaims)
if !ok {
return data, errors.New("failed to parse token")
}
if !token.Valid {
return data, errors.New("invalid token")
}
email, ok := claims[string(jwtclaimenum.EMAIL)].(string)
if !ok {
return data, errors.New("invalid email")
}
sessionId, ok := claims[string(jwtclaimenum.SESSION_ID)].(string)
data = authdomain.AuthToken{
Email: email,
SessionID: sessionId,
}
return data, nil
} }

View File

@ -120,7 +120,7 @@ paths:
message: message:
type: string type: string
/staff/{id}/profile: /staff/profile:
get: get:
summary: "get staff profile" summary: "get staff profile"
tags: tags:
@ -164,7 +164,7 @@ paths:
message: message:
type: string type: string
/user/{id}/profile: /user/profile:
get: get:
summary: "get staff profile" summary: "get staff profile"
tags: tags: