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

mendersoftware / mender-server / 1893336421

27 Jun 2025 07:09AM UTC coverage: 65.651% (-0.08%) from 65.731%
1893336421

Pull #764

gitlab-ci

bahaa-ghazal
fix(useradm): Use contenttype middleware for login endpoint

Signed-off-by: Bahaa Aldeen Ghazal <bahaa.ghazal@northern.tech>
Pull Request #764: refactor(useradm): Migrate from ant0nie/go-json-rest to gin-gonic/gin

265 of 312 new or added lines in 6 files covered. (84.94%)

5 existing lines in 2 files now uncovered.

32387 of 49332 relevant lines covered (65.65%)

1.39 hits per line

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

87.23
/backend/services/useradm/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/gin-gonic/gin"
23
        "github.com/google/uuid"
24
        "github.com/pkg/errors"
25

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

29
        "github.com/mendersoftware/mender-server/services/useradm/authz"
30
        "github.com/mendersoftware/mender-server/services/useradm/jwt"
31
        "github.com/mendersoftware/mender-server/services/useradm/model"
32
        "github.com/mendersoftware/mender-server/services/useradm/store"
33
        useradm "github.com/mendersoftware/mender-server/services/useradm/user"
34
)
35

36
const (
37
        defaultTimeout = time.Second * 5
38
        hdrETag        = "ETag"
39
        hdrIfMatch     = "If-Match"
40
)
41

42
var (
43
        ErrAuthHeader   = errors.New("invalid or missing auth header")
44
        ErrUserNotFound = errors.New("user not found")
45

46
        ErrAuthzUnauthorized = errors.New("unauthorized")
47
        ErrAuthzNoAuth       = errors.New("authorization not present in header")
48
        ErrInvalidAuthHeader = errors.New("malformed Authorization header")
49
        ErrAuthzTokenInvalid = errors.New("invalid jwt")
50
)
51

52
type UserAdmApiHandlers struct {
53
        userAdm    useradm.App
54
        db         store.DataStore
55
        jwth       map[int]jwt.Handler
56
        config     Config
57
        authorizer authz.Authorizer
58
}
59

60
type Config struct {
61
        // maximum expiration time for Personal Access Token
62
        TokenMaxExpSeconds int
63

64
        JWTFallback jwt.Handler
65
}
66

67
// return an ApiHandler for user administration and authentiacation app
68
func NewUserAdmApiHandlers(
69
        userAdm useradm.App,
70
        db store.DataStore,
71
        jwth map[int]jwt.Handler,
72
        config Config,
73
        authorizer authz.Authorizer,
74
) *UserAdmApiHandlers {
3✔
75
        return &UserAdmApiHandlers{
3✔
76
                userAdm:    userAdm,
3✔
77
                db:         db,
3✔
78
                jwth:       jwth,
3✔
79
                config:     config,
3✔
80
                authorizer: authorizer,
3✔
81
        }
3✔
82
}
3✔
83

84
// Getting nil means that a rest error has allready been renderd
85
func (i *UserAdmApiHandlers) authTokenExtractor(c *gin.Context) *jwt.Token {
3✔
86
        tokstr, err := ExtractToken(c.Request)
3✔
87
        if err != nil {
3✔
NEW
88
                rest.RenderError(c, http.StatusUnauthorized, err)
×
NEW
89
                return nil
×
NEW
90
        }
×
91

92
        keyId := jwt.GetKeyId(tokstr)
3✔
93

3✔
94
        if _, ok := i.jwth[keyId]; !ok {
3✔
NEW
95
                // we have not found the corresponding handler for the key by id
×
NEW
96
                // on purpose we do not return common.ErrKeyIdNotFound -- to not allow
×
NEW
97
                // the enumeration attack
×
NEW
98
                rest.RenderError(c, http.StatusUnauthorized, ErrAuthzTokenInvalid)
×
NEW
99
                return nil
×
NEW
100
        }
×
101

102
        // parse token
103
        token, err := i.jwth[keyId].FromJWT(tokstr)
3✔
104
        if err != nil && i.config.JWTFallback != nil {
3✔
NEW
105
                token, err = i.config.JWTFallback.FromJWT(tokstr)
×
NEW
106
        }
×
107
        if err != nil {
4✔
108
                rest.RenderError(c, http.StatusUnauthorized, ErrAuthzTokenInvalid)
1✔
109
                return nil
1✔
110
        }
1✔
111

112
        // extract resource action
113
        action, err := ExtractResourceAction(c.Request)
3✔
114
        if err != nil {
4✔
115
                rest.RenderInternalError(c, err)
1✔
116
                return nil
1✔
117
        }
1✔
118

119
        ctx := c.Request.Context()
3✔
120

3✔
121
        //authorize, no authz = http 403
3✔
122
        err = i.authorizer.Authorize(ctx, token, action.Resource, action.Method)
3✔
123
        if err != nil {
3✔
NEW
124
                if err == ErrAuthzUnauthorized {
×
NEW
125
                        rest.RenderError(c, http.StatusForbidden, ErrAuthzUnauthorized)
×
NEW
126
                } else if err == ErrAuthzTokenInvalid {
×
NEW
127
                        rest.RenderError(c, http.StatusUnauthorized, ErrAuthzTokenInvalid)
×
NEW
128
                } else {
×
NEW
129
                        rest.RenderInternalError(c, err)
×
NEW
130
                }
×
NEW
131
                return nil
×
132
        }
133

134
        return token
3✔
135
}
136

