Passed
Push — main ( 01f0b4...eeea6b )
by Yume
03:58 queued 01:52
created

app/v1/controllers/middleware.go   A

Size/Duplication

Total Lines 96
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
cc 11
eloc 52
dl 0
loc 96
rs 10
c 0
b 0
f 0

3 Methods

Rating   Name   Duplication   Size   Complexity  
A controllers.*JwtController.VerifyPermissions 0 2 1
C controllers.*JwtController.IsConnectedMiddleware 0 58 9
A controllers.NewJwtController 0 2 1
1
package controllers
2
3
import (
4
	"errors"
5
	"log/slog"
6
	"strconv"
7
8
	"github.com/getsentry/sentry-go"
9
	"github.com/gofiber/fiber/v2"
10
	"github.com/gofiber/fiber/v2/log"
11
	"github.com/memnix/memnix-rest/app/v1/views"
12
	"github.com/memnix/memnix-rest/cmd/v1/config"
13
	"github.com/memnix/memnix-rest/domain"
14
	"github.com/memnix/memnix-rest/infrastructures"
15
	"github.com/memnix/memnix-rest/services/user"
16
)
17
18
// JwtController is the controller for the jwt routes.
19
type JwtController struct {
20
	user.IUseCase
21
}
22
23
// NewJwtController creates a new jwt controller.
24
func NewJwtController(user user.IUseCase) JwtController {
25
	return JwtController{IUseCase: user}
26
}
27
28
// VerifyPermissions checks if the user has the required permissions.
29
func (*JwtController) VerifyPermissions(user domain.User, p domain.Permission) bool {
30
	return user.HasPermission(p)
31
}
32
33
// IsConnectedMiddleware checks if the user is connected and has the required permissions
34
// the permissions are defined in the route definition
35
// returns an error if the user is not connected or has not the required permissions
36
//
37
// if the user is connected and has the required permissions, it sets the user in the locals
38
// and calls the next middleware.
39
func (j *JwtController) IsConnectedMiddleware(p domain.Permission) func(c *fiber.Ctx) error {
40
	return func(c *fiber.Ctx) error {
41
		// check if the permission is valid
42
		if !p.IsValid() {
43
			return c.Status(fiber.StatusInternalServerError).JSON(
44
				views.NewHTTPResponseVMFromError(errors.New("invalid permission")))
45
		}
46
47
		// if the route is public, we don't need to check if the userModel is connected
48
		if p == domain.PermissionNone {
49
			return c.Next()
50
		}
51
52
		_, span := infrastructures.GetTracerInstance().Tracer().Start(c.UserContext(), "IsConnectedMiddleware")
53
		defer span.End()
54
55
		// get the token from the request header
56
		tokenHeader := c.Get("Authorization")
57
		// if the token is empty, the userModel is not connected, and we return an error
58
		if tokenHeader == "" {
59
			return c.Status(fiber.StatusUnauthorized).JSON(
60
				views.NewHTTPResponseVMFromError(errors.New("unauthorized: token missing")))
61
		}
62
63
		// get the userModel from the token
64
		// if the token is invalid, we return an error
65
		userID, err := config.GetJwtInstance().GetJwt().GetConnectedUserID(c.UserContext(), tokenHeader)
66
		if err != nil {
67
			return c.Status(fiber.StatusUnauthorized).JSON(
68
				views.NewHTTPResponseVMFromError(errors.New("unauthorized: invalid token")))
69
		}
70
71
		// get the userModel from the database
72
		userModel, err := j.IUseCase.GetByID(c.UserContext(), userID)
73
		if err != nil {
74
			log.WithContext(c.UserContext()).Error("error getting user / not connected", slog.Any("error", err))
75
			return c.Status(fiber.StatusUnauthorized).JSON(
76
				views.NewHTTPResponseVMFromError(errors.New("unauthorized: invalid user")))
77
		}
78
79
		sentry.ConfigureScope(func(scope *sentry.Scope) {
80
			scope.SetUser(sentry.User{
81
				ID:       strconv.Itoa(int(userModel.ID)),
82
				Username: userModel.Username,
83
				Email:    userModel.Email,
84
			})
85
		})
86
87
		// Check permissions
88
		if !j.VerifyPermissions(userModel, p) {
89
			log.WithContext(c.UserContext()).Debug("Not authorized")
90
			return c.Status(fiber.StatusUnauthorized).JSON(views.NewHTTPResponseVMFromError(errors.New("unauthorized: insufficient permissions")))
91
		}
92
93
		// Set userModel in locals
94
		SetUserToContext(c, userModel)
95
		span.End()
96
		return c.Next()
97
	}
98
}
99