Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ require (
github.com/klauspost/connect-compress/v2 v2.1.0
github.com/lestrrat-go/jwx/v2 v2.1.6
github.com/markbates/goth v1.82.0
github.com/metal-stack/api v0.0.35
github.com/metal-stack/api v0.0.36-0.20251204145649-7e54b95637f8
github.com/metal-stack/go-ipam v1.14.13
github.com/metal-stack/masterdata-api v0.13.0
github.com/metal-stack/metal-lib v0.23.5
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -313,8 +313,8 @@ github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mdelapenya/tlscert v0.2.0 h1:7H81W6Z/4weDvZBNOfQte5GpIMo0lGYEeWbkGp5LJHI=
github.com/mdelapenya/tlscert v0.2.0/go.mod h1:O4njj3ELLnJjGdkN7M/vIVCpZ+Cf0L6muqOG4tLSl8o=
github.com/metal-stack/api v0.0.35 h1:XxxYKTscSeYJg/ftL519nY3FAZ01atPeyD7+Zz/amQQ=
github.com/metal-stack/api v0.0.35/go.mod h1:EBwS/oZr5tIcnV6hM7iK4aBQrw4wlU7vF5p+O1p3YIU=
github.com/metal-stack/api v0.0.36-0.20251204145649-7e54b95637f8 h1:Mxfj78U/Zew4v7p/DnAVOTsjOd2Umk4XrVkFVucFQFM=
github.com/metal-stack/api v0.0.36-0.20251204145649-7e54b95637f8/go.mod h1:EBwS/oZr5tIcnV6hM7iK4aBQrw4wlU7vF5p+O1p3YIU=
github.com/metal-stack/go-ipam v1.14.13 h1:/W5/MDBX5EU18xNDjlBvV6JjQ1Ot12dO2WxLvV6S8vc=
github.com/metal-stack/go-ipam v1.14.13/go.mod h1:eif3UGUFP7CWJdrgLIOjhVM3G2K19GN8lhCgPVfvLDs=
github.com/metal-stack/masterdata-api v0.13.0 h1:1AxnsiWiTMstjMsphZ0wMFT7aW3QQhAMumXnQ81PtHk=
Expand Down
116 changes: 79 additions & 37 deletions pkg/service/token/token-service.go
Original file line number Diff line number Diff line change
Expand Up @@ -449,10 +449,10 @@ type tokenRequest interface {
}

func (t *tokenService) validateTokenRequest(ctx context.Context, currentToken *apiv2.Token, req tokenRequest) error {
currentPermission, err := t.authorizer.TokenPermissions(ctx, currentToken)
if err != nil {
return err
}
// currentPermission, err := t.authorizer.TokenPermissions(ctx, currentToken)
// if err != nil {
// return err
// }

var (
adminRole *apiv2.AdminRole
Expand All @@ -466,14 +466,14 @@ func (t *tokenService) validateTokenRequest(ctx context.Context, currentToken *a
infraRole = req.GetInfraRole().Enum()
}

for _, tr := range req.GetTenantRoles() {
for tenant, tr := range req.GetTenantRoles() {
if tr == apiv2.TenantRole_TENANT_ROLE_UNSPECIFIED {
return fmt.Errorf("requested tenant role: %q is not allowed", tr)
return fmt.Errorf("requested tenant role %q for tenant %s is not allowed", tr, tenant)
}
}
for _, pr := range req.GetProjectRoles() {
for proj, pr := range req.GetProjectRoles() {
if pr == apiv2.ProjectRole_PROJECT_ROLE_UNSPECIFIED {
return fmt.Errorf("requested project role: %q is not allowed", pr)
return fmt.Errorf("requested project role %q for project %s is not allowed", pr, proj)
}
}
for _, permission := range req.GetPermissions() {
Expand All @@ -485,40 +485,82 @@ func (t *tokenService) validateTokenRequest(ctx context.Context, currentToken *a

}

requestedPermissions, err := t.authorizer.TokenPermissions(ctx, &apiv2.Token{
User: currentToken.User,
Permissions: req.GetPermissions(),
ProjectRoles: req.GetProjectRoles(),
TenantRoles: req.GetTenantRoles(),
AdminRole: adminRole,
InfraRole: infraRole,
})
if err != nil {
return err
// requestedPermissions, err := t.authorizer.TokenPermissions(ctx, &apiv2.Token{
// User: currentToken.User,
// Permissions: req.GetPermissions(),
// ProjectRoles: req.GetProjectRoles(),
// TenantRoles: req.GetTenantRoles(),
// AdminRole: adminRole,
// InfraRole: infraRole,
// })
// if err != nil {
// return err
// }

if !isAdminRoleAllowed(adminRole, currentToken.AdminRole) {
return fmt.Errorf("requested admin role %q is not allowed with your current token", adminRole)
}

for method, subjects := range requestedPermissions {
currentSubjects, ok := currentPermission[method]
if !ok {
return errors.New("requested methods are not allowed with your current token")
}
if !isInfraRoleAllowed(infraRole, currentToken.InfraRole) {
return fmt.Errorf("requested infra role %q is not allowed with your current token", infraRole)
}

if _, ok := currentSubjects["*"]; ok {
continue
}
// It is possible to request any subjects to be able to have a token
// which is able to make calls to projects which will be created in the future.
// The actually possible subjects are calculated at request time.
if _, ok := subjects["*"]; ok {
continue
}
deniedProjRoles := deniedProjectRoles(req.GetProjectRoles(), currentToken)
if len(deniedProjRoles) > 0 {
return fmt.Errorf("requested project roles %v are not allowed with you curret token", deniedProjRoles)
}

for subject := range subjects {
if _, ok := currentSubjects[subject]; !ok {
return errors.New("requested subjects are not allowed with your current token")
}
// TODO: collect all errors and return them joined

// CONTINUE: check that
// - each project or tenant scoped method that is requested is allowed for the project/tenant it was requested for
// - each admin or infra method is allowed by the current token
// - each tenant role requested is allowed for the tenant it was requested for
// - each subject requested is part of current token

return nil
}

func isAdminRoleAllowed(requestedRole, tokenRole *apiv2.AdminRole) bool {
if requestedRole == nil {
return true
}
if *requestedRole == apiv2.AdminRole_ADMIN_ROLE_UNSPECIFIED {
return false
}
if tokenRole == nil {
return false
}
return roleIsLowerOrEqual(requestedRole.String(), tokenRole.String(), permissions.GetServicePermissions().Roles.Admin)
}

func isInfraRoleAllowed(requestedRole, tokenRole *apiv2.InfraRole) bool {
if requestedRole == nil {
return true
}
if *requestedRole == apiv2.InfraRole_INFRA_ROLE_UNSPECIFIED {
return false
}
if tokenRole == nil {
return false
}
return roleIsLowerOrEqual(requestedRole.String(), tokenRole.String(), permissions.GetServicePermissions().Roles.Infra)
}

func deniedProjectRoles(requestedRoles map[string]apiv2.ProjectRole, token *apiv2.Token) map[string]apiv2.ProjectRole {
deniedRoles := make(map[string]apiv2.ProjectRole)
for proj, role := range requestedRoles {
tokenRole, ok := token.ProjectRoles[proj]
if !ok {
deniedRoles[proj] = role
}
if !roleIsLowerOrEqual(role.String(), tokenRole.String(), permissions.GetServicePermissions().Roles.Project) {
deniedRoles[proj] = role
}
}
return deniedRoles
}

return nil
func roleIsLowerOrEqual(requestedRole, tokenRole string, roleMethods map[string][]string) bool {
return len(roleMethods[requestedRole]) <= len(roleMethods[tokenRole])
}
Loading