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

mendersoftware / useradm / 1325239969

08 Dec 2023 03:39PM UTC coverage: 87.019%. Remained the same
1325239969

push

gitlab-ci

web-flow
Merge pull request #401 from mendersoftware/dependabot/go_modules/go.mongodb.org/mongo-driver-1.13.1

chore: bump go.mongodb.org/mongo-driver from 1.12.1 to 1.13.1

2869 of 3297 relevant lines covered (87.02%)

130.99 hits per line

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

90.59
/api/http/api_useradm.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
        "net/http"
19
        "strings"
20
        "time"
21

22
        "github.com/ant0ine/go-json-rest/rest"
23
        "github.com/google/uuid"
24
        "github.com/mendersoftware/go-lib-micro/identity"
25
        "github.com/mendersoftware/go-lib-micro/log"
26
        "github.com/mendersoftware/go-lib-micro/rest_utils"
27
        "github.com/mendersoftware/go-lib-micro/routing"
28
        "github.com/pkg/errors"
29

30
        "github.com/mendersoftware/useradm/authz"
31
        "github.com/mendersoftware/useradm/jwt"
32
        "github.com/mendersoftware/useradm/model"
33
        "github.com/mendersoftware/useradm/store"
34
        useradm "github.com/mendersoftware/useradm/user"
35
)
36

37
const (
38
        apiUrlManagementV1       = "/api/management/v1/useradm"
39
        uriManagementAuthLogin   = apiUrlManagementV1 + "/auth/login"
40
        uriManagementAuthLogout  = apiUrlManagementV1 + "/auth/logout"
41
        uriManagementUser        = apiUrlManagementV1 + "/users/#id"
42
        uriManagementUsers       = apiUrlManagementV1 + "/users"
43
        uriManagementSettings    = apiUrlManagementV1 + "/settings"
44
        uriManagementSettingsMe  = apiUrlManagementV1 + "/settings/me"
45
        uriManagementTokens      = apiUrlManagementV1 + "/settings/tokens"
46
        uriManagementToken       = apiUrlManagementV1 + "/settings/tokens/#id"
47
        uriManagementPlans       = apiUrlManagementV1 + "/plans"
48
        uriManagementPlanBinding = apiUrlManagementV1 + "/plan_binding"
49

50
        apiUrlInternalV1  = "/api/internal/v1/useradm"
51
        uriInternalAlive  = apiUrlInternalV1 + "/alive"
52
        uriInternalHealth = apiUrlInternalV1 + "/health"
53

54
        uriInternalAuthVerify  = apiUrlInternalV1 + "/auth/verify"
55
        uriInternalTenants     = apiUrlInternalV1 + "/tenants"
56
        uriInternalTenantUsers = apiUrlInternalV1 + "/tenants/#id/users"
57
        uriInternalTenantUser  = apiUrlInternalV1 + "/tenants/#id/users/#userid"
58
        uriInternalTokens      = apiUrlInternalV1 + "/tokens"
59
)
60

61
const (
62
        defaultTimeout = time.Second * 5
63
        hdrETag        = "ETag"
64
        hdrIfMatch     = "If-Match"
65
)
66

67
var (
68
        ErrAuthHeader   = errors.New("invalid or missing auth header")
69
        ErrUserNotFound = errors.New("user not found")
70
)
71

72
type UserAdmApiHandlers struct {
73
        userAdm useradm.App
74
        db      store.DataStore
75
        jwth    map[int]jwt.Handler
76
        config  Config
77
}
78

79
type Config struct {
80
        // maximum expiration time for Personal Access Token
81
        TokenMaxExpSeconds int
82
}
83

84
// return an ApiHandler for user administration and authentiacation app
85
func NewUserAdmApiHandlers(
86
        userAdm useradm.App,
87
        db store.DataStore,
88
        jwth map[int]jwt.Handler,
89
        config Config,
90
) ApiHandler {
94✔
91
        return &UserAdmApiHandlers{
94✔
92
                userAdm: userAdm,
94✔
93
                db:      db,
94✔
94
                jwth:    jwth,
94✔
95
                config:  config,
94✔
96
        }
94✔
97
}
94✔
98

