Skip to content

Commit b71fa70

Browse files
committed
Refactor token create/update validation
1 parent fbaec55 commit b71fa70

File tree

2 files changed

+136
-114
lines changed

2 files changed

+136
-114
lines changed

pkg/service/token/token-service.go

Lines changed: 53 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,12 @@ package token
33
import (
44
"context"
55
"errors"
6-
"fmt"
76
"log/slog"
87
"slices"
98
"time"
109

1110
apiv2 "github.com/metal-stack/api/go/metalstack/api/v2"
1211
"github.com/metal-stack/api/go/metalstack/api/v2/apiv2connect"
13-
"github.com/metal-stack/api/go/permissions"
1412
"github.com/metal-stack/metal-apiserver/pkg/errorutil"
1513
"github.com/metal-stack/metal-apiserver/pkg/request"
1614

@@ -33,14 +31,14 @@ type Config struct {
3331
}
3432

3533
type tokenService struct {
36-
issuer string
37-
adminSubjects []string
38-
tokens tokenutil.TokenStore
39-
certs certs.CertStore
40-
log *slog.Logger
41-
servicePermissions *permissions.ServicePermissions
34+
issuer string
35+
adminSubjects []string
36+
tokens tokenutil.TokenStore
37+
certs certs.CertStore
38+
log *slog.Logger
4239

4340
projectsAndTenantsGetter func(ctx context.Context, userId string) (*repository.ProjectsAndTenants, error)
41+
authorizer request.Authorizer
4442
}
4543

4644
type TokenService interface {
@@ -51,19 +49,20 @@ type TokenService interface {
5149
}
5250

5351
func New(c Config) TokenService {
54-
servicePermissions := permissions.GetServicePermissions()
52+
projectsAndTenantsGetter := func(ctx context.Context, userId string) (*repository.ProjectsAndTenants, error) {
53+
return c.Repo.UnscopedProject().AdditionalMethods().GetProjectsAndTenants(ctx, userId)
54+
}
55+
log := c.Log.WithGroup("tokenService")
5556

5657
return &tokenService{
57-
tokens: c.TokenStore,
58-
certs: c.CertStore,
59-
issuer: c.Issuer,
60-
log: c.Log.WithGroup("tokenService"),
61-
servicePermissions: servicePermissions,
62-
adminSubjects: c.AdminSubjects,
58+
tokens: c.TokenStore,
59+
certs: c.CertStore,
60+
issuer: c.Issuer,
61+
log: log,
62+
adminSubjects: c.AdminSubjects,
6363

64-
projectsAndTenantsGetter: func(ctx context.Context, userId string) (*repository.ProjectsAndTenants, error) {
65-
return c.Repo.UnscopedProject().AdditionalMethods().GetProjectsAndTenants(ctx, userId)
66-
},
64+
projectsAndTenantsGetter: projectsAndTenantsGetter,
65+
authorizer: request.NewAuthorizer(log, projectsAndTenantsGetter),
6766
}
6867
}
6968

@@ -119,6 +118,7 @@ func (t *tokenService) CreateApiTokenWithoutPermissionCheck(ctx context.Context,
119118
token.ProjectRoles = req.ProjectRoles
120119
token.TenantRoles = req.TenantRoles
121120
token.AdminRole = req.AdminRole
121+
token.InfraRole = req.InfraRole
122122

123123
err = t.tokens.Set(ctx, token)
124124
if err != nil {
@@ -167,9 +167,10 @@ func (t *tokenService) Update(ctx context.Context, req *apiv2.TokenServiceUpdate
167167
ProjectRoles: req.ProjectRoles,
168168
TenantRoles: req.TenantRoles,
169169
AdminRole: req.AdminRole,
170+
InfraRole: req.InfraRole,
170171
}
171172

172-
err := t.validateCreate(ctx, token, createRequest)
173+
err := t.validateTokenRequest(ctx, token, createRequest)
173174
if err != nil {
174175
return nil, errorutil.NewPermissionDenied(err)
175176
}
@@ -187,11 +188,12 @@ func (t *tokenService) Update(ctx context.Context, req *apiv2.TokenServiceUpdate
187188
ProjectRoles: projectsAndTenants.ProjectRoles,
188189
TenantRoles: projectsAndTenants.TenantRoles,
189190
AdminRole: nil,
191+
InfraRole: token.InfraRole,
190192
}
191193
if slices.Contains(t.adminSubjects, token.User) {
192194
fullUserToken.AdminRole = apiv2.AdminRole_ADMIN_ROLE_EDITOR.Enum()
193195
}
194-
err = t.validateCreate(ctx, fullUserToken, createRequest)
196+
err = t.validateTokenRequest(ctx, fullUserToken, createRequest)
195197
if err != nil {
196198
return nil, errorutil.PermissionDenied("outdated token: %w", err)
197199
}
@@ -254,7 +256,7 @@ func (t *tokenService) CreateTokenForUser(ctx context.Context, user *string, req
254256
// we first validate token permission elevation for the token used in the token create request,
255257
// which might be an API token with restricted permissions
256258

257-
err := t.validateCreate(ctx, token, req)
259+
err := t.validateTokenRequest(ctx, token, req)
258260
if err != nil {
259261
return nil, errorutil.NewPermissionDenied(err)
260262
}
@@ -272,6 +274,7 @@ func (t *tokenService) CreateTokenForUser(ctx context.Context, user *string, req
272274
ProjectRoles: projectsAndTenants.ProjectRoles,
273275
TenantRoles: projectsAndTenants.TenantRoles,
274276
AdminRole: nil,
277+
InfraRole: token.InfraRole,
275278
}
276279

277280
tokenUser := token.GetUser()
@@ -288,7 +291,7 @@ func (t *tokenService) CreateTokenForUser(ctx context.Context, user *string, req
288291
tokenUser = *user
289292
}
290293

291-
err = t.validateCreate(ctx, fullUserToken, req)
294+
err = t.validateTokenRequest(ctx, fullUserToken, req)
292295
if err != nil {
293296
return nil, errorutil.PermissionDenied("outdated token: %w", err)
294297
}
@@ -308,6 +311,7 @@ func (t *tokenService) CreateTokenForUser(ctx context.Context, user *string, req
308311
token.ProjectRoles = req.ProjectRoles
309312
token.TenantRoles = req.TenantRoles
310313
token.AdminRole = req.AdminRole
314+
token.InfraRole = req.InfraRole
311315

312316
err = t.tokens.Set(ctx, token)
313317
if err != nil {
@@ -374,9 +378,10 @@ func (t *tokenService) Refresh(ctx context.Context, _ *apiv2.TokenServiceRefresh
374378
ProjectRoles: oldtoken.ProjectRoles,
375379
TenantRoles: oldtoken.TenantRoles,
376380
AdminRole: oldtoken.AdminRole,
381+
InfraRole: oldtoken.InfraRole,
377382
}
378383

379-
err = t.validateCreate(ctx, token, createRequest)
384+
err = t.validateTokenRequest(ctx, token, createRequest)
380385
if err != nil {
381386
return nil, errorutil.NewPermissionDenied(err)
382387
}
@@ -394,11 +399,12 @@ func (t *tokenService) Refresh(ctx context.Context, _ *apiv2.TokenServiceRefresh
394399
ProjectRoles: projectsAndTenants.ProjectRoles,
395400
TenantRoles: projectsAndTenants.TenantRoles,
396401
AdminRole: nil,
402+
InfraRole: token.InfraRole,
397403
}
398404
if slices.Contains(t.adminSubjects, token.User) {
399405
fullUserToken.AdminRole = apiv2.AdminRole_ADMIN_ROLE_EDITOR.Enum()
400406
}
401-
err = t.validateCreate(ctx, fullUserToken, createRequest)
407+
err = t.validateTokenRequest(ctx, fullUserToken, createRequest)
402408
if err != nil {
403409
return nil, errorutil.PermissionDenied("outdated token: %w", err)
404410
}
@@ -423,6 +429,7 @@ func (t *tokenService) Refresh(ctx context.Context, _ *apiv2.TokenServiceRefresh
423429
newToken.ProjectRoles = oldtoken.ProjectRoles
424430
newToken.TenantRoles = oldtoken.TenantRoles
425431
newToken.AdminRole = oldtoken.AdminRole
432+
newToken.InfraRole = oldtoken.InfraRole
426433

427434
err = t.tokens.Set(ctx, newToken)
428435
if err != nil {
@@ -435,29 +442,39 @@ func (t *tokenService) Refresh(ctx context.Context, _ *apiv2.TokenServiceRefresh
435442
}, nil
436443
}
437444

438-
type TokenRequest interface {
445+
type tokenRequest interface {
439446
GetPermissions() []*apiv2.MethodPermission
440447
GetProjectRoles() map[string]apiv2.ProjectRole
441448
GetTenantRoles() map[string]apiv2.TenantRole
442449
GetAdminRole() apiv2.AdminRole
443450
GetInfraRole() apiv2.InfraRole
444451
}
445452

446-
func (t *tokenService) validateCreate(ctx context.Context, currentToken *apiv2.Token, req TokenRequest) error {
447-
authorizer := request.NewAuthorizer(t.log, t.projectsAndTenantsGetter)
448-
449-
currentPermission, err := authorizer.TokenPermissions(ctx, currentToken)
453+
func (t *tokenService) validateTokenRequest(ctx context.Context, currentToken *apiv2.Token, req tokenRequest) error {
454+
currentPermission, err := t.authorizer.TokenPermissions(ctx, currentToken)
450455
if err != nil {
451456
return err
452457
}
453458

454-
requestedPermissions, err := authorizer.TokenPermissions(ctx, &apiv2.Token{
459+
var (
460+
adminRole *apiv2.AdminRole
461+
infraRole *apiv2.InfraRole
462+
)
463+
464+
if req.GetAdminRole() != apiv2.AdminRole_ADMIN_ROLE_UNSPECIFIED {
465+
adminRole = req.GetAdminRole().Enum()
466+
}
467+
if req.GetInfraRole() != apiv2.InfraRole_INFRA_ROLE_UNSPECIFIED {
468+
infraRole = req.GetInfraRole().Enum()
469+
}
470+
471+
requestedPermissions, err := t.authorizer.TokenPermissions(ctx, &apiv2.Token{
455472
User: currentToken.User,
456473
Permissions: req.GetPermissions(),
457474
ProjectRoles: req.GetProjectRoles(),
458475
TenantRoles: req.GetTenantRoles(),
459-
AdminRole: req.GetAdminRole().Enum(),
460-
InfraRole: req.GetInfraRole().Enum(),
476+
AdminRole: adminRole,
477+
InfraRole: infraRole,
461478
})
462479
if err != nil {
463480
return err
@@ -466,18 +483,19 @@ func (t *tokenService) validateCreate(ctx context.Context, currentToken *apiv2.T
466483
for method, subjects := range requestedPermissions {
467484
currentSubjects, ok := currentPermission[method]
468485
if !ok {
469-
return fmt.Errorf("requested method %q is not allowed in your current token", method)
486+
return errors.New("requested methods are not allowed with your current token")
470487
}
471488

472-
if _, ok := subjects["*"]; ok {
489+
if _, ok := currentSubjects["*"]; ok {
473490
continue
474491
}
475492

476493
for subject := range subjects {
477494
if _, ok := currentSubjects[subject]; !ok {
478-
return fmt.Errorf("requested subject %q on method %q is not allowed in your current token", subject, method)
495+
return errors.New("requested subjects are not allowed with your current token")
479496
}
480497
}
481498
}
499+
482500
return nil
483501
}

0 commit comments

Comments
 (0)