Skip to content

Commit

Permalink
Abstract Login to services
Browse files Browse the repository at this point in the history
  • Loading branch information
QUDUSKUNLE committed Aug 6, 2023
1 parent 248d73c commit f891d96
Show file tree
Hide file tree
Showing 8 changed files with 168 additions and 146 deletions.
13 changes: 6 additions & 7 deletions internal/adapters/handlers/address.handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,18 @@ func (service *HTTPHandler) SaveAddress(ctx *gin.Context) {
ctx.JSON(http.StatusBadRequest, gin.H{"error": service.CompileErrors(err) })
return
}
user, err := service.CurrentUser(ctx)
if err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error() })
user, fal := ctx.Get("user")
if !fal {
ctx.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized", "status": false })
return
}

profile, err := service.ExternalServicesAdapter.ReadProfile((user.ID).String())
profile, err := service.ServicesAdapter.ReadProfile(((user.(*domain.User)).ID).String())
if err != nil {
ctx.JSON(http.StatusNotFound, gin.H{"error": "Profile record not found", "status": false})
return
}

if err := service.ExternalServicesAdapter.SaveAddress(
if err := service.ServicesAdapter.SaveAddress(
domain.Address{
StreetNo: address.StreetNo,
StreetName: address.StreetName,
Expand All @@ -44,7 +43,7 @@ func (service *HTTPHandler) ReadAddress(ctx *gin.Context) {
}

func (service *HTTPHandler) ReadAddresses(ctx *gin.Context) {
addresses, err := service.ExternalServicesAdapter.ReadAddresses()
addresses, err := service.ServicesAdapter.ReadAddresses()
if err != nil {
ctx.JSON(http.StatusInternalServerError, gin.H{ "error": err.Error(), "status": false})
return
Expand Down
112 changes: 2 additions & 110 deletions internal/adapters/handlers/handlers.go
Original file line number Diff line number Diff line change
@@ -1,123 +1,15 @@
package handlers

import (
"fmt"
"os"
"strconv"
"time"
services "server/internal/core/services"
"net/http"
"errors"
"strings"
"github.com/go-playground/validator/v10"
"server/internal/core/domain"
"github.com/google/uuid"
"github.com/golang-jwt/jwt/v4"
"github.com/gin-gonic/gin"
)

type ErrorMessage struct {
Field string `json:"field"`
Message string `json:"message"`
}

var privateKey = []byte(os.Getenv("JWT_PRIVATE_KEY"))

type HTTPHandler struct {
ExternalServicesAdapter services.ServicesHandler
InternalServicesAdapter services.ServicesHandler
ServicesAdapter services.ServicesHandler
}

func HTTPAdapter(services services.ServicesHandler) *HTTPHandler {
return &HTTPHandler{
ExternalServicesAdapter: services,
InternalServicesAdapter: services,
}
}

func (service *HTTPHandler) JWTAuthMiddleware() gin.HandlerFunc {
return func(ctx *gin.Context) {
token, err := service.ExtractToken(ctx)
if err != nil {
ctx.JSON(http.StatusUnauthorized, gin.H{"error": err.Error()})
ctx.Abort()
return
}
if _, err := service.ValidateJWToken(token); err != nil {
ctx.JSON(http.StatusUnauthorized, gin.H{"error": err.Error()})
ctx.Abort()
return
}
ctx.Next()
}
}

func (service *HTTPHandler) UUidMiddleware() gin.HandlerFunc {
return func(context *gin.Context) {
if !service.ValidateUUID(context.Param("id")) {
context.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "invalid id credential"})
return
}
context.Next()
}
}

func (service *HTTPHandler) ExtractToken(context *gin.Context) (*jwt.Token, error) {
bearerToken := context.Request.Header.Get("Authorization")
splitToken := strings.Split(bearerToken, " ")
token, err := jwt.Parse(splitToken[1], func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
return privateKey, nil
})
return token, err
}

func (service *HTTPHandler) ValidateJWToken(token *jwt.Token) (jwt.MapClaims, error) {
claim, ok := token.Claims.(jwt.MapClaims)
if ok && token.Valid {
return claim, nil
}
return nil, errors.New("invalid token")
}

func (service *HTTPHandler) ValidateUUID(uu string) bool {
if _, err := uuid.Parse(uu); err != nil {
return false
}
return true
}

func (service *HTTPHandler) GenerateJWToken(user domain.User) (string, error) {
tokenTTL, _ := strconv.Atoi(os.Getenv("TOKEN_TTL"))
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"id": user.ID,
"iat": time.Now().Unix(),
"eat": time.Now().Add(time.Second * time.Duration(tokenTTL)).Unix(),
})
return token.SignedString(privateKey)
}