137
func (u *UserAdmApiHandlers) AliveHandler(c *gin.Context) {
2✔
138
        c.Status(http.StatusNoContent)
2✔
139
}
2✔
140

141
func (u *UserAdmApiHandlers) HealthHandler(c *gin.Context) {
2✔
142
        ctx := c.Request.Context()
2✔
143
        ctx, cancel := context.WithTimeout(ctx, defaultTimeout)
2✔
144
        defer cancel()
2✔
145

2✔
146
        err := u.userAdm.HealthCheck(ctx)
2✔
147
        if err != nil {
3✔
148
                rest.RenderError(c, http.StatusServiceUnavailable, err)
1✔
149
                return
1✔
150
        }
1✔
151
        c.Status(http.StatusNoContent)
2✔
152
}
153

154
func (u *UserAdmApiHandlers) AuthLoginHandler(c *gin.Context) {
3✔
155
        ctx := c.Request.Context()
3✔
156

3✔
157
        //parse auth header
3✔
158
        user, pass, ok := c.Request.BasicAuth()
3✔
159
        if !ok {
5✔
160
                rest.RenderError(c, http.StatusUnauthorized, ErrAuthHeader)
2✔
161
                return
2✔
162
        }
2✔
163
        email := model.Email(strings.ToLower(user))
3✔
164

3✔
165
        options := &useradm.LoginOptions{}
3✔
166
        err := c.ShouldBindJSON(options)
3✔
167
        if err != nil && c.Request.ContentLength != 0 {
4✔
168
                rest.RenderError(c, http.StatusBadRequest, err)
1✔
169
                return
1✔
170
        }
1✔
171

172
        token, err := u.userAdm.Login(ctx, email, pass, options)
3✔
173
        if err != nil {
5✔
174
                switch {
2✔
175
                case err == useradm.ErrUnauthorized || err == useradm.ErrTenantAccountSuspended:
2✔
176
                        rest.RenderError(c, http.StatusUnauthorized, err)
2✔
177
                default:
1✔
178
                        rest.RenderInternalError(c, err)
1✔
179
                }
180
                return
2✔
181
        }
182

183
        raw, err := u.userAdm.SignToken(ctx, token)
3✔
184
        if err != nil {
4✔
185
                rest.RenderInternalError(c, err)
1✔
186
                return
1✔
187
        }
1✔
188

189
        writer := c.Writer
3✔
190
        writer.Header().Set("Content-Type", "application/jwt")
3✔
191
        _, _ = writer.Write([]byte(raw))
3✔
192
}
193

194
func (u *UserAdmApiHandlers) AuthLogoutHandler(c *gin.Context) {
2✔
195
        ctx := c.Request.Context()
2✔
196

2✔
197
        if tokenStr, err := ExtractToken(c.Request); err == nil {
4✔
198
                keyId := jwt.GetKeyId(tokenStr)
2✔
199
                if _, ok := u.jwth[keyId]; !ok {
2✔
NEW
200
                        rest.RenderInternalError(c, err)
×
201
                        return
×
202
                }
×
203

204
                token, err := u.jwth[keyId].FromJWT(tokenStr)
2✔
205
                if err != nil {
3✔
206
                        rest.RenderInternalError(c, err)
1✔
207
                        return
1✔
208
                }
1✔
209
                if err := u.userAdm.Logout(ctx, token); err != nil {
3✔
210
                        rest.RenderInternalError(c, err)
1✔
211
                        return
1✔
212
                }
1✔
213
        }
214

215
        c.Status(http.StatusAccepted)
2✔
216
}
217

218
func (u *UserAdmApiHandlers) AuthVerifyHandler(c *gin.Context) {
3✔
219
        ctx := c.Request.Context()
3✔
220

3✔
221
        // note that the request has passed through authz - the token is valid
3✔
222
        token := u.authTokenExtractor(c)
3✔
223
        if token == nil {
4✔
224
                return
1✔
225
        }
1✔
226

227
        err := u.userAdm.Verify(ctx, token)
3✔
228
        if err != nil {
5✔
229
                if err == useradm.ErrUnauthorized {
4✔
230
                        rest.RenderError(c, http.StatusUnauthorized, useradm.ErrUnauthorized)
2✔
231
                } else {
3✔
232
                        rest.RenderInternalError(c, err)
1✔
233
                }
1✔
234
                return
2✔
235
        }
236

237
        c.Status(http.StatusOK)
3✔
238
}
239

240
func (u *UserAdmApiHandlers) CreateTenantUserHandler(c *gin.Context) {
2✔
241
        ctx := c.Request.Context()
2✔
242

2✔
243
        user, err := parseUserInternal(c)
2✔
244
        if err != nil {
4✔
245
                rest.RenderError(c, http.StatusBadRequest, err)
2✔
246
                return
2✔
247
        }
2✔
248

249
        tenantId := c.Param("id")
1✔
250
        if tenantId == "" {
2✔
251
                rest.RenderError(c, http.StatusNotFound, errors.New("Entity not found"))
1✔
252
                return
1✔
253
        }
1✔
254
        ctx = getTenantContext(ctx, tenantId)
1✔
255
        err = u.userAdm.CreateUserInternal(ctx, user)
1✔
256
        if err != nil {
2✔
257
                if err == store.ErrDuplicateEmail {
2✔
258
                        rest.RenderError(c, http.StatusUnprocessableEntity, err)
1✔
259
                } else {
1✔
NEW
260
                        rest.RenderInternalError(c, err)
×
261
                }
×
262
                return
1✔
263
        }
264

265
        c.Status(http.StatusCreated)
1✔
266

267
}
268

269
func (u *UserAdmApiHandlers) AddUserHandler(c *gin.Context) {
2✔
270
        ctx := c.Request.Context()
2✔
271

2✔
272
        user, err := parseUser(c)
2✔
273
        if err != nil {
4✔
274
                if err == model.ErrPasswordTooShort {
4✔
275
                        rest.RenderError(c, http.StatusUnprocessableEntity, err)
2✔
276
                } else {
4✔
277
                        rest.RenderError(c, http.StatusBadRequest, err)
2✔
278
                }
2✔
279
                return
2✔
280
        }
281

282
        err = u.userAdm.CreateUser(ctx, user)
2✔
283
        if err != nil {
3✔
284
                if err == store.ErrDuplicateEmail || err == useradm.ErrPassAndMailTooSimilar {
2✔
285
                        rest.RenderError(c, http.StatusUnprocessableEntity, err)
1✔
286
                } else {
1✔
NEW
287
                        rest.RenderInternalError(c, err)
×
288
                }
×
289
                return
1✔
290
        }
291

292
        c.Writer.Header().Add("Location", "users/"+string(user.ID))
2✔
293
        c.Status(http.StatusCreated)
2✔
294

295
}
296

297
func (u *UserAdmApiHandlers) GetUsersHandler(c *gin.Context) {
3✔
298
        ctx := c.Request.Context()
3✔
299

3✔
300
        if err := c.Request.ParseForm(); err != nil {
4✔
301
                err = errors.Wrap(err, "api: bad form parameters")
1✔
302
                rest.RenderError(c, http.StatusBadRequest, err)
1✔
303
                return
1✔
304
        }
1✔
305

306
        fltr := model.UserFilter{}
3✔
307
        if err := fltr.ParseForm(c.Request.Form); err != nil {
4✔
308
                err = errors.Wrap(err, "api: invalid form values")
1✔
309
                rest.RenderError(c, http.StatusBadRequest, err)
1✔
310
                return
1✔
311
        }
1✔
312

313
        users, err := u.userAdm.GetUsers(ctx, fltr)
3✔
314
        if err != nil {
4✔
315
                rest.RenderInternalError(c, err)
1✔
316
                return
1✔
317
        }
1✔
318

319
        c.JSON(http.StatusOK, users)
3✔
320
}
321

322
func (u *UserAdmApiHandlers) GetTenantUsersHandler(c *gin.Context) {
2✔
323
        ctx := c.Request.Context()
2✔
324
        ctx = identity.WithContext(ctx, &identity.Identity{
2✔
325
                Tenant: c.Param("id"),
2✔
326
        })
2✔
327
        c.Request = c.Request.WithContext(ctx)
2✔
328
        u.GetUsersHandler(c)
2✔
329
}
2✔
330

331
func (u *UserAdmApiHandlers) GetUserHandler(c *gin.Context) {
2✔
332
        ctx := c.Request.Context()
2✔
333

2✔
334
        id := c.Param("id")
2✔
335
        user, err := u.userAdm.GetUser(ctx, id)
2✔
336
        if err != nil {
3✔
337
                rest.RenderInternalError(c, err)
1✔
338
                return
1✔
339
        }
1✔
340

341
        if user == nil {
4✔
342
                rest.RenderError(c, http.StatusNotFound, ErrUserNotFound)
2✔
343
                return
2✔
344
        }
2✔
345

346
        c.JSON(http.StatusOK, user)
2✔
347
}
348

349
func (u *UserAdmApiHandlers) UpdateUserHandler(c *gin.Context) {
2✔
350
        ctx := c.Request.Context()
2✔
351
        idty := identity.FromContext(ctx)
2✔
352

2✔
353
        userUpdate, err := parseUserUpdate(c)
2✔
354
        if err != nil {
4✔
355
                if err == model.ErrPasswordTooShort {
3✔
356
                        rest.RenderError(c, http.StatusUnprocessableEntity, err)
1✔
357
                } else {
3✔
358
                        rest.RenderError(c, http.StatusBadRequest, err)
2✔
359
                }
2✔
360
                return
2✔
361
        }
362

363
        // extract the token used to update the user
364
        if tokenStr, err := ExtractToken(c.Request); err == nil {
4✔
365
                keyId := jwt.GetKeyId(tokenStr)
2✔
366
                if _, ok := u.jwth[keyId]; !ok {
2✔
NEW
367
                        rest.RenderInternalError(c, err)
×
368
                        return
×
369
                }
×
370

371
                token, err := u.jwth[keyId].FromJWT(tokenStr)
2✔
372
                if err != nil {
2✔
NEW
373
                        rest.RenderInternalError(c, err)
×
374
                        return
×
375
                }
×
376
                userUpdate.Token = token
2✔
377
        }
378

379
        id := c.Param("id")
2✔
380
        if strings.EqualFold(id, "me") {
4✔
381
                id = idty.Subject
2✔
382
        }
2✔
383
        err = u.userAdm.UpdateUser(ctx, id, userUpdate)
2✔
384
        if err != nil {
4✔
385
                switch err {
2✔
386
                case store.ErrDuplicateEmail,
387
                        useradm.ErrCurrentPasswordMismatch,
388
                        useradm.ErrPassAndMailTooSimilar,
389
                        useradm.ErrCannotModifyPassword:
2✔
390
                        rest.RenderError(c, http.StatusUnprocessableEntity, err)
2✔
391
                case store.ErrUserNotFound:
1✔
392
                        rest.RenderError(c, http.StatusNotFound, err)
1✔
393
                case useradm.ErrETagMismatch:
1✔
394
                        rest.RenderError(c, http.StatusConflict, err)
1✔
395
                default:
×
NEW
396
                        rest.RenderInternalError(c, err)
×
397
                }
398
        }
399

400
        c.Status(http.StatusNoContent)
2✔
401
}
402

