• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mendersoftware / mender-server / 1833359013

23 May 2025 01:25PM UTC coverage: 66.318% (+0.5%) from 65.861%
1833359013

Pull #674

gitlab-ci

mzedel
fix(gui): prevented device tag editor to be shown when no tags exist

- this is to reduce confusion about tags defined async to the current session not being visible

Ticket: ME-528
Signed-off-by: Manuel Zedel <manuel.zedel@northern.tech>
Pull Request #674: ME-529, ME-528 - adjustments to device tag editing

29554 of 44564 relevant lines covered (66.32%)

1.45 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

76.3
/backend/pkg/identity/middleware.go
1
// Copyright 2023 Northern.tech AS
2
//
3
//    Licensed under the Apache License, Version 2.0 (the "License");
4
//    you may not use this file except in compliance with the License.
5
//    You may obtain a copy of the License at
6
//
7
//        http://www.apache.org/licenses/LICENSE-2.0
8
//
9
//    Unless required by applicable law or agreed to in writing, software
10
//    distributed under the License is distributed on an "AS IS" BASIS,
11
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
//    See the License for the specific language governing permissions and
13
//    limitations under the License.
14

15
package identity
16

17
import (
18
        "net/http"
19
        "regexp"
20

21
        "github.com/ant0ine/go-json-rest/rest"
22
        "github.com/gin-gonic/gin"
23

24
        "github.com/mendersoftware/mender-server/pkg/log"
25
        urest "github.com/mendersoftware/mender-server/pkg/rest.utils"
26
)
27

28
type MiddlewareOptions struct {
29
        // PathRegex sets the regex for the path for which this middleware
30
        // applies. Defaults to "^/api/management/v[0-9.]{1,6}/.+".
31
        PathRegex *string
32

33
        // UpdateLogger adds the decoded identity to the log context.
34
        UpdateLogger *bool
35
}
36

37
func NewMiddlewareOptions() *MiddlewareOptions {
4✔
38
        return new(MiddlewareOptions)
4✔
39
}
4✔
40

41
func (opts *MiddlewareOptions) SetPathRegex(regex string) *MiddlewareOptions {
2✔
42
        opts.PathRegex = &regex
2✔
43
        return opts
2✔
44
}
2✔
45

46
func (opts *MiddlewareOptions) SetUpdateLogger(updateLogger bool) *MiddlewareOptions {
4✔
47
        opts.UpdateLogger = &updateLogger
4✔
48
        return opts
4✔
49
}
4✔
50

51
func middlewareWithLogger(c *gin.Context) {
4✔
52
        var (
4✔
53
                err    error
4✔
54
                jwt    string
4✔
55
                idty   Identity
4✔
56
                logCtx = log.Ctx{}
4✔
57
                key    = "sub"
4✔
58
                ctx    = c.Request.Context()
4✔
59
                l      = log.FromContext(ctx)
4✔
60
        )
4✔
61
        jwt, err = ExtractJWTFromHeader(c.Request)
4✔
62
        if err != nil {
4✔
63
                goto exitUnauthorized
×
64
        }
65
        idty, err = ExtractIdentity(jwt)
4✔
66
        if err != nil {
4✔
67
                goto exitUnauthorized
×
68
        }
69
        ctx = WithContext(ctx, &idty)
4✔
70
        if idty.IsDevice {
6✔
71
                key = "device_id"
2✔
72
        } else if idty.IsUser {
10✔
73
                key = "user_id"
4✔
74
        }
4✔
75
        logCtx[key] = idty.Subject
4✔
76
        if idty.Tenant != "" {
7✔
77
                logCtx["tenant_id"] = idty.Tenant
3✔
78
        }
3✔
79
        if idty.Plan != "" {
4✔
80
                logCtx["plan"] = idty.Plan
×
81
        }
×
82
        ctx = log.WithContext(ctx, l.F(logCtx))
4✔
83

4✔
84
        c.Request = c.Request.WithContext(ctx)
4✔
85
        return
4✔
86
exitUnauthorized:
4✔
87
        c.Header("WWW-Authenticate", `Bearer realm="ManagementJWT"`)
×
88
        urest.RenderError(c, http.StatusUnauthorized, err)
×
89
        c.Abort()
×
90
}
91

