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

mendersoftware / mender-server / 1861958901

10 Jun 2025 08:53AM UTC coverage: 65.74%. First build
1861958901

push

gitlab-ci

web-flow
Merge pull request #711 from mendersoftware/MEN-8235

Merge MEN-8235 into main

285 of 347 new or added lines in 13 files covered. (82.13%)

32499 of 49436 relevant lines covered (65.74%)

1.39 hits per line

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

89.58
/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
}
52

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

57
func NewDevAuthApiHandlers(devAuth devauth.App, db store.DataStore) *DevAuthApiHandlers {
3✔
58
        return &DevAuthApiHandlers{
3✔
59
                app: devAuth,
3✔
60
                db:  db,
3✔
61
        }
3✔
62
}
3✔
63

64
func (i *DevAuthApiHandlers) AliveHandler(c *gin.Context) {
2✔
65
        c.Status(http.StatusNoContent)
2✔
66
}
2✔
67

68
func (i *DevAuthApiHandlers) HealthCheckHandler(c *gin.Context) {
2✔
69
        ctx := c.Request.Context()
2✔
70
        ctx, cancel := context.WithTimeout(ctx, defaultTimeout)
2✔
71
        defer cancel()
2✔
72

2✔
73
        err := i.app.HealthCheck(ctx)
2✔
74
        if err != nil {
3✔
75
                rest.RenderError(c, http.StatusServiceUnavailable, err)
1✔
76
                return
1✔
77
        }
1✔
78
        c.Status(http.StatusNoContent)
2✔
79
}
80

81
func (i *DevAuthApiHandlers) SubmitAuthRequestHandler(c *gin.Context) {
3✔
82
        var authreq model.AuthReq
3✔
83

3✔
84
        ctx := c.Request.Context()
3✔
85

3✔
86
        //validate req body by reading raw content manually
3✔
87
        //(raw body will be needed later, DecodeJsonPayload would
3✔
88
        //unmarshal and close it)
3✔
89
        body, err := utils.ReadBodyRaw(c.Request)
3✔
90
        if err != nil {
4✔
91
                err = errors.Wrap(err, "failed to decode auth request")
1✔
92
                rest.RenderError(c, http.StatusBadRequest, err)
1✔
93
                return
1✔
94
        }
1✔
95

96
        err = json.Unmarshal(body, &authreq)
3✔
97
        if err != nil {
4✔
98
                err = errors.Wrap(err, "failed to decode auth request")
1✔
99
                rest.RenderError(c, http.StatusBadRequest, err)
1✔
100
                return
1✔
101
        }
1✔
102

103
        err = authreq.Validate()
3✔
104
        if err != nil {
5✔
105
                err = errors.Wrap(err, "invalid auth request")
2✔
106
                rest.RenderError(c, http.StatusBadRequest, err)
2✔
107
                return
2✔
108
        }
2✔
109

110
        //verify signature
111
        signature := c.GetHeader(HdrAuthReqSign)
3✔
112
        if signature == "" {
4✔
113
                rest.RenderError(c, http.StatusBadRequest,
1✔
114
                        errors.New("missing request signature header"),
1✔
115
                )
1✔
116
                return
1✔
117
        }
1✔
118

119
        token, err := i.app.SubmitAuthRequest(ctx, &authreq)
3✔
120
        if err != nil {
6✔
121
                if devauth.IsErrDevAuthUnauthorized(err) {
6✔
122
                        rest.RenderError(c,
3✔
123
                                http.StatusUnauthorized,
3✔
124
                                errors.Cause(err),
3✔
125
                        )
3✔
126
                        return
3✔
127
                } else if devauth.IsErrDevAuthBadRequest(err) {
3✔
NEW
128
                        rest.RenderError(c,
×
NEW
129
                                http.StatusBadRequest,
×
NEW
130
                                errors.Cause(err),
×
NEW
131
                        )
×
132
                        return
×
133
                }
×
134
        }
135

136
        switch err {
3✔
137
        case nil:
3✔
138
                err = utils.VerifyAuthReqSign(signature, authreq.PubKeyStruct, body)
3✔
139
                if err != nil {
4✔
140
                        rest.RenderErrorWithMessage(c,
1✔
141
                                http.StatusUnauthorized,
1✔
142
                                errors.Cause(err),
1✔
143
                                "signature verification failed",
1✔
144
                        )
1✔
145
                        return
1✔
146
                }
1✔
147
                c.Header("Content-Type", "application/jwt")
3✔
148
                _, _ = c.Writer.Write([]byte(token))
3✔
149
                return
3✔
150
        case devauth.ErrDevIdAuthIdMismatch, devauth.ErrMaxDeviceCountReached:
×
151
                // error is always set to unauthorized, client does not need to
×
152
                // know why
×
NEW
153
                rest.RenderErrorWithMessage(c,
×
NEW
154
                        http.StatusUnauthorized,
×
NEW
155
                        errors.Cause(err),
×
NEW
156
                        "unauthorized",
×
NEW
157
                )
×
158
                return
×
159
        default:
×
NEW
160
                rest.RenderInternalError(c, err)
×
161
                return
×
162
        }
163
}
164

165
func (i *DevAuthApiHandlers) PostDevicesV2Handler(c *gin.Context) {
2✔
166
        ctx := c.Request.Context()
2✔
167

2✔
168
        req, err := parsePreAuthReq(c.Request.Body)
2✔
169
        if err != nil {
4✔
170
                err = errors.Wrap(err, "failed to decode preauth request")
2✔
171
                rest.RenderError(c, http.StatusBadRequest, err)
2✔
172
                return
2✔
173
        }
2✔
174

175
        reqDbModel, err := req.getDbModel()
2✔
176
        if err != nil {
2✔
NEW
177
                rest.RenderInternalError(c, err)
×
178
                return
×
179
        }
×
180

181
        device, err := i.app.PreauthorizeDevice(ctx, reqDbModel)
2✔
182
        switch err {
2✔
183
        case nil:
2✔
184
                c.Header("Location", "devices/"+reqDbModel.DeviceId)
2✔
185
                c.Status(http.StatusCreated)
2✔
186
        case devauth.ErrDeviceExists:
2✔
187
                c.JSON(http.StatusConflict, device)
2✔
188
        default:
1✔
189
                rest.RenderInternalError(c, err)
1✔
190
        }
191
}
192

193
func (i *DevAuthApiHandlers) SearchDevicesV2Handler(c *gin.Context) {
3✔
194
        ctx := c.Request.Context()
3✔
195

3✔
196
        page, perPage, err := rest.ParsePagingParameters(c.Request)
3✔
197
        if err != nil {
4✔
198
                rest.RenderError(c, http.StatusBadRequest, err)
1✔
199
                return
1✔
200
        }
1✔
201
        fltr := model.DeviceFilter{}
3✔
202

3✔
203
        switch strings.ToLower(c.GetHeader("Content-Type")) {
3✔
204
        case "application/json", "":
1✔
205
                err := c.ShouldBindJSON(&fltr)
1✔
206
                if err != nil {
2✔
207
                        err = errors.Wrap(err, "api: malformed request body")
1✔
208
                        rest.RenderError(c, http.StatusBadRequest, err)
1✔
209
                        return
1✔
210
                }
1✔
211
        case "application/x-www-form-urlencoded":
3✔
212
                if err = c.Request.ParseForm(); err != nil {
3✔
213
                        err = errors.Wrap(err, "api: malformed query parameters")
×
NEW
214
                        rest.RenderError(c, http.StatusBadRequest, err)
×
215
                        return
×
216
                }
×
217
                if err = fltr.ParseForm(c.Request.Form); err != nil {
3✔
NEW
218
                        rest.RenderError(c, http.StatusBadRequest, err)
×
219
                        return
×
220
                }
×
221

222
        default:
×
NEW
223
                rest.RenderError(c,
×
NEW
224
                        http.StatusUnsupportedMediaType,
×
NEW
225
                        errors.Errorf(
×
NEW
226
                                "Content-Type '%s' not supported",
×
NEW
227
                                c.GetHeader("Content-Type"),
×
NEW
228
                        ))
×
229
                return
×
230
        }
231

232
        skip := (page - 1) * perPage
3✔
233
        limit := perPage + 1
3✔
234
        devs, err := i.app.GetDevices(ctx, uint(skip), uint(limit), fltr)
3✔
235
        if err != nil {
4✔
236
                rest.RenderInternalError(c, err)
1✔
237
                return
1✔
238
        }
1✔
239

240
        numDevs := len(devs)
3✔
241
        hasNext := false
3✔
242
        if int64(numDevs) > perPage {
6✔
243
                hasNext = true
3✔
244
                numDevs = int(perPage)
3✔
245
        }
3✔
246
        hints := rest.NewPagingHints().
3✔
247
                SetPage(page).
3✔
248
                SetPerPage(perPage).
3✔
249
                SetHasNext(hasNext).
3✔
250
                SetTotalCount(int64(numDevs))
3✔
251
        links, err := rest.MakePagingHeaders(c.Request, hints)
3✔
252
        if err != nil {
3✔
NEW
253
                rest.RenderError(c, http.StatusBadRequest, err)
×
NEW
254
        }
×
255
        for _, l := range links {
6✔
256
                c.Writer.Header().Add("Link", l)
3✔
257
        }
3✔
258
        c.JSON(http.StatusOK, devs[:numDevs])
3✔
259
}
260

261
func (i *DevAuthApiHandlers) GetDevicesV2Handler(c *gin.Context) {
3✔
262
        c.Request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
3✔
263
        i.SearchDevicesV2Handler(c)
3✔
264
}
3✔
265

266
func (i *DevAuthApiHandlers) GetDevicesCountHandler(c *gin.Context) {
3✔
267
        ctx := c.Request.Context()
3✔
268
        status := c.Query("status")
3✔
269

3✔
270
        switch status {
3✔
271
        case model.DevStatusAccepted,
272
                model.DevStatusRejected,
273
                model.DevStatusPending,
274
                model.DevStatusPreauth,
275
                model.DevStatusNoAuth,
276
                "":
3✔
277
        default:
2✔
278
                rest.RenderError(c,
2✔
279
                        http.StatusBadRequest,
2✔
280
                        errors.New("status must be one of: pending, accepted, rejected, preauthorized, noauth"),
2✔
281
                )
2✔
282
                return
2✔
283
        }
284

285
        count, err := i.app.GetDevCountByStatus(ctx, status)
3✔
286

3✔
287
        if err != nil {
4✔
288
                rest.RenderInternalError(c, err)
1✔
289
                return
1✔
290
        }
1✔
291

292
        c.JSON(http.StatusOK, model.Count{Count: count})
3✔
293
}
294

295
func (i *DevAuthApiHandlers) GetDeviceV2Handler(c *gin.Context) {
3✔
296

3✔
297
        ctx := c.Request.Context()
3✔
298

3✔
299
        devId := c.Param("id")
3✔
300

3✔
301
        dev, err := i.app.GetDevice(ctx, devId)
3✔
302
        switch {
3✔
303
        case err == store.ErrDevNotFound:
3✔
304
                rest.RenderError(c, http.StatusNotFound, err)
3✔
305
        case dev != nil:
3✔
306
                c.JSON(http.StatusOK, dev)
3✔
307
        default:
1✔
308
                rest.RenderInternalError(c, err)
1✔
309
        }
310
}
311

312
func (i *DevAuthApiHandlers) DecommissionDeviceHandler(c *gin.Context) {
3✔
313

3✔
314
        ctx := c.Request.Context()
3✔
315

3✔
316
        devId := c.Param("id")
3✔
317

3✔
318
        if err := i.app.DecommissionDevice(ctx, devId); err != nil {
6✔
319
                if err == store.ErrDevNotFound {
6✔
320
                        c.Status(http.StatusNotFound)
3✔
321
                        return
3✔
322
                }
3✔
323
                rest.RenderInternalError(c, err)
2✔
324
                return
2✔
325
        }
326

327
        c.Status(http.StatusNoContent)
3✔
328
}
329