403
func (u *UserAdmApiHandlers) DeleteTenantUserHandler(c *gin.Context) {
2✔
404
        ctx := c.Request.Context()
2✔
405

2✔
406
        tenantId := c.Param("id")
2✔
407
        if tenantId != "" {
4✔
408
                ctx = getTenantContext(ctx, tenantId)
2✔
409
        }
2✔
410

411
        err := u.userAdm.DeleteUser(ctx, c.Param("userid"))
2✔
412
        if err != nil {
3✔
413
                rest.RenderInternalError(c, err)
1✔
414
                return
1✔
415
        }
1✔
416

417
        c.Status(http.StatusNoContent)
2✔
418
}
419

420
func (u *UserAdmApiHandlers) DeleteUserHandler(c *gin.Context) {
2✔
421
        ctx := c.Request.Context()
2✔
422

2✔
423
        err := u.userAdm.DeleteUser(ctx, c.Param("id"))
2✔
424
        if err != nil {
3✔
425
                rest.RenderInternalError(c, err)
1✔
426
                return
1✔
427
        }
1✔
428

429
        c.Status(http.StatusNoContent)
2✔
430
}
431

432
func parseUser(c *gin.Context) (*model.User, error) {
2✔
433
        user := model.User{}
2✔
434

2✔
435
        //decode body
2✔
436
        err := c.ShouldBindJSON(&user)
2✔
437
        if err != nil {
3✔
438
                return nil, errors.Wrap(err, "failed to decode request body")
1✔
439
        }
1✔
440

441
        if err := user.Validate(); err != nil {
4✔
442
                return nil, err
2✔
443
        }
2✔
444

445
        return &user, nil
2✔
446
}
447

448
func parseUserInternal(c *gin.Context) (*model.UserInternal, error) {
2✔
449
        user := model.UserInternal{}
2✔
450

2✔
451
        //decode body
2✔
452
        err := c.ShouldBindJSON(&user)
2✔
453
        if err != nil {
4✔
454
                return nil, errors.Wrap(err, "failed to decode request body")
2✔
455
        }
2✔
456

457
        if err := user.Validate(); err != nil {
2✔
458
                return nil, err
1✔
459
        }
1✔
460

461
        return &user, nil
1✔
462
}
463

464
func parseUserUpdate(c *gin.Context) (*model.UserUpdate, error) {
2✔
465
        userUpdate := model.UserUpdate{}
2✔
466

2✔
467
        //decode body
2✔
468
        err := c.ShouldBindJSON(&userUpdate)
2✔
469
        if err != nil {
3✔
470
                return nil, errors.Wrap(err, "failed to decode request body")
1✔
471
        }
1✔
472

473
        if err := userUpdate.Validate(); err != nil {
4✔
474
                return nil, err
2✔
475
        }
2✔
476

477
        return &userUpdate, nil
2✔
478
}
479

480
func (u *UserAdmApiHandlers) CreateTenantHandler(c *gin.Context) {
3✔
481
        ctx := c.Request.Context()
3✔
482

3✔
483
        var newTenant model.NewTenant
3✔
484

3✔
485
        if err := c.ShouldBindJSON(&newTenant); err != nil {
5✔
486
                rest.RenderError(c, http.StatusBadRequest, err)
2✔
487
                return
2✔
488
        }
2✔
489

490
        if err := newTenant.Validate(); err != nil {
4✔
491
                rest.RenderError(c, http.StatusBadRequest, err)
2✔
492
                return
2✔
493
        }
2✔
494

495
        err := u.userAdm.CreateTenant(ctx, model.NewTenant{
2✔
496
                ID: newTenant.ID,
2✔
497
        })
2✔
498
        if err != nil {
3✔
499
                rest.RenderInternalError(c, err)
1✔
500
                return
1✔
501
        }
1✔
502

503
        c.Status(http.StatusCreated)
2✔
504
}
505

506
func getTenantContext(ctx context.Context, tenantId string) context.Context {
2✔
507
        if ctx == nil {
2✔
508
                ctx = context.Background()
×
509
        }
×
510
        if tenantId != "" {
4✔
511
                id := &identity.Identity{
2✔
512
                        Tenant: tenantId,
2✔
513
                }
2✔
514

2✔
515
                ctx = identity.WithContext(ctx, id)
2✔
516
        }
2✔
517

518
        return ctx
2✔
519
}
520

