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

mendersoftware / mender-server / 2029630506

09 Sep 2025 08:06AM UTC coverage: 65.283% (-0.07%) from 65.357%
2029630506

Pull #941

gitlab-ci

web-flow
Merge pull request #904 from bahaa-ghazal/MEN-8649

MEN-8649: Clean OS services from tenantadm related code 🧹
Pull Request #941: Merge MEN-8649 into main

18 of 26 new or added lines in 4 files covered. (69.23%)

11 existing lines in 3 files now uncovered.

31634 of 48457 relevant lines covered (65.28%)

1.4 hits per line

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

92.62
/backend/services/useradm/user/useradm.go
1
// Copyright 2025 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

15
package useradm
16

17
import (
18
        "context"
19
        "time"
20

21
        "github.com/google/uuid"
22
        "github.com/pkg/errors"
23
        "golang.org/x/crypto/bcrypt"
24

25
        "github.com/mendersoftware/mender-server/pkg/identity"
26
        "github.com/mendersoftware/mender-server/pkg/log"
27
        "github.com/mendersoftware/mender-server/pkg/mongo/oid"
28

29
        "github.com/mendersoftware/mender-server/services/useradm/common"
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/scope"
33
        "github.com/mendersoftware/mender-server/services/useradm/store"
34
        "github.com/mendersoftware/mender-server/services/useradm/utils"
35
)
36

37
var (
38
        ErrUserNotFound           = store.ErrUserNotFound
39
        ErrDuplicateEmail         = store.ErrDuplicateEmail
40
        ErrETagMismatch           = errors.New("entity tag did not match any records")
41
        ErrUnauthorized           = errors.New("unauthorized")
42
        ErrAuthExpired            = errors.New("token expired")
43
        ErrAuthInvalid            = errors.New("token is invalid")
44
        ErrTenantAccountSuspended = errors.New("tenant account suspended")
45
        ErrInvalidTenantID        = errors.New("invalid tenant id")
46
        ErrTooManyTokens          = errors.New(
47
                "maximum number of personal acess tokens reached for this user")
48
        ErrDuplicateTokenName = errors.New(
49
                "Personal Access Token with a given name already exists")
50
        // password mismatch
51
        ErrCurrentPasswordMismatch = errors.New("current password mismatch")
52
        // modification of other user's password is not allowed
53
        ErrCannotModifyPassword = errors.New("password cannot be modified")
54
        // password too similar to the email
55
        ErrPassAndMailTooSimilar = errors.New("password is too similar to the email")
56
)
57

58
const (
59
        TenantStatusSuspended = "suspended"
60
        userIdMe              = "me"
61
)
62

63
//go:generate ../../../utils/mockgen.sh
64
type App interface {
65
        HealthCheck(ctx context.Context) error
66
        // Login accepts email/password, returns JWT
67
        Login(ctx context.Context, email model.Email, pass string,
68
                options *LoginOptions) (*jwt.Token, error)
69
        Logout(ctx context.Context, token *jwt.Token) error
70
        CreateUser(ctx context.Context, u *model.User) error
71
        CreateUserInternal(ctx context.Context, u *model.UserInternal) error
72
        UpdateUser(ctx context.Context, id string, u *model.UserUpdate) error
73
        Verify(ctx context.Context, token *jwt.Token) error
74
        GetUsers(ctx context.Context, fltr model.UserFilter) ([]model.User, error)
75
        GetUser(ctx context.Context, id string) (*model.User, error)
76
        DeleteUser(ctx context.Context, id string) error
77
        SetPassword(ctx context.Context, u model.UserUpdate) error
78

79
        // SignToken generates a signed
80
        // token using configuration & method set up in UserAdmApp
81
        SignToken(ctx context.Context, t *jwt.Token) (string, error)
82
        DeleteToken(ctx context.Context, id string) error
83

84
        // IssuePersonalAccessToken issues Personal Access Token
85
        IssuePersonalAccessToken(ctx context.Context, tr *model.TokenRequest) (string, error)
86
        // GetPersonalAccessTokens returns list of Personal Access Tokens
87
        GetPersonalAccessTokens(ctx context.Context, userID string) ([]model.PersonalAccessToken, error)
88

89
        DeleteTokens(ctx context.Context, tenantId, userId string) error
90

91
        CreateTenant(ctx context.Context, tenant model.NewTenant) error
92
        GetPlans(ctx context.Context, skip, limit int) []model.Plan
93
        GetPlanBinding(ctx context.Context) (*model.PlanBindingDetails, error)
94
}
95