330
func (i *DevAuthApiHandlers) DeleteDeviceAuthSetHandler(c *gin.Context) {
3✔
331

3✔
332
        ctx := c.Request.Context()
3✔
333

3✔
334
        devId := c.Param("id")
3✔
335
        authId := c.Param("aid")
3✔
336

3✔
337
        if err := i.app.DeleteAuthSet(ctx, devId, authId); err != nil {
6✔
338
                if err == store.ErrAuthSetNotFound {
6✔
339
                        c.Status(http.StatusNotFound)
3✔
340
                        return
3✔
341
                }
3✔
342
                rest.RenderInternalError(c, err)
1✔
343
                return
1✔
344
        }
345

346
        c.Status(http.StatusNoContent)
3✔
347
}
348

349
func (i *DevAuthApiHandlers) DeleteTokenHandler(c *gin.Context) {
2✔
350
        ctx := c.Request.Context()
2✔
351

2✔
352
        tokenID := c.Param("id")
2✔
353
        err := i.app.RevokeToken(ctx, tokenID)
2✔
354
        if err != nil {
3✔
355
                if err == store.ErrTokenNotFound ||
1✔
356
                        err == devauth.ErrInvalidAuthSetID {
2✔
357
                        c.Status(http.StatusNotFound)
1✔
358
                        return
1✔
359
                }
1✔
360
                rest.RenderInternalError(c, err)
1✔
361
                return
1✔
362
        }
363

364
        c.Status(http.StatusNoContent)
2✔
365
}
366

367
func (i *DevAuthApiHandlers) VerifyTokenHandler(c *gin.Context) {
3✔
368
        ctx := c.Request.Context()
3✔
369

3✔
370
        tokenStr, err := extractToken(c.Request.Header)
3✔
371
        if err != nil {
3✔
NEW
372
                rest.RenderError(c, http.StatusUnauthorized, ErrNoAuthHeader)
×
373
                return
×
374
        }
×
375

376
        ctx = ctxhttpheader.WithContext(ctx,
3✔
377
                c.Request.Header,
3✔
378
                "X-Forwarded-Method",
3✔
379
                "X-Forwarded-Uri")
3✔
380

3✔
381
        // verify token
3✔
382
        err = i.app.VerifyToken(ctx, tokenStr)
3✔
383
        code := http.StatusOK
3✔
384
        if err != nil {
6✔
385
                switch e := errors.Cause(err); e {
3✔
386
                case jwt.ErrTokenExpired:
1✔
387
                        code = http.StatusForbidden
1✔
388
                case store.ErrTokenNotFound, store.ErrAuthSetNotFound, jwt.ErrTokenInvalid:
3✔
389
                        code = http.StatusUnauthorized
3✔
390
                case cache.ErrTooManyRequests:
1✔
391
                        code = http.StatusTooManyRequests
1✔
392
                default:
1✔
393
                        if _, ok := e.(access.PermissionError); ok {
1✔
NEW
394
                                rest.RenderError(c, http.StatusForbidden, e)
×
395
                        } else {
1✔
396
                                rest.RenderInternalError(c, err)
1✔
397
                        }
1✔
398
                        return
1✔
399
                }
400
        }
401

402
        c.Status(code)
3✔
403
}
404

