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

mendersoftware / deviceauth / 1284672998

09 May 2024 01:19PM UTC coverage: 81.658% (-1.1%) from 82.796%
1284672998

Pull #715

gitlab-ci

alfrunes
test(acceptance/os): :broom: Remove unused fixtures

Signed-off-by: Alf-Rune Siqveland <alf.rune@northern.tech>
Pull Request #715: Acceptance test fixup

4808 of 5888 relevant lines covered (81.66%)

51.13 hits per line

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

90.22
/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/ant0ine/go-json-rest/rest"
24
        "github.com/mendersoftware/go-lib-micro/accesslog"
25
        ctxhttpheader "github.com/mendersoftware/go-lib-micro/context/httpheader"
26
        "github.com/mendersoftware/go-lib-micro/identity"
27
        "github.com/mendersoftware/go-lib-micro/log"
28
        "github.com/mendersoftware/go-lib-micro/requestid"
29
        "github.com/mendersoftware/go-lib-micro/requestlog"
30
        "github.com/mendersoftware/go-lib-micro/rest_utils"
31
        "github.com/pkg/errors"
32

33
        "github.com/mendersoftware/deviceauth/access"
34
        "github.com/mendersoftware/deviceauth/cache"
35
        "github.com/mendersoftware/deviceauth/devauth"
36
        "github.com/mendersoftware/deviceauth/jwt"
37
        "github.com/mendersoftware/deviceauth/model"
38
        "github.com/mendersoftware/deviceauth/store"
39
        "github.com/mendersoftware/deviceauth/utils"
40
)
41

42
const (
43
        uriAuthReqs = "/api/devices/v1/authentication/auth_requests"
44

45
        // internal API
46
        uriAlive              = "/api/internal/v1/devauth/alive"
47
        uriHealth             = "/api/internal/v1/devauth/health"
48
        uriTokenVerify        = "/api/internal/v1/devauth/tokens/verify"
49
        uriTenantLimit        = "/api/internal/v1/devauth/tenant/#id/limits/#name"
50
        uriTokens             = "/api/internal/v1/devauth/tokens"
51
        uriTenants            = "/api/internal/v1/devauth/tenants"
52
        uriTenantDevice       = "/api/internal/v1/devauth/tenants/#tid/devices/#did"
53
        uriTenantDeviceStatus = "/api/internal/v1/devauth/tenants/#tid/devices/#did/status"
54
        uriTenantDevices      = "/api/internal/v1/devauth/tenants/#tid/devices"
55
        uriTenantDevicesCount = "/api/internal/v1/devauth/tenants/#tid/devices/count"
56

57
        // management API v2
58
        v2uriDevices             = "/api/management/v2/devauth/devices"
59
        v2uriDevicesCount        = "/api/management/v2/devauth/devices/count"
60
        v2uriDevicesSearch       = "/api/management/v2/devauth/devices/search"
61
        v2uriDevice              = "/api/management/v2/devauth/devices/#id"
62
        v2uriDeviceAuthSet       = "/api/management/v2/devauth/devices/#id/auth/#aid"
63
        v2uriDeviceAuthSetStatus = "/api/management/v2/devauth/devices/#id/auth/#aid/status"
64
        v2uriToken               = "/api/management/v2/devauth/tokens/#id"
65
        v2uriDevicesLimit        = "/api/management/v2/devauth/limits/#name"
66

67
        HdrAuthReqSign = "X-MEN-Signature"
68
)
69

70
func init() {
2✔
71
        rest.ErrorFieldName = "error"
2✔
72
}
2✔
73

74
const (
75
        defaultTimeout = time.Second * 5
76
)
77

78
var (
79
        ErrIncorrectStatus = errors.New("incorrect device status")
80
        ErrNoAuthHeader    = errors.New("no authorization header")
81
)
82

83
type DevAuthApiHandlers struct {
84
        devAuth devauth.App
85
        db      store.DataStore
86
}
87

88
type DevAuthApiStatus struct {
89
        Status string `json:"status"`
90
}
91

92
func NewDevAuthApiHandlers(devAuth devauth.App, db store.DataStore) ApiHandler {
94✔
93
        return &DevAuthApiHandlers{
94✔
94
                devAuth: devAuth,
94✔
95
                db:      db,
94✔
96
        }
94✔
97
}
94✔
98

99
func wrapMiddleware(middleware rest.Middleware, routes ...*rest.Route) []*rest.Route {
94✔
100
        for _, route := range routes {
1,211✔
101
                route.Func = middleware.MiddlewareFunc(route.Func)
1,117✔
102
        }
1,117✔
103
        return routes
94✔
104
}
105