521
func (u *UserAdmApiHandlers) DeleteTokensHandler(c *gin.Context) {
2✔
522

2✔
523
        ctx := c.Request.Context()
2✔
524

2✔
525
        tenantId := c.Request.URL.Query().Get("tenant_id")
2✔
526
        if tenantId == "" {
4✔
527
                rest.RenderError(c, http.StatusBadRequest, errors.New("tenant_id must be provided"))
2✔
528
                return
2✔
529
        }
2✔
530
        userId := c.Request.URL.Query().Get("user_id")
1✔
531

1✔
532
        err := u.userAdm.DeleteTokens(ctx, tenantId, userId)
1✔
533
        switch err {
1✔
534
        case nil:
1✔
535
                c.Status(http.StatusNoContent)
1✔
536
        default:
1✔
537
                rest.RenderInternalError(c, err)
1✔
538
        }
539
}
540

541
func (u *UserAdmApiHandlers) SaveSettingsHandler(c *gin.Context) {
2✔
542
        u.saveSettingsHandler(c, false)
2✔
543
}
2✔
544

NEW
545
func (u *UserAdmApiHandlers) SaveSettingsMeHandler(c *gin.Context) {
×
NEW
546
        u.saveSettingsHandler(c, true)
×
UNCOV
547
}
×
548

549
func (u *UserAdmApiHandlers) saveSettingsHandler(c *gin.Context, me bool) {
2✔
550
        ctx := c.Request.Context()
2✔
551

2✔
552
        settings := &model.Settings{}
2✔
553
        err := c.ShouldBindJSON(settings)
2✔
554
        if err != nil {
4✔
555
                rest.RenderError(c, http.StatusBadRequest, errors.New("cannot parse request body as json"))
2✔
556
                return
2✔
557
        }
2✔
558

559
        if err := settings.Validate(); err != nil {
3✔
560
                rest.RenderError(c, http.StatusBadRequest, err)
1✔
561
                return
1✔
562
        }
1✔
563

564
        ifMatchHeader := c.Request.Header.Get(hdrIfMatch)
2✔
565

2✔
566
        settings.ETag = uuid.NewString()
2✔
567
        if me {
2✔
568
                id := identity.FromContext(ctx)
×
569
                if id == nil {
×
NEW
570
                        rest.RenderInternalError(c, errors.New("identity not present"))
×
571
                        return
×
572
                }
×
573
                err = u.db.SaveUserSettings(ctx, id.Subject, settings, ifMatchHeader)
×
574
        } else {
2✔
575
                err = u.db.SaveSettings(ctx, settings, ifMatchHeader)
2✔
576
        }
2✔
577
        if err == store.ErrETagMismatch {
3✔
578
                rest.RenderError(c, http.StatusPreconditionFailed, err)
1✔
579
                return
1✔
580
        } else if err != nil {
4✔
581
                rest.RenderInternalError(c, err)
1✔
582
                return
1✔
583
        }
1✔
584

585
        c.Status(http.StatusCreated)
2✔
586
}
587

588
func (u *UserAdmApiHandlers) GetSettingsHandler(c *gin.Context) {
2✔
589
        u.getSettingsHandler(c, false)
2✔
590
}
2✔
591

NEW
592
func (u *UserAdmApiHandlers) GetSettingsMeHandler(c *gin.Context) {
×
NEW
593
        u.getSettingsHandler(c, true)
×
UNCOV
594
}
×
595

596
func (u *UserAdmApiHandlers) getSettingsHandler(c *gin.Context, me bool) {
2✔
597
        ctx := c.Request.Context()
2✔
598

2✔
599
        var settings *model.Settings
2✔
600
        var err error
2✔
601
        if me {
2✔
602
                id := identity.FromContext(ctx)
×
603
                if id == nil {
×
NEW
604
                        rest.RenderInternalError(c, errors.New("identity not present"))
×
605
                        return
×
606
                }
×
607
                settings, err = u.db.GetUserSettings(ctx, id.Subject)
×
608
        } else {
2✔
609
                settings, err = u.db.GetSettings(ctx)
2✔
610
        }
2✔
611

612
        if err != nil {
3✔
613
                rest.RenderInternalError(c, err)
1✔
614
                return
1✔
615
        } else if settings == nil {
4✔
616
                settings = &model.Settings{
1✔
617
                        Values: model.SettingsValues{},
1✔
618
                }
1✔
619
        }
1✔
620

621
        if settings.ETag != "" {
4✔
622
                c.Writer.Header().Set(hdrETag, settings.ETag)
2✔
623
        }
2✔
624
        c.JSON(http.StatusOK, settings)
2✔
625
}
626