405
func (i *DevAuthApiHandlers) UpdateDeviceStatusHandler(c *gin.Context) {
3✔
406
        ctx := c.Request.Context()
3✔
407

3✔
408
        devid := c.Param("id")
3✔
409
        authid := c.Param("aid")
3✔
410

3✔
411
        var status DevAuthApiStatus
3✔
412
        err := c.ShouldBindJSON(&status)
3✔
413
        if err != nil {
5✔
414
                err = errors.Wrap(err, "failed to decode status data")
2✔
415
                rest.RenderError(c, http.StatusBadRequest, err)
2✔
416
                return
2✔
417
        }
2✔
418

419
        if err := statusValidate(&status); err != nil {
5✔
420
                rest.RenderError(c, http.StatusBadRequest, err)
2✔
421
                return
2✔
422
        }
2✔
423

424
        if status.Status == model.DevStatusAccepted {
6✔
425
                err = i.app.AcceptDeviceAuth(ctx, devid, authid)
3✔
426
        } else if status.Status == model.DevStatusRejected {
9✔
427
                err = i.app.RejectDeviceAuth(ctx, devid, authid)
3✔
428
        } else if status.Status == model.DevStatusPending {
7✔
429
                err = i.app.ResetDeviceAuth(ctx, devid, authid)
2✔
430
        }
2✔
431
        if err != nil {
6✔
432
                switch err {
3✔
433
                case store.ErrDevNotFound, store.ErrAuthSetNotFound:
3✔
434
                        rest.RenderError(c, http.StatusNotFound, err)
3✔
435
                case devauth.ErrDevIdAuthIdMismatch, devauth.ErrDevAuthBadRequest:
1✔
436
                        rest.RenderError(c, http.StatusBadRequest, err)
1✔
437
                case devauth.ErrMaxDeviceCountReached:
1✔
438
                        rest.RenderError(c, http.StatusUnprocessableEntity, err)
1✔
439
                default:
2✔
440
                        rest.RenderInternalError(c, err)
2✔
441
                }
442
                return
3✔
443
        }
444

445
        c.Status(http.StatusNoContent)
3✔
446
}
447

448
type LimitValue struct {
449
        Limit uint64 `json:"limit"`
450
}
451

452
func (i *DevAuthApiHandlers) PutTenantLimitHandler(c *gin.Context) {
3✔
453
        ctx := c.Request.Context()
3✔
454

3✔
455
        tenantId := c.Param("id")
3✔
456
        reqLimitName := c.Param("name")
3✔
457

3✔
458
        if !model.IsValidLimit(reqLimitName) {
4✔
459
                rest.RenderError(c,
1✔
460
                        http.StatusBadRequest,
1✔
461
                        errors.Errorf("unsupported limit %v", reqLimitName))
1✔
462
                return
1✔
463
        }
1✔
464

465
        var value LimitValue
3✔
466
        err := c.ShouldBindJSON(&value)
3✔
467
        if err != nil {
6✔
468
                err = errors.Wrap(err, "failed to decode limit request")
3✔
469
                rest.RenderError(c, http.StatusBadRequest, err)
3✔
470
                return
3✔
471
        }
3✔
472

473
        limit := model.Limit{
2✔
474
                Value: value.Limit,
2✔
475
                Name:  reqLimitName,
2✔
476
        }
2✔
477

2✔
478
        if err := i.app.SetTenantLimit(ctx, tenantId, limit); err != nil {
3✔
479
                rest.RenderInternalError(c, err)
1✔
480
                return
1✔
481
        }
1✔
482

483
        c.Status(http.StatusNoContent)
2✔
484
}
485

486
func (i *DevAuthApiHandlers) DeleteTenantLimitHandler(c *gin.Context) {
2✔
487
        ctx := c.Request.Context()
2✔
488

2✔
489
        tenantId := c.Param("id")
2✔
490
        reqLimitName := c.Param("name")
2✔
491

2✔
492
        if !model.IsValidLimit(reqLimitName) {
3✔
493
                rest.RenderError(c,
1✔
494
                        http.StatusBadRequest,
1✔
495
                        errors.Errorf("unsupported limit %v", reqLimitName))
1✔
496
                return
1✔
497
        }
1✔
498

499
        if err := i.app.DeleteTenantLimit(ctx, tenantId, reqLimitName); err != nil {
3✔
500
                rest.RenderInternalError(c, err)
1✔
501
                return
1✔
502
        }
1✔
503

504
        c.Status(http.StatusNoContent)
2✔
505
}
506