106
func (d *DevAuthApiHandlers) Build() (http.Handler, error) {
94✔
107
        identityMiddleware := &identity.IdentityMiddleware{
94✔
108
                UpdateLogger: true,
94✔
109
        }
94✔
110
        internalRoutes := []*rest.Route{
94✔
111
                rest.Get(uriAlive, d.AliveHandler),
94✔
112
                rest.Get(uriHealth, d.HealthCheckHandler),
94✔
113
                rest.Get(uriTokenVerify, identityMiddleware.MiddlewareFunc(
94✔
114
                        d.VerifyTokenHandler,
94✔
115
                )),
94✔
116
                rest.Post(uriTokenVerify, identityMiddleware.MiddlewareFunc(
94✔
117
                        d.VerifyTokenHandler,
94✔
118
                )),
94✔
119
                rest.Delete(uriTokens, d.DeleteTokensHandler),
94✔
120

94✔
121
                rest.Put(uriTenantLimit, d.PutTenantLimitHandler),
94✔
122
                rest.Get(uriTenantLimit, d.GetTenantLimitHandler),
94✔
123
                rest.Delete(uriTenantLimit, d.DeleteTenantLimitHandler),
94✔
124

94✔
125
                rest.Post(uriTenants, d.ProvisionTenantHandler),
94✔
126
                rest.Get(uriTenantDeviceStatus, d.GetTenantDeviceStatus),
94✔
127
                rest.Get(uriTenantDevices, d.GetTenantDevicesHandler),
94✔
128
                rest.Get(uriTenantDevicesCount, d.GetTenantDevicesCountHandler),
94✔
129
                rest.Delete(uriTenantDevice, d.DeleteDeviceHandler),
94✔
130
        }
94✔
131
        publicRoutes := []*rest.Route{
94✔
132
                // Devices API
94✔
133
                rest.Post(uriAuthReqs, d.SubmitAuthRequestHandler),
94✔
134

94✔
135
                // API v2
94✔
136
                rest.Get(v2uriDevicesCount, d.GetDevicesCountHandler),
94✔
137
                rest.Get(v2uriDevices, d.GetDevicesV2Handler),
94✔
138
                rest.Post(v2uriDevicesSearch, d.SearchDevicesV2Handler),
94✔
139
                rest.Post(v2uriDevices, d.PostDevicesV2Handler),
94✔
140
                rest.Get(v2uriDevice, d.GetDeviceV2Handler),
94✔
141
                rest.Delete(v2uriDevice, d.DecommissionDeviceHandler),
94✔
142
                rest.Delete(v2uriDeviceAuthSet, d.DeleteDeviceAuthSetHandler),
94✔
143
                rest.Put(v2uriDeviceAuthSetStatus, d.UpdateDeviceStatusHandler),
94✔
144
                rest.Get(v2uriDeviceAuthSetStatus, d.GetAuthSetStatusHandler),
94✔
145
                rest.Delete(v2uriToken, d.DeleteTokenHandler),
94✔
146
                rest.Get(v2uriDevicesLimit, d.GetLimitHandler),
94✔
147
        }
94✔
148
        publicRoutes = wrapMiddleware(identityMiddleware, publicRoutes...)
94✔
149

94✔
150
        routes := append(publicRoutes, internalRoutes...)
94✔
151

94✔
152
        app, err := rest.MakeRouter(
94✔
153
                // augment routes with OPTIONS handler
94✔
154
                AutogenOptionsRoutes(routes, AllowHeaderOptionsGenerator)...,
94✔
155
        )
94✔
156
        if err != nil {
94✔
157
                return nil, errors.Wrap(err, "failed to create router")
×
158
        }
×
159

160
        api := rest.NewApi()
94✔
161
        api.SetApp(app)
94✔
162
        api.Use(
94✔
163
                &requestlog.RequestLogMiddleware{},
94✔
164
                &requestid.RequestIdMiddleware{},
94✔
165
                &accesslog.AccessLogMiddleware{
94✔
166
                        Format: accesslog.SimpleLogFormat,
94✔
167
                        DisableLog: func(statusCode int, r *rest.Request) bool {
196✔
168
                                if statusCode < 300 &&
102✔
169
                                        (r.Request.URL.Path == uriAlive ||
102✔
170
                                                r.Request.URL.Path == uriHealth ||
102✔
171
                                                r.Request.URL.Path == uriTokenVerify) {
107✔
172
                                        return true
5✔
173
                                }
5✔
174
                                return false
98✔
175
                        },
176
                },
177
                // verifies the request Content-Type header
178
                // The expected Content-Type is 'application/json'
179
                // if the content is non-null
180
                &rest.ContentTypeCheckerMiddleware{},
181
        )
182

183
        return api.MakeHandler(), nil
94✔
184
}
185

186
func (d *DevAuthApiHandlers) AliveHandler(w rest.ResponseWriter, r *rest.Request) {
1✔
187
        w.WriteHeader(http.StatusNoContent)
1✔
188
}
1✔
189

190
func (d *DevAuthApiHandlers) HealthCheckHandler(w rest.ResponseWriter, r *rest.Request) {
2✔
191
        ctx := r.Context()
2✔
192
        l := log.FromContext(ctx)
2✔
193
        ctx, cancel := context.WithTimeout(ctx, defaultTimeout)
2✔
194
        defer cancel()
2✔
195

2✔
196
        err := d.devAuth.HealthCheck(ctx)
2✔
197
        if err != nil {
3✔
198
                rest_utils.RestErrWithLog(w, r, l, err, http.StatusServiceUnavailable)
1✔
199
                return
1✔
200
        }
1✔
201
        w.WriteHeader(http.StatusNoContent)
1✔
202
}
203