96
type Config struct {
97
        // token issuer
98
        Issuer string
99
        // token expiration time
100
        ExpirationTimeSeconds int64
101
        // maximum number of log in tokens per user
102
        // zero means no limit
103
        LimitSessionsPerUser int
104
        // maximum number of personal access tokens per user
105
        // zero means no limit
106
        LimitTokensPerUser int
107
        // how often we should update personal access token
108
        // with last used timestamp
109
        TokenLastUsedUpdateFreqMinutes int
110
        // path to the private key, used to generate kid header in JWT and to get all the keys
111
        PrivateKeyPath string
112
        // PrivateKeyFileNamePattern holds the regular expression used
113
        // to get the key id from a filename
114
        PrivateKeyFileNamePattern string
115
}
116

117
type UserAdm struct {
118
        // JWT serialized/deserializer
119
        jwtHandlers map[int]jwt.Handler
120
        db          store.DataStore
121
        config      Config
122
}
123

124
func NewUserAdm(jwtHandlers map[int]jwt.Handler, db store.DataStore, config Config) *UserAdm {
3✔
125

3✔
126
        return &UserAdm{
3✔
127
                jwtHandlers: jwtHandlers,
3✔
128
                db:          db,
3✔
129
                config:      config,
3✔
130
        }
3✔
131
}
3✔
132

133
func (u *UserAdm) HealthCheck(ctx context.Context) error {
2✔
134
        err := u.db.Ping(ctx)
2✔
135
        if err != nil {
3✔
136
                return errors.Wrap(err, "error reaching MongoDB")
1✔
137
        }
1✔
138

139
        return nil
2✔
140
}
141

142
func (u *UserAdm) Login(ctx context.Context, email model.Email, pass string,
143
        options *LoginOptions) (*jwt.Token, error) {
3✔
144
        var ident identity.Identity
3✔
145
        l := log.FromContext(ctx)
3✔
146

3✔
147
        if email == "" {
3✔
148
                return nil, ErrUnauthorized
×
149
        }
×
150

151
        //get user
152
        user, err := u.db.GetUserByEmail(ctx, email)
3✔
153

3✔
154
        if user == nil && err == nil {
5✔
155
                return nil, ErrUnauthorized
2✔
156
        }
2✔
157

158
        if err != nil {
4✔
159
                return nil, errors.Wrap(err, "useradm: failed to get user")
1✔
160
        }
1✔
161

162
        //verify password
163
        err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(pass))
3✔
164
        if err != nil {
5✔
165
                return nil, ErrUnauthorized
2✔
166
        }
2✔
167

168
        //generate and save token
169
        t, err := u.generateToken(
3✔
170
                user.ID,
3✔
171
                scope.All,
3✔
172
                ident.Tenant,
3✔
173
                options.NoExpiry,
3✔
174
                common.KeyIdFromPath(u.config.PrivateKeyPath, u.config.PrivateKeyFileNamePattern),
3✔
175
        )
3✔
176
        if err != nil {
3✔
177
                return nil, errors.Wrap(err, "useradm: failed to generate token")
×
178
        }
×
179

180
        err = u.db.SaveToken(ctx, t)
3✔
181
        if err != nil {
4✔
182
                return nil, errors.Wrap(err, "useradm: failed to save token")
1✔
183
        }
1✔
184
        if u.config.LimitSessionsPerUser > 0 {
6✔
185
                err = u.db.EnsureSessionTokensLimit(ctx, t.Subject, u.config.LimitSessionsPerUser)
3✔
186
                if err != nil {
4✔
187
                        return nil, errors.Wrap(err, "useradm: failed to ensure session tokens limit")
1✔
188
                }
1✔
189
        }
190

191
        if err = u.db.UpdateLoginTs(ctx, user.ID); err != nil {
3✔
UNCOV
192
                l.Warnf("failed to update login timestamp: %s", err.Error())
×
UNCOV
193
        }
×
194

195
        return t, nil
3✔
196
}
197