92
func middlewareBase(c *gin.Context) {
×
93
        var (
×
94
                err  error
×
95
                jwt  string
×
96
                idty Identity
×
97
                ctx  = c.Request.Context()
×
98
        )
×
99
        jwt, err = ExtractJWTFromHeader(c.Request)
×
100
        if err != nil {
×
101
                goto exitUnauthorized
×
102
        }
103
        idty, err = ExtractIdentity(jwt)
×
104
        if err != nil {
×
105
                goto exitUnauthorized
×
106
        }
107
        ctx = WithContext(ctx, &idty)
×
108
        c.Request = c.Request.WithContext(ctx)
×
109
        return
×
110
exitUnauthorized:
×
111
        c.Header("WWW-Authenticate", `Bearer realm="ManagementJWT"`)
×
112
        urest.RenderError(c, http.StatusUnauthorized, err)
×
113
        c.Abort()
×
114
}
115

116
func Middleware(opts ...*MiddlewareOptions) gin.HandlerFunc {
4✔
117

4✔
118
        var middleware gin.HandlerFunc
4✔
119

4✔
120
        // Initialize default options
4✔
121
        opt := NewMiddlewareOptions().
4✔
122
                SetUpdateLogger(true)
4✔
123
        for _, o := range opts {
6✔
124
                if o == nil {
2✔
125
                        continue
×
126
                }
127
                if o.PathRegex != nil {
4✔
128
                        opt.PathRegex = o.PathRegex
2✔
129
                }
2✔
130
                if o.UpdateLogger != nil {
2✔
131
                        opt.UpdateLogger = o.UpdateLogger
×
132
                }
×
133
        }
134

135
        if *opt.UpdateLogger {
8✔
136
                middleware = middlewareWithLogger
4✔
137
        } else {
4✔
138
                middleware = middlewareBase
×
139
        }
×
140

141
        if opt.PathRegex != nil {
6✔
142
                pathRegex := regexp.MustCompile(*opt.PathRegex)
2✔
143
                return func(c *gin.Context) {
4✔
144
                        if !pathRegex.MatchString(c.FullPath()) {
4✔
145
                                return
2✔
146
                        }
2✔
147
                        middleware(c)
2✔
148
                }
149
        }
150
        return middleware
3✔
151
}
152

153
// IdentityMiddleware adds the identity extracted from JWT token to the request's context.
154
// IdentityMiddleware does not perform any form of token signature verification.
155
// If it is not possible to extract identity from header error log will be generated.
156
// IdentityMiddleware will not stop control propagating through the chain in any case.
157
// It is recommended to use IdentityMiddleware with RequestLogMiddleware and
158
// RequestLogMiddleware should be placed before IdentityMiddleware.
159
// Otherwise, log generated by IdentityMiddleware will not contain "request_id" field.
160
type IdentityMiddleware struct {
161
        // If set to true, the middleware will update context logger setting
162
        // 'user_id' or 'device_id' to the value of subject field, if the token
163
        // is not a user or a device token, the middelware will add a 'sub'
164
        // field to the logger
165
        UpdateLogger bool
166
}
167

168
// MiddlewareFunc makes IdentityMiddleware implement the Middleware interface.
169
func (mw *IdentityMiddleware) MiddlewareFunc(h rest.HandlerFunc) rest.HandlerFunc {
5✔
170
        return func(w rest.ResponseWriter, r *rest.Request) {
10✔
171
                jwt, err := ExtractJWTFromHeader(r.Request)
5✔
172
                if err != nil {
10✔
173
                        h(w, r)
5✔
174
                        return
5✔
175
                }
5✔
176

177
                ctx := r.Context()
5✔
178
                l := log.FromContext(ctx)
5✔
179

5✔
180
                identity, err := ExtractIdentity(jwt)
5✔
181
                if err != nil {
8✔
182
                        l.Warnf("Failed to parse extracted JWT: %s",
3✔
183
                                err.Error(),
3✔
184
                        )
3✔
185
                } else {
7✔
186
                        if mw.UpdateLogger {
8✔
187
                                logCtx := log.Ctx{}
4✔
188

4✔
189
                                key := "sub"
4✔
190
                                if identity.IsDevice {
7✔
191
                                        key = "device_id"
3✔
192
                                } else if identity.IsUser {
11✔
193
                                        key = "user_id"
4✔
194
                                }
4✔
195

196
                                logCtx[key] = identity.Subject
4✔
197

4✔
198
                                if identity.Tenant != "" {
5✔
199
                                        logCtx["tenant_id"] = identity.Tenant
1✔
200
                                }
1✔
201

202
                                if identity.Plan != "" {
6✔
203
                                        logCtx["plan"] = identity.Plan
2✔
204
                                }
2✔
205

206
                                l = l.F(logCtx)
4✔
207
                                ctx = log.WithContext(ctx, l)
4✔
208
                        }
209
                        ctx = WithContext(ctx, &identity)
4✔
210
                        r.Request = r.WithContext(ctx)
4✔
211
                }
212

213
                h(w, r)
5✔
214
        }
215
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc