Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[v14] Add support for Protobuf Enums into Operator CRDs #32557

Merged
merged 3 commits into from Sep 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
19 changes: 19 additions & 0 deletions api/types/authentication.go
Expand Up @@ -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
}
72 changes: 60 additions & 12 deletions api/types/extension.go
Expand Up @@ -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
}

Expand All @@ -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
}
21 changes: 20 additions & 1 deletion api/types/role.go
Expand Up @@ -1822,6 +1822,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:
Expand All @@ -1830,7 +1840,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 {
Expand All @@ -1848,6 +1858,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{}
Expand Down
14 changes: 8 additions & 6 deletions api/types/role_test.go
Expand Up @@ -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)
}
Expand Down
Expand Up @@ -963,17 +963,15 @@ 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.
type: string
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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -2160,17 +2156,15 @@ 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.
type: string
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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down
Expand Up @@ -963,17 +963,15 @@ 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.
type: string
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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -2160,17 +2156,15 @@ 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.
type: string
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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down
Expand Up @@ -82,19 +82,44 @@ 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: `
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,
},
},
},
{
Expand Down