func (service *HTTPHandler) SetErrorMessage(message validator.FieldError) string {
switch message.Tag() {
case "required":
return "This field is required"
case "lte":
return "Should be less than " + message.Param()
case "gte":
return "Should be greater than " + message.Param()
}
return "unknown"
}

func (service *HTTPHandler) CompileErrors(err error) []ErrorMessage {
var ve validator.ValidationErrors
var result []ErrorMessage
if errors.As(err, &ve) {
for _, fe := range ve {
result = append(result, ErrorMessage{fe.Field(), service.SetErrorMessage(fe)})
}
ServicesAdapter: services,
}
return result
}
37 changes: 37 additions & 0 deletions internal/adapters/handlers/helpers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package handlers

import (
"os"
"errors"
"github.com/go-playground/validator/v10"
)

type ErrorMessage struct {
Field string `json:"field"`
Message string `json:"message"`
}

var privateKey = []byte(os.Getenv("JWT_PRIVATE_KEY"))

func (service *HTTPHandler) SetErrorMessage(message validator.FieldError) string {
switch message.Tag() {
case "required":
return "This field is required"
case "lte":
return "Should be less than " + message.Param()
case "gte":
return "Should be greater than " + message.Param()
}
return "unknown"
}

func (service *HTTPHandler) CompileErrors(err error) []ErrorMessage {
var ve validator.ValidationErrors
var result []ErrorMessage
if errors.As(err, &ve) {
for _, fe := range ve {
result = append(result, ErrorMessage{fe.Field(), service.SetErrorMessage(fe)})
}
}
return result
}
75 changes: 75 additions & 0 deletions internal/adapters/handlers/middlewares.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package handlers

import (
"fmt"
"net/http"
"errors"
"strings"
"github.com/google/uuid"
"github.com/golang-jwt/jwt/v4"
"github.com/gin-gonic/gin"
)

func (service *HTTPHandler) JWTAuthentication() gin.HandlerFunc {
return func(ctx *gin.Context) {
token, err := extractToken(ctx)
if err != nil {
ctx.JSON(http.StatusUnauthorized, gin.H{"error": err.Error()})
ctx.Abort()
return
}
cla, err := validateJWToken(token)
if err != nil {
ctx.JSON(http.StatusUnauthorized, gin.H{"error": err.Error()})
ctx.Abort()
return
}
UserID := cla["id"].(string)
user, err := service.ServicesAdapter.ReadUser(UserID)
if err != nil {
ctx.JSON(http.StatusUnauthorized, gin.H{"error": err.Error()})
ctx.Abort()
return
}
ctx.Set("user", user)
ctx.Next()
}
}

func (service *HTTPHandler) UUIDMiddleware() gin.HandlerFunc {
return func (ctx *gin.Context) {
if !validateUUID(ctx.Param("id")) {
ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "invalid id credential"})
return
}
ctx.Next()
}
}

func extractToken(ctx *gin.Context) (*jwt.Token, error) {
bearerToken := ctx.Request.Header.Get("Authorization")
splitToken := strings.Split(bearerToken, " ")
token, err := jwt.Parse(splitToken[1], func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
return privateKey, nil
})
return token, err
}

func validateJWToken(token *jwt.Token) (jwt.MapClaims, error) {
claim, ok := token.Claims.(jwt.MapClaims)
if ok && token.Valid {
return claim, nil
}
return nil, errors.New("invalid token")
}

func validateUUID(str string) bool {
if _, err := uuid.Parse(str); err != nil {
return false
}
return true
}