198
func (u *UserAdm) generateToken(subject, scope, tenant string,
199
        noExpiry bool, keyId int) (*jwt.Token, error) {
3✔
200
        id := oid.NewUUIDv4()
3✔
201
        subjectID := oid.FromString(subject)
3✔
202
        now := jwt.Time{Time: time.Now()}
3✔
203
        ret := &jwt.Token{
3✔
204
                KeyId: keyId,
3✔
205
                Claims: jwt.Claims{
3✔
206
                        ID:        id,
3✔
207
                        Subject:   subjectID,
3✔
208
                        Issuer:    u.config.Issuer,
3✔
209
                        IssuedAt:  now,
3✔
210
                        NotBefore: now,
3✔
211
                        Tenant:    tenant,
3✔
212
                        Scope:     scope,
3✔
213
                        User:      true,
3✔
214
                }}
3✔
215
        if !noExpiry {
6✔
216
                ret.Claims.ExpiresAt = &jwt.Time{
3✔
217
                        Time: now.Add(time.Second * time.Duration(u.config.ExpirationTimeSeconds)),
3✔
218
                }
3✔
219
        }
3✔
220
        return ret, ret.Claims.Valid()
3✔
221
}
222

223
func (u *UserAdm) SignToken(ctx context.Context, t *jwt.Token) (string, error) {
3✔
224
        if _, ok := u.jwtHandlers[t.KeyId]; !ok {
3✔
225
                return "", common.ErrKeyIdNotFound
×
226
        }
×
227
        return u.jwtHandlers[t.KeyId].ToJWT(t)
3✔
228
}
229

230
func (u *UserAdm) Logout(ctx context.Context, token *jwt.Token) error {
2✔
231
        return u.db.DeleteToken(ctx, token.Subject, token.ID)
2✔
232
}
2✔
233

234
func (ua *UserAdm) CreateUser(ctx context.Context, u *model.User) error {
2✔
235
        if utils.CheckIfPassSimilarToEmailRaw(string(u.Email), string(u.Password)) {
3✔
236
                return ErrPassAndMailTooSimilar
1✔
237
        }
1✔
238
        hash, err := bcrypt.GenerateFromPassword([]byte(u.Password), bcrypt.DefaultCost)
2✔
239
        if err != nil {
2✔
240
                return errors.Wrap(err, "failed to generate password hash")
×
241
        }
×
242
        u.Password = string(hash)
2✔
243

2✔
244
        // Generate a unique user ID for the new user
2✔
245
        u.ID = uuid.NewString()
2✔
246

2✔
247
        return ua.doCreateUser(ctx, u)
2✔
248
}
249

250
func (ua *UserAdm) CreateUserInternal(ctx context.Context, u *model.UserInternal) error {
2✔
251
        if u.PasswordHash != "" {
2✔
252
                u.Password = u.PasswordHash
×
253
        } else {
2✔
254
                hash, err := bcrypt.GenerateFromPassword([]byte(u.Password), bcrypt.DefaultCost)
2✔
255
                if err != nil {
2✔
256
                        return errors.Wrap(err, "failed to generate password hash")
×
257
                }
×
258
                u.Password = string(hash)
2✔
259
        }
260

261
        return ua.doCreateUser(ctx, &u.User)
2✔
262
}
263

264
func (ua *UserAdm) doCreateUser(ctx context.Context, u *model.User) error {
3✔
265
        if u.ID == "" {
6✔
266
                // Generate a unique user ID for the new user
3✔
267
                u.ID = uuid.NewString()
3✔
268
        }
3✔
269

270
        if err := ua.db.CreateUser(ctx, u); err != nil {
4✔
271
                if err == store.ErrDuplicateEmail {
2✔
272
                        return err
1✔
273
                }
1✔
274

275
                return errors.Wrap(err, "useradm: failed to create user in the db")
1✔
276
        }
277

278
        return nil
3✔
279
}
280

281
func (ua *UserAdm) validateUserUpdate(
282
        ctx context.Context,
283
        user *model.User,
284
        u *model.UserUpdate,
285
        me bool,
286
) error {
2✔
287
        // user can change own password only
2✔
288
        if !me {
4✔
289
                if len(u.Password) > 0 {
4✔
290
                        return ErrCannotModifyPassword
2✔
291
                }
2✔
292
        } else {
2✔
293
                // when changing own password or email address
2✔
294
                // user has to provide current password
2✔
295
                if len(u.Password) > 0 || (u.Email != "" && u.Email != user.Email) {
4✔
296
                        if err := bcrypt.CompareHashAndPassword(
2✔
297
                                []byte(user.Password),
2✔
298
                                []byte(u.CurrentPassword),
2✔
299
                        ); err != nil {
4✔
300
                                return ErrCurrentPasswordMismatch
2✔
301
                        }
2✔
302
                }
303
        }
304
        return nil
2✔
305
}
306

307
func (ua *UserAdm) deleteAndInvalidateUserTokens(
308
        ctx context.Context,
309
        userID string,
310
        token *jwt.Token,
311
) error {
2✔
312
        var err error
2✔
313
        if token != nil {
4✔
314
                err = ua.db.DeleteTokensByUserIdExceptCurrentOne(ctx, userID, token.ID)
2✔
315
        } else {
3✔
316
                err = ua.db.DeleteTokensByUserId(ctx, userID)
1✔
317
        }
1✔
318
        return err
2✔
319
}
320

321
func (ua *UserAdm) UpdateUser(ctx context.Context, id string, userUpdate *model.UserUpdate) error {
2✔
322
        idty := identity.FromContext(ctx)
2✔
323
        me := idty.Subject == id
2✔
324
        user, err := ua.db.GetUserAndPasswordById(ctx, id)
2✔
325
        if err != nil {
3✔
326
                return errors.Wrap(err, "useradm: failed to get user")
1✔
327
        } else if user == nil {
5✔
328
                return store.ErrUserNotFound
2✔
329
        }
2✔
330

331
        if err := ua.validateUserUpdate(ctx, user, userUpdate, me); err != nil {
4✔
332
                return err
2✔
333
        }
2✔
334

335
        if userUpdate.ETag == nil {
4✔
336
                // Update without the support for etags.
2✔
337
                next := user.NextETag()
2✔
338
                userUpdate.ETagUpdate = &next
2✔
339
                userUpdate.ETag = user.ETag
2✔
340
                if user.ETag == nil {
4✔
341
                        // If the ETag field is not set, assign the etag to nil
2✔
342
                        user.ETag = &model.ETagNil
2✔
343
                }
2✔
344
        } else if user.ETag == nil || *userUpdate.ETag != *user.ETag {
2✔
345
                return ErrETagMismatch
1✔
346
        }
1✔
347

348
        if utils.CheckIfPassSimilarToEmail(user, userUpdate) {
3✔
349
                return ErrPassAndMailTooSimilar
1✔
350
        }
1✔
351

352
        _, err = ua.db.UpdateUser(ctx, id, userUpdate)
2✔
353
        switch err {
2✔
354
        case nil:
2✔
355
                // invalidate the JWT tokens but the one used to update the user
2✔
356
                err = ua.deleteAndInvalidateUserTokens(ctx, id, userUpdate.Token)
2✔
357
                err = errors.Wrap(err, "useradm: failed to invalidate tokens")
2✔
358

359
        case store.ErrUserNotFound:
1✔
360
                // We matched the user earlier, the ETag must have changed in
1✔
361
                // the meantime
1✔
362
                err = ErrETagMismatch
1✔
363
        case store.ErrDuplicateEmail:
1✔
364
                break
1✔
365

366
        default:
1✔
367
                err = errors.Wrap(err, "useradm: failed to update user information")
1✔
368
        }
369

370
        return err
2✔
371
}
372

373
func (ua *UserAdm) Verify(ctx context.Context, token *jwt.Token) error {
3✔
374

3✔
375
        if token == nil {
4✔
376
                return ErrUnauthorized
1✔
377
        }
1✔
378

379
        l := log.FromContext(ctx)
3✔
380

3✔
381
        if !token.Claims.User {
4✔
382
                l.Errorf("not a user token")
1✔
383
                return ErrUnauthorized
1✔
384
        }
1✔
385

386
        user, err := ua.db.GetUserById(ctx, token.Claims.Subject.String())
3✔
387
        if user == nil && err == nil {
4✔
388
                return ErrUnauthorized
1✔
389
        }
1✔
390
        if err != nil {
4✔
391
                return errors.Wrap(err, "useradm: failed to get user")
1✔
392
        }
1✔
393

394
        dbToken, err := ua.db.GetTokenById(ctx, token.ID)
3✔
395
        if dbToken == nil && err == nil {
5✔
396
                return ErrUnauthorized
2✔
397
        }
2✔
398
        if err != nil {
4✔
399
                return errors.Wrap(err, "useradm: failed to get token")
1✔
400
        }
1✔
401

402
        // in case the token is a personal access token, update last used timestam
403
        // to not overload the database with writes to tokens collection, we do not
404
        // update the timestamp every time, but instead we wait some configurable
405
        // amount of time between updates
406
        if dbToken.TokenName != nil && ua.config.TokenLastUsedUpdateFreqMinutes > 0 {
4✔
407
                t := time.Now().Add(
1✔
408
                        (-time.Minute * time.Duration(ua.config.TokenLastUsedUpdateFreqMinutes)))
1✔
409
                if dbToken.LastUsed == nil || dbToken.LastUsed.Before(t) {
2✔
410
                        if err := ua.db.UpdateTokenLastUsed(ctx, token.ID); err != nil {
1✔
411
                                return err
×
412
                        }
×
413
                }
414
        }
415

416
        return nil
3✔
417
}
418

419
func (ua *UserAdm) GetUsers(ctx context.Context, fltr model.UserFilter) ([]model.User, error) {
3✔
420
        users, err := ua.db.GetUsers(ctx, fltr)
3✔
421
        if err != nil {
4✔
422
                return nil, errors.Wrap(err, "useradm: failed to get users")
1✔
423
        }
1✔
424

425
        return users, nil
3✔
426
}
427

428
func (ua *UserAdm) GetUser(ctx context.Context, id string) (*model.User, error) {
2✔
429
        if id == userIdMe {
3✔
430
                id = identity.FromContext(ctx).Subject
1✔
431
        }
1✔
432
        user, err := ua.db.GetUserById(ctx, id)
2✔
433
        if err != nil {
3✔
434
                return nil, errors.Wrap(err, "useradm: failed to get user")
1✔
435
        }
1✔
436

437
        return user, nil
2✔
438
}
439

440
func (ua *UserAdm) DeleteUser(ctx context.Context, id string) error {
3✔
441
        err := ua.db.DeleteUser(ctx, id)
3✔
442
        if err != nil {
4✔
443
                return errors.Wrap(err, "useradm: failed to delete user")
1✔
444
        }
1✔
445

446
        // remove user tokens
447
        err = ua.db.DeleteTokensByUserId(ctx, id)
3✔
448
        if err != nil {
4✔
449
                return errors.Wrap(err, "useradm: failed to delete user tokens")
1✔
450
        }
1✔
451

452
        return nil
3✔
453
}
454

455
func (u *UserAdm) CreateTenant(ctx context.Context, tenant model.NewTenant) error {
2✔
456
        return nil
2✔
457
}
2✔
458

459
func (ua *UserAdm) SetPassword(ctx context.Context, uu model.UserUpdate) error {
2✔
460
        u, err := ua.db.GetUserByEmail(ctx, uu.Email)
2✔
461
        if err != nil {
3✔
462
                return errors.Wrap(err, "useradm: failed to get user by email")
1✔
463

1✔
464
        }
1✔
465
        if u == nil {
3✔
466
                return ErrUserNotFound
1✔
467
        }
1✔
468
        if utils.CheckIfPassSimilarToEmail(u, &uu) {
3✔
469
                return ErrPassAndMailTooSimilar
1✔
470
        }
1✔
471

472
        _, err = ua.db.UpdateUser(ctx, u.ID, &uu)
2✔
473

2✔
474
        // if we changed the password, invalidate the JWT tokens but the one used to update the user
2✔
475
        if err == nil && uu.Password != "" {
4✔
476
                if uu.Token != nil {
3✔
477
                        err = ua.db.DeleteTokensByUserIdExceptCurrentOne(ctx, u.ID, uu.Token.ID)
1✔
478
                } else {
3✔
479
                        err = ua.db.DeleteTokensByUserId(ctx, u.ID)
2✔
480
                }
2✔
481
        }
482
        if err != nil {
3✔
483
                return errors.Wrap(err, "useradm: failed to update user information")
1✔
484
        }
1✔
485

486
        return nil
2✔
487
}
488

489
func (ua *UserAdm) DeleteTokens(ctx context.Context, tenantId, userId string) error {
1✔
490
        ctx = identity.WithContext(ctx, &identity.Identity{
1✔
491
                Tenant: tenantId,
1✔
492
        })
1✔
493

1✔
494
        var err error
1✔
495

1✔
496
        if userId != "" {
2✔
497
                err = ua.db.DeleteTokensByUserId(ctx, userId)
1✔
498
        } else {
2✔
499
                err = ua.db.DeleteTokens(ctx)
1✔
500
        }
1✔
501

502
        if err != nil && err != store.ErrTokenNotFound {
2✔
503
                return errors.Wrapf(
1✔
504
                        err,
1✔
505
                        "failed to delete tokens for tenant: %v, user id: %v",
1✔
506
                        tenantId,
1✔
507
                        userId,
1✔
508
                )
1✔
509
        }
1✔
510

511
        return nil
1✔
512
}
513

