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

mendersoftware / mender-server / 1939754365

21 Jul 2025 03:18PM UTC coverage: 65.552% (+0.06%) from 65.492%
1939754365

Pull #810

gitlab-ci

alfrunes
fix(useradm): Always generate a unique user ID for newly created users

Changelog: Title
Ticket: MEN-8514
Signed-off-by: Alf-Rune Siqveland <alf.rune@northern.tech>
Pull Request #810: fix(useradm): Always generate a unique user ID for newly created users

12 of 12 new or added lines in 2 files covered. (100.0%)

249 existing lines in 7 files now uncovered.

32207 of 49132 relevant lines covered (65.55%)

1.4 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✔
UNCOV
128
                        rest.RenderError(c,
×
UNCOV
129
                                http.StatusBadRequest,
×
UNCOV
130
                                errors.Cause(err),
×
UNCOV
131
                        )
×
UNCOV
132
                        return
×
UNCOV
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✔
UNCOV
150
        case devauth.ErrDevIdAuthIdMismatch, devauth.ErrMaxDeviceCountReached:
×
UNCOV
151
                // error is always set to unauthorized, client does not need to
×
UNCOV
152
                // know why
×
UNCOV
153
                rest.RenderErrorWithMessage(c,
×
UNCOV
154
                        http.StatusUnauthorized,
×
UNCOV
155
                        errors.Cause(err),
×
UNCOV
156
                        "unauthorized",
×
UNCOV
157
                )
×
UNCOV
158
                return
×
UNCOV
159
        default:
×
UNCOV
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✔
UNCOV
177
                rest.RenderInternalError(c, err)
×
UNCOV
178
                return
×
UNCOV
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✔
UNCOV
213
                        err = errors.Wrap(err, "api: malformed query parameters")
×
UNCOV
214
                        rest.RenderError(c, http.StatusBadRequest, err)
×
UNCOV
215
                        return
×
UNCOV
216
                }
×
217
                if err = fltr.ParseForm(c.Request.Form); err != nil {
3✔
UNCOV
218
                        rest.RenderError(c, http.StatusBadRequest, err)
×
UNCOV
219
                        return
×
UNCOV
220
                }
×
221

UNCOV
222
        default:
×
UNCOV
223
                rest.RenderError(c,
×
224
                        http.StatusUnsupportedMediaType,
×
225
                        errors.Errorf(
×
226
                                "Content-Type '%s' not supported",
×
227
                                c.GetHeader("Content-Type"),
×
UNCOV
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 := int64(len(devs))
3✔
241
        hasNext := false
3✔
242

3✔
243
        if numDevs > perPage {
6✔
244
                hasNext = true
3✔
245
                numDevs -= 1
3✔
246
        }
3✔
247

248
        hints := rest.NewPagingHints().
3✔
249
                SetPage(page).
3✔
250
                SetPerPage(perPage).
3✔
251
                SetHasNext(hasNext)
3✔
252
        links, err := rest.MakePagingHeaders(c.Request, hints)
3✔
253
        if err != nil {
3✔
UNCOV
254
                rest.RenderError(c, http.StatusBadRequest, err)
×
UNCOV
255
        }
×
256
        for _, l := range links {
6✔
257
                c.Writer.Header().Add("Link", l)
3✔
258
        }
3✔
259
        c.JSON(http.StatusOK, devs[:numDevs])
3✔
260
}
261

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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