mirror of
https://github.com/gravitational/teleport
synced 2024-10-19 00:33:50 +00:00
Add support for Protobuf Enums into Operator CRDs (#32469)
* Add support for Protobuf Enums into Operator CRDs This PR marks the Teleport enum fields as integer or string values. The integer option is to ensure we are backwards compatibile with previously installed CRDs. Users can now represent their roles in Kubernetes custom resources and refer enum fields as strings while their protobuf wire type is int32. Fixes #29686 * add tests * fix unit test
This commit is contained in:
parent
13c283036b
commit
9a556d8ab1
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
*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 = 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)
|
||||
}
|
||||
*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 = CertExtensionMode(val)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -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{}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue