all repos

onasty @ bcf5056

a one-time notes service

onasty/internal/transport/http/apiv1/middleware.go (view raw)

Smirnov Oleksandr Smirnov Oleksandr
ss2316544@gmail.com
chore(golangci-lint): upgrade config (#14)..., 1 year ago
1
package apiv1
2
3
import (
4
	"context"
5
	"strings"
6
7
	"github.com/gin-gonic/gin"
8
	"github.com/gofrs/uuid/v5"
9
	"github.com/olexsmir/onasty/internal/models"
10
)
11
12
const userIDCtxKey = "userID"
13
14
// authorizedMiddleware is a middleware that checks if user is authorized
15
// and if so sets user metadata to context
16
//
17
// being authorized is required for making the request for specific endpoint
18
func (a *APIV1) authorizedMiddleware(c *gin.Context) {
19
	token, ok := getTokenFromAuthHeaders(c)
20
	if !ok {
21
		errorResponse(c, ErrUnauthorized)
22
		return
23
	}
24
25
	uid, err := a.validateAuthorizedUser(c.Request.Context(), token)
26
	if err != nil {
27
		errorResponse(c, err)
28
		return
29
	}
30
31
	c.Set(userIDCtxKey, uid)
32
33
	c.Next()
34
}
35
36
// couldBeAuthorizedMiddleware is a middleware that checks if user is authorized and
37
// if so sets user metadata to context
38
//
39
// it is NOT required to be authorized for making the request for specific endpoint
40
func (a *APIV1) couldBeAuthorizedMiddleware(c *gin.Context) {
41
	token, ok := getTokenFromAuthHeaders(c)
42
	if ok {
43
		uid, err := a.validateAuthorizedUser(c.Request.Context(), token)
44
		if err != nil {
45
			errorResponse(c, err)
46
			return
47
		}
48
49
		c.Set(userIDCtxKey, uid)
50
	}
51
52
	c.Next()
53
}
54
55
//nolint:unused // TODO: remove me later
56
func (a *APIV1) isUserAuthorized(c *gin.Context) bool {
57
	return !a.getUserID(c).IsNil()
58
}
59
60
func getTokenFromAuthHeaders(c *gin.Context) (token string, ok bool) { //nolint:nonamedreturns
61
	header := c.GetHeader("Authorization")
62
	if header == "" {
63
		return "", false
64
	}
65
66
	headerParts := strings.Split(header, " ")
67
	if len(headerParts) != 2 && headerParts[0] != "Bearer" {
68
		return "", false
69
	}
70
71
	if len(headerParts[1]) == 0 {
72
		return "", false
73
	}
74
75
	return headerParts[1], true
76
}
77
78
// getUserId returns userId from the context
79
// getting user id is only possible if user is authorized
80
func (a *APIV1) getUserID(c *gin.Context) uuid.UUID {
81
	userID, exists := c.Get(userIDCtxKey)
82
	if !exists {
83
		return uuid.Nil
84
	}
85
86
	uid, ok := userID.(uuid.UUID)
87
	if !ok {
88
		return uuid.Nil
89
	}
90
91
	return uid
92
}
93
94
func (a *APIV1) validateAuthorizedUser(ctx context.Context, accessToken string) (uuid.UUID, error) {
95
	tokenPayload, err := a.usersrv.ParseJWTToken(accessToken)
96
	if err != nil {
97
		return uuid.Nil, err
98
	}
99
100
	userID := uuid.Must(uuid.FromString(tokenPayload.UserID))
101
102
	ok, err := a.usersrv.CheckIfUserExists(ctx, userID)
103
	if err != nil {
104
		return uuid.Nil, err
105
	}
106
107
	if !ok {
108
		return uuid.Nil, ErrUnauthorized
109
	}
110
111
	ok, err = a.usersrv.CheckIfUserIsActivated(ctx, userID)
112
	if err != nil {
113
		return uuid.Nil, err
114
	}
115
116
	if !ok {
117
		return uuid.Nil, models.ErrUserIsNotActivated
118
	}
119
120
	return userID, nil
121
}