99
func (i *UserAdmApiHandlers) GetApp() (rest.App, error) {
94✔
100
        routes := []*rest.Route{
94✔
101
                rest.Get(uriInternalAlive, i.AliveHandler),
94✔
102
                rest.Get(uriInternalHealth, i.HealthHandler),
94✔
103

94✔
104
                rest.Get(uriInternalAuthVerify, i.AuthVerifyHandler),
94✔
105
                rest.Post(uriInternalAuthVerify, i.AuthVerifyHandler),
94✔
106
                rest.Post(uriInternalTenants, i.CreateTenantHandler),
94✔
107
                rest.Post(uriInternalTenantUsers, i.CreateTenantUserHandler),
94✔
108
                rest.Delete(uriInternalTenantUser, i.DeleteTenantUserHandler),
94✔
109
                rest.Get(uriInternalTenantUsers, i.GetTenantUsersHandler),
94✔
110
                rest.Delete(uriInternalTokens, i.DeleteTokensHandler),
94✔
111

94✔
112
                rest.Post(uriManagementAuthLogin, i.AuthLoginHandler),
94✔
113
                rest.Post(uriManagementAuthLogout, i.AuthLogoutHandler),
94✔
114
                rest.Post(uriManagementUsers, i.AddUserHandler),
94✔
115
                rest.Get(uriManagementUsers, i.GetUsersHandler),
94✔
116
                rest.Get(uriManagementUser, i.GetUserHandler),
94✔
117
                rest.Put(uriManagementUser, i.UpdateUserHandler),
94✔
118
                rest.Delete(uriManagementUser, i.DeleteUserHandler),
94✔
119
                rest.Post(uriManagementSettings, i.SaveSettingsHandler),
94✔
120
                rest.Get(uriManagementSettings, i.GetSettingsHandler),
94✔
121
                rest.Post(uriManagementSettingsMe, i.SaveSettingsMeHandler),
94✔
122
                rest.Get(uriManagementSettingsMe, i.GetSettingsMeHandler),
94✔
123
                rest.Post(uriManagementTokens, i.IssueTokenHandler),
94✔
124
                rest.Get(uriManagementTokens, i.GetTokensHandler),
94✔
125
                rest.Delete(uriManagementToken, i.DeleteTokenHandler),
94✔
126
                // plans
94✔
127
                rest.Get(uriManagementPlans, i.GetPlansHandler),
94✔
128
                rest.Get(uriManagementPlanBinding, i.GetPlanBindingHandler),
94✔
129
        }
94✔
130

94✔
131
        app, err := rest.MakeRouter(
94✔
132
                // augment routes with OPTIONS handler
94✔
133
                routing.AutogenOptionsRoutes(routes, routing.AllowHeaderOptionsGenerator)...,
94✔
134
        )
94✔
135
        if err != nil {
94✔
136
                return nil, errors.Wrap(err, "failed to create router")
×
137
        }
×
138

139
        return app, nil
94✔
140
}
141

142
func (u *UserAdmApiHandlers) AliveHandler(w rest.ResponseWriter, r *rest.Request) {
1✔
143
        w.WriteHeader(http.StatusNoContent)
1✔
144
}
1✔
145

146
func (u *UserAdmApiHandlers) HealthHandler(w rest.ResponseWriter, r *rest.Request) {
2✔
147
        ctx := r.Context()
2✔
148
        l := log.FromContext(ctx)
2✔
149
        ctx, cancel := context.WithTimeout(ctx, defaultTimeout)
2✔
150
        defer cancel()
2✔
151

2✔
152
        err := u.userAdm.HealthCheck(ctx)
2✔
153
        if err != nil {
3✔
154
                rest_utils.RestErrWithLog(w, r, l, err, http.StatusServiceUnavailable)
1✔
155
                return
1✔
156
        }
1✔
157
        w.WriteHeader(http.StatusNoContent)
1✔
158
}
159

