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

mendersoftware / mender-server / 1904691647

03 Jul 2025 12:59PM UTC coverage: 65.451% (-0.03%) from 65.478%
1904691647

Pull #769

gitlab-ci

alfrunes
chore(deviceauth): Add missing config env key replacer

The replacer did not exist because there were no prior configuration
settings with a period or dash in the key name.

Signed-off-by: Alf-Rune Siqveland <alf.rune@northern.tech>
Pull Request #769: MEN-7744: Rate limits for authenticated requests

192 of 358 new or added lines in 12 files covered. (53.63%)

2 existing lines in 2 files now uncovered.

32325 of 49388 relevant lines covered (65.45%)

1.38 hits per line

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

88.95
/backend/services/deviceauth/api/http/api_devauth.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
package http
15

16
import (
17
        "context"
18
        "encoding/json"
19
        "net/http"
20
        "strings"
21
        "time"
22

23
        "github.com/gin-gonic/gin"
24
        "github.com/pkg/errors"
25

26
        ctxhttpheader "github.com/mendersoftware/mender-server/pkg/context/httpheader"
27
        "github.com/mendersoftware/mender-server/pkg/identity"
28
        "github.com/mendersoftware/mender-server/pkg/rest.utils"
29

30
        "github.com/mendersoftware/mender-server/services/deviceauth/access"
31
        "github.com/mendersoftware/mender-server/services/deviceauth/cache"
32
        "github.com/mendersoftware/mender-server/services/deviceauth/devauth"
33
        "github.com/mendersoftware/mender-server/services/deviceauth/jwt"
34
        "github.com/mendersoftware/mender-server/services/deviceauth/model"
35
        "github.com/mendersoftware/mender-server/services/deviceauth/store"
36
        "github.com/mendersoftware/mender-server/services/deviceauth/utils"
37
)
38

39
const (
40
        defaultTimeout = time.Second * 5
41
)
42

43
var (
44
        ErrIncorrectStatus = errors.New("incorrect device status")
45
        ErrNoAuthHeader    = errors.New("Authorization not present in header")
46
)
47

48
type DevAuthApiHandlers struct {
49
        app         devauth.App
50
        db          store.DataStore
51
        rateLimiter gin.HandlerFunc
52
}
53

54
type DevAuthApiStatus struct {
55
        Status string `json:"status"`
56
}
57

58
func NewDevAuthApiHandlers(
59
        devAuth devauth.App,
60
        db store.DataStore,
61
        options ...Option,
62
) *DevAuthApiHandlers {
3✔
63
        var cfg Config
3✔
64
        for _, opt := range options {
3✔
NEW
65
                opt(&cfg)
×
NEW
66
        }
×
67

68
        return &DevAuthApiHandlers{
3✔
69
                app:         devAuth,
3✔
70
                db:          db,
3✔
71
                rateLimiter: cfg.AuthVerifyRatelimits,
3✔
72
        }
3✔
73
}
74

75
func (i *DevAuthApiHandlers) AliveHandler(c *gin.Context) {
2✔
76
        c.Status(http.StatusNoContent)
2✔
77
}
2✔
78

79
func (i *DevAuthApiHandlers) HealthCheckHandler(c *gin.Context) {
2✔
80
        ctx := c.Request.Context()
2✔
81
        ctx, cancel := context.WithTimeout(ctx, defaultTimeout)
2✔
82
        defer cancel()
2✔
83

2✔
84
        err := i.app.HealthCheck(ctx)
2✔
85
        if err != nil {
3✔
86
                rest.RenderError(c, http.StatusServiceUnavailable, err)
1✔
87
                return
1✔
88
        }
1✔
89
        c.Status(http.StatusNoContent)
2✔
90
}
91