514
func (u *UserAdm) IssuePersonalAccessToken(
515
        ctx context.Context,
516
        tr *model.TokenRequest,
517
) (string, error) {
2✔
518
        id := identity.FromContext(ctx)
2✔
519
        if id == nil {
2✔
520
                return "", errors.New("identity not present in the context")
×
521
        }
×
522
        if u.config.LimitTokensPerUser > 0 {
4✔
523
                count, err := u.db.CountPersonalAccessTokens(ctx, id.Subject)
2✔
524
                if err != nil {
3✔
525
                        return "", errors.Wrap(err, "useradm: failed to count personal access tokens")
1✔
526
                }
1✔
527
                if count >= int64(u.config.LimitTokensPerUser) {
4✔
528
                        return "", ErrTooManyTokens
2✔
529
                }
2✔
530
        }
531
        //generate and save token
532
        keyId := common.KeyIdFromPath(u.config.PrivateKeyPath, u.config.PrivateKeyFileNamePattern)
2✔
533
        t, err := u.generateToken(
2✔
534
                id.Subject,
2✔
535
                scope.All,
2✔
536
                id.Tenant,
2✔
537
                tr.ExpiresIn == 0,
2✔
538
                keyId,
2✔
539
        )
2✔
540
        if err != nil {
2✔
541
                return "", errors.Wrap(err, "useradm: failed to generate token")
×
542
        }
×
543
        // update claims
544
        t.TokenName = tr.Name
2✔
545
        if tr.ExpiresIn > 0 {
4✔
546
                expires := jwt.Time{
2✔
547
                        Time: time.Now().Add(time.Second * time.Duration(tr.ExpiresIn)),
2✔
548
                }
2✔
549
                t.ExpiresAt = &expires
2✔
550
        }
2✔
551

552
        err = u.db.SaveToken(ctx, t)
2✔
553
        if err == store.ErrDuplicateTokenName {
4✔
554
                return "", ErrDuplicateTokenName
2✔
555
        } else if err != nil {
5✔
556
                return "", errors.Wrap(err, "useradm: failed to save token")
1✔
557
        }
1✔
558

559
        if _, ok := u.jwtHandlers[keyId]; !ok {
2✔
560
                return "", common.ErrKeyIdNotFound
×
561
        }
×
562
        // sign token
563
        return u.jwtHandlers[keyId].ToJWT(t)
2✔
564
}
565

566
func (ua *UserAdm) GetPersonalAccessTokens(
567
        ctx context.Context,
568
        userID string,
569
) ([]model.PersonalAccessToken, error) {
1✔
570
        tokens, err := ua.db.GetPersonalAccessTokens(ctx, userID)
1✔
571
        if err != nil {
1✔
572
                return nil, errors.Wrap(err, "useradm: failed to get tokens")
×
573
        }
×
574

575
        return tokens, nil
1✔
576
}
577

578
func (ua *UserAdm) DeleteToken(ctx context.Context, id string) error {
1✔
579
        identity := identity.FromContext(ctx)
1✔
580
        if identity == nil {
1✔
581
                return errors.New("identity not present in the context")
×
582
        }
×
583
        err := ua.db.DeleteToken(ctx, oid.FromString(identity.Subject), oid.FromString(id))
1✔
584
        if err != nil {
1✔
585
                return errors.Wrap(err, "useradm: failed to delete token")
×
586
        }
×
587

588
        return nil
1✔
589
}
590

591
func (ua *UserAdm) GetPlans(ctx context.Context, skip, limit int) []model.Plan {
1✔
592
        if (skip + limit) <= len(model.PlanList) {
2✔
593
                return model.PlanList[skip:(skip + limit)]
1✔
594
        } else if skip <= len(model.PlanList) {
3✔
595
                return model.PlanList[skip:]
1✔
596
        }
1✔
597
        return []model.Plan{}
1✔
598
}
599

600
func (ua *UserAdm) GetPlanBinding(ctx context.Context) (*model.PlanBindingDetails, error) {
1✔
601
        if len(model.PlanList) == 1 {
2✔
602
                return &model.PlanBindingDetails{
1✔
603
                        Plan: model.PlanList[0],
1✔
604
                }, nil
1✔
605
        }
1✔
606
        return &model.PlanBindingDetails{}, nil
1✔
607
}
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