160
func (u *UserAdmApiHandlers) AuthLoginHandler(w rest.ResponseWriter, r *rest.Request) {
147✔
161
        ctx := r.Context()
147✔
162

147✔
163
        l := log.FromContext(ctx)
147✔
164

147✔
165
        //parse auth header
147✔
166
        user, pass, ok := r.BasicAuth()
147✔
167
        if !ok {
148✔
168
                rest_utils.RestErrWithLog(w, r, l,
1✔
169
                        ErrAuthHeader, http.StatusUnauthorized)
1✔
170
                return
1✔
171
        }
1✔
172
        email := model.Email(strings.ToLower(user))
146✔
173

146✔
174
        options := &useradm.LoginOptions{}
146✔
175
        err := r.DecodeJsonPayload(options)
146✔
176
        if err != nil && err != rest.ErrJsonPayloadEmpty {
147✔
177
                rest_utils.RestErrWithLog(w, r, l, err, http.StatusBadRequest)
1✔
178
                return
1✔
179
        }
1✔
180

181
        token, err := u.userAdm.Login(ctx, email, pass, options)
145✔
182
        if err != nil {
159✔
183
                switch {
14✔
184
                case err == useradm.ErrUnauthorized || err == useradm.ErrTenantAccountSuspended:
12✔
185
                        rest_utils.RestErrWithLog(w, r, l, err, http.StatusUnauthorized)
12✔
186
                default:
2✔
187
                        rest_utils.RestErrWithLogInternal(w, r, l, err)
2✔
188
                }
189
                return
14✔
190
        }
191

192
        raw, err := u.userAdm.SignToken(ctx, token)
131✔
193
        if err != nil {
132✔
194
                rest_utils.RestErrWithLogInternal(w, r, l, err)
1✔
195
                return
1✔
196
        }
1✔
197

198
        writer := w.(http.ResponseWriter)
130✔
199
        writer.Header().Set("Content-Type", "application/jwt")
130✔
200
        _, _ = writer.Write([]byte(raw))
130✔
201
}
202

203
func (u *UserAdmApiHandlers) AuthLogoutHandler(w rest.ResponseWriter, r *rest.Request) {
4✔
204
        ctx := r.Context()
4✔
205
        l := log.FromContext(ctx)
4✔
206

4✔
207
        if tokenStr, err := authz.ExtractToken(r.Request); err == nil {
8✔
208
                keyId := jwt.GetKeyId(tokenStr)
4✔
209
                if _, ok := u.jwth[keyId]; !ok {
4✔
210
                        rest_utils.RestErrWithLogInternal(w, r, l, errors.New("internal error"))
×
211
                        return
×
212
                }
×
213

214
                token, err := u.jwth[keyId].FromJWT(tokenStr)
4✔
215
                if err != nil {
5✔
216
                        rest_utils.RestErrWithLogInternal(w, r, l, err)
1✔
217
                        return
1✔
218
                }
1✔
219
                if err := u.userAdm.Logout(ctx, token); err != nil {
4✔
220
                        rest_utils.RestErrWithLogInternal(w, r, l, err)
1✔
221
                        return
1✔
222
                }
1✔
223
        }
224

225
        w.WriteHeader(http.StatusAccepted)
2✔
226
}
227

228
func (u *UserAdmApiHandlers) AuthVerifyHandler(w rest.ResponseWriter, r *rest.Request) {
125✔
229
        ctx := r.Context()
125✔
230

125✔
231
        l := log.FromContext(ctx)
125✔
232

125✔
233
        // note that the request has passed through authz - the token is valid
125✔
234
        token := authz.GetRequestToken(r.Env)
125✔
235

125✔
236
        err := u.userAdm.Verify(ctx, token)
125✔
237
        if err != nil {
142✔
238
                if err == useradm.ErrUnauthorized {
31✔
239
                        rest_utils.RestErrWithLog(w, r, l, useradm.ErrUnauthorized, http.StatusUnauthorized)
14✔
240
                } else {
17✔
241
                        rest_utils.RestErrWithLogInternal(w, r, l, err)
3✔
242
                }
3✔
243
                return
17✔
244
        }
245

246
        w.WriteHeader(http.StatusOK)
108✔
247
}
248

249
func (u *UserAdmApiHandlers) CreateTenantUserHandler(w rest.ResponseWriter, r *rest.Request) {
19✔
250
        ctx := r.Context()
19✔
251

19✔
252
        l := log.FromContext(ctx)
19✔
253

19✔
254
        user, err := parseUserInternal(r)
19✔
255
        if err != nil {
29✔
256
                rest_utils.RestErrWithLog(w, r, l, err, http.StatusBadRequest)
10✔
257
                return
10✔
258
        }
10✔
259

260
        tenantId := r.PathParam("id")
9✔
261
        if tenantId == "" {
10✔
262
                rest_utils.RestErrWithLog(w, r, l, errors.New("Entity not found"), http.StatusNotFound)
1✔
263
                return
1✔
264
        }
1✔
265
        ctx = getTenantContext(ctx, tenantId)
8✔
266
        err = u.userAdm.CreateUserInternal(ctx, user)
8✔
267
        if err != nil {
9✔
268
                if err == store.ErrDuplicateEmail {
2✔
269
                        rest_utils.RestErrWithLog(w, r, l, err, http.StatusUnprocessableEntity)
1✔
270
                } else {
1✔
271
                        rest_utils.RestErrWithLogInternal(w, r, l, err)
×
272
                }
×
273
                return
1✔
274
        }
275

276
        w.WriteHeader(http.StatusCreated)
7✔
277

278
}
279