507
func (i *DevAuthApiHandlers) GetTenantLimitHandler(c *gin.Context) {
3✔
508
        ctx := c.Request.Context()
3✔
509

3✔
510
        tenantId := c.Param("id")
3✔
511
        limitName := c.Param("name")
3✔
512

3✔
513
        if !model.IsValidLimit(limitName) {
4✔
514
                rest.RenderError(c,
1✔
515
                        http.StatusBadRequest,
1✔
516
                        errors.Errorf("unsupported limit %v", limitName))
1✔
517
                return
1✔
518
        }
1✔
519

520
        lim, err := i.app.GetTenantLimit(ctx, limitName, tenantId)
3✔
521
        if err != nil {
4✔
522
                rest.RenderInternalError(c, err)
1✔
523
                return
1✔
524
        }
1✔
525

526
        c.JSON(http.StatusOK, LimitValue{lim.Value})
3✔
527
}
528

529
func (i *DevAuthApiHandlers) GetLimitHandler(c *gin.Context) {
2✔
530
        ctx := c.Request.Context()
2✔
531

2✔
532
        name := c.Param("name")
2✔
533

2✔
534
        if !model.IsValidLimit(name) {
3✔
535
                rest.RenderError(c,
1✔
536
                        http.StatusBadRequest,
1✔
537
                        errors.Errorf("unsupported limit %v", name))
1✔
538
                return
1✔
539
        }
1✔
540

541
        lim, err := i.app.GetLimit(ctx, name)
2✔
542
        if err != nil {
3✔
543
                rest.RenderInternalError(c, err)
1✔
544
                return
1✔
545
        }
1✔
546

547
        c.JSON(http.StatusOK, LimitValue{lim.Value})
2✔
548
}
549

550
func (i *DevAuthApiHandlers) DeleteTokensHandler(c *gin.Context) {
2✔
551

2✔
552
        ctx := c.Request.Context()
2✔
553

2✔
554
        tenantId := c.Query("tenant_id")
2✔
555
        if tenantId == "" {
4✔
556
                rest.RenderError(c,
2✔
557
                        http.StatusBadRequest,
2✔
558
                        errors.New("tenant_id must be provided"))
2✔
559
                return
2✔
560
        }
2✔
561
        devId := c.Query("device_id")
1✔
562

1✔
563
        err := i.app.DeleteTokens(ctx, tenantId, devId)
1✔
564
        switch err {
1✔
565
        case nil:
1✔
566
                c.Status(http.StatusNoContent)
1✔
567
        default:
1✔
568
                rest.RenderInternalError(c, err)
1✔
569
        }
570
}
571

572
func (i *DevAuthApiHandlers) GetAuthSetStatusHandler(c *gin.Context) {
1✔
573
        ctx := c.Request.Context()
1✔
574

1✔
575
        devid := c.Param("id")
1✔
576
        authid := c.Param("aid")
1✔
577

1✔
578
        // get authset directly from store
1✔
579
        aset, err := i.db.GetAuthSetById(ctx, authid)
1✔
580
        switch err {
1✔
581
        case nil:
1✔
582
                c.JSON(http.StatusOK, &model.Status{Status: aset.Status})
1✔
583
        case store.ErrDevNotFound, store.ErrAuthSetNotFound:
1✔
584
                rest.RenderError(c, http.StatusNotFound, store.ErrAuthSetNotFound)
1✔
585
        default:
×
NEW
586
                rest.RenderInternalError(c,
×
587
                        errors.Wrapf(err,
×
588
                                "failed to fetch auth set %s for device %s",
×
589
                                authid, devid))
×
590
        }
591
}
592

593
func (i *DevAuthApiHandlers) ProvisionTenantHandler(c *gin.Context) {
3✔
594
        // NOTE: This handler was used to initialize database collections. This is no longer
3✔
595
        //       needed after migration 2.0.0.
3✔
596
        c.Status(http.StatusCreated)
3✔
597
}
3✔
598

