From a8da302a5af615fa902aac89eb1b988923c07285 Mon Sep 17 00:00:00 2001 From: Kirill Sibirev Date: Fri, 10 Jan 2025 14:07:00 +0100 Subject: [PATCH] Support not creating non-existing users (#416) * Support disable user creating for OAuth * Add test cases * Remove trailing linebreak diff * Remove trailing linebreak diff --- api/v1/ytsaurus_types.go | 2 + api/v1/zz_generated.deepcopy.go | 5 + .../bases/cluster.ytsaurus.tech_ytsaurus.yaml | 4 + docs/api.md | 1 + .../test.canondata | 155 ++++++++++++++++++ .../test.canondata | 155 ++++++++++++++++++ pkg/ytconfig/generator.go | 30 +++- pkg/ytconfig/generator_test.go | 19 +++ pkg/ytconfig/proxy.go | 8 +- .../crds/ytsaurus.cluster.ytsaurus.tech.yaml | 4 + 10 files changed, 374 insertions(+), 9 deletions(-) create mode 100644 pkg/ytconfig/canondata/TestGetHTTPProxyConfigDisableCreateOauthUser/test.canondata create mode 100644 pkg/ytconfig/canondata/TestGetHTTPProxyConfigEnableCreateOauthUser/test.canondata diff --git a/api/v1/ytsaurus_types.go b/api/v1/ytsaurus_types.go index 3ae39c6b..b90e90d1 100644 --- a/api/v1/ytsaurus_types.go +++ b/api/v1/ytsaurus_types.go @@ -227,6 +227,8 @@ type OauthServiceSpec struct { //+kubebuilder:default:=false Secure bool `json:"secure,omitempty"` UserInfo OauthUserInfoHandlerSpec `json:"userInfoHandler,omitempty"` + // If DisableUserCreation is set, proxies will NOT create non-existing users with OAuth authentication. + DisableUserCreation *bool `json:"disableUserCreation,omitempty"` } type HealthcheckProbeParams struct { diff --git a/api/v1/zz_generated.deepcopy.go b/api/v1/zz_generated.deepcopy.go index cf994d1c..b838f267 100644 --- a/api/v1/zz_generated.deepcopy.go +++ b/api/v1/zz_generated.deepcopy.go @@ -965,6 +965,11 @@ func (in *MastersSpec) DeepCopy() *MastersSpec { func (in *OauthServiceSpec) DeepCopyInto(out *OauthServiceSpec) { *out = *in in.UserInfo.DeepCopyInto(&out.UserInfo) + if in.DisableUserCreation != nil { + in, out := &in.DisableUserCreation, &out.DisableUserCreation + *out = new(bool) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OauthServiceSpec. diff --git a/config/crd/bases/cluster.ytsaurus.tech_ytsaurus.yaml b/config/crd/bases/cluster.ytsaurus.tech_ytsaurus.yaml index 01312df7..db05dc16 100644 --- a/config/crd/bases/cluster.ytsaurus.tech_ytsaurus.yaml +++ b/config/crd/bases/cluster.ytsaurus.tech_ytsaurus.yaml @@ -14893,6 +14893,10 @@ spec: type: object oauthService: properties: + disableUserCreation: + description: If DisableUserCreation is set, proxies will NOT create + non-existing users with O + type: boolean host: minLength: 1 type: string diff --git a/docs/api.md b/docs/api.md index 8a425699..d5752954 100644 --- a/docs/api.md +++ b/docs/api.md @@ -1047,6 +1047,7 @@ _Appears in:_ | `port` _integer_ | | 80 | | | `secure` _boolean_ | | false | | | `userInfoHandler` _[OauthUserInfoHandlerSpec](#oauthuserinfohandlerspec)_ | | | | +| `disableUserCreation` _boolean_ | If DisableUserCreation is set, proxies will NOT create non-existing users with OAuth authentication. | | | #### OauthUserInfoHandlerSpec diff --git a/pkg/ytconfig/canondata/TestGetHTTPProxyConfigDisableCreateOauthUser/test.canondata b/pkg/ytconfig/canondata/TestGetHTTPProxyConfigDisableCreateOauthUser/test.canondata new file mode 100644 index 00000000..1a6f95cb --- /dev/null +++ b/pkg/ytconfig/canondata/TestGetHTTPProxyConfigDisableCreateOauthUser/test.canondata @@ -0,0 +1,155 @@ +{ + "address_resolver"={ + "enable_ipv4"=%true; + "enable_ipv6"=%false; + retries=1000; + }; + "solomon_exporter"={ + host="{POD_SHORT_HOSTNAME}"; + "instance_tags"={ + pod="{K8S_POD_NAME}"; + }; + }; + logging={ + writers={ + info={ + type=file; + "file_name"="/var/log/http-proxy.info.log"; + format="plain_text"; + "enable_system_messages"=%true; + }; + stderr={ + type=stderr; + format="plain_text"; + "enable_system_messages"=%true; + }; + }; + rules=[ + { + "min_level"=info; + writers=[ + info; + ]; + family="plain_text"; + }; + { + "min_level"=error; + writers=[ + stderr; + ]; + family="plain_text"; + }; + ]; + "flush_period"=3000; + }; + "monitoring_port"=10016; + "rpc_port"=9016; + "timestamp_provider"={ + addresses=[ + "ms-test-0.masters-test.fake.svc.fake.zone:9010"; + ]; + }; + "cluster_connection"={ + "cluster_name"=test; + "primary_master"={ + addresses=[ + "ms-test-0.masters-test.fake.svc.fake.zone:9010"; + ]; + peers=[ + { + address="ms-test-0.masters-test.fake.svc.fake.zone:9010"; + voting=%true; + }; + ]; + "cell_id"="65726e65-ad6b7562-259-79747361"; + }; + "discovery_connection"={ + addresses=[ + "ds-test-0.discovery-test.fake.svc.fake.zone:9020"; + "ds-test-1.discovery-test.fake.svc.fake.zone:9020"; + "ds-test-2.discovery-test.fake.svc.fake.zone:9020"; + ]; + }; + "master_cache"={ + addresses=[ + "msc-test-0.master-caches-test.fake.svc.fake.zone:9018"; + "msc-test-1.master-caches-test.fake.svc.fake.zone:9018"; + "msc-test-2.master-caches-test.fake.svc.fake.zone:9018"; + ]; + "cell_id"="65726e65-ad6b7562-259-79747361"; + "enable_master_cache_discovery"=%false; + }; + }; + "cypress_annotations"={ + "k8s_node_name"="{K8S_NODE_NAME}"; + "k8s_pod_name"="{K8S_POD_NAME}"; + "k8s_pod_namespace"="{K8S_POD_NAMESPACE}"; + "physical_host"="{K8S_NODE_NAME}"; + }; + port=80; + auth={ + "cypress_cookie_manager"={ + }; + "cypress_user_manager"={ + }; + "cypress_token_authenticator"={ + secure=%true; + }; + "oauth_service"={ + host="oauth-host"; + port=433; + secure=%true; + "user_info_endpoint"="user-info-endpoint"; + "user_info_login_field"=login; + "login_transformations"=[ + { + "match_pattern"="(.*)@ytsaurus.team"; + replacement="\\1"; + }; + ]; + }; + "oauth_cookie_authenticator"={ + "create_user_if_not_exists"=%false; + }; + "oauth_token_authenticator"={ + "create_user_if_not_exists"=%false; + }; + "require_authentication"=%true; + }; + coordinator={ + enable=%true; + "default_role_filter"=default; + }; + driver={ + "timestamp_provider"={ + addresses=[ + "ms-test-0.masters-test.fake.svc.fake.zone:9010"; + ]; + }; + "primary_master"={ + addresses=[ + "ms-test-0.masters-test.fake.svc.fake.zone:9010"; + ]; + peers=[ + { + address="ms-test-0.masters-test.fake.svc.fake.zone:9010"; + voting=%true; + }; + ]; + "cell_id"="65726e65-ad6b7562-259-79747361"; + }; + }; + role=control; + "https_server"={ + port=443; + credentials={ + "cert_chain"={ + "file_name"="/tls/https_secret/tls.crt"; + }; + "private_key"={ + "file_name"="/tls/https_secret/tls.key"; + }; + "update_period"=60000; + }; + }; +} \ No newline at end of file diff --git a/pkg/ytconfig/canondata/TestGetHTTPProxyConfigEnableCreateOauthUser/test.canondata b/pkg/ytconfig/canondata/TestGetHTTPProxyConfigEnableCreateOauthUser/test.canondata new file mode 100644 index 00000000..b5c828ab --- /dev/null +++ b/pkg/ytconfig/canondata/TestGetHTTPProxyConfigEnableCreateOauthUser/test.canondata @@ -0,0 +1,155 @@ +{ + "address_resolver"={ + "enable_ipv4"=%true; + "enable_ipv6"=%false; + retries=1000; + }; + "solomon_exporter"={ + host="{POD_SHORT_HOSTNAME}"; + "instance_tags"={ + pod="{K8S_POD_NAME}"; + }; + }; + logging={ + writers={ + info={ + type=file; + "file_name"="/var/log/http-proxy.info.log"; + format="plain_text"; + "enable_system_messages"=%true; + }; + stderr={ + type=stderr; + format="plain_text"; + "enable_system_messages"=%true; + }; + }; + rules=[ + { + "min_level"=info; + writers=[ + info; + ]; + family="plain_text"; + }; + { + "min_level"=error; + writers=[ + stderr; + ]; + family="plain_text"; + }; + ]; + "flush_period"=3000; + }; + "monitoring_port"=10016; + "rpc_port"=9016; + "timestamp_provider"={ + addresses=[ + "ms-test-0.masters-test.fake.svc.fake.zone:9010"; + ]; + }; + "cluster_connection"={ + "cluster_name"=test; + "primary_master"={ + addresses=[ + "ms-test-0.masters-test.fake.svc.fake.zone:9010"; + ]; + peers=[ + { + address="ms-test-0.masters-test.fake.svc.fake.zone:9010"; + voting=%true; + }; + ]; + "cell_id"="65726e65-ad6b7562-259-79747361"; + }; + "discovery_connection"={ + addresses=[ + "ds-test-0.discovery-test.fake.svc.fake.zone:9020"; + "ds-test-1.discovery-test.fake.svc.fake.zone:9020"; + "ds-test-2.discovery-test.fake.svc.fake.zone:9020"; + ]; + }; + "master_cache"={ + addresses=[ + "msc-test-0.master-caches-test.fake.svc.fake.zone:9018"; + "msc-test-1.master-caches-test.fake.svc.fake.zone:9018"; + "msc-test-2.master-caches-test.fake.svc.fake.zone:9018"; + ]; + "cell_id"="65726e65-ad6b7562-259-79747361"; + "enable_master_cache_discovery"=%false; + }; + }; + "cypress_annotations"={ + "k8s_node_name"="{K8S_NODE_NAME}"; + "k8s_pod_name"="{K8S_POD_NAME}"; + "k8s_pod_namespace"="{K8S_POD_NAMESPACE}"; + "physical_host"="{K8S_NODE_NAME}"; + }; + port=80; + auth={ + "cypress_cookie_manager"={ + }; + "cypress_user_manager"={ + }; + "cypress_token_authenticator"={ + secure=%true; + }; + "oauth_service"={ + host="oauth-host"; + port=433; + secure=%true; + "user_info_endpoint"="user-info-endpoint"; + "user_info_login_field"=login; + "login_transformations"=[ + { + "match_pattern"="(.*)@ytsaurus.team"; + replacement="\\1"; + }; + ]; + }; + "oauth_cookie_authenticator"={ + "create_user_if_not_exists"=%true; + }; + "oauth_token_authenticator"={ + "create_user_if_not_exists"=%true; + }; + "require_authentication"=%true; + }; + coordinator={ + enable=%true; + "default_role_filter"=default; + }; + driver={ + "timestamp_provider"={ + addresses=[ + "ms-test-0.masters-test.fake.svc.fake.zone:9010"; + ]; + }; + "primary_master"={ + addresses=[ + "ms-test-0.masters-test.fake.svc.fake.zone:9010"; + ]; + peers=[ + { + address="ms-test-0.masters-test.fake.svc.fake.zone:9010"; + voting=%true; + }; + ]; + "cell_id"="65726e65-ad6b7562-259-79747361"; + }; + }; + role=control; + "https_server"={ + port=443; + credentials={ + "cert_chain"={ + "file_name"="/tls/https_secret/tls.crt"; + }; + "private_key"={ + "file_name"="/tls/https_secret/tls.key"; + }; + "update_period"=60000; + }; + }; +} \ No newline at end of file diff --git a/pkg/ytconfig/generator.go b/pkg/ytconfig/generator.go index 0f97243b..99008ed6 100644 --- a/pkg/ytconfig/generator.go +++ b/pkg/ytconfig/generator.go @@ -495,10 +495,17 @@ func (g *Generator) getRPCProxyConfigImpl(spec *ytv1.RPCProxiesSpec) (RPCProxySe g.fillCommonService(&c.CommonServer, &spec.InstanceSpec) - if g.ytsaurus.Spec.OauthService != nil { - c.OauthService = ptr.To(getOauthService(*g.ytsaurus.Spec.OauthService)) + oauthService := g.ytsaurus.Spec.OauthService + if oauthService != nil { + c.OauthService = ptr.To(getOauthService(*oauthService)) c.CypressUserManager = CypressUserManager{} - c.OauthTokenAuthenticator = &OauthTokenAuthenticator{} + var createUserIfNotExist *bool + if oauthService.DisableUserCreation != nil { + createUserIfNotExist = ptr.To(!*oauthService.DisableUserCreation) + } + c.OauthTokenAuthenticator = &OauthTokenAuthenticator{ + CreateUserIfNotExists: createUserIfNotExist, + } c.RequireAuthentication = ptr.To(true) } @@ -670,10 +677,19 @@ func (g *Generator) getHTTPProxyConfigImpl(spec *ytv1.HTTPProxiesSpec) (HTTPProx g.fillCommonService(&c.CommonServer, &spec.InstanceSpec) g.fillBusServer(&c.CommonServer, spec.NativeTransport) - if g.ytsaurus.Spec.OauthService != nil { - c.Auth.OauthService = ptr.To(getOauthService(*g.ytsaurus.Spec.OauthService)) - c.Auth.OauthCookieAuthenticator = &OauthCookieAuthenticator{} - c.Auth.OauthTokenAuthenticator = &OauthTokenAuthenticator{} + oauthService := g.ytsaurus.Spec.OauthService + if oauthService != nil { + c.Auth.OauthService = ptr.To(getOauthService(*oauthService)) + var createUserIfNotExist *bool + if oauthService.DisableUserCreation != nil { + createUserIfNotExist = ptr.To(!*oauthService.DisableUserCreation) + } + c.Auth.OauthCookieAuthenticator = &OauthCookieAuthenticator{ + CreateUserIfNotExists: createUserIfNotExist, + } + c.Auth.OauthTokenAuthenticator = &OauthTokenAuthenticator{ + CreateUserIfNotExists: createUserIfNotExist, + } } return c, nil diff --git a/pkg/ytconfig/generator_test.go b/pkg/ytconfig/generator_test.go index f3074b82..e7a845a4 100644 --- a/pkg/ytconfig/generator_test.go +++ b/pkg/ytconfig/generator_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/stretchr/testify/require" + "github.com/ytsaurus/ytsaurus-k8s-operator/pkg/consts" "k8s.io/utils/ptr" @@ -264,6 +265,24 @@ func TestGetHTTPProxyConfig(t *testing.T) { canonize.Assert(t, cfg) } +func TestGetHTTPProxyConfigDisableCreateOauthUser(t *testing.T) { + spec := getYtsaurusWithEverything() + spec.Spec.OauthService.DisableUserCreation = ptr.To(true) + g := NewGenerator(spec, testClusterDomain) + cfg, err := g.GetHTTPProxyConfig(getHTTPProxySpec()) + require.NoError(t, err) + canonize.Assert(t, cfg) +} + +func TestGetHTTPProxyConfigEnableCreateOauthUser(t *testing.T) { + spec := getYtsaurusWithEverything() + spec.Spec.OauthService.DisableUserCreation = ptr.To(false) + g := NewGenerator(spec, testClusterDomain) + cfg, err := g.GetHTTPProxyConfig(getHTTPProxySpec()) + require.NoError(t, err) + canonize.Assert(t, cfg) +} + func TestGetMasterConfig(t *testing.T) { ytsaurus := getYtsaurusWithEverything() g := NewGenerator(ytsaurus, testClusterDomain) diff --git a/pkg/ytconfig/proxy.go b/pkg/ytconfig/proxy.go index 471b992f..2363f82e 100644 --- a/pkg/ytconfig/proxy.go +++ b/pkg/ytconfig/proxy.go @@ -32,8 +32,12 @@ type LoginTransformation struct { Replacement string `yson:"replacement,omitempty"` } -type OauthCookieAuthenticator struct{} -type OauthTokenAuthenticator struct{} +type OauthCookieAuthenticator struct { + CreateUserIfNotExists *bool `yson:"create_user_if_not_exists,omitempty"` +} +type OauthTokenAuthenticator struct { + CreateUserIfNotExists *bool `yson:"create_user_if_not_exists,omitempty"` +} type Coordinator struct { Enable bool `yson:"enable"` diff --git a/ytop-chart/templates/crds/ytsaurus.cluster.ytsaurus.tech.yaml b/ytop-chart/templates/crds/ytsaurus.cluster.ytsaurus.tech.yaml index 6ca0cfaf..747c4a87 100644 --- a/ytop-chart/templates/crds/ytsaurus.cluster.ytsaurus.tech.yaml +++ b/ytop-chart/templates/crds/ytsaurus.cluster.ytsaurus.tech.yaml @@ -14904,6 +14904,10 @@ spec: type: object oauthService: properties: + disableUserCreation: + description: If DisableUserCreation is set, proxies will NOT create + non-existing users with O + type: boolean host: minLength: 1 type: string