280
func (u *UserAdmApiHandlers) AddUserHandler(w rest.ResponseWriter, r *rest.Request) {
17✔
281
        ctx := r.Context()
17✔
282

17✔
283
        l := log.FromContext(ctx)
17✔
284

17✔
285
        user, err := parseUser(r)
17✔
286
        if err != nil {
26✔
287
                if err == model.ErrPasswordTooShort {
12✔
288
                        rest_utils.RestErrWithLog(w, r, l, err, http.StatusUnprocessableEntity)
3✔
289
                } else {
9✔
290
                        rest_utils.RestErrWithLog(w, r, l, err, http.StatusBadRequest)
6✔
291
                }
6✔
292
                return
9✔
293
        }
294

295
        err = u.userAdm.CreateUser(ctx, user)
8✔
296
        if err != nil {
11✔
297
                if err == store.ErrDuplicateEmail {
6✔
298
                        rest_utils.RestErrWithLog(w, r, l, err, http.StatusUnprocessableEntity)
3✔
299
                } else {
3✔
300
                        rest_utils.RestErrWithLogInternal(w, r, l, err)
×
301
                }
×
302
                return
3✔
303
        }
304

305
        w.Header().Add("Location", "users/"+string(user.ID))
5✔
306
        w.WriteHeader(http.StatusCreated)
5✔
307

308
}
309

310
func (u *UserAdmApiHandlers) GetUsersHandler(w rest.ResponseWriter, r *rest.Request) {
349✔
311
        ctx := r.Context()
349✔
312

349✔
313
        l := log.FromContext(ctx)
349✔
314

349✔
315
        if err := r.ParseForm(); err != nil {
351✔
316
                err = errors.Wrap(err, "api: bad form parameters")
2✔
317
                rest_utils.RestErrWithLog(w, r, l, err, http.StatusBadRequest)
2✔
318
                return
2✔
319
        }
2✔
320

321
        fltr := model.UserFilter{}
347✔
322
        if err := fltr.ParseForm(r.Form); err != nil {
349✔
323
                err = errors.Wrap(err, "api: invalid form values")
2✔
324
                rest_utils.RestErrWithLog(w, r, l, err, http.StatusBadRequest)
2✔
325
                return
2✔
326
        }
2✔
327

328
        users, err := u.userAdm.GetUsers(ctx, fltr)
345✔
329
        if err != nil {
347✔
330
                rest_utils.RestErrWithLogInternal(w, r, l, err)
2✔
331
                return
2✔
332
        }
2✔
333

334
        _ = w.WriteJson(users)
343✔
335
}
336

337
func (u *UserAdmApiHandlers) GetTenantUsersHandler(w rest.ResponseWriter, r *rest.Request) {
5✔
338
        ctx := r.Context()
5✔
339
        ctx = identity.WithContext(ctx, &identity.Identity{
5✔
340
                Tenant: r.PathParam("id"),
5✔
341
        })
5✔
342
        r.Request = r.Request.WithContext(ctx)
5✔
343
        u.GetUsersHandler(w, r)
5✔
344
}
5✔
345

346
func (u *UserAdmApiHandlers) GetUserHandler(w rest.ResponseWriter, r *rest.Request) {
33✔
347
        ctx := r.Context()
33✔
348

33✔
349
        l := log.FromContext(ctx)
33✔
350

33✔
351
        id := r.PathParam("id")
33✔
352
        user, err := u.userAdm.GetUser(ctx, id)
33✔
353
        if err != nil {
34✔
354
                rest_utils.RestErrWithLogInternal(w, r, l, err)
1✔
355
                return
1✔
356
        }
1✔
357

358
        if user == nil {
36✔
359
                rest_utils.RestErrWithLog(w, r, l, ErrUserNotFound, 404)
4✔
360
                return
4✔
361
        }
4✔
362

363
        _ = w.WriteJson(user)
28✔
364
}
365