599
func (i *DevAuthApiHandlers) GetTenantDeviceStatus(c *gin.Context) {
2✔
600
        ctx := c.Request.Context()
2✔
601

2✔
602
        tid := c.Param("tid")
2✔
603
        did := c.Param("did")
2✔
604

2✔
605
        if did == "" {
3✔
606
                rest.RenderError(c,
1✔
607
                        http.StatusBadRequest,
1✔
608
                        errors.New("device id (did) cannot be empty"))
1✔
609
                return
1✔
610
        }
1✔
611

612
        status, err := i.app.GetTenantDeviceStatus(ctx, tid, did)
2✔
613
        switch err {
2✔
614
        case nil:
1✔
615
                c.JSON(http.StatusOK, status)
1✔
616
        case devauth.ErrDeviceNotFound:
2✔
617
                rest.RenderError(c, http.StatusNotFound, err)
2✔
618
        default:
1✔
619
                rest.RenderInternalError(c, err)
1✔
620
        }
621
}
622

623
func (i *DevAuthApiHandlers) GetTenantDevicesHandler(c *gin.Context) {
2✔
624
        ctx := c.Request.Context()
2✔
625
        if tid := c.Param("tid"); tid != "" {
4✔
626
                ctx = identity.WithContext(ctx, &identity.Identity{Tenant: tid})
2✔
627
        }
2✔
628
        c.Request = c.Request.WithContext(ctx)
2✔
629

2✔
630
        i.GetDevicesV2Handler(c)
2✔
631
}
632

633
func (i *DevAuthApiHandlers) GetTenantDevicesCountHandler(c *gin.Context) {
2✔
634
        ctx := c.Request.Context()
2✔
635
        if tid := c.Param("tid"); tid != "" {
4✔
636
                ctx = identity.WithContext(ctx, &identity.Identity{Tenant: tid})
2✔
637
        }
2✔
638
        c.Request = c.Request.WithContext(ctx)
2✔
639

2✔
640
        i.GetDevicesCountHandler(c)
2✔
641
}
642

643
func (i *DevAuthApiHandlers) DeleteDeviceHandler(c *gin.Context) {
2✔
644
        ctx := c.Request.Context()
2✔
645
        did := c.Param("did")
2✔
646

2✔
647
        err := i.app.DeleteDevice(ctx, did)
2✔
648
        switch err {
2✔
649
        case nil:
2✔
650
                c.Status(http.StatusNoContent)
2✔
651
        case devauth.ErrInvalidDeviceID:
×
652
                didErr := errors.New("device id (did) cannot be empty")
×
NEW
653
                rest.RenderError(c, http.StatusBadRequest, didErr)
×
654
        case store.ErrDevNotFound:
1✔
655
                c.Status(http.StatusNotFound)
1✔
656
        default:
×
NEW
657
                rest.RenderInternalError(c, err)
×
658
        }
659
}
660

661
// Validate status.
662
// Expected statuses:
663
// - "accepted"
664
// - "rejected"
665
// - "pending"
666
func statusValidate(status *DevAuthApiStatus) error {
3✔
667
        if status.Status != model.DevStatusAccepted &&
3✔
668
                status.Status != model.DevStatusRejected &&
3✔
669
                status.Status != model.DevStatusPending {
5✔
670
                return ErrIncorrectStatus
2✔
671
        } else {
5✔
672
                return nil
3✔
673
        }
3✔
674
}
675

676
// extracts JWT from authorization header
677
func extractToken(header http.Header) (string, error) {
3✔
678
        const authHeaderName = "Authorization"
3✔
679
        authHeader := header.Get(authHeaderName)
3✔
680
        if authHeader == "" {
3✔
681
                return "", ErrNoAuthHeader
×
682
        }
×
683
        tokenStr := strings.Replace(authHeader, "Bearer", "", 1)
3✔
684
        tokenStr = strings.Replace(tokenStr, "bearer", "", 1)
3✔
685
        return strings.TrimSpace(tokenStr), nil
3✔
686
}
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