From 207d4fc7d8b255f105beeda6e5defb6c0a5bd8c8 Mon Sep 17 00:00:00 2001 From: sarataha Date: Wed, 12 Apr 2023 14:14:48 +0200 Subject: [PATCH] Add oidc prefix support for impersonation This commit adds support for configuring OIDC username and groups prefixes and use them in the impersonation client. --- cmd/gitops-server/cmd/cmd.go | 21 +++++++++++- core/clustersmngr/client_test.go | 2 +- core/clustersmngr/cluster/single.go | 24 +++++++------- core/clustersmngr/cluster/single_test.go | 7 ++-- core/clustersmngr/factory_caches_test.go | 9 ++--- core/clustersmngr/factory_test.go | 7 ++-- core/clustersmngr/fetcher/single_test.go | 3 +- core/clustersmngr/suite_test.go | 5 +-- core/nsaccess/nsaccess_test.go | 2 +- core/server/featureflags_test.go | 2 +- core/server/suite_test.go | 2 +- pkg/kube/config_getter.go | 39 +++++++++++++++++----- pkg/kube/config_getter_test.go | 42 +++++++++++++++++++++--- pkg/server/auth/server.go | 26 ++++++++------- pkg/services/crd/suite_test.go | 3 +- 15 files changed, 141 insertions(+), 53 deletions(-) diff --git a/cmd/gitops-server/cmd/cmd.go b/cmd/gitops-server/cmd/cmd.go index 4d192708c2..4769cd33c5 100644 --- a/cmd/gitops-server/cmd/cmd.go +++ b/cmd/gitops-server/cmd/cmd.go @@ -115,6 +115,10 @@ func NewCommand() *cobra.Command { cmd.Flags().StringVar(&options.OIDC.ClaimsConfig.Username, "oidc-username-claim", auth.ClaimUsername, "JWT claim to use as the user name. By default email, which is expected to be a unique identifier of the end user. Admins can choose other claims, such as sub or name, depending on their provider") cmd.Flags().StringVar(&options.OIDC.ClaimsConfig.Groups, "oidc-groups-claim", auth.ClaimGroups, "JWT claim to use as the user's group. If the claim is present it must be an array of strings") cmd.Flags().StringSliceVar(&options.OIDC.Scopes, "custom-oidc-scopes", auth.DefaultScopes, "Customise the requested scopes for then OIDC authentication flow - openid will always be requested") + // OIDC prefixes + cmd.Flags().StringVar(&options.OIDC.UsernamePrefix, "oidc-username-prefix", "", "Prefix to add to the username when impersonating") + cmd.Flags().StringVar(&options.OIDC.GroupsPrefix, "oidc-group-prefix", "", "Prefix to add to the groups when impersonating") + // Metrics cmd.Flags().BoolVar(&options.EnableMetrics, "enable-metrics", false, "Starts the metrics listener") cmd.Flags().StringVar(&options.MetricsAddress, "metrics-address", ":2112", "If the metrics listener is enabled, bind to this address") @@ -187,7 +191,22 @@ func runCmd(cmd *cobra.Command, args []string) error { ctx := context.Background() - cl, err := cluster.NewSingleCluster(cluster.DefaultCluster, rest, scheme, cluster.DefaultKubeConfigOptions...) + oidcPrefixes := kube.UserPrefixes{ + UsernamePrefix: options.OIDC.UsernamePrefix, + GroupsPrefix: options.OIDC.GroupsPrefix, + } + + // Incorporate values from authServer.AuthConfig.OIDCConfig + if authServer.AuthConfig.OIDCConfig.UsernamePrefix != "" { + log.V(logger.LogLevelWarn).Info("OIDC username prefix configured by both CLI and secret. Secret values will take precedence.") + oidcPrefixes.UsernamePrefix = authServer.AuthConfig.OIDCConfig.UsernamePrefix + } + if authServer.AuthConfig.OIDCConfig.GroupsPrefix != "" { + log.V(logger.LogLevelWarn).Info("OIDC groups prefix configured by both CLI and secret. Secret values will take precedence.") + oidcPrefixes.GroupsPrefix = authServer.AuthConfig.OIDCConfig.GroupsPrefix + } + + cl, err := cluster.NewSingleCluster(cluster.DefaultCluster, rest, scheme, oidcPrefixes, cluster.DefaultKubeConfigOptions...) if err != nil { return fmt.Errorf("failed to create cluster client; %w", err) } diff --git a/core/clustersmngr/client_test.go b/core/clustersmngr/client_test.go index 37a1d03742..904d814f77 100644 --- a/core/clustersmngr/client_test.go +++ b/core/clustersmngr/client_test.go @@ -519,7 +519,7 @@ func createClusterClientsPool(g *GomegaWithT, clusterName string) clustersmngr.C }) g.Expect(err).To(BeNil()) - cluster, err := cluster.NewSingleCluster(clusterName, k8sEnv.Rest, scheme) + cluster, err := cluster.NewSingleCluster(clusterName, k8sEnv.Rest, scheme, kube.UserPrefixes{}) g.Expect(err).To(BeNil()) err = clientsPool.Add( client, diff --git a/core/clustersmngr/cluster/single.go b/core/clustersmngr/cluster/single.go index 81ce67d747..16721cfbad 100644 --- a/core/clustersmngr/cluster/single.go +++ b/core/clustersmngr/cluster/single.go @@ -14,12 +14,13 @@ import ( ) type singleCluster struct { - name string - restConfig *rest.Config - scheme *apiruntime.Scheme + name string + restConfig *rest.Config + scheme *apiruntime.Scheme + userPrefixes kube.UserPrefixes } -func NewSingleCluster(name string, config *rest.Config, scheme *apiruntime.Scheme, kubeConfigOptions ...KubeConfigOption) (Cluster, error) { +func NewSingleCluster(name string, config *rest.Config, scheme *apiruntime.Scheme, userPrefixes kube.UserPrefixes, kubeConfigOptions ...KubeConfigOption) (Cluster, error) { // TODO: why does the cluster care about options? config.Timeout = kubeClientTimeout config.Dial = (&net.Dialer{ @@ -38,9 +39,10 @@ func NewSingleCluster(name string, config *rest.Config, scheme *apiruntime.Schem } return &singleCluster{ - name: name, - restConfig: config, - scheme: scheme, + name: name, + restConfig: config, + scheme: scheme, + userPrefixes: userPrefixes, }, nil } @@ -69,16 +71,16 @@ func getClientFromConfig(config *rest.Config, scheme *apiruntime.Scheme) (client return client, nil } -func getImpersonatedConfig(config *rest.Config, user *auth.UserPrincipal) (*rest.Config, error) { +func getImpersonatedConfig(config *rest.Config, user *auth.UserPrincipal, userPrefixes kube.UserPrefixes) (*rest.Config, error) { if !user.Valid() { return nil, fmt.Errorf("no user ID or Token found in UserPrincipal") } - return kube.ConfigWithPrincipal(user, config), nil + return kube.ConfigWithPrincipal(user, config, userPrefixes), nil } func (c *singleCluster) GetUserClient(user *auth.UserPrincipal) (client.Client, error) { - cfg, err := getImpersonatedConfig(c.restConfig, user) + cfg, err := getImpersonatedConfig(c.restConfig, user, c.userPrefixes) if err != nil { return nil, err } @@ -101,7 +103,7 @@ func (c *singleCluster) GetServerClient() (client.Client, error) { } func (c *singleCluster) GetUserClientset(user *auth.UserPrincipal) (kubernetes.Interface, error) { - cfg, err := getImpersonatedConfig(c.restConfig, user) + cfg, err := getImpersonatedConfig(c.restConfig, user, c.userPrefixes) if err != nil { return nil, err } diff --git a/core/clustersmngr/cluster/single_test.go b/core/clustersmngr/cluster/single_test.go index 88988653df..b854523f3d 100644 --- a/core/clustersmngr/cluster/single_test.go +++ b/core/clustersmngr/cluster/single_test.go @@ -5,6 +5,7 @@ import ( "testing" . "github.com/onsi/gomega" + "github.com/weaveworks/weave-gitops/pkg/kube" "github.com/weaveworks/weave-gitops/pkg/server/auth" "github.com/weaveworks/weave-gitops/pkg/testutils" "k8s.io/apimachinery/pkg/util/rand" @@ -19,7 +20,7 @@ func TestSingleCluster(t *testing.T) { g := NewGomegaWithT(t) - cluster, err := NewSingleCluster("Default", config, nil) + cluster, err := NewSingleCluster("Default", config, nil, kube.UserPrefixes{}) g.Expect(err).To(BeNil()) g.Expect(cluster.GetName()).To(Equal("Default")) @@ -77,9 +78,9 @@ func TestClientConfigWithUser(t *testing.T) { // Set up clusterName := fmt.Sprintf("clustersmngr-test-%d-%s", idx, rand.String(5)) - cluster, err := NewSingleCluster(clusterName, k8sEnv.Rest, nil) + cluster, err := NewSingleCluster(clusterName, k8sEnv.Rest, nil, kube.UserPrefixes{}) g.Expect(err).NotTo(HaveOccurred()) - res, err := getImpersonatedConfig(cluster.(*singleCluster).restConfig, tt.principal) + res, err := getImpersonatedConfig(cluster.(*singleCluster).restConfig, tt.principal, kube.UserPrefixes{}) // Test if tt.expectedErr != nil { diff --git a/core/clustersmngr/factory_caches_test.go b/core/clustersmngr/factory_caches_test.go index 60cb1da6cd..b12de63ac9 100644 --- a/core/clustersmngr/factory_caches_test.go +++ b/core/clustersmngr/factory_caches_test.go @@ -10,6 +10,7 @@ import ( . "github.com/onsi/gomega" "github.com/weaveworks/weave-gitops/core/clustersmngr" "github.com/weaveworks/weave-gitops/core/clustersmngr/cluster" + "github.com/weaveworks/weave-gitops/pkg/kube" "github.com/weaveworks/weave-gitops/pkg/server/auth" v1 "k8s.io/api/core/v1" "k8s.io/client-go/rest" @@ -36,7 +37,7 @@ func TestUsersNamespaces(t *testing.T) { }) t.Run("all namespaces from all", func(t *testing.T) { - cl, err := cluster.NewSingleCluster(clusterName, &rest.Config{}, nil) + cl, err := cluster.NewSingleCluster(clusterName, &rest.Config{}, nil, kube.UserPrefixes{}) g.Expect(err).NotTo(HaveOccurred()) nsMap := un.GetAll(user, []cluster.Cluster{cl}) g.Expect(nsMap).To(Equal(map[string][]v1.Namespace{clusterName: {ns}})) @@ -51,10 +52,10 @@ func TestClusters(t *testing.T) { c1 := "cluster-1" c2 := "cluster-2" - cluster1, err := cluster.NewSingleCluster(c1, &rest.Config{}, nil) + cluster1, err := cluster.NewSingleCluster(c1, &rest.Config{}, nil, kube.UserPrefixes{}) g.Expect(err).NotTo(HaveOccurred()) - cluster2, err := cluster.NewSingleCluster(c2, &rest.Config{}, nil) + cluster2, err := cluster.NewSingleCluster(c2, &rest.Config{}, nil, kube.UserPrefixes{}) g.Expect(err).NotTo(HaveOccurred()) testClusters := []cluster.Cluster{cluster1, cluster2} @@ -127,7 +128,7 @@ func TestClusterSet_Set(t *testing.T) { } func newTestCluster(t *testing.T, name, server string) cluster.Cluster { - c, err := cluster.NewSingleCluster(name, &rest.Config{Host: server}, nil) + c, err := cluster.NewSingleCluster(name, &rest.Config{Host: server}, nil, kube.UserPrefixes{}) if err != nil { t.Error("Expected error to be nil, got", err) } diff --git a/core/clustersmngr/factory_test.go b/core/clustersmngr/factory_test.go index 101216e7b6..5d96fbeeb3 100644 --- a/core/clustersmngr/factory_test.go +++ b/core/clustersmngr/factory_test.go @@ -14,6 +14,7 @@ import ( "github.com/weaveworks/weave-gitops/core/nsaccess" "github.com/weaveworks/weave-gitops/core/nsaccess/nsaccessfakes" "github.com/weaveworks/weave-gitops/pkg/featureflags" + "github.com/weaveworks/weave-gitops/pkg/kube" "github.com/weaveworks/weave-gitops/pkg/server/auth" "golang.org/x/net/context" v1 "k8s.io/api/core/v1" @@ -33,7 +34,7 @@ func TestGetImpersonatedClient(t *testing.T) { nsChecker := &nsaccessfakes.FakeChecker{} nsChecker.FilterAccessibleNamespacesReturns([]v1.Namespace{*ns2}, nil) - cluster, err := cluster.NewSingleCluster("test", k8sEnv.Rest, nil, cluster.DefaultKubeConfigOptions...) + cluster, err := cluster.NewSingleCluster("test", k8sEnv.Rest, nil, kube.UserPrefixes{}, cluster.DefaultKubeConfigOptions...) g.Expect(err).To(BeNil()) clustersFetcher := fetcher.NewSingleClusterFetcher(cluster) @@ -84,7 +85,7 @@ func TestUseUserClientForNamespaces(t *testing.T) { nsChecker := &nsaccessfakes.FakeChecker{} nsChecker.FilterAccessibleNamespacesReturns([]v1.Namespace{*ns2}, nil) - cluster, err := cluster.NewSingleCluster("test", k8sEnv.Rest, nil, cluster.DefaultKubeConfigOptions...) + cluster, err := cluster.NewSingleCluster("test", k8sEnv.Rest, nil, kube.UserPrefixes{}, cluster.DefaultKubeConfigOptions...) g.Expect(err).To(BeNil()) clustersFetcher := fetcher.NewSingleClusterFetcher(cluster) @@ -134,7 +135,7 @@ func TestGetImpersonatedDiscoveryClient(t *testing.T) { nsChecker := &nsaccessfakes.FakeChecker{} nsChecker.FilterAccessibleNamespacesReturns([]v1.Namespace{*ns1}, nil) - cl, err := cluster.NewSingleCluster(cluster.DefaultCluster, k8sEnv.Rest, nil, cluster.DefaultKubeConfigOptions...) + cl, err := cluster.NewSingleCluster(cluster.DefaultCluster, k8sEnv.Rest, nil, kube.UserPrefixes{}, cluster.DefaultKubeConfigOptions...) g.Expect(err).To(BeNil()) clustersFetcher := fetcher.NewSingleClusterFetcher(cl) diff --git a/core/clustersmngr/fetcher/single_test.go b/core/clustersmngr/fetcher/single_test.go index 3db5c8e7f9..5f1039e093 100644 --- a/core/clustersmngr/fetcher/single_test.go +++ b/core/clustersmngr/fetcher/single_test.go @@ -7,6 +7,7 @@ import ( . "github.com/onsi/gomega" "github.com/weaveworks/weave-gitops/core/clustersmngr/cluster" "github.com/weaveworks/weave-gitops/core/clustersmngr/fetcher" + "github.com/weaveworks/weave-gitops/pkg/kube" "k8s.io/client-go/rest" ) @@ -18,7 +19,7 @@ func TestSingleFetcher(t *testing.T) { g := NewGomegaWithT(t) - cluster, err := cluster.NewSingleCluster("Default", config, nil) + cluster, err := cluster.NewSingleCluster("Default", config, nil, kube.UserPrefixes{}) g.Expect(err).To(BeNil()) fetcher := fetcher.NewSingleClusterFetcher(cluster) diff --git a/core/clustersmngr/suite_test.go b/core/clustersmngr/suite_test.go index 5240b88ad1..f8b624fe88 100644 --- a/core/clustersmngr/suite_test.go +++ b/core/clustersmngr/suite_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/weaveworks/weave-gitops/core/clustersmngr/cluster" + "github.com/weaveworks/weave-gitops/pkg/kube" "github.com/weaveworks/weave-gitops/pkg/testutils" "k8s.io/client-go/rest" ) @@ -30,7 +31,7 @@ func TestMain(m *testing.M) { } func makeLeafCluster(t *testing.T, name string) cluster.Cluster { - cluster, err := cluster.NewSingleCluster(name, k8sEnv.Rest, nil) + cluster, err := cluster.NewSingleCluster(name, k8sEnv.Rest, nil, kube.UserPrefixes{}) if err != nil { t.Error("Expected err to be nil, got", err) } @@ -45,7 +46,7 @@ func makeUnreachableLeafCluster(t *testing.T, name string) cluster.Cluster { // FIXME: better addresses? c.Host = "0.0.0.0:65535" - cluster, err := cluster.NewSingleCluster(name, c, nil) + cluster, err := cluster.NewSingleCluster(name, c, nil, kube.UserPrefixes{}) if err != nil { t.Error("Expected err to be nil, got", err) } diff --git a/core/nsaccess/nsaccess_test.go b/core/nsaccess/nsaccess_test.go index b21615494d..35573e1525 100644 --- a/core/nsaccess/nsaccess_test.go +++ b/core/nsaccess/nsaccess_test.go @@ -341,7 +341,7 @@ func newRestConfigWithRole(t *testing.T, testCfg *rest.Config, roleName types.Na t.Fatal(err) } - cluster, err := cluster.NewSingleCluster("test", testCfg, scheme) + cluster, err := cluster.NewSingleCluster("test", testCfg, scheme, kube.UserPrefixes{}) if err != nil { t.Fatal(err) } diff --git a/core/server/featureflags_test.go b/core/server/featureflags_test.go index afb67d758e..62892a2bf1 100644 --- a/core/server/featureflags_test.go +++ b/core/server/featureflags_test.go @@ -28,7 +28,7 @@ func TestGetFeatureFlags(t *testing.T) { t.Fatal(err) } - cluster, err := cluster.NewSingleCluster("Default", k8sEnv.Rest, scheme) + cluster, err := cluster.NewSingleCluster("Default", k8sEnv.Rest, scheme, kube.UserPrefixes{}) if err != nil { t.Fatal(err) } diff --git a/core/server/suite_test.go b/core/server/suite_test.go index 51a5298df8..d0e89d854f 100644 --- a/core/server/suite_test.go +++ b/core/server/suite_test.go @@ -65,7 +65,7 @@ func makeGRPCServer(cfg *rest.Config, t *testing.T) pb.CoreClient { t.Fatal(err) } - cluster, err := cluster.NewSingleCluster("Default", k8sEnv.Rest, scheme) + cluster, err := cluster.NewSingleCluster("Default", k8sEnv.Rest, scheme, kube.UserPrefixes{}) if err != nil { t.Fatal(err) } diff --git a/pkg/kube/config_getter.go b/pkg/kube/config_getter.go index 5b70077b95..9658a1d94b 100644 --- a/pkg/kube/config_getter.go +++ b/pkg/kube/config_getter.go @@ -7,6 +7,13 @@ import ( "k8s.io/client-go/rest" ) +// UserPrefixes contains the prefixes for the user and groups +// that will be used when impersonating a user. +type UserPrefixes struct { + UsernamePrefix string + GroupsPrefix string +} + // ConfigGetter implementations should extract the details from a context and // create a *rest.Config for use in clients. type ConfigGetter interface { @@ -20,14 +27,15 @@ var _ ConfigGetter = &ImpersonatingConfigGetter{} // principal and if it finds one, it configures the *rest.Config to impersonate // that principal. Otherwise it returns a copy of the base config. type ImpersonatingConfigGetter struct { - insecure bool - cfg *rest.Config + insecure bool + cfg *rest.Config + userPrefixes UserPrefixes } // NewImpersonatingConfigGetter creates and returns a ConfigGetter with a known // config. -func NewImpersonatingConfigGetter(cfg *rest.Config, insecure bool) *ImpersonatingConfigGetter { - return &ImpersonatingConfigGetter{cfg: cfg, insecure: insecure} +func NewImpersonatingConfigGetter(cfg *rest.Config, insecure bool, userPrefixes UserPrefixes) *ImpersonatingConfigGetter { + return &ImpersonatingConfigGetter{cfg: cfg, insecure: insecure, userPrefixes: userPrefixes} } // Config returns a *rest.Config configured to impersonate a user or @@ -36,7 +44,7 @@ func (r *ImpersonatingConfigGetter) Config(ctx context.Context) *rest.Config { cfg := rest.CopyConfig(r.cfg) if p := auth.Principal(ctx); p != nil { - cfg = ConfigWithPrincipal(p, cfg) + cfg = ConfigWithPrincipal(p, cfg, r.userPrefixes) } if r.insecure { @@ -50,7 +58,7 @@ func (r *ImpersonatingConfigGetter) Config(ctx context.Context) *rest.Config { // ConfigWithPrincipal returns a new config with the principal set as the // impersonated user or bearer token. -func ConfigWithPrincipal(user *auth.UserPrincipal, config *rest.Config) *rest.Config { +func ConfigWithPrincipal(user *auth.UserPrincipal, config *rest.Config, userPrefixes UserPrefixes) *rest.Config { cfg := rest.CopyConfig(config) if tok := user.Token(); tok != "" { @@ -58,11 +66,26 @@ func ConfigWithPrincipal(user *auth.UserPrincipal, config *rest.Config) *rest.Co // Clear the token file as it takes precedence over the token. cfg.BearerTokenFile = "" } else { + prefixedGroups := user.Groups + if prefixedGroups != nil { + prefixedGroups = addGroupsPrefix(user.Groups, userPrefixes.GroupsPrefix) + } + cfg.Impersonate = rest.ImpersonationConfig{ - UserName: user.ID, - Groups: user.Groups, + UserName: userPrefixes.UsernamePrefix + user.ID, + Groups: prefixedGroups, } } return cfg } + +func addGroupsPrefix(groups []string, prefix string) []string { + prefixedGroups := make([]string, len(groups)) + + for i, group := range groups { + prefixedGroups[i] = prefix + group + } + + return prefixedGroups +} diff --git a/pkg/kube/config_getter_test.go b/pkg/kube/config_getter_test.go index feb0c9076a..27fcd57577 100644 --- a/pkg/kube/config_getter_test.go +++ b/pkg/kube/config_getter_test.go @@ -13,7 +13,7 @@ import ( var _ kube.ConfigGetter = (*kube.ImpersonatingConfigGetter)(nil) func TestImpersonatingConfigGetterPrincipalInContext(t *testing.T) { - g := kube.NewImpersonatingConfigGetter(&rest.Config{}, false) + g := kube.NewImpersonatingConfigGetter(&rest.Config{}, false, kube.UserPrefixes{}) ctx := auth.WithPrincipal(context.TODO(), &auth.UserPrincipal{ID: "user@example.com"}) cfg := g.Config(ctx) @@ -29,7 +29,7 @@ func TestImpersonatingConfigGetterPrincipalInContext(t *testing.T) { } func TestImpersonatingConfigGetterPrincipalInContextWithGroups(t *testing.T) { - g := kube.NewImpersonatingConfigGetter(&rest.Config{}, false) + g := kube.NewImpersonatingConfigGetter(&rest.Config{}, false, kube.UserPrefixes{}) ctx := auth.WithPrincipal(context.TODO(), &auth.UserPrincipal{ID: "user@example.com", Groups: []string{"test-group"}}) cfg := g.Config(ctx) @@ -46,7 +46,7 @@ func TestImpersonatingConfigGetterPrincipalInContextWithGroups(t *testing.T) { } func TestImpersonatingConfigGetterInsecureClient(t *testing.T) { - g := kube.NewImpersonatingConfigGetter(&rest.Config{}, true) + g := kube.NewImpersonatingConfigGetter(&rest.Config{}, true, kube.UserPrefixes{}) ctx := auth.WithPrincipal(context.TODO(), &auth.UserPrincipal{ID: "user@example.com"}) cfg := g.Config(ctx) @@ -65,7 +65,7 @@ func TestImpersonatingConfigGetterInsecureClient(t *testing.T) { } func TestImpersonatingConfigGetterNoPrincipalInContext(t *testing.T) { - g := kube.NewImpersonatingConfigGetter(&rest.Config{}, true) + g := kube.NewImpersonatingConfigGetter(&rest.Config{}, true, kube.UserPrefixes{}) cfg := g.Config(context.TODO()) @@ -78,3 +78,37 @@ func TestImpersonatingConfigGetterNoPrincipalInContext(t *testing.T) { t.Fatalf("incorrect client config:\n%s", diff) } } + +func TestConfigWithPrincipal(t *testing.T) { + user := &auth.UserPrincipal{ + ID: "user-id", + Groups: []string{"group1", "group2"}, + } + config := &rest.Config{ + Host: "https://example.com", + } + + userPrefixes := kube.UserPrefixes{ + UsernamePrefix: "prefix-", + GroupsPrefix: "prefix-", + } + + // First call. + cfg := kube.ConfigWithPrincipal(user, config, userPrefixes) + + expectedUser := "prefix-user-id" + if cfg.Impersonate.UserName != expectedUser { + t.Fatalf("cfg username didn't match expected: %s", cfg.Impersonate.UserName) + } + + expectedGroups := []string{"prefix-group1", "prefix-group2"} + if diff := cmp.Diff(expectedGroups, cfg.Impersonate.Groups); diff != "" { + t.Fatalf("cfg groups didn't match expected:\n%s", diff) + } + + // Second call. + cfg = kube.ConfigWithPrincipal(user, config, userPrefixes) + if diff := cmp.Diff(expectedGroups, cfg.Impersonate.Groups); diff != "" { + t.Fatalf("cfg groups didn't match expected:\n%s", diff) + } +} diff --git a/pkg/server/auth/server.go b/pkg/server/auth/server.go index fd96e76ab6..d29680823f 100644 --- a/pkg/server/auth/server.go +++ b/pkg/server/auth/server.go @@ -50,13 +50,15 @@ var DefaultScopes = []string{ // OIDCConfig is used to configure an AuthServer to interact with // an OIDC issuer. type OIDCConfig struct { - IssuerURL string - ClientID string - ClientSecret string - RedirectURL string - TokenDuration time.Duration - Scopes []string - ClaimsConfig *ClaimsConfig + IssuerURL string + ClientID string + ClientSecret string + RedirectURL string + TokenDuration time.Duration + Scopes []string + ClaimsConfig *ClaimsConfig + UsernamePrefix string + GroupsPrefix string } // This is only used if the OIDCConfig doesn't have a TokenDuration set. If @@ -108,10 +110,12 @@ type UserInfo struct { // - customScopes - defaults to "openid","offline_access","email","groups" func NewOIDCConfigFromSecret(secret corev1.Secret) OIDCConfig { cfg := OIDCConfig{ - IssuerURL: string(secret.Data["issuerURL"]), - ClientID: string(secret.Data["clientID"]), - ClientSecret: string(secret.Data["clientSecret"]), - RedirectURL: string(secret.Data["redirectURL"]), + IssuerURL: string(secret.Data["issuerURL"]), + ClientID: string(secret.Data["clientID"]), + ClientSecret: string(secret.Data["clientSecret"]), + RedirectURL: string(secret.Data["redirectURL"]), + UsernamePrefix: string(secret.Data["oidcUsernamePrefix"]), + GroupsPrefix: string(secret.Data["oidcGroupsPrefix"]), } cfg.ClaimsConfig = claimsConfigFromSecret(secret) diff --git a/pkg/services/crd/suite_test.go b/pkg/services/crd/suite_test.go index 7ddb3ebcb7..1c4a4f5add 100644 --- a/pkg/services/crd/suite_test.go +++ b/pkg/services/crd/suite_test.go @@ -12,6 +12,7 @@ import ( "github.com/weaveworks/weave-gitops/core/clustersmngr/cluster" "github.com/weaveworks/weave-gitops/core/clustersmngr/clustersmngrfakes" "github.com/weaveworks/weave-gitops/core/nsaccess" + "github.com/weaveworks/weave-gitops/pkg/kube" "github.com/weaveworks/weave-gitops/pkg/testutils" v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -58,7 +59,7 @@ func createClient(k8sEnv *testutils.K8sTestEnv) (clustersmngr.Client, clustersmn ctx := context.Background() log := logr.Discard() - singleCluster, err := cluster.NewSingleCluster(defaultClusterName, k8sEnv.Rest, nil) + singleCluster, err := cluster.NewSingleCluster(defaultClusterName, k8sEnv.Rest, nil, kube.UserPrefixes{}) if err != nil { return nil, nil, err }