366
func (u *UserAdmApiHandlers) UpdateUserHandler(w rest.ResponseWriter, r *rest.Request) {
34✔
367
        ctx := r.Context()
34✔
368
        idty := identity.FromContext(ctx)
34✔
369
        l := log.FromContext(ctx)
34✔
370

34✔
371
        userUpdate, err := parseUserUpdate(r)
34✔
372
        if err != nil {
40✔
373
                if err == model.ErrPasswordTooShort {
7✔
374
                        rest_utils.RestErrWithLog(w, r, l, err, http.StatusUnprocessableEntity)
1✔
375
                } else {
6✔
376
                        rest_utils.RestErrWithLog(w, r, l, err, http.StatusBadRequest)
5✔
377
                }
5✔
378
                return
6✔
379
        }
380

381
        // extract the token used to update the user
382
        if tokenStr, err := authz.ExtractToken(r.Request); err == nil {
52✔
383
                keyId := jwt.GetKeyId(tokenStr)
24✔
384
                if _, ok := u.jwth[keyId]; !ok {
24✔
385
                        rest_utils.RestErrWithLogInternal(w, r, l, errors.New("internal error"))
×
386
                        return
×
387
                }
×
388

389
                token, err := u.jwth[keyId].FromJWT(tokenStr)
24✔
390
                if err != nil {
24✔
391
                        rest_utils.RestErrWithLogInternal(w, r, l, err)
×
392
                        return
×
393
                }
×
394
                userUpdate.Token = token
24✔
395
        }
396

397
        id := r.PathParam("id")
28✔
398
        if strings.EqualFold(id, "me") {
39✔
399
                id = idty.Subject
11✔
400
        }
11✔
401
        err = u.userAdm.UpdateUser(ctx, id, userUpdate)
28✔
402
        if err != nil {
40✔
403
                switch err {
12✔
404
                case store.ErrDuplicateEmail,
405
                        useradm.ErrCurrentPasswordMismatch,
406
                        useradm.ErrCannotModifyPassword:
8✔
407
                        rest_utils.RestErrWithLog(w, r, l, err, http.StatusUnprocessableEntity)
8✔
408
                case store.ErrUserNotFound:
3✔
409
                        rest_utils.RestErrWithLog(w, r, l, err, http.StatusNotFound)
3✔
410
                case useradm.ErrETagMismatch:
1✔
411
                        rest_utils.RestErrWithLog(w, r, l, err, http.StatusConflict)
1✔
412
                default:
×
413
                        rest_utils.RestErrWithLogInternal(w, r, l, err)
×
414
                }
415
        }
416

417
        w.WriteHeader(http.StatusNoContent)
28✔
418
}
419

420
func (u *UserAdmApiHandlers) DeleteTenantUserHandler(w rest.ResponseWriter, r *rest.Request) {
3✔
421
        ctx := r.Context()
3✔
422

3✔
423
        tenantId := r.PathParam("id")
3✔
424
        if tenantId != "" {
4✔
425
                ctx = getTenantContext(ctx, tenantId)
1✔
426
        }
1✔
427

428
        l := log.FromContext(ctx)
3✔
429
        err := u.userAdm.DeleteUser(ctx, r.PathParam("userid"))
3✔
430
        if err != nil {
4✔
431
                rest_utils.RestErrWithLogInternal(w, r, l, err)
1✔
432
                return
1✔
433
        }
1✔
434

435
        w.WriteHeader(http.StatusNoContent)
2✔
436
}
437

438
func (u *UserAdmApiHandlers) DeleteUserHandler(w rest.ResponseWriter, r *rest.Request) {
8✔
439
        ctx := r.Context()
8✔
440

8✔
441
        l := log.FromContext(ctx)
8✔
442

8✔
443
        err := u.userAdm.DeleteUser(ctx, r.PathParam("id"))
8✔
444
        if err != nil {
9✔
445
                rest_utils.RestErrWithLogInternal(w, r, l, err)
1✔
446
                return
1✔
447
        }
1✔
448

449
        w.WriteHeader(http.StatusNoContent)
7✔
450
}
451

452
func parseUser(r *rest.Request) (*model.User, error) {
17✔
453
        user := model.User{}
17✔
454

17✔
455
        //decode body
17✔
456
        err := r.DecodeJsonPayload(&user)
17✔
457
        if err != nil {
18✔
458
                return nil, errors.Wrap(err, "failed to decode request body")
1✔
459
        }
1✔
460

461
        if err := user.Validate(); err != nil {
24✔
462
                return nil, err
8✔
463
        }
8✔
464

465
        return &user, nil
8✔
466
}
467