627
func (u *UserAdmApiHandlers) IssueTokenHandler(c *gin.Context) {
2✔
628
        ctx := c.Request.Context()
2✔
629

2✔
630
        var tokenRequest model.TokenRequest
2✔
631

2✔
632
        if err := c.ShouldBindJSON(&tokenRequest); err != nil {
2✔
NEW
633
                rest.RenderError(c, http.StatusBadRequest, errors.New("cannot parse request body as json"))
×
634
                return
×
635
        }
×
636
        if err := tokenRequest.Validate(u.config.TokenMaxExpSeconds); err != nil {
3✔
637
                rest.RenderError(c, http.StatusBadRequest, err)
1✔
638
                return
1✔
639
        }
1✔
640

641
        token, err := u.userAdm.IssuePersonalAccessToken(ctx, &tokenRequest)
2✔
642
        switch err {
2✔
643
        case nil:
2✔
644
                writer := c.Writer
2✔
645
                writer.Header().Set("Content-Type", "application/jwt")
2✔
646
                _, _ = writer.Write([]byte(token))
2✔
647
        case useradm.ErrTooManyTokens:
2✔
648
                rest.RenderError(c, http.StatusUnprocessableEntity, err)
2✔
649
        case useradm.ErrDuplicateTokenName:
2✔
650
                rest.RenderError(c, http.StatusConflict, err)
2✔
UNCOV
651
        default:
×
NEW
652
                rest.RenderInternalError(c, err)
×
653
        }
654
}
655

656
func (u *UserAdmApiHandlers) GetTokensHandler(c *gin.Context) {
2✔
657
        ctx := c.Request.Context()
2✔
658
        id := identity.FromContext(ctx)
2✔
659
        if id == nil {
2✔
NEW
660
                rest.RenderInternalError(c, errors.New("identity not present"))
×
661
                return
×
662
        }
×
663

664
        tokens, err := u.userAdm.GetPersonalAccessTokens(ctx, id.Subject)
2✔
665
        if err != nil {
3✔
666
                rest.RenderInternalError(c, err)
1✔
667
                return
1✔
668
        }
1✔
669

670
        c.JSON(http.StatusOK, tokens)
2✔
671
}
672

673
func (u *UserAdmApiHandlers) DeleteTokenHandler(c *gin.Context) {
1✔
674
        ctx := c.Request.Context()
1✔
675

1✔
676
        err := u.userAdm.DeleteToken(ctx, c.Param("id"))
1✔
677
        if err != nil {
1✔
NEW
678
                rest.RenderInternalError(c, err)
×
679
                return
×
680
        }
×
681

682
        c.Status(http.StatusNoContent)
1✔
683
}
684

685
// plans and plan binding
686

687
func (u *UserAdmApiHandlers) GetPlansHandler(c *gin.Context) {
1✔
688
        ctx := c.Request.Context()
1✔
689
        page, perPage, err := rest.ParsePagingParameters(c.Request)
1✔
690
        if err != nil {
2✔
691
                rest.RenderError(c, http.StatusBadRequest, err)
1✔
692
                return
1✔
693
        }
1✔
694

695
        plans := u.userAdm.GetPlans(ctx, int((page-1)*perPage), int(perPage))
1✔
696
        if plans == nil {
2✔
697
                plans = []model.Plan{}
1✔
698
        }
1✔
699

700
        c.JSON(http.StatusOK, plans)
1✔
701
}
702

703
func (u *UserAdmApiHandlers) GetPlanBindingHandler(c *gin.Context) {
1✔
704
        ctx := c.Request.Context()
1✔
705

1✔
706
        planBinding, err := u.userAdm.GetPlanBinding(ctx)
1✔
707
        if err != nil {
2✔
708
                rest.RenderInternalError(c, err)
1✔
709
                return
1✔
710
        }
1✔
711

712
        c.JSON(http.StatusOK, planBinding)
1✔
713
}
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