204
func (d *DevAuthApiHandlers) SubmitAuthRequestHandler(w rest.ResponseWriter, r *rest.Request) {
11✔
205
        var authreq model.AuthReq
11✔
206

11✔
207
        ctx := r.Context()
11✔
208

11✔
209
        l := log.FromContext(ctx)
11✔
210

11✔
211
        //validate req body by reading raw content manually
11✔
212
        //(raw body will be needed later, DecodeJsonPayload would
11✔
213
        //unmarshal and close it)
11✔
214
        body, err := utils.ReadBodyRaw(r)
11✔
215
        if err != nil {
11✔
216
                err = errors.Wrap(err, "failed to decode auth request")
×
217
                rest_utils.RestErrWithLog(w, r, l, err, http.StatusBadRequest)
×
218
                return
×
219
        }
×
220

221
        err = json.Unmarshal(body, &authreq)
11✔
222
        if err != nil {
12✔
223
                err = errors.Wrap(err, "failed to decode auth request")
1✔
224
                rest_utils.RestErrWithLog(w, r, l, err, http.StatusBadRequest)
1✔
225
                return
1✔
226
        }
1✔
227

228
        err = authreq.Validate()
10✔
229
        if err != nil {
15✔
230
                err = errors.Wrap(err, "invalid auth request")
5✔
231
                rest_utils.RestErrWithLog(w, r, l, err, http.StatusBadRequest)
5✔
232
                return
5✔
233
        }
5✔
234

235
        //verify signature
236
        signature := r.Header.Get(HdrAuthReqSign)
6✔
237
        if signature == "" {
7✔
238
                rest_utils.RestErrWithLog(
1✔
239
                        w,
1✔
240
                        r,
1✔
241
                        l,
1✔
242
                        errors.New("missing request signature header"),
1✔
243
                        http.StatusBadRequest,
1✔
244
                )
1✔
245
                return
1✔
246
        }
1✔
247

248
        token, err := d.devAuth.SubmitAuthRequest(ctx, &authreq)
5✔
249
        if err != nil {
7✔
250
                if devauth.IsErrDevAuthUnauthorized(err) {
4✔
251
                        rest_utils.RestErrWithWarningMsg(w, r, l, err,
2✔
252
                                http.StatusUnauthorized, errors.Cause(err).Error())
2✔
253
                        return
2✔
254
                } else if devauth.IsErrDevAuthBadRequest(err) {
2✔
255
                        rest_utils.RestErrWithWarningMsg(w, r, l, err,
×
256
                                http.StatusBadRequest, errors.Cause(err).Error())
×
257
                        return
×
258
                }
×
259
        }
260

261
        switch err {
4✔
262
        case nil:
4✔
263
                err = utils.VerifyAuthReqSign(signature, authreq.PubKeyStruct, body)
4✔
264
                if err != nil {
5✔
265
                        rest_utils.RestErrWithLogMsg(
1✔
266
                                w,
1✔
267
                                r,
1✔
268
                                l,
1✔
269
                                err,
1✔
270
                                http.StatusUnauthorized,
1✔
271
                                "signature verification failed",
1✔
272
                        )
1✔
273
                        return
1✔
274
                }
1✔
275
                w.Header().Set("Content-Type", "application/jwt")
3✔
276
                _, _ = w.(http.ResponseWriter).Write([]byte(token))
3✔
277
                return
3✔
278
        case devauth.ErrDevIdAuthIdMismatch, devauth.ErrMaxDeviceCountReached:
×
279
                // error is always set to unauthorized, client does not need to
×
280
                // know why
×
281
                rest_utils.RestErrWithWarningMsg(w, r, l, devauth.ErrDevAuthUnauthorized,
×
282
                        http.StatusUnauthorized, "unauthorized")
×
283
                return
×
284
        default:
×
285
                rest_utils.RestErrWithLogInternal(w, r, l, err)
×
286
                return
×
287
        }
288
}
289