468
func parseUserInternal(r *rest.Request) (*model.UserInternal, error) {
19✔
469
        user := model.UserInternal{}
19✔
470

19✔
471
        //decode body
19✔
472
        err := r.DecodeJsonPayload(&user)
19✔
473
        if err != nil {
20✔
474
                return nil, errors.Wrap(err, "failed to decode request body")
1✔
475
        }
1✔
476

477
        if err := user.Validate(); err != nil {
27✔
478
                return nil, err
9✔
479
        }
9✔
480

481
        return &user, nil
9✔
482
}
483

484
func parseUserUpdate(r *rest.Request) (*model.UserUpdate, error) {
34✔
485
        userUpdate := model.UserUpdate{}
34✔
486

34✔
487
        //decode body
34✔
488
        err := r.DecodeJsonPayload(&userUpdate)
34✔
489
        if err != nil {
35✔
490
                return nil, errors.Wrap(err, "failed to decode request body")
1✔
491
        }
1✔
492

493
        if err := userUpdate.Validate(); err != nil {
38✔
494
                return nil, err
5✔
495
        }
5✔
496

497
        return &userUpdate, nil
28✔
498
}
499

500
func (u *UserAdmApiHandlers) CreateTenantHandler(w rest.ResponseWriter, r *rest.Request) {
8✔
501
        ctx := r.Context()
8✔
502

8✔
503
        l := log.FromContext(ctx)
8✔
504

8✔
505
        var newTenant model.NewTenant
8✔
506

8✔
507
        if err := r.DecodeJsonPayload(&newTenant); err != nil {
9✔
508
                rest_utils.RestErrWithLog(w, r, l, err, http.StatusBadRequest)
1✔
509
                return
1✔
510
        }
1✔
511

512
        if err := newTenant.Validate(); err != nil {
9✔
513
                rest_utils.RestErrWithLog(w, r, l, err, http.StatusBadRequest)
2✔
514
                return
2✔
515
        }
2✔
516

517
        err := u.userAdm.CreateTenant(ctx, model.NewTenant{
5✔
518
                ID: newTenant.ID,
5✔
519
        })
5✔
520
        if err != nil {
6✔
521
                rest_utils.RestErrWithLogInternal(w, r, l, err)
1✔
522
                return
1✔
523
        }
1✔
524

525
        w.WriteHeader(http.StatusCreated)
4✔
526
}
527

528
func getTenantContext(ctx context.Context, tenantId string) context.Context {
9✔
529
        if ctx == nil {
9✔
530
                ctx = context.Background()
×
531
        }
×
532
        if tenantId != "" {
18✔
533
                id := &identity.Identity{
9✔
534
                        Tenant: tenantId,
9✔
535
                }
9✔
536

9✔
537
                ctx = identity.WithContext(ctx, id)
9✔
538
        }
9✔
539

540
        return ctx
9✔
541
}
542

543
func (u *UserAdmApiHandlers) DeleteTokensHandler(w rest.ResponseWriter, r *rest.Request) {
10✔
544

10✔
545
        ctx := r.Context()
10✔
546

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

10✔
549
        tenantId := r.URL.Query().Get("tenant_id")
10✔
550
        if tenantId == "" {
13✔
551
                rest_utils.RestErrWithLog(
3✔
552
                        w,
3✔
553
                        r,
3✔
554
                        l,
3✔
555
                        errors.New("tenant_id must be provided"),
3✔
556
                        http.StatusBadRequest,
3✔
557
                )
3✔
558
                return
3✔
559
        }
3✔
560
        userId := r.URL.Query().Get("user_id")
7✔
561

7✔
562
        err := u.userAdm.DeleteTokens(ctx, tenantId, userId)
7✔
563
        switch err {
7✔
564
        case nil:
6✔
565
                w.WriteHeader(http.StatusNoContent)
6✔
566
        default:
1✔
567
                rest_utils.RestErrWithLogInternal(w, r, l, err)
1✔
568
        }
569
}
570

571
func (u *UserAdmApiHandlers) SaveSettingsHandler(w rest.ResponseWriter, r *rest.Request) {
15✔
572
        u.saveSettingsHandler(w, r, false)
15✔
573
}
15✔
574

575
func (u *UserAdmApiHandlers) SaveSettingsMeHandler(w rest.ResponseWriter, r *rest.Request) {
×
576
        u.saveSettingsHandler(w, r, true)
×
577
}
×
578

