all repos

onasty @ bf8dc57251dbc6827d93ef4bb0e5461f6084a099

a one-time notes service

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

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