diff --git a/api/types/authentication.go b/api/types/authentication.go index 24f125e5db398..cee98c5c7d02b 100644 --- a/api/types/authentication.go +++ b/api/types/authentication.go @@ -953,8 +953,27 @@ func (r *RequireMFAType) decode(val interface{}) error { } else { *r = RequireMFAType_OFF } + case int32: + return trace.Wrap(r.setFromEnum(v)) + case int64: + return trace.Wrap(r.setFromEnum(int32(v))) + case int: + return trace.Wrap(r.setFromEnum(int32(v))) + case float64: + return trace.Wrap(r.setFromEnum(int32(v))) + case float32: + return trace.Wrap(r.setFromEnum(int32(v))) default: return trace.BadParameter("RequireMFAType invalid type %T", val) } return nil } + +// setFromEnum sets the value from enum value as int32. +func (r *RequireMFAType) setFromEnum(val int32) error { + if _, ok := RequireMFAType_name[val]; !ok { + return trace.BadParameter("invalid required mfa mode %v", val) + } + *r = RequireMFAType(val) + return nil +} diff --git a/api/types/extension.go b/api/types/extension.go index e44cc4a95f02b..fcfb5ef78a74a 100644 --- a/api/types/extension.go +++ b/api/types/extension.go @@ -39,16 +39,40 @@ func (t CertExtensionType) MarshalJSON() ([]byte, error) { } func (t *CertExtensionType) UnmarshalJSON(b []byte) error { - var stringVal string - if err := json.Unmarshal(b, &stringVal); err != nil { + var anyVal any + if err := json.Unmarshal(b, &anyVal); err != nil { return err } - val, ok := certExtensionTypeValue[stringVal] - if !ok { - return trace.Errorf("invalid certificate extension type: %q", string(b)) + switch val := anyVal.(type) { + case string: + enumVal, ok := certExtensionTypeValue[val] + if !ok { + return trace.Errorf("invalid certificate extension type: %q", string(b)) + } + *t = enumVal + return nil + case int32: + return t.setFromEnum(val) + case int: + return t.setFromEnum(int32(val)) + case int64: + return t.setFromEnum(int32(val)) + case float64: + return trace.Wrap(t.setFromEnum(int32(val))) + case float32: + return trace.Wrap(t.setFromEnum(int32(val))) + default: + return trace.BadParameter("unexpected type %T", val) + } +} + +// setFromEnum sets the value from enum value as int32. +func (t *CertExtensionType) setFromEnum(val int32) error { + if _, ok := CertExtensionType_name[val]; !ok { + return trace.BadParameter("invalid cert extension mode %v", val) } - *t = val + *t = CertExtensionType(val) return nil } @@ -69,14 +93,38 @@ func (t CertExtensionMode) MarshalJSON() ([]byte, error) { } func (t *CertExtensionMode) UnmarshalJSON(b []byte) error { - var stringVal string - if err := json.Unmarshal(b, &stringVal); err != nil { + var anyVal any + if err := json.Unmarshal(b, &anyVal); err != nil { return err } - val, ok := certExtensionModeValue[stringVal] - if !ok { - return trace.Errorf("invalid certificate extension mode: %q", string(b)) + switch val := anyVal.(type) { + case string: + enumVal, ok := certExtensionModeValue[val] + if !ok { + return trace.Errorf("invalid certificate extension mode: %q", string(b)) + } + *t = enumVal + return nil + case int32: + return t.setFromEnum(val) + case int: + return t.setFromEnum(int32(val)) + case int64: + return t.setFromEnum(int32(val)) + case float64: + return trace.Wrap(t.setFromEnum(int32(val))) + case float32: + return trace.Wrap(t.setFromEnum(int32(val))) + default: + return trace.BadParameter("unexpected type %T", val) + } +} + +// setFromEnum sets the value from enum value as int32. +func (t *CertExtensionMode) setFromEnum(val int32) error { + if _, ok := CertExtensionMode_name[val]; !ok { + return trace.BadParameter("invalid cert extension mode %v", val) } - *t = val + *t = CertExtensionMode(val) return nil } diff --git a/api/types/role.go b/api/types/role.go index 0db2dc2a97a21..358b42930bddb 100644 --- a/api/types/role.go +++ b/api/types/role.go @@ -1828,6 +1828,16 @@ func (h CreateHostUserMode) encode() (string, error) { func (h *CreateHostUserMode) decode(val any) error { var valS string switch val := val.(type) { + case int32: + return trace.Wrap(h.setFromEnum(val)) + case int64: + return trace.Wrap(h.setFromEnum(int32(val))) + case int: + return trace.Wrap(h.setFromEnum(int32(val))) + case float64: + return trace.Wrap(h.setFromEnum(int32(val))) + case float32: + return trace.Wrap(h.setFromEnum(int32(val))) case string: valS = val case bool: @@ -1836,7 +1846,7 @@ func (h *CreateHostUserMode) decode(val any) error { } valS = createHostUserModeOffString default: - return trace.BadParameter("bad value type %T, expected string", val) + return trace.BadParameter("bad value type %T, expected string or int", val) } switch valS { @@ -1854,6 +1864,15 @@ func (h *CreateHostUserMode) decode(val any) error { return nil } +// setFromEnum sets the value from enum value as int32. +func (h *CreateHostUserMode) setFromEnum(val int32) error { + if _, ok := CreateHostUserMode_name[val]; !ok { + return trace.BadParameter("invalid host user mode %v", val) + } + *h = CreateHostUserMode(val) + return nil +} + // UnmarshalYAML supports parsing CreateHostUserMode from string. func (h *CreateHostUserMode) UnmarshalYAML(unmarshal func(interface{}) error) error { var val interface{} diff --git a/api/types/role_test.go b/api/types/role_test.go index 946817147cc73..4093ca3f2c2e7 100644 --- a/api/types/role_test.go +++ b/api/types/role_test.go @@ -400,15 +400,17 @@ func TestMarshallCreateHostUserModeYAML(t *testing.T) { func TestUnmarshallCreateHostUserModeJSON(t *testing.T) { for _, tc := range []struct { expected CreateHostUserMode - input string + input any }{ - {expected: CreateHostUserMode_HOST_USER_MODE_OFF, input: "off"}, - {expected: CreateHostUserMode_HOST_USER_MODE_UNSPECIFIED, input: ""}, - {expected: CreateHostUserMode_HOST_USER_MODE_DROP, input: "drop"}, - {expected: CreateHostUserMode_HOST_USER_MODE_KEEP, input: "keep"}, + {expected: CreateHostUserMode_HOST_USER_MODE_OFF, input: "\"off\""}, + {expected: CreateHostUserMode_HOST_USER_MODE_UNSPECIFIED, input: "\"\""}, + {expected: CreateHostUserMode_HOST_USER_MODE_DROP, input: "\"drop\""}, + {expected: CreateHostUserMode_HOST_USER_MODE_KEEP, input: "\"keep\""}, + {expected: CreateHostUserMode_HOST_USER_MODE_KEEP, input: 3}, + {expected: CreateHostUserMode_HOST_USER_MODE_OFF, input: 1}, } { var got CreateHostUserMode - err := json.Unmarshal([]byte(fmt.Sprintf("%q", tc.input)), &got) + err := json.Unmarshal([]byte(fmt.Sprintf("%v", tc.input)), &got) require.NoError(t, err) require.Equal(t, tc.expected, got) } diff --git a/examples/chart/teleport-cluster/charts/teleport-operator/templates/resources.teleport.dev_roles.yaml b/examples/chart/teleport-cluster/charts/teleport-operator/templates/resources.teleport.dev_roles.yaml index c3e75bce3fc3d..e41e29a6605df 100644 --- a/examples/chart/teleport-cluster/charts/teleport-operator/templates/resources.teleport.dev_roles.yaml +++ b/examples/chart/teleport-cluster/charts/teleport-operator/templates/resources.teleport.dev_roles.yaml @@ -963,8 +963,7 @@ spec: mode: description: Mode is the type of extension to be used -- currently critical-option is not supported - format: int32 - type: integer + x-kubernetes-int-or-string: true name: description: Name specifies the key to be used in the cert extension. @@ -972,8 +971,7 @@ spec: type: description: Type represents the certificate type being extended, only ssh is supported at this time. - format: int32 - type: integer + x-kubernetes-int-or-string: true value: description: Value specifies the value to be used in the cert extension. @@ -1006,8 +1004,7 @@ spec: create_host_user_mode: description: CreateHostUserMode allows users to be automatically created on a host when not set to off - format: int32 - type: integer + x-kubernetes-int-or-string: true desktop_clipboard: description: DesktopClipboard indicates whether clipboard sharing is allowed between the user's workstation and the remote desktop. @@ -1118,8 +1115,7 @@ spec: require_session_mfa: description: RequireMFAType is the type of MFA requirement enforced for this user. - format: int32 - type: integer + x-kubernetes-int-or-string: true ssh_file_copy: description: SSHFileCopy indicates whether remote file operations via SCP or SFTP are allowed over an SSH session. It defaults @@ -2160,8 +2156,7 @@ spec: mode: description: Mode is the type of extension to be used -- currently critical-option is not supported - format: int32 - type: integer + x-kubernetes-int-or-string: true name: description: Name specifies the key to be used in the cert extension. @@ -2169,8 +2164,7 @@ spec: type: description: Type represents the certificate type being extended, only ssh is supported at this time. - format: int32 - type: integer + x-kubernetes-int-or-string: true value: description: Value specifies the value to be used in the cert extension. @@ -2203,8 +2197,7 @@ spec: create_host_user_mode: description: CreateHostUserMode allows users to be automatically created on a host when not set to off - format: int32 - type: integer + x-kubernetes-int-or-string: true desktop_clipboard: description: DesktopClipboard indicates whether clipboard sharing is allowed between the user's workstation and the remote desktop. @@ -2315,8 +2308,7 @@ spec: require_session_mfa: description: RequireMFAType is the type of MFA requirement enforced for this user. - format: int32 - type: integer + x-kubernetes-int-or-string: true ssh_file_copy: description: SSHFileCopy indicates whether remote file operations via SCP or SFTP are allowed over an SSH session. It defaults diff --git a/integrations/operator/config/crd/bases/resources.teleport.dev_roles.yaml b/integrations/operator/config/crd/bases/resources.teleport.dev_roles.yaml index f88328a2e3ea9..ac49f4b375dad 100644 --- a/integrations/operator/config/crd/bases/resources.teleport.dev_roles.yaml +++ b/integrations/operator/config/crd/bases/resources.teleport.dev_roles.yaml @@ -963,8 +963,7 @@ spec: mode: description: Mode is the type of extension to be used -- currently critical-option is not supported - format: int32 - type: integer + x-kubernetes-int-or-string: true name: description: Name specifies the key to be used in the cert extension. @@ -972,8 +971,7 @@ spec: type: description: Type represents the certificate type being extended, only ssh is supported at this time. - format: int32 - type: integer + x-kubernetes-int-or-string: true value: description: Value specifies the value to be used in the cert extension. @@ -1006,8 +1004,7 @@ spec: create_host_user_mode: description: CreateHostUserMode allows users to be automatically created on a host when not set to off - format: int32 - type: integer + x-kubernetes-int-or-string: true desktop_clipboard: description: DesktopClipboard indicates whether clipboard sharing is allowed between the user's workstation and the remote desktop. @@ -1118,8 +1115,7 @@ spec: require_session_mfa: description: RequireMFAType is the type of MFA requirement enforced for this user. - format: int32 - type: integer + x-kubernetes-int-or-string: true ssh_file_copy: description: SSHFileCopy indicates whether remote file operations via SCP or SFTP are allowed over an SSH session. It defaults @@ -2160,8 +2156,7 @@ spec: mode: description: Mode is the type of extension to be used -- currently critical-option is not supported - format: int32 - type: integer + x-kubernetes-int-or-string: true name: description: Name specifies the key to be used in the cert extension. @@ -2169,8 +2164,7 @@ spec: type: description: Type represents the certificate type being extended, only ssh is supported at this time. - format: int32 - type: integer + x-kubernetes-int-or-string: true value: description: Value specifies the value to be used in the cert extension. @@ -2203,8 +2197,7 @@ spec: create_host_user_mode: description: CreateHostUserMode allows users to be automatically created on a host when not set to off - format: int32 - type: integer + x-kubernetes-int-or-string: true desktop_clipboard: description: DesktopClipboard indicates whether clipboard sharing is allowed between the user's workstation and the remote desktop. @@ -2315,8 +2308,7 @@ spec: require_session_mfa: description: RequireMFAType is the type of MFA requirement enforced for this user. - format: int32 - type: integer + x-kubernetes-int-or-string: true ssh_file_copy: description: SSHFileCopy indicates whether remote file operations via SCP or SFTP are allowed over an SSH session. It defaults diff --git a/integrations/operator/controllers/resources/role_controller_test.go b/integrations/operator/controllers/resources/role_controller_test.go index d5744415c9018..34f739a8a3721 100644 --- a/integrations/operator/controllers/resources/role_controller_test.go +++ b/integrations/operator/controllers/resources/role_controller_test.go @@ -82,6 +82,26 @@ func TestRoleCreationFromYAML(t *testing.T) { shouldFail bool expectedSpec *types.RoleSpecV6 }{ + { + name: "Valid login list with integer create_host_user_mode", + roleSpecYAML: ` +allow: + logins: + - ubuntu + - root +options: + create_host_user_mode: 2 +`, + shouldFail: false, + expectedSpec: &types.RoleSpecV6{ + Allow: types.RoleConditions{ + Logins: []string{"ubuntu", "root"}, + }, + Options: types.RoleOptions{ + CreateHostUserMode: types.CreateHostUserMode_HOST_USER_MODE_DROP, + }, + }, + }, { name: "Valid login list", roleSpecYAML: ` @@ -89,12 +109,17 @@ allow: logins: - ubuntu - root +options: + create_host_user_mode: keep `, shouldFail: false, expectedSpec: &types.RoleSpecV6{ Allow: types.RoleConditions{ Logins: []string{"ubuntu", "root"}, }, + Options: types.RoleOptions{ + CreateHostUserMode: types.CreateHostUserMode_HOST_USER_MODE_KEEP, + }, }, }, { diff --git a/integrations/operator/crdgen/schemagen.go b/integrations/operator/crdgen/schemagen.go index d461da84f9556..ed633faf25ec8 100644 --- a/integrations/operator/crdgen/schemagen.go +++ b/integrations/operator/crdgen/schemagen.go @@ -297,9 +297,11 @@ func (generator *SchemaGenerator) singularProp(field *Field, prop *apiextv1.JSON case field.IsTime(): prop.Type = "string" prop.Format = "date-time" - case field.IsInt32() || field.IsUint32() || field.desc.IsEnum(): + case field.IsInt32() || field.IsUint32(): prop.Type = "integer" prop.Format = "int32" + case field.desc.IsEnum(): + prop.XIntOrString = true case field.IsInt64() || field.IsUint64(): prop.Type = "integer" prop.Format = "int64" diff --git a/integrations/operator/crdgen/testdata/golden/resources.teleport.dev_roles.yaml b/integrations/operator/crdgen/testdata/golden/resources.teleport.dev_roles.yaml index f88328a2e3ea9..ac49f4b375dad 100644 --- a/integrations/operator/crdgen/testdata/golden/resources.teleport.dev_roles.yaml +++ b/integrations/operator/crdgen/testdata/golden/resources.teleport.dev_roles.yaml @@ -963,8 +963,7 @@ spec: mode: description: Mode is the type of extension to be used -- currently critical-option is not supported - format: int32 - type: integer + x-kubernetes-int-or-string: true name: description: Name specifies the key to be used in the cert extension. @@ -972,8 +971,7 @@ spec: type: description: Type represents the certificate type being extended, only ssh is supported at this time. - format: int32 - type: integer + x-kubernetes-int-or-string: true value: description: Value specifies the value to be used in the cert extension. @@ -1006,8 +1004,7 @@ spec: create_host_user_mode: description: CreateHostUserMode allows users to be automatically created on a host when not set to off - format: int32 - type: integer + x-kubernetes-int-or-string: true desktop_clipboard: description: DesktopClipboard indicates whether clipboard sharing is allowed between the user's workstation and the remote desktop. @@ -1118,8 +1115,7 @@ spec: require_session_mfa: description: RequireMFAType is the type of MFA requirement enforced for this user. - format: int32 - type: integer + x-kubernetes-int-or-string: true ssh_file_copy: description: SSHFileCopy indicates whether remote file operations via SCP or SFTP are allowed over an SSH session. It defaults @@ -2160,8 +2156,7 @@ spec: mode: description: Mode is the type of extension to be used -- currently critical-option is not supported - format: int32 - type: integer + x-kubernetes-int-or-string: true name: description: Name specifies the key to be used in the cert extension. @@ -2169,8 +2164,7 @@ spec: type: description: Type represents the certificate type being extended, only ssh is supported at this time. - format: int32 - type: integer + x-kubernetes-int-or-string: true value: description: Value specifies the value to be used in the cert extension. @@ -2203,8 +2197,7 @@ spec: create_host_user_mode: description: CreateHostUserMode allows users to be automatically created on a host when not set to off - format: int32 - type: integer + x-kubernetes-int-or-string: true desktop_clipboard: description: DesktopClipboard indicates whether clipboard sharing is allowed between the user's workstation and the remote desktop. @@ -2315,8 +2308,7 @@ spec: require_session_mfa: description: RequireMFAType is the type of MFA requirement enforced for this user. - format: int32 - type: integer + x-kubernetes-int-or-string: true ssh_file_copy: description: SSHFileCopy indicates whether remote file operations via SCP or SFTP are allowed over an SSH session. It defaults