579
func (u *UserAdmApiHandlers) saveSettingsHandler(w rest.ResponseWriter, r *rest.Request, me bool) {
15✔
580
        ctx := r.Context()
15✔
581

15✔
582
        l := log.FromContext(ctx)
15✔
583

15✔
584
        settings := &model.Settings{}
15✔
585
        err := r.DecodeJsonPayload(settings)
15✔
586
        if err != nil {
19✔
587
                rest_utils.RestErrWithLog(
4✔
588
                        w,
4✔
589
                        r,
4✔
590
                        l,
4✔
591
                        errors.New("cannot parse request body as json"),
4✔
592
                        http.StatusBadRequest,
4✔
593
                )
4✔
594
                return
4✔
595
        }
4✔
596

597
        if err := settings.Validate(); err != nil {
12✔
598
                rest_utils.RestErrWithLog(w, r, l, err, http.StatusBadRequest)
1✔
599
                return
1✔
600
        }
1✔
601

602
        ifMatchHeader := r.Header.Get(hdrIfMatch)
10✔
603

10✔
604
        settings.ETag = uuid.NewString()
10✔
605
        if me {
10✔
606
                id := identity.FromContext(ctx)
×
607
                if id == nil {
×
608
                        rest_utils.RestErrWithLogInternal(w, r, l, errors.New("identity not present"))
×
609
                        return
×
610
                }
×
611
                err = u.db.SaveUserSettings(ctx, id.Subject, settings, ifMatchHeader)
×
612
        } else {
10✔
613
                err = u.db.SaveSettings(ctx, settings, ifMatchHeader)
10✔
614
        }
10✔
615
        if err == store.ErrETagMismatch {
11✔
616
                rest_utils.RestErrWithInfoMsg(w, r, l, err, http.StatusPreconditionFailed, err.Error())
1✔
617
                return
1✔
618
        } else if err != nil {
11✔
619
                rest_utils.RestErrWithLogInternal(w, r, l, err)
1✔
620
                return
1✔
621
        }
1✔
622

623
        w.WriteHeader(http.StatusCreated)
8✔
624
}
625

626
func (u *UserAdmApiHandlers) GetSettingsHandler(w rest.ResponseWriter, r *rest.Request) {
11✔
627
        u.getSettingsHandler(w, r, false)
11✔
628
}
11✔
629

630
func (u *UserAdmApiHandlers) GetSettingsMeHandler(w rest.ResponseWriter, r *rest.Request) {
×
631
        u.getSettingsHandler(w, r, true)
×
632
}
×
633

634
func (u *UserAdmApiHandlers) getSettingsHandler(w rest.ResponseWriter, r *rest.Request, me bool) {
11✔
635
        ctx := r.Context()
11✔
636

11✔
637
        l := log.FromContext(ctx)
11✔
638

11✔
639
        var settings *model.Settings
11✔
640
        var err error
11✔
641
        if me {
11✔
642
                id := identity.FromContext(ctx)
×
643
                if id == nil {
×
644
                        rest_utils.RestErrWithLogInternal(w, r, l, errors.New("identity not present"))
×
645
                        return
×
646
                }
×
647
                settings, err = u.db.GetUserSettings(ctx, id.Subject)
×
648
        } else {
11✔
649
                settings, err = u.db.GetSettings(ctx)
11✔
650
        }
11✔
651

652
        if err != nil {
12✔
653
                rest_utils.RestErrWithLogInternal(w, r, l, err)
1✔
654
                return
1✔
655
        } else if settings == nil {
12✔
656
                settings = &model.Settings{
1✔
657
                        Values: model.SettingsValues{},
1✔
658
                }
1✔
659
        }
1✔
660

661
        if settings.ETag != "" {
18✔
662
                w.Header().Set(hdrETag, settings.ETag)
8✔
663
        }
8✔
664
        _ = w.WriteJson(settings)
10✔
665
}
666