290
func (d *DevAuthApiHandlers) PostDevicesV2Handler(w rest.ResponseWriter, r *rest.Request) {
9✔
291
        ctx := r.Context()
9✔
292

9✔
293
        l := log.FromContext(ctx)
9✔
294

9✔
295
        req, err := parsePreAuthReq(r.Body)
9✔
296
        if err != nil {
14✔
297
                err = errors.Wrap(err, "failed to decode preauth request")
5✔
298
                rest_utils.RestErrWithLog(w, r, l, err, http.StatusBadRequest)
5✔
299
                return
5✔
300
        }
5✔
301

302
        reqDbModel, err := req.getDbModel()
4✔
303
        if err != nil {
4✔
304
                rest_utils.RestErrWithLogInternal(w, r, l, err)
×
305
                return
×
306
        }
×
307

308
        device, err := d.devAuth.PreauthorizeDevice(ctx, reqDbModel)
4✔
309
        switch err {
4✔
310
        case nil:
2✔
311
                w.Header().Set("Location", "devices/"+reqDbModel.DeviceId)
2✔
312
                w.WriteHeader(http.StatusCreated)
2✔
313
        case devauth.ErrDeviceExists:
1✔
314
                l.Error(err)
1✔
315
                w.WriteHeader(http.StatusConflict)
1✔
316
                _ = w.WriteJson(device)
1✔
317
        default:
1✔
318
                rest_utils.RestErrWithLogInternal(w, r, l, err)
1✔
319
        }
320
}
321

322
func (d *DevAuthApiHandlers) SearchDevicesV2Handler(w rest.ResponseWriter, r *rest.Request) {
15✔
323
        ctx := r.Context()
15✔
324
        l := log.FromContext(ctx)
15✔
325

15✔
326
        page, perPage, err := rest_utils.ParsePagination(r)
15✔
327
        if err != nil {
16✔
328
                rest_utils.RestErrWithLog(w, r, l, err, http.StatusBadRequest)
1✔
329
                return
1✔
330
        }
1✔
331
        fltr := model.DeviceFilter{}
14✔
332

14✔
333
        switch strings.ToLower(r.Header.Get("Content-Type")) {
14✔
334
        case "application/json", "":
3✔
335
                err := r.DecodeJsonPayload(&fltr)
3✔
336
                if err != nil {
4✔
337
                        err = errors.Wrap(err, "api: malformed request body")
1✔
338
                        rest_utils.RestErrWithLog(w, r, l, err, http.StatusBadRequest)
1✔
339
                        return
1✔
340
                }
1✔
341

342
        case "application/x-www-form-urlencoded":
11✔
343
                if err = r.ParseForm(); err != nil {
11✔
344
                        err = errors.Wrap(err, "api: malformed query parameters")
×
345
                        rest_utils.RestErrWithLog(w, r, l, err, http.StatusBadRequest)
×
346
                        return
×
347
                }
×
348
                if err = fltr.ParseForm(r.Form); err != nil {
11✔
349
                        rest_utils.RestErrWithLog(w, r, l, err, http.StatusBadRequest)
×
350
                        return
×
351
                }
×
352

353
        default:
×
354
                rest_utils.RestErrWithLog(w, r, l, errors.Errorf(
×
355
                        "Content-Type '%s' not supported",
×
356
                        r.Header.Get("Content-Type"),
×
357
                ), http.StatusUnsupportedMediaType)
×
358
                return
×
359
        }
360

361
        skip := (page - 1) * perPage
13✔
362
        limit := perPage + 1
13✔
363
        devs, err := d.devAuth.GetDevices(ctx, uint(skip), uint(limit), fltr)
13✔
364
        if err != nil {
15✔
365
                rest_utils.RestErrWithLogInternal(w, r, l, err)
2✔
366
                return
2✔
367
        }
2✔
368

369
        numDevs := len(devs)
11✔
370
        hasNext := false
11✔
371
        if uint64(numDevs) > perPage {
14✔
372
                hasNext = true
3✔
373
                numDevs = int(perPage)
3✔
374
        }
3✔
375

376
        links := rest_utils.MakePageLinkHdrs(r, page, perPage, hasNext)
11✔
377

11✔
378
        for _, l := range links {
26✔
379
                w.Header().Add("Link", l)
15✔
380
        }
15✔
381

382
        _ = w.WriteJson(devs[:numDevs])
11✔
383
}
384

385
func (d *DevAuthApiHandlers) GetDevicesV2Handler(w rest.ResponseWriter, r *rest.Request) {
11✔
386
        r.Header.Set("Content-Type", "application/x-www-form-urlencoded")
11✔
387
        d.SearchDevicesV2Handler(w, r)
11✔
388
}
11✔
389

390
func (d *DevAuthApiHandlers) GetDevicesCountHandler(w rest.ResponseWriter, r *rest.Request) {
13✔
391
        ctx := r.Context()
13✔
392
        l := log.FromContext(ctx)
13✔
393

13✔
394
        status := r.URL.Query().Get("status")
13✔
395

13✔
396
        switch status {
13✔
397
        case model.DevStatusAccepted,
398
                model.DevStatusRejected,
399
                model.DevStatusPending,
400
                model.DevStatusPreauth,
401
                model.DevStatusNoAuth,
402
                "":
12✔
403
        default:
1✔
404
                rest_utils.RestErrWithLog(
1✔
405
                        w,
1✔
406
                        r,
1✔
407
                        l,
1✔
408
                        errors.New("status must be one of: pending, accepted, rejected, preauthorized, noauth"),
1✔
409
                        http.StatusBadRequest,
1✔
410
                )
1✔
411
                return
1✔
412
        }
413

414
        count, err := d.devAuth.GetDevCountByStatus(ctx, status)
12✔
415

12✔
416
        if err != nil {
14✔
417
                rest_utils.RestErrWithLogInternal(w, r, l, err)
2✔
418
                return
2✔
419
        }
2✔
420

421
        _ = w.WriteJson(model.Count{Count: count})
10✔
422
}
423