14 changes: 7 additions & 7 deletions internal/adapters/handlers/profile.handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,16 @@ func (service *HTTPHandler) SaveProfile(ctx *gin.Context) {
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error() })
return
}
user, err := service.CurrentUser(ctx)
if err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error() })
user, fal := ctx.Get("user")
if !fal {
ctx.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized", "status": false })
return
}
if err := service.ExternalServicesAdapter.SaveProfile(
if err := service.ServicesAdapter.SaveProfile(
domain.Profile{
FirstName: strings.TrimSpace(profileDto.FirstName),
LastName: strings.TrimSpace(profileDto.LastName),
User: &user,
User: user.(*domain.User),
}); err != nil {
ctx.JSON(http.StatusConflict, gin.H{"error": err.Error()})
return
Expand All @@ -32,7 +32,7 @@ func (service *HTTPHandler) SaveProfile(ctx *gin.Context) {
}

func (service *HTTPHandler) ReadProfile(ctx *gin.Context) {
profile, err := service.ExternalServicesAdapter.ReadProfile(ctx.Param("id"))
profile, err := service.ServicesAdapter.ReadProfile(ctx.Param("id"))
if err != nil {
ctx.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
return
Expand All @@ -46,7 +46,7 @@ func (service *HTTPHandler) ReadProfiles(ctx *gin.Context) {
ctx.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
return
}
profiles, err := service.ExternalServicesAdapter.ReadProfiles()
profiles, err := service.ServicesAdapter.ReadProfiles()
if err != nil {
ctx.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
return
Expand Down
24 changes: 8 additions & 16 deletions internal/adapters/handlers/users.handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ func (service *HTTPHandler) SaveUser(ctx *gin.Context) {
if err := ctx.ShouldBindJSON(&user); err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{"error": service.CompileErrors(err) })
}
if err := service.ExternalServicesAdapter.SaveUser(
if err := service.ServicesAdapter.SaveUser(
domain.User{ Email: user.Email, Password: user.Password },
); err != nil {
ctx.JSON(http.StatusConflict, gin.H{"error": err.Error(), "status": false})
Expand All @@ -22,7 +22,7 @@ func (service *HTTPHandler) SaveUser(ctx *gin.Context) {
}

func (service *HTTPHandler) ReadUsers(ctx *gin.Context) {
result, err := service.ExternalServicesAdapter.ReadUsers()
result, err := service.ServicesAdapter.ReadUsers()
if err != nil {
ctx.JSON(http.StatusInternalServerError, gin.H{ "error": err.Error(), "status": false})
return
Expand All @@ -36,34 +36,26 @@ func (service *HTTPHandler) Login(ctx *gin.Context) {
ctx.JSON(http.StatusBadRequest, gin.H{"error": service.CompileErrors(err), "status": false })
return
}
user, err := service.InternalServicesAdapter.ReadUserByEmail(login.Email)
if err != nil {
ctx.JSON(http.StatusNotFound, gin.H{"error": err.Error(), "status": false})
return
}
if err := user.ValidatePassword(login.Password); err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error(), "status": false })
return
}
jwt, err := service.GenerateJWToken(*user)

jwt, err := service.ServicesAdapter.Login(login)
if err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{"error": service.CompileErrors(err), "status": false })
return
}
ctx.JSON(http.StatusOK, gin.H{"token": jwt, "status": true })
}

func (service *HTTPHandler) CurrentUser(ctx *gin.Context) (domain.User, error) {
token, err := service.ExtractToken(ctx)
func (service *HTTPHandler) currentUser(ctx *gin.Context) (domain.User, error) {
token, err := extractToken(ctx)
if err != nil {
return domain.User{}, err
}
claim, err := service.ValidateJWToken(token);
claim, err := validateJWToken(token);
if err != nil {
return domain.User{}, err
}
UserID := claim["id"].(string)
user, err := service.ExternalServicesAdapter.ReadUser(UserID)
user, err := service.ServicesAdapter.ReadUser(UserID)
if err != nil {
return domain.User{}, err
}
Expand Down
Loading

0 comments on commit f891d96

Please sign in to comment.