667
func (u *UserAdmApiHandlers) IssueTokenHandler(w rest.ResponseWriter, r *rest.Request) {
57✔
668
        ctx := r.Context()
57✔
669

57✔
670
        l := log.FromContext(ctx)
57✔
671

57✔
672
        var tokenRequest model.TokenRequest
57✔
673

57✔
674
        if err := r.DecodeJsonPayload(&tokenRequest); err != nil {
57✔
675
                rest_utils.RestErrWithLog(
×
676
                        w,
×
677
                        r,
×
678
                        l,
×
679
                        errors.New("cannot parse request body as json"),
×
680
                        http.StatusBadRequest,
×
681
                )
×
682
                return
×
683
        }
×
684
        if err := tokenRequest.Validate(u.config.TokenMaxExpSeconds); err != nil {
59✔
685
                rest_utils.RestErrWithLog(
2✔
686
                        w,
2✔
687
                        r,
2✔
688
                        l,
2✔
689
                        err,
2✔
690
                        http.StatusBadRequest,
2✔
691
                )
2✔
692
                return
2✔
693
        }
2✔
694

695
        token, err := u.userAdm.IssuePersonalAccessToken(ctx, &tokenRequest)
55✔
696
        switch err {
55✔
697
        case nil:
44✔
698
                writer := w.(http.ResponseWriter)
44✔
699
                writer.Header().Set("Content-Type", "application/jwt")
44✔
700
                _, _ = writer.Write([]byte(token))
44✔
701
        case useradm.ErrTooManyTokens:
4✔
702
                rest_utils.RestErrWithLog(
4✔
703
                        w,
4✔
704
                        r,
4✔
705
                        l,
4✔
706
                        err,
4✔
707
                        http.StatusUnprocessableEntity,
4✔
708
                )
4✔
709
        case useradm.ErrDuplicateTokenName:
7✔
710
                rest_utils.RestErrWithLog(
7✔
711
                        w,
7✔
712
                        r,
7✔
713
                        l,
7✔
714
                        err,
7✔
715
                        http.StatusConflict,
7✔
716
                )
7✔
717
        default:
×
718
                rest_utils.RestErrWithLogInternal(w, r, l, err)
×
719
        }
720
}
721

722
func (u *UserAdmApiHandlers) GetTokensHandler(w rest.ResponseWriter, r *rest.Request) {
8✔
723
        ctx := r.Context()
8✔
724
        l := log.FromContext(ctx)
8✔
725
        id := identity.FromContext(ctx)
8✔
726
        if id == nil {
8✔
727
                rest_utils.RestErrWithLogInternal(w, r, l, errors.New("identity not present"))
×
728
                return
×
729
        }
×
730

731
        tokens, err := u.userAdm.GetPersonalAccessTokens(ctx, id.Subject)
8✔
732
        if err != nil {
9✔
733
                rest_utils.RestErrWithLogInternal(w, r, l, err)
1✔
734
                return
1✔
735
        }
1✔
736

737
        _ = w.WriteJson(tokens)
7✔
738
}
739

740
func (u *UserAdmApiHandlers) DeleteTokenHandler(w rest.ResponseWriter, r *rest.Request) {
3✔
741
        ctx := r.Context()
3✔
742

3✔
743
        l := log.FromContext(ctx)
3✔
744

3✔
745
        err := u.userAdm.DeleteToken(ctx, r.PathParam("id"))
3✔
746
        if err != nil {
3✔
747
                rest_utils.RestErrWithLogInternal(w, r, l, err)
×
748
                return
×
749
        }
×
750

751
        w.WriteHeader(http.StatusNoContent)
3✔
752
}
753

754
// plans and plan binding
755

756
func (u *UserAdmApiHandlers) GetPlansHandler(w rest.ResponseWriter, r *rest.Request) {
5✔
757
        ctx := r.Context()
5✔
758
        l := log.FromContext(ctx)
5✔
759
        page, perPage, err := rest_utils.ParsePagination(r)
5✔
760
        if err != nil {
7✔
761
                rest_utils.RestErrWithLog(w, r, l, err, http.StatusBadRequest)
2✔
762
                return
2✔
763
        }
2✔
764

765
        plans := u.userAdm.GetPlans(ctx, int((page-1)*perPage), int(perPage))
3✔
766
        if plans == nil {
4✔
767
                plans = []model.Plan{}
1✔
768
        }
1✔
769

770
        _ = w.WriteJson(plans)
3✔
771
}
772

773
func (u *UserAdmApiHandlers) GetPlanBindingHandler(w rest.ResponseWriter, r *rest.Request) {
2✔
774
        ctx := r.Context()
2✔
775
        l := log.FromContext(ctx)
2✔
776

2✔
777
        planBinding, err := u.userAdm.GetPlanBinding(ctx)
2✔
778
        if err != nil {
3✔
779
                rest_utils.RestErrWithLogInternal(w, r, l, err)
1✔
780
                return
1✔
781
        }
1✔
782

783
        _ = w.WriteJson(planBinding)
1✔
784
}
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