424
func (d *DevAuthApiHandlers) GetDeviceV2Handler(w rest.ResponseWriter, r *rest.Request) {
4✔
425

4✔
426
        ctx := r.Context()
4✔
427

4✔
428
        l := log.FromContext(ctx)
4✔
429

4✔
430
        devId := r.PathParam("id")
4✔
431

4✔
432
        dev, err := d.devAuth.GetDevice(ctx, devId)
4✔
433
        switch {
4✔
434
        case err == store.ErrDevNotFound:
2✔
435
                rest_utils.RestErrWithLog(w, r, l, err, http.StatusNotFound)
2✔
436
        case dev != nil:
2✔
437
                _ = w.WriteJson(dev)
2✔
438
        default:
1✔
439
                rest_utils.RestErrWithLogInternal(w, r, l, err)
1✔
440
        }
441
}
442

443
func (d *DevAuthApiHandlers) DecommissionDeviceHandler(w rest.ResponseWriter, r *rest.Request) {
4✔
444

4✔
445
        ctx := r.Context()
4✔
446

4✔
447
        l := log.FromContext(ctx)
4✔
448

4✔
449
        devId := r.PathParam("id")
4✔
450

4✔
451
        if err := d.devAuth.DecommissionDevice(ctx, devId); err != nil {
7✔
452
                if err == store.ErrDevNotFound {
5✔
453
                        w.WriteHeader(http.StatusNotFound)
2✔
454
                        return
2✔
455
                }
2✔
456
                rest_utils.RestErrWithLogInternal(w, r, l, err)
2✔
457
                return
2✔
458
        }
459

460
        w.WriteHeader(http.StatusNoContent)
2✔
461
}
462

463
func (d *DevAuthApiHandlers) DeleteDeviceAuthSetHandler(w rest.ResponseWriter, r *rest.Request) {
4✔
464

4✔
465
        ctx := r.Context()
4✔
466

4✔
467
        l := log.FromContext(ctx)
4✔
468

4✔
469
        devId := r.PathParam("id")
4✔
470
        authId := r.PathParam("aid")
4✔
471

4✔
472
        if err := d.devAuth.DeleteAuthSet(ctx, devId, authId); err != nil {
7✔
473
                if err == store.ErrAuthSetNotFound {
5✔
474
                        w.WriteHeader(http.StatusNotFound)
2✔
475
                        return
2✔
476
                }
2✔
477
                rest_utils.RestErrWithLogInternal(w, r, l, err)
1✔
478
                return
1✔
479
        }
480

481
        w.WriteHeader(http.StatusNoContent)
2✔
482
}
483

484
func (d *DevAuthApiHandlers) DeleteTokenHandler(w rest.ResponseWriter, r *rest.Request) {
4✔
485
        ctx := r.Context()
4✔
486

4✔
487
        l := log.FromContext(ctx)
4✔
488

4✔
489
        tokenID := r.PathParam("id")
4✔
490
        err := d.devAuth.RevokeToken(ctx, tokenID)
4✔
491
        if err != nil {
6✔
492
                if err == store.ErrTokenNotFound ||
2✔
493
                        err == devauth.ErrInvalidAuthSetID {
3✔
494
                        w.WriteHeader(http.StatusNotFound)
1✔
495
                        return
1✔
496
                }
1✔
497
                rest_utils.RestErrWithLogInternal(w, r, l, err)
1✔
498
                return
1✔
499
        }
500

501
        w.WriteHeader(http.StatusNoContent)
2✔
502
}
503

504
func (d *DevAuthApiHandlers) VerifyTokenHandler(w rest.ResponseWriter, r *rest.Request) {
9✔
505
        ctx := r.Context()
9✔
506

9✔
507
        l := log.FromContext(ctx)
9✔
508

9✔
509
        tokenStr, err := extractToken(r.Header)
9✔
510
        if err != nil {
11✔
511
                rest_utils.RestErrWithLog(w, r, l, ErrNoAuthHeader, http.StatusUnauthorized)
2✔
512
                return
2✔
513
        }
2✔
514

515
        ctx = ctxhttpheader.WithContext(ctx,
8✔
516
                r.Header,
8✔
517
                "X-Forwarded-Method",
8✔
518
                "X-Forwarded-Uri")
8✔
519

8✔
520
        // verify token
8✔
521
        err = d.devAuth.VerifyToken(ctx, tokenStr)
8✔
522
        code := http.StatusOK
8✔
523
        if err != nil {
14✔
524
                switch e := errors.Cause(err); e {
6✔
525
                case jwt.ErrTokenExpired:
1✔
526
                        code = http.StatusForbidden
1✔
527
                case store.ErrTokenNotFound, store.ErrAuthSetNotFound, jwt.ErrTokenInvalid:
3✔
528
                        code = http.StatusUnauthorized
3✔
529
                case cache.ErrTooManyRequests:
1✔
530
                        code = http.StatusTooManyRequests
1✔
531
                default:
1✔
532
                        if _, ok := e.(access.PermissionError); ok {
1✔
533
                                rest_utils.RestErrWithLog(w, r, l, e, http.StatusForbidden)
×
534
                        } else {
1✔
535
                                rest_utils.RestErrWithLogInternal(w, r, l, err)
1✔
536
                        }
1✔
537
                        return
1✔
538
                }
539
                l.Error(err)
5✔
540
        }
541

542
        w.WriteHeader(code)
7✔
543
}
544

