Passed
Push — main ( 3ec306...c5cfaa )
by Yume
01:53 queued 44s
created

controllers.*JwtController.IsConnectedMiddleware   C

Complexity

Conditions 9

Size

Total Lines 54
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 29
dl 0
loc 54
rs 6.6666
c 0
b 0
f 0
nop 1

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
package controllers
2
3
import (
4
	"errors"
5
	"strconv"
6
7
	"github.com/getsentry/sentry-go"
8
	"github.com/gofiber/fiber/v2"
9
	"github.com/memnix/memnix-rest/config"
10
	"github.com/memnix/memnix-rest/domain"
11
	"github.com/memnix/memnix-rest/infrastructures"
12
	"github.com/memnix/memnix-rest/internal/user"
13
	"github.com/memnix/memnix-rest/views"
14
	"github.com/uptrace/opentelemetry-go-extra/otelzap"
15
	"go.uber.org/zap"
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(views.NewHTTPResponseVMFromError(errors.New("invalid permission")))
44
		}
45
46
		// if the route is public, we don't need to check if the userModel is connected
47
		if p == domain.PermissionNone {
48
			return c.Next()
49
		}
50
51
		_, span := infrastructures.GetFiberTracer().Start(c.UserContext(), "IsConnectedMiddleware")
52
		defer span.End()
53
54
		// get the token from the request header
55
		tokenHeader := c.Get("Authorization")
56
		// if the token is empty, the userModel is not connected, and we return an error
57
		if tokenHeader == "" {
58
			return c.Status(fiber.StatusUnauthorized).JSON(views.NewHTTPResponseVMFromError(errors.New("unauthorized: token missing")))
59
		}
60
61
		// get the userModel from the token
62
		// if the token is invalid, we return an error
63
		userID, err := config.GetJwtInstance().GetConnectedUserID(c.UserContext(), tokenHeader)
64
		if err != nil {
65
			return c.Status(fiber.StatusUnauthorized).JSON(views.NewHTTPResponseVMFromError(errors.New("unauthorized: invalid token")))
66
		}
67
68
		// get the userModel from the database
69
		userModel, err := j.IUseCase.GetByID(c.UserContext(), userID)
70
		if err != nil {
71
			otelzap.Ctx(c.UserContext()).Error("error getting user / not connected", zap.Error(err))
72
			return c.Status(fiber.StatusUnauthorized).JSON(views.NewHTTPResponseVMFromError(errors.New("unauthorized: invalid user")))
73
		}
74
75
		sentry.ConfigureScope(func(scope *sentry.Scope) {
76
			scope.SetUser(sentry.User{
77
				ID:       strconv.Itoa(int(userModel.ID)),
78
				Username: userModel.Username,
79
				Email:    userModel.Email,
80
			})
81
		})
82
83
		// Check permissions
84
		if !j.VerifyPermissions(userModel, p) {
85
			otelzap.Ctx(c.UserContext()).Warn("Not authorized", zap.Error(errors.New("unauthorized: insufficient permissions")))
86
			return c.Status(fiber.StatusUnauthorized).JSON(views.NewHTTPResponseVMFromError(errors.New("unauthorized: insufficient permissions")))
87
		}
88
89
		// Set userModel in locals
90
		SetUserToContext(c, userModel)
91
		span.End()
92
		return c.Next()
93
	}
94
}
95