From b5022d351df07f7db3d01faec5e4b18475b151ea Mon Sep 17 00:00:00 2001 From: Andrew Burke <31974658+atburke@users.noreply.github.com> Date: Wed, 11 Oct 2023 16:31:18 -0700 Subject: [PATCH] Make system roles case-insensitive in provision tokens (#33260) This change makes system roles set in a provision token case-insensitive. --- api/types/provisioning.go | 11 +++++++---- api/types/provisioning_test.go | 17 +++++++++++++++++ api/types/system_role.go | 23 +++++++++++++++++------ api/types/system_role_test.go | 6 ++++++ 4 files changed, 47 insertions(+), 10 deletions(-) diff --git a/api/types/provisioning.go b/api/types/provisioning.go index 788454f2bcea3..4069ffd41e167 100644 --- a/api/types/provisioning.go +++ b/api/types/provisioning.go @@ -183,15 +183,17 @@ func (p *ProvisionTokenV2) CheckAndSetDefaults() error { if len(p.Spec.Roles) == 0 { return trace.BadParameter("provisioning token is missing roles") } - if err := SystemRoles(p.Spec.Roles).Check(); err != nil { + roles, err := NewTeleportRoles(SystemRoles(p.Spec.Roles).StringSlice()) + if err != nil { return trace.Wrap(err) } + p.Spec.Roles = roles - if SystemRoles(p.Spec.Roles).Include(RoleBot) && p.Spec.BotName == "" { + if roles.Include(RoleBot) && p.Spec.BotName == "" { return trace.BadParameter("token with role %q must set bot_name", RoleBot) } - if p.Spec.BotName != "" && !SystemRoles(p.Spec.Roles).Include(RoleBot) { + if p.Spec.BotName != "" && !roles.Include(RoleBot) { return trace.BadParameter("can only set bot_name on token with role %q", RoleBot) } @@ -324,7 +326,8 @@ func (p *ProvisionTokenV2) GetVersion() string { // that will be granted to the user of the token // in the crendentials func (p *ProvisionTokenV2) GetRoles() SystemRoles { - return p.Spec.Roles + // Ensure that roles are case-insensitive. + return normalizedSystemRoles(SystemRoles(p.Spec.Roles).StringSlice()) } // SetRoles sets teleport roles diff --git a/api/types/provisioning_test.go b/api/types/provisioning_test.go index 2f9925a71e27a..c9d11ca1cf36a 100644 --- a/api/types/provisioning_test.go +++ b/api/types/provisioning_test.go @@ -811,3 +811,20 @@ func TestProvisionTokenV2_GetSafeName(t *testing.T) { require.Equal(t, "12345678", got) }) } + +func TestProvisionTokenV2_CaseInsensitiveRoles(t *testing.T) { + t.Parallel() + t.Run("via constructor", func(t *testing.T) { + tok, err := NewProvisionToken("token", SystemRoles{"nOde", "AuTh"}, time.Now()) + require.NoError(t, err) + require.Equal(t, SystemRoles{RoleNode, RoleAuth}, tok.GetRoles()) + }) + t.Run("via struct", func(t *testing.T) { + tok := &ProvisionTokenV2{ + Spec: ProvisionTokenSpecV2{ + Roles: []SystemRole{"nOdE", "AuTh"}, + }, + } + require.Equal(t, SystemRoles{RoleNode, RoleAuth}, tok.GetRoles()) + }) +} diff --git a/api/types/system_role.go b/api/types/system_role.go index 6cf61fd6918c0..1de865f7aecca 100644 --- a/api/types/system_role.go +++ b/api/types/system_role.go @@ -105,6 +105,21 @@ var roleMappings = map[string]SystemRole{ "mdm": RoleMDM, } +func normalizedSystemRole(s string) SystemRole { + if role, ok := roleMappings[strings.ToLower(strings.TrimSpace(s))]; ok { + return role + } + return SystemRole(s) +} + +func normalizedSystemRoles(s []string) []SystemRole { + roles := make([]SystemRole, 0, len(s)) + for _, role := range s { + roles = append(roles, normalizedSystemRole(role)) + } + return roles +} + // localServiceMappings is the subset of role mappings which happen to be true // teleport services (e.g. db, kube, etc), excluding those which represent remote // services (i.e. remoteproxy). @@ -141,10 +156,7 @@ func LocalServiceMappings() SystemRoles { // NewTeleportRoles return a list of teleport roles from slice of strings func NewTeleportRoles(in []string) (SystemRoles, error) { - var roles SystemRoles - for _, val := range in { - roles = append(roles, SystemRole(val)) - } + roles := SystemRoles(normalizedSystemRoles(in)) return roles, roles.Check() } @@ -153,8 +165,7 @@ func NewTeleportRoles(in []string) (SystemRoles, error) { func ParseTeleportRoles(str string) (SystemRoles, error) { var roles SystemRoles for _, s := range strings.Split(str, ",") { - cleaned := strings.ToLower(strings.TrimSpace(s)) - if r, ok := roleMappings[cleaned]; ok && r.Check() == nil { + if r := normalizedSystemRole(s); r.Check() == nil { roles = append(roles, r) continue } diff --git a/api/types/system_role_test.go b/api/types/system_role_test.go index 0425ca33484f9..d62f3be5ccdb8 100644 --- a/api/types/system_role_test.go +++ b/api/types/system_role_test.go @@ -84,6 +84,12 @@ func TestParseTeleportRoles(t *testing.T) { out: SystemRoles{RoleNode}, wantErr: false, }, + { + // case insensitive + in: "nOdE,prOXY", + out: SystemRoles{RoleNode, RoleProxy}, + wantErr: false, + }, { in: "windowsdesktop", out: SystemRoles{RoleWindowsDesktop},