545
func (d *DevAuthApiHandlers) UpdateDeviceStatusHandler(w rest.ResponseWriter, r *rest.Request) {
10✔
546
        ctx := r.Context()
10✔
547

10✔
548
        l := log.FromContext(ctx)
10✔
549

10✔
550
        devid := r.PathParam("id")
10✔
551
        authid := r.PathParam("aid")
10✔
552

10✔
553
        var status DevAuthApiStatus
10✔
554
        err := r.DecodeJsonPayload(&status)
10✔
555
        if err != nil {
11✔
556
                err = errors.Wrap(err, "failed to decode status data")
1✔
557
                rest_utils.RestErrWithLog(w, r, l, err, http.StatusBadRequest)
1✔
558
                return
1✔
559
        }
1✔
560

561
        if err := statusValidate(&status); err != nil {
10✔
562
                rest_utils.RestErrWithLog(w, r, l, err, http.StatusBadRequest)
1✔
563
                return
1✔
564
        }
1✔
565

566
        if status.Status == model.DevStatusAccepted {
13✔
567
                err = d.devAuth.AcceptDeviceAuth(ctx, devid, authid)
5✔
568
        } else if status.Status == model.DevStatusRejected {
11✔
569
                err = d.devAuth.RejectDeviceAuth(ctx, devid, authid)
2✔
570
        } else if status.Status == model.DevStatusPending {
8✔
571
                err = d.devAuth.ResetDeviceAuth(ctx, devid, authid)
3✔
572
        }
3✔
573
        if err != nil {
13✔
574
                switch err {
5✔
575
                case store.ErrDevNotFound, store.ErrAuthSetNotFound:
2✔
576
                        rest_utils.RestErrWithLog(w, r, l, err, http.StatusNotFound)
2✔
577
                case devauth.ErrDevIdAuthIdMismatch, devauth.ErrDevAuthBadRequest:
1✔
578
                        rest_utils.RestErrWithLog(w, r, l, err, http.StatusBadRequest)
1✔
579
                case devauth.ErrMaxDeviceCountReached:
1✔
580
                        rest_utils.RestErrWithLog(w, r, l, err, http.StatusUnprocessableEntity)
1✔
581
                default:
2✔
582
                        rest_utils.RestErrWithLogInternal(w, r, l, err)
2✔
583
                }
584
                return
5✔
585
        }
586

587
        w.WriteHeader(http.StatusNoContent)
4✔
588
}
589

590
type LimitValue struct {
591
        Limit uint64 `json:"limit"`
592
}
593

594
func (d *DevAuthApiHandlers) PutTenantLimitHandler(w rest.ResponseWriter, r *rest.Request) {
5✔
595
        ctx := r.Context()
5✔
596

5✔
597
        l := log.FromContext(ctx)
5✔
598

5✔
599
        tenantId := r.PathParam("id")
5✔
600
        reqLimitName := r.PathParam("name")
5✔
601

5✔
602
        if !model.IsValidLimit(reqLimitName) {
6✔
603
                rest_utils.RestErrWithLog(w, r, l,
1✔
604
                        errors.Errorf("unsupported limit %v", reqLimitName),
1✔
605
                        http.StatusBadRequest)
1✔
606
                return
1✔
607
        }
1✔
608

609
        var value LimitValue
4✔
610
        err := r.DecodeJsonPayload(&value)
4✔
611
        if err != nil {
6✔
612
                err = errors.Wrap(err, "failed to decode limit request")
2✔
613
                rest_utils.RestErrWithLog(w, r, l, err, http.StatusBadRequest)
2✔
614
                return
2✔
615
        }
2✔
616

617
        limit := model.Limit{
3✔
618
                Value: value.Limit,
3✔
619
                Name:  reqLimitName,
3✔
620
        }
3✔
621

3✔
622
        if err := d.devAuth.SetTenantLimit(ctx, tenantId, limit); err != nil {
4✔
623
                rest_utils.RestErrWithLogInternal(w, r, l, err)
1✔
624
                return
1✔
625
        }
1✔
626

627
        w.WriteHeader(http.StatusNoContent)
2✔
628
}
629