92
func (i *DevAuthApiHandlers) SubmitAuthRequestHandler(c *gin.Context) {
3✔
93
        var authreq model.AuthReq
3✔
94

3✔
95
        ctx := c.Request.Context()
3✔
96

3✔
97
        //validate req body by reading raw content manually
3✔
98
        //(raw body will be needed later, DecodeJsonPayload would
3✔
99
        //unmarshal and close it)
3✔
100
        body, err := utils.ReadBodyRaw(c.Request)
3✔
101
        if err != nil {
4✔
102
                err = errors.Wrap(err, "failed to decode auth request")
1✔
103
                rest.RenderError(c, http.StatusBadRequest, err)
1✔
104
                return
1✔
105
        }
1✔
106

107
        err = json.Unmarshal(body, &authreq)
3✔
108
        if err != nil {
4✔
109
                err = errors.Wrap(err, "failed to decode auth request")
1✔
110
                rest.RenderError(c, http.StatusBadRequest, err)
1✔
111
                return
1✔
112
        }
1✔
113

114
        err = authreq.Validate()
3✔
115
        if err != nil {
5✔
116
                err = errors.Wrap(err, "invalid auth request")
2✔
117
                rest.RenderError(c, http.StatusBadRequest, err)
2✔
118
                return
2✔
119
        }
2✔
120

121
        //verify signature
122
        signature := c.GetHeader(HdrAuthReqSign)
3✔
123
        if signature == "" {
4✔
124
                rest.RenderError(c, http.StatusBadRequest,
1✔
125
                        errors.New("missing request signature header"),
1✔
126
                )
1✔
127
                return
1✔
128
        }
1✔
129

130
        token, err := i.app.SubmitAuthRequest(ctx, &authreq)
3✔
131
        if err != nil {
6✔
132
                if devauth.IsErrDevAuthUnauthorized(err) {
6✔
133
                        rest.RenderError(c,
3✔
134
                                http.StatusUnauthorized,
3✔
135
                                errors.Cause(err),
3✔
136
                        )
3✔
137
                        return
3✔
138
                } else if devauth.IsErrDevAuthBadRequest(err) {
3✔
139
                        rest.RenderError(c,
×
140
                                http.StatusBadRequest,
×
141
                                errors.Cause(err),
×
142
                        )
×
143
                        return
×
144
                }
×
145
        }
146

147
        switch err {
3✔
148
        case nil:
3✔
149
                err = utils.VerifyAuthReqSign(signature, authreq.PubKeyStruct, body)
3✔
150
                if err != nil {
4✔
151
                        rest.RenderErrorWithMessage(c,
1✔
152
                                http.StatusUnauthorized,
1✔
153
                                errors.Cause(err),
1✔
154
                                "signature verification failed",
1✔
155
                        )
1✔
156
                        return
1✔
157
                }
1✔
158
                c.Header("Content-Type", "application/jwt")
3✔
159
                _, _ = c.Writer.Write([]byte(token))
3✔
160
                return
3✔
161
        case devauth.ErrDevIdAuthIdMismatch, devauth.ErrMaxDeviceCountReached:
×
162
                // error is always set to unauthorized, client does not need to
×
163
                // know why
×
164
                rest.RenderErrorWithMessage(c,
×
165
                        http.StatusUnauthorized,
×
166
                        errors.Cause(err),
×
167
                        "unauthorized",
×
168
                )
×
169
                return
×
170
        default:
×
171
                rest.RenderInternalError(c, err)
×
172
                return
×
173
        }
174
}
175

176
func (i *DevAuthApiHandlers) PostDevicesV2Handler(c *gin.Context) {
2✔
177
        ctx := c.Request.Context()
2✔
178

2✔
179
        req, err := parsePreAuthReq(c.Request.Body)
2✔
180
        if err != nil {
4✔
181
                err = errors.Wrap(err, "failed to decode preauth request")
2✔
182
                rest.RenderError(c, http.StatusBadRequest, err)
2✔
183
                return
2✔
184
        }
2✔
185

186
        reqDbModel, err := req.getDbModel()
2✔
187
        if err != nil {
2✔
188
                rest.RenderInternalError(c, err)
×
189
                return
×
190
        }
×
191

192
        device, err := i.app.PreauthorizeDevice(ctx, reqDbModel)
2✔
193
        switch err {
2✔
194
        case nil:
2✔
195
                c.Header("Location", "devices/"+reqDbModel.DeviceId)
2✔
196
                c.Status(http.StatusCreated)
2✔
197
        case devauth.ErrDeviceExists:
2✔
198
                c.JSON(http.StatusConflict, device)
2✔
199
        default:
1✔
200
                rest.RenderInternalError(c, err)
1✔
201
        }
202
}
203

204
func (i *DevAuthApiHandlers) SearchDevicesV2Handler(c *gin.Context) {
3✔
205
        ctx := c.Request.Context()
3✔
206

3✔
207
        page, perPage, err := rest.ParsePagingParameters(c.Request)
3✔
208
        if err != nil {
4✔
209
                rest.RenderError(c, http.StatusBadRequest, err)
1✔
210
                return
1✔
211
        }
1✔
212
        fltr := model.DeviceFilter{}
3✔
213

3✔
214
        switch strings.ToLower(c.GetHeader("Content-Type")) {
3✔
215
        case "application/json", "":
1✔
216
                err := c.ShouldBindJSON(&fltr)
1✔
217
                if err != nil {
2✔
218
                        err = errors.Wrap(err, "api: malformed request body")
1✔
219
                        rest.RenderError(c, http.StatusBadRequest, err)
1✔
220
                        return
1✔
221
                }
1✔
222
        case "application/x-www-form-urlencoded":
3✔
223
                if err = c.Request.ParseForm(); err != nil {
3✔
224
                        err = errors.Wrap(err, "api: malformed query parameters")
×
225
                        rest.RenderError(c, http.StatusBadRequest, err)
×
226
                        return
×
227
                }
×
228
                if err = fltr.ParseForm(c.Request.Form); err != nil {
3✔
229
                        rest.RenderError(c, http.StatusBadRequest, err)
×
230
                        return
×
231
                }
×
232

233
        default:
×
234
                rest.RenderError(c,
×
235
                        http.StatusUnsupportedMediaType,
×
236
                        errors.Errorf(
×
237
                                "Content-Type '%s' not supported",
×
238
                                c.GetHeader("Content-Type"),
×
239
                        ))
×
240
                return
×
241
        }
242

243
        skip := (page - 1) * perPage
3✔
244
        limit := perPage + 1
3✔
245
        devs, err := i.app.GetDevices(ctx, uint(skip), uint(limit), fltr)
3✔
246
        if err != nil {
4✔
247
                rest.RenderInternalError(c, err)
1✔
248
                return
1✔
249
        }
1✔
250

251
        numDevs := len(devs)
3✔
252
        hasNext := false
3✔
253
        if int64(numDevs) > perPage {
6✔
254
                hasNext = true
3✔
255
                numDevs = int(perPage)
3✔
256
        }
3✔
257
        hints := rest.NewPagingHints().
3✔
258
                SetPage(page).
3✔
259
                SetPerPage(perPage).
3✔
260
                SetHasNext(hasNext).
3✔
261
                SetTotalCount(int64(numDevs))
3✔
262
        links, err := rest.MakePagingHeaders(c.Request, hints)
3✔
263
        if err != nil {
3✔
264
                rest.RenderError(c, http.StatusBadRequest, err)
×
265
        }
×
266
        for _, l := range links {
6✔
267
                c.Writer.Header().Add("Link", l)
3✔
268
        }
3✔
269
        c.JSON(http.StatusOK, devs[:numDevs])
3✔
270
}
271

272
func (i *DevAuthApiHandlers) GetDevicesV2Handler(c *gin.Context) {
3✔
273
        c.Request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
3✔
274
        i.SearchDevicesV2Handler(c)
3✔
275
}
3✔
276

277
func (i *DevAuthApiHandlers) GetDevicesCountHandler(c *gin.Context) {
3✔
278
        ctx := c.Request.Context()
3✔
279
        status := c.Query("status")
3✔
280

3✔
281
        switch status {
3✔
282
        case model.DevStatusAccepted,
283
                model.DevStatusRejected,
284
                model.DevStatusPending,
285
                model.DevStatusPreauth,
286
                model.DevStatusNoAuth,
287
                "":
3✔
288
        default:
2✔
289
                rest.RenderError(c,
2✔
290
                        http.StatusBadRequest,
2✔
291
                        errors.New("status must be one of: pending, accepted, rejected, preauthorized, noauth"),
2✔
292
                )
2✔
293
                return
2✔
294
        }
295

296
        count, err := i.app.GetDevCountByStatus(ctx, status)
3✔
297

3✔
298
        if err != nil {
4✔
299
                rest.RenderInternalError(c, err)
1✔
300
                return
1✔
301
        }
1✔
302

303
        c.JSON(http.StatusOK, model.Count{Count: count})
3✔
304
}
305

306
func (i *DevAuthApiHandlers) GetDeviceV2Handler(c *gin.Context) {
3✔
307

3✔
308
        ctx := c.Request.Context()
3✔
309

3✔
310
        devId := c.Param("id")
3✔
311

3✔
312
        dev, err := i.app.GetDevice(ctx, devId)
3✔
313
        switch {
3✔
314
        case err == store.ErrDevNotFound:
3✔
315
                rest.RenderError(c, http.StatusNotFound, err)
3✔
316
        case dev != nil:
3✔
317
                c.JSON(http.StatusOK, dev)
3✔
318
        default:
1✔
319
                rest.RenderInternalError(c, err)
1✔
320
        }
321
}
322

323
func (i *DevAuthApiHandlers) DecommissionDeviceHandler(c *gin.Context) {
3✔
324

3✔
325
        ctx := c.Request.Context()
3✔
326

3✔
327
        devId := c.Param("id")
3✔
328

3✔
329
        if err := i.app.DecommissionDevice(ctx, devId); err != nil {
6✔
330
                if err == store.ErrDevNotFound {
6✔
331
                        c.Status(http.StatusNotFound)
3✔
332
                        return
3✔
333
                }
3✔
334
                rest.RenderInternalError(c, err)
2✔
335
                return
2✔
336
        }
337

338
        c.Status(http.StatusNoContent)
3✔
339
}
340

341
func (i *DevAuthApiHandlers) DeleteDeviceAuthSetHandler(c *gin.Context) {
3✔
342

3✔
343
        ctx := c.Request.Context()
3✔
344

3✔
345
        devId := c.Param("id")
3✔
346
        authId := c.Param("aid")
3✔
347

3✔
348
        if err := i.app.DeleteAuthSet(ctx, devId, authId); err != nil {
6✔
349
                if err == store.ErrAuthSetNotFound {
6✔
350
                        c.Status(http.StatusNotFound)
3✔
351
                        return
3✔
352
                }
3✔
353
                rest.RenderInternalError(c, err)
1✔
354
                return
1✔
355
        }
356

357
        c.Status(http.StatusNoContent)
3✔
358
}
359

360
func (i *DevAuthApiHandlers) DeleteTokenHandler(c *gin.Context) {
2✔
361
        ctx := c.Request.Context()
2✔
362

2✔
363
        tokenID := c.Param("id")
2✔
364
        err := i.app.RevokeToken(ctx, tokenID)
2✔
365
        if err != nil {
3✔
366
                if err == store.ErrTokenNotFound ||
1✔
367
                        err == devauth.ErrInvalidAuthSetID {
2✔
368
                        c.Status(http.StatusNotFound)
1✔
369
                        return
1✔
370
                }
1✔
371
                rest.RenderInternalError(c, err)
1✔
372
                return
1✔
373
        }
374

375
        c.Status(http.StatusNoContent)
2✔
376
}
377

378
func (i *DevAuthApiHandlers) VerifyTokenHandler(c *gin.Context) {
3✔
379
        ctx := c.Request.Context()
3✔
380

3✔
381
        tokenStr, err := extractToken(c.Request.Header)
3✔
382
        if err != nil {
3✔
383
                rest.RenderError(c, http.StatusUnauthorized, ErrNoAuthHeader)
×
384
                return
×
385
        }
×
386

387
        ctx = ctxhttpheader.WithContext(ctx,
3✔
388
                c.Request.Header,
3✔
389
                "X-Forwarded-Method",
3✔
390
                "X-Forwarded-Uri")
3✔
391

3✔
392
        // verify token
3✔
393
        err = i.app.VerifyToken(ctx, tokenStr)
3✔
394
        code := http.StatusOK
3✔
395
        if err != nil {
6✔
396
                switch e := errors.Cause(err); e {
3✔
397
                case jwt.ErrTokenExpired:
1✔
398
                        code = http.StatusForbidden
1✔
399
                case store.ErrTokenNotFound, store.ErrAuthSetNotFound, jwt.ErrTokenInvalid:
3✔
400
                        code = http.StatusUnauthorized
3✔
401
                case cache.ErrTooManyRequests:
1✔
402
                        code = http.StatusTooManyRequests
1✔
403
                default:
1✔
404
                        if _, ok := e.(access.PermissionError); ok {
1✔
405
                                rest.RenderError(c, http.StatusForbidden, e)
×
406
                        } else {
1✔
407
                                rest.RenderInternalError(c, err)
1✔
408
                        }
1✔
409
                        return
1✔
410
                }
411
        }
412
        if i.rateLimiter != nil {
3✔
NEW
413
                i.rateLimiter(c)
×
NEW
414
        }
×
415

416
        c.Status(code)
3✔
417
}
418

419
func (i *DevAuthApiHandlers) UpdateDeviceStatusHandler(c *gin.Context) {
3✔
420
        ctx := c.Request.Context()
3✔
421

3✔
422
        devid := c.Param("id")
3✔
423
        authid := c.Param("aid")
3✔
424

3✔
425
        var status DevAuthApiStatus
3✔
426
        err := c.ShouldBindJSON(&status)
3✔
427
        if err != nil {
5✔
428
                err = errors.Wrap(err, "failed to decode status data")
2✔
429
                rest.RenderError(c, http.StatusBadRequest, err)
2✔
430
                return
2✔
431
        }
2✔
432

433
        if err := statusValidate(&status); err != nil {
5✔
434
                rest.RenderError(c, http.StatusBadRequest, err)
2✔
435
                return
2✔
436
        }
2✔
437

438
        if status.Status == model.DevStatusAccepted {
6✔
439
                err = i.app.AcceptDeviceAuth(ctx, devid, authid)
3✔
440
        } else if status.Status == model.DevStatusRejected {
9✔
441
                err = i.app.RejectDeviceAuth(ctx, devid, authid)
3✔
442
        } else if status.Status == model.DevStatusPending {
7✔
443
                err = i.app.ResetDeviceAuth(ctx, devid, authid)
2✔
444
        }
2✔
445
        if err != nil {
6✔
446
                switch err {
3✔
447
                case store.ErrDevNotFound, store.ErrAuthSetNotFound:
3✔
448
                        rest.RenderError(c, http.StatusNotFound, err)
3✔
449
                case devauth.ErrDevIdAuthIdMismatch, devauth.ErrDevAuthBadRequest:
1✔
450
                        rest.RenderError(c, http.StatusBadRequest, err)
1✔
451
                case devauth.ErrMaxDeviceCountReached:
1✔
452
                        rest.RenderError(c, http.StatusUnprocessableEntity, err)
1✔
453
                default:
2✔
454
                        rest.RenderInternalError(c, err)
2✔
455
                }
456
                return
3✔
457
        }
458

459
        c.Status(http.StatusNoContent)
3✔
460
}
461

462
type LimitValue struct {
463
        Limit uint64 `json:"limit"`
464
}
465

466
func (i *DevAuthApiHandlers) PutTenantLimitHandler(c *gin.Context) {
3✔
467
        ctx := c.Request.Context()
3✔
468

3✔
469
        tenantId := c.Param("id")
3✔
470
        reqLimitName := c.Param("name")
3✔
471

3✔
472
        if !model.IsValidLimit(reqLimitName) {
4✔
473
                rest.RenderError(c,
1✔
474
                        http.StatusBadRequest,
1✔
475
                        errors.Errorf("unsupported limit %v", reqLimitName))
1✔
476
                return
1✔
477
        }
1✔
478

479
        var value LimitValue
3✔
480
        err := c.ShouldBindJSON(&value)
3✔
481
        if err != nil {
6✔
482
                err = errors.Wrap(err, "failed to decode limit request")
3✔
483
                rest.RenderError(c, http.StatusBadRequest, err)
3✔
484
                return
3✔
485
        }
3✔
486

487
        limit := model.Limit{
2✔
488
                Value: value.Limit,
2✔
489
                Name:  reqLimitName,
2✔
490
        }
2✔
491

2✔
492
        if err := i.app.SetTenantLimit(ctx, tenantId, limit); err != nil {
3✔
493
                rest.RenderInternalError(c, err)
1✔
494
                return
1✔
495
        }
1✔
496

497
        c.Status(http.StatusNoContent)
2✔
498
}
499

500
func (i *DevAuthApiHandlers) DeleteTenantLimitHandler(c *gin.Context) {
2✔
501
        ctx := c.Request.Context()
2✔
502

2✔
503
        tenantId := c.Param("id")
2✔
504
        reqLimitName := c.Param("name")
2✔
505

2✔
506
        if !model.IsValidLimit(reqLimitName) {
3✔
507
                rest.RenderError(c,
1✔
508
                        http.StatusBadRequest,
1✔
509
                        errors.Errorf("unsupported limit %v", reqLimitName))
1✔
510
                return
1✔
511
        }
1✔
512

513
        if err := i.app.DeleteTenantLimit(ctx, tenantId, reqLimitName); err != nil {
3✔
514
                rest.RenderInternalError(c, err)
1✔
515
                return
1✔
516
        }
1✔
517

518
        c.Status(http.StatusNoContent)
2✔
519
}
520

521
func (i *DevAuthApiHandlers) GetTenantLimitHandler(c *gin.Context) {
3✔
522
        ctx := c.Request.Context()
3✔
523

3✔
524
        tenantId := c.Param("id")
3✔
525
        limitName := c.Param("name")
3✔
526

3✔
527
        if !model.IsValidLimit(limitName) {
4✔
528
                rest.RenderError(c,
1✔
529
                        http.StatusBadRequest,
1✔
530
                        errors.Errorf("unsupported limit %v", limitName))
1✔
531
                return
1✔
532
        }
1✔
533

534
        lim, err := i.app.GetTenantLimit(ctx, limitName, tenantId)
3✔
535
        if err != nil {
4✔
536
                rest.RenderInternalError(c, err)
1✔
537
                return
1✔
538
        }
1✔
539

540
        c.JSON(http.StatusOK, LimitValue{lim.Value})
3✔
541
}
542

543
func (i *DevAuthApiHandlers) GetLimitHandler(c *gin.Context) {
2✔
544
        ctx := c.Request.Context()
2✔
545

2✔
546
        name := c.Param("name")
2✔
547

2✔
548
        if !model.IsValidLimit(name) {
3✔
549
                rest.RenderError(c,
1✔
550
                        http.StatusBadRequest,
1✔
551
                        errors.Errorf("unsupported limit %v", name))
1✔
552
                return
1✔
553
        }
1✔
554

555
        lim, err := i.app.GetLimit(ctx, name)
2✔
556
        if err != nil {
3✔
557
                rest.RenderInternalError(c, err)
1✔
558
                return
1✔
559
        }
1✔
560

561
        c.JSON(http.StatusOK, LimitValue{lim.Value})
2✔
562
}
563

564
func (i *DevAuthApiHandlers) DeleteTokensHandler(c *gin.Context) {
2✔
565

2✔
566
        ctx := c.Request.Context()
2✔
567

2✔
568
        tenantId := c.Query("tenant_id")
2✔
569
        if tenantId == "" {
4✔
570
                rest.RenderError(c,
2✔
571
                        http.StatusBadRequest,
2✔
572
                        errors.New("tenant_id must be provided"))
2✔
573
                return
2✔
574
        }
2✔
575
        devId := c.Query("device_id")
1✔
576

1✔
577
        err := i.app.DeleteTokens(ctx, tenantId, devId)
1✔
578
        switch err {
1✔
579
        case nil:
1✔
580
                c.Status(http.StatusNoContent)
1✔
581
        default:
1✔
582
                rest.RenderInternalError(c, err)
1✔
583
        }
584
}
585

586
func (i *DevAuthApiHandlers) GetAuthSetStatusHandler(c *gin.Context) {
1✔
587
        ctx := c.Request.Context()
1✔
588

1✔
589
        devid := c.Param("id")
1✔
590
        authid := c.Param("aid")
1✔
591

1✔
592
        // get authset directly from store
1✔
593
        aset, err := i.db.GetAuthSetById(ctx, authid)
1✔
594
        switch err {
1✔
595
        case nil:
1✔
596
                c.JSON(http.StatusOK, &model.Status{Status: aset.Status})
1✔
597
        case store.ErrDevNotFound, store.ErrAuthSetNotFound:
1✔
598
                rest.RenderError(c, http.StatusNotFound, store.ErrAuthSetNotFound)
1✔
599
        default:
×
600
                rest.RenderInternalError(c,
×
601
                        errors.Wrapf(err,
×
602
                                "failed to fetch auth set %s for device %s",
×
603
                                authid, devid))
×
604
        }
605
}
606

607
func (i *DevAuthApiHandlers) ProvisionTenantHandler(c *gin.Context) {
3✔
608
        // NOTE: This handler was used to initialize database collections. This is no longer
3✔
609
        //       needed after migration 2.0.0.
3✔
610
        c.Status(http.StatusCreated)
3✔
611
}
3✔
612

613
func (i *DevAuthApiHandlers) GetTenantDeviceStatus(c *gin.Context) {
2✔
614
        ctx := c.Request.Context()
2✔
615

2✔
616
        tid := c.Param("tid")
2✔
617
        did := c.Param("did")
2✔
618

2✔
619
        if did == "" {
3✔
620
                rest.RenderError(c,
1✔
621
                        http.StatusBadRequest,
1✔
622
                        errors.New("device id (did) cannot be empty"))
1✔
623
                return
1✔
624
        }
1✔
625

626
        status, err := i.app.GetTenantDeviceStatus(ctx, tid, did)
2✔
627
        switch err {
2✔
628
        case nil:
1✔
629
                c.JSON(http.StatusOK, status)
1✔
630
        case devauth.ErrDeviceNotFound:
2✔
631
                rest.RenderError(c, http.StatusNotFound, err)
2✔
632
        default:
1✔
633
                rest.RenderInternalError(c, err)
1✔
634
        }
635
}
636

637
func (i *DevAuthApiHandlers) GetTenantDevicesHandler(c *gin.Context) {
2✔
638
        ctx := c.Request.Context()
2✔
639
        if tid := c.Param("tid"); tid != "" {
4✔
640
                ctx = identity.WithContext(ctx, &identity.Identity{Tenant: tid})
2✔
641
        }
2✔
642
        c.Request = c.Request.WithContext(ctx)
2✔
643

2✔
644
        i.GetDevicesV2Handler(c)
2✔
645
}
646

647
func (i *DevAuthApiHandlers) GetTenantDevicesCountHandler(c *gin.Context) {
2✔
648
        ctx := c.Request.Context()
2✔
649
        if tid := c.Param("tid"); tid != "" {
4✔
650
                ctx = identity.WithContext(ctx, &identity.Identity{Tenant: tid})
2✔
651
        }
2✔
652
        c.Request = c.Request.WithContext(ctx)
2✔
653

2✔
654
        i.GetDevicesCountHandler(c)
2✔
655
}
656

657
func (i *DevAuthApiHandlers) DeleteDeviceHandler(c *gin.Context) {
2✔
658
        ctx := c.Request.Context()
2✔
659
        did := c.Param("did")
2✔
660

2✔
661
        err := i.app.DeleteDevice(ctx, did)
2✔
662
        switch err {
2✔
663
        case nil:
2✔
664
                c.Status(http.StatusNoContent)
2✔
665
        case devauth.ErrInvalidDeviceID:
×
666
                didErr := errors.New("device id (did) cannot be empty")
×
667
                rest.RenderError(c, http.StatusBadRequest, didErr)
×
668
        case store.ErrDevNotFound:
1✔
669
                c.Status(http.StatusNotFound)
1✔
670
        default:
×
671
                rest.RenderInternalError(c, err)
×
672
        }
673
}
674

675
// Validate status.
676
// Expected statuses:
677
// - "accepted"
678
// - "rejected"
679
// - "pending"
680
func statusValidate(status *DevAuthApiStatus) error {
3✔
681
        if status.Status != model.DevStatusAccepted &&
3✔
682
                status.Status != model.DevStatusRejected &&
3✔
683
                status.Status != model.DevStatusPending {
5✔
684
                return ErrIncorrectStatus
2✔
685
        } else {
5✔
686
                return nil
3✔
687
        }
3✔
688
}
689

690
// extracts JWT from authorization header
691
func extractToken(header http.Header) (string, error) {
3✔
692
        const authHeaderName = "Authorization"
3✔
693
        authHeader := header.Get(authHeaderName)
3✔
694
        if authHeader == "" {
3✔
695
                return "", ErrNoAuthHeader
×
696
        }
×
697
        tokenStr := strings.Replace(authHeader, "Bearer", "", 1)
3✔
698
        tokenStr = strings.Replace(tokenStr, "bearer", "", 1)
3✔
699
        return strings.TrimSpace(tokenStr), nil
3✔
700
}
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