630
func (d *DevAuthApiHandlers) DeleteTenantLimitHandler(w rest.ResponseWriter, r *rest.Request) {
3✔
631
        ctx := r.Context()
3✔
632

3✔
633
        l := log.FromContext(ctx)
3✔
634

3✔
635
        tenantId := r.PathParam("id")
3✔
636
        reqLimitName := r.PathParam("name")
3✔
637

3✔
638
        if !model.IsValidLimit(reqLimitName) {
4✔
639
                rest_utils.RestErrWithLog(w, r, l,
1✔
640
                        errors.Errorf("unsupported limit %v", reqLimitName),
1✔
641
                        http.StatusBadRequest)
1✔
642
                return
1✔
643
        }
1✔
644

645
        if err := d.devAuth.DeleteTenantLimit(ctx, tenantId, reqLimitName); err != nil {
3✔
646
                rest_utils.RestErrWithLogInternal(w, r, l, err)
1✔
647
                return
1✔
648
        }
1✔
649

650
        w.WriteHeader(http.StatusNoContent)
1✔
651
}
652

653
func (d *DevAuthApiHandlers) GetTenantLimitHandler(w rest.ResponseWriter, r *rest.Request) {
4✔
654
        ctx := r.Context()
4✔
655

4✔
656
        l := log.FromContext(ctx)
4✔
657

4✔
658
        tenantId := r.PathParam("id")
4✔
659
        limitName := r.PathParam("name")
4✔
660

4✔
661
        if !model.IsValidLimit(limitName) {
5✔
662
                rest_utils.RestErrWithLog(w, r, l,
1✔
663
                        errors.Errorf("unsupported limit %v", limitName),
1✔
664
                        http.StatusBadRequest)
1✔
665
                return
1✔
666
        }
1✔
667

668
        lim, err := d.devAuth.GetTenantLimit(ctx, limitName, tenantId)
3✔
669
        if err != nil {
4✔
670
                rest_utils.RestErrWithLogInternal(w, r, l, err)
1✔
671
                return
1✔
672
        }
1✔
673

674
        _ = w.WriteJson(LimitValue{lim.Value})
2✔
675
}
676

677
func (d *DevAuthApiHandlers) GetLimitHandler(w rest.ResponseWriter, r *rest.Request) {
4✔
678
        ctx := r.Context()
4✔
679

4✔
680
        l := log.FromContext(ctx)
4✔
681

4✔
682
        name := r.PathParam("name")
4✔
683

4✔
684
        if !model.IsValidLimit(name) {
5✔
685
                rest_utils.RestErrWithLog(w, r, l,
1✔
686
                        errors.Errorf("unsupported limit %v", name),
1✔
687
                        http.StatusBadRequest)
1✔
688
                return
1✔
689
        }
1✔
690

691
        lim, err := d.devAuth.GetLimit(ctx, name)
3✔
692
        if err != nil {
4✔
693
                rest_utils.RestErrWithLogInternal(w, r, l, err)
1✔
694
                return
1✔
695
        }
1✔
696

697
        _ = w.WriteJson(LimitValue{lim.Value})
2✔
698
}
699

700
func (d *DevAuthApiHandlers) DeleteTokensHandler(w rest.ResponseWriter, r *rest.Request) {
4✔
701

4✔
702
        ctx := r.Context()
4✔
703

4✔
704
        l := log.FromContext(ctx)
4✔
705

4✔
706
        tenantId := r.URL.Query().Get("tenant_id")
4✔
707
        if tenantId == "" {
5✔
708
                rest_utils.RestErrWithLog(
1✔
709
                        w,
1✔
710
                        r,
1✔
711
                        l,
1✔
712
                        errors.New("tenant_id must be provided"),
1✔
713
                        http.StatusBadRequest,
1✔
714
                )
1✔
715
                return
1✔
716
        }
1✔
717
        devId := r.URL.Query().Get("device_id")
3✔
718

3✔
719
        err := d.devAuth.DeleteTokens(ctx, tenantId, devId)
3✔
720
        switch err {
3✔
721
        case nil:
2✔
722
                w.WriteHeader(http.StatusNoContent)
2✔
723
        default:
1✔
724
                rest_utils.RestErrWithLogInternal(w, r, l, err)
1✔
725
        }
726
}
727

728
func (d *DevAuthApiHandlers) GetAuthSetStatusHandler(w rest.ResponseWriter, r *rest.Request) {
×
729
        ctx := r.Context()
×
730
        l := log.FromContext(ctx)
×
731

×
732
        devid := r.PathParam("id")
×
733
        authid := r.PathParam("aid")
×
734

×
735
        // get authset directly from store
×
736
        aset, err := d.db.GetAuthSetById(ctx, authid)
×
737
        switch err {
×
738
        case nil:
×
739
                _ = w.WriteJson(&model.Status{Status: aset.Status})
×
740
        case store.ErrDevNotFound, store.ErrAuthSetNotFound:
×
741
                rest_utils.RestErrWithLog(w, r, l, store.ErrAuthSetNotFound, http.StatusNotFound)
×
742
        default:
×
743
                rest_utils.RestErrWithLogInternal(w, r, l,
×
744
                        errors.Wrapf(err,
×
745
                                "failed to fetch auth set %s for device %s",
×
746
                                authid, devid))
×
747
        }
748
}
749

750
func (d *DevAuthApiHandlers) ProvisionTenantHandler(w rest.ResponseWriter, r *rest.Request) {
2✔
751
        // NOTE: This handler was used to initialize database collections. This is no longer
2✔
752
        //       needed after migration 2.0.0.
2✔
753
        w.WriteHeader(http.StatusCreated)
2✔
754
}
2✔
755

756
func (d *DevAuthApiHandlers) GetTenantDeviceStatus(w rest.ResponseWriter, r *rest.Request) {
5✔
757
        ctx := r.Context()
5✔
758

5✔
759
        l := log.FromContext(ctx)
5✔
760

5✔
761
        tid := r.PathParam("tid")
5✔
762
        did := r.PathParam("did")
5✔
763

5✔
764
        if did == "" {
6✔
765
                rest_utils.RestErrWithLog(
1✔
766
                        w,
1✔
767
                        r,
1✔
768
                        l,
1✔
769
                        errors.New("device id (did) cannot be empty"),
1✔
770
                        http.StatusBadRequest,
1✔
771
                )
1✔
772
                return
1✔
773
        }
1✔
774

775
        status, err := d.devAuth.GetTenantDeviceStatus(ctx, tid, did)
4✔
776
        switch err {
4✔
777
        case nil:
2✔
778
                _ = w.WriteJson(status)
2✔
779
        case devauth.ErrDeviceNotFound:
1✔
780
                rest_utils.RestErrWithLog(w, r, l, err, http.StatusNotFound)
1✔
781
        default:
1✔
782
                rest_utils.RestErrWithLogInternal(w, r, l, err)
1✔
783
        }
784
}
785

786
func (d *DevAuthApiHandlers) GetTenantDevicesHandler(w rest.ResponseWriter, r *rest.Request) {
6✔
787
        ctx := r.Context()
6✔
788
        if tid := r.PathParam("tid"); tid != "" {
11✔
789
                ctx = identity.WithContext(ctx, &identity.Identity{Tenant: tid})
5✔
790
        }
5✔
791
        r.Request = r.WithContext(ctx)
6✔
792

6✔
793
        d.GetDevicesV2Handler(w, r)
6✔
794
}
795

796
func (d *DevAuthApiHandlers) GetTenantDevicesCountHandler(w rest.ResponseWriter, r *rest.Request) {
4✔
797
        ctx := r.Context()
4✔
798
        if tid := r.PathParam("tid"); tid != "" {
7✔
799
                ctx = identity.WithContext(ctx, &identity.Identity{Tenant: tid})
3✔
800
        }
3✔
801
        r.Request = r.WithContext(ctx)
4✔
802

4✔
803
        d.GetDevicesCountHandler(w, r)
4✔
804
}
805

806
func (d *DevAuthApiHandlers) DeleteDeviceHandler(w rest.ResponseWriter, r *rest.Request) {
1✔
807
        ctx := r.Context()
1✔
808
        l := log.FromContext(ctx)
1✔
809
        did := r.PathParam("did")
1✔
810

1✔
811
        err := d.devAuth.DeleteDevice(ctx, did)
1✔
812
        switch err {
1✔
813
        case nil:
1✔
814
                w.WriteHeader(http.StatusNoContent)
1✔
815
        case devauth.ErrInvalidDeviceID:
×
816
                didErr := errors.New("device id (did) cannot be empty")
×
817
                rest_utils.RestErrWithLog(w, r, l, didErr, http.StatusBadRequest)
×
818
        case store.ErrDevNotFound:
×
819
                w.WriteHeader(http.StatusNotFound)
×
820
        default:
×
821
                rest_utils.RestErrWithLogInternal(w, r, l, err)
×
822
        }
823
}
824

825
// Validate status.
826
// Expected statuses:
827
// - "accepted"
828
// - "rejected"
829
// - "pending"
830
func statusValidate(status *DevAuthApiStatus) error {
9✔
831
        if status.Status != model.DevStatusAccepted &&
9✔
832
                status.Status != model.DevStatusRejected &&
9✔
833
                status.Status != model.DevStatusPending {
10✔
834
                return ErrIncorrectStatus
1✔
835
        } else {
9✔
836
                return nil
8✔
837
        }
8✔
838
}
839

840
// extracts JWT from authorization header
841
func extractToken(header http.Header) (string, error) {
9✔
842
        const authHeaderName = "Authorization"
9✔
843
        authHeader := header.Get(authHeaderName)
9✔
844
        if authHeader == "" {
11✔
845
                return "", ErrNoAuthHeader
2✔
846
        }
2✔
847
        tokenStr := strings.Replace(authHeader, "Bearer", "", 1)
8✔
848
        tokenStr = strings.Replace(tokenStr, "bearer", "", 1)
8✔
849
        return strings.TrimSpace(tokenStr), nil
8✔
850
}
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