// ReadPluginConfig will read a plugin configuration object from a reader stream func ReadPluginConfig(reader io.Reader, config runtime.Object) error { if reader == nil || reflect.ValueOf(reader).IsNil() { return nil } configBytes, err := ioutil.ReadAll(reader) if err != nil { return err } err = configlatest.ReadYAML(configBytes, config) if err != nil { return err } return nil }
func ReadConfig(configFile io.Reader) (*api.ClusterResourceOverrideConfig, error) { obj, err := configlatest.ReadYAML(configFile) if err != nil { glog.V(5).Infof("%s error reading config: %v", api.PluginName, err) return nil, err } if obj == nil { return nil, nil } config, ok := obj.(*api.ClusterResourceOverrideConfig) if !ok { return nil, fmt.Errorf("unexpected config object: %#v", obj) } glog.V(5).Infof("%s config is: %v", api.PluginName, config) return config, nil }
func readConfig(reader io.Reader) (*api.PodNodeConstraintsConfig, error) { if reader == nil || reflect.ValueOf(reader).IsNil() { return nil, nil } obj, err := configlatest.ReadYAML(reader) if err != nil { return nil, err } if obj == nil { return nil, nil } config, ok := obj.(*api.PodNodeConstraintsConfig) if !ok { return nil, fmt.Errorf("unexpected config object: %#v", obj) } // No validation needed since config is just list of strings return config, nil }
func readConfig(reader io.Reader) (*requestlimitapi.ProjectRequestLimitConfig, error) { obj, err := configlatest.ReadYAML(reader) if err != nil { return nil, err } if obj == nil { return nil, nil } config, ok := obj.(*requestlimitapi.ProjectRequestLimitConfig) if !ok { return nil, fmt.Errorf("unexpected config object: %#v", obj) } errs := requestlimitapivalidation.ValidateProjectRequestLimitConfig(config) if len(errs) > 0 { return nil, errs.ToAggregate() } return config, nil }
func readConfig(reader io.Reader) (*api.RunOnceDurationConfig, error) { obj, err := configlatest.ReadYAML(reader) if err != nil { return nil, err } if obj == nil { return nil, nil } config, ok := obj.(*api.RunOnceDurationConfig) if !ok { return nil, fmt.Errorf("unexpected config object %#v", obj) } errs := validation.ValidateRunOnceDurationConfig(config) if len(errs) > 0 { return nil, errs.ToAggregate() } return config, nil }
func readConfig(reader io.Reader) (*api.RunOnceDurationConfig, error) { config := &api.RunOnceDurationConfig{} if reader == nil || reflect.ValueOf(reader).IsNil() { return config, nil } configBytes, err := ioutil.ReadAll(reader) if err != nil { return nil, err } err = configlatest.ReadYAML(configBytes, config) if err != nil { return nil, err } errs := validation.ValidateRunOnceDurationConfig(config) if len(errs) > 0 { return nil, errs.ToAggregate() } return config, nil }
func ReadConfig(configFile io.Reader) (*api.ClusterResourceOverrideConfig, error) { if configFile == nil || reflect.ValueOf(configFile).IsNil() /* pointer to nil */ { glog.V(5).Infof("%s has no config to read.", api.PluginName) return nil, nil } configBytes, err := ioutil.ReadAll(configFile) if err != nil { return nil, err } config := &api.ClusterResourceOverrideConfig{} err = configlatest.ReadYAML(configBytes, config) if err != nil { glog.V(5).Infof("%s error reading config: %v", api.PluginName, err) return nil, err } glog.V(5).Infof("%s config is: %v", api.PluginName, config) return config, nil }
func readConfig(reader io.Reader) (*ProjectRequestLimitConfig, error) { if reader == nil || reflect.ValueOf(reader).IsNil() { return &ProjectRequestLimitConfig{}, nil } configBytes, err := ioutil.ReadAll(reader) if err != nil { return nil, err } config := &ProjectRequestLimitConfig{} err = configlatest.ReadYAML(configBytes, config) if err != nil { return nil, err } errs := ValidateProjectRequestLimitConfig(config) if len(errs) > 0 { return nil, errs.ToAggregate() } return config, nil }
func init() { admission.RegisterPlugin(api.PluginName, func(client clientset.Interface, input io.Reader) (admission.Interface, error) { obj, err := configlatest.ReadYAML(input) if err != nil { return nil, err } if obj == nil { return nil, nil } config, ok := obj.(*api.ImagePolicyConfig) if !ok { return nil, fmt.Errorf("unexpected config object: %#v", obj) } if errs := validation.Validate(config); len(errs) > 0 { return nil, errs.ToAggregate() } glog.V(5).Infof("%s admission controller loaded with config: %#v", api.PluginName, config) return newImagePolicyPlugin(client, config) }) }
func TestDefaultPolicy(t *testing.T) { input, err := os.Open("api/v1/default-policy.yaml") if err != nil { t.Fatal(err) } obj, err := configlatest.ReadYAML(input) if err != nil { t.Fatal(err) } if obj == nil { t.Fatal(obj) } config, ok := obj.(*api.ImagePolicyConfig) if !ok { t.Fatal(config) } if errs := validation.Validate(config); len(errs) > 0 { t.Fatal(errs.ToAggregate()) } plugin, err := newImagePolicyPlugin(nil, config) if err != nil { t.Fatal(err) } goodImage := &imageapi.Image{ ObjectMeta: kapi.ObjectMeta{Name: "sha256:good"}, DockerImageReference: "integrated.registry/goodns/goodimage:good", } badImage := &imageapi.Image{ ObjectMeta: kapi.ObjectMeta{ Name: "sha256:bad", Annotations: map[string]string{ "images.openshift.io/deny-execution": "true", }, }, DockerImageReference: "integrated.registry/badns/badimage:bad", } client := testclient.NewSimpleFake( goodImage, badImage, // respond to image stream tag in this order: &unversioned.Status{ Reason: unversioned.StatusReasonNotFound, Code: 404, Details: &unversioned.StatusDetails{ Kind: "ImageStreamTag", }, }, &imageapi.ImageStreamTag{ ObjectMeta: kapi.ObjectMeta{Name: "mysql:goodtag", Namespace: "repo"}, Image: *goodImage, }, &imageapi.ImageStreamTag{ ObjectMeta: kapi.ObjectMeta{Name: "mysql:badtag", Namespace: "repo"}, Image: *badImage, }, ) store := setDefaultCache(plugin) plugin.SetOpenshiftClient(client) plugin.SetDefaultRegistryFunc(func() (string, bool) { return "integrated.registry", true }) if err := plugin.Validate(); err != nil { t.Fatal(err) } originalNowFn := now defer (func() { now = originalNowFn })() now = func() time.Time { return time.Unix(1, 0) } // should allow a non-integrated image attrs := admission.NewAttributesRecord( &kapi.Pod{Spec: kapi.PodSpec{Containers: []kapi.Container{{Image: "index.docker.io/mysql:latest"}}}}, nil, unversioned.GroupVersionKind{Version: "v1", Kind: "Pod"}, "default", "pod1", unversioned.GroupVersionResource{Version: "v1", Resource: "pods"}, "", admission.Create, nil, ) if err := plugin.Admit(attrs); err != nil { t.Fatal(err) } // should resolve the non-integrated image and allow it attrs = admission.NewAttributesRecord( &kapi.Pod{Spec: kapi.PodSpec{Containers: []kapi.Container{{Image: "index.docker.io/mysql@sha256:good"}}}}, nil, unversioned.GroupVersionKind{Version: "v1", Kind: "Pod"}, "default", "pod1", unversioned.GroupVersionResource{Version: "v1", Resource: "pods"}, "", admission.Create, nil, ) if err := plugin.Admit(attrs); err != nil { t.Fatal(err) } // should resolve the integrated image by digest and allow it attrs = admission.NewAttributesRecord( &kapi.Pod{Spec: kapi.PodSpec{Containers: []kapi.Container{{Image: "integrated.registry/repo/mysql@sha256:good"}}}}, nil, unversioned.GroupVersionKind{Version: "v1", Kind: "Pod"}, "default", "pod1", unversioned.GroupVersionResource{Version: "v1", Resource: "pods"}, "", admission.Create, nil, ) if err := plugin.Admit(attrs); err != nil { t.Fatal(err) } // should attempt resolve the integrated image by tag and fail because tag not found attrs = admission.NewAttributesRecord( &kapi.Pod{Spec: kapi.PodSpec{Containers: []kapi.Container{{Image: "integrated.registry/repo/mysql:missingtag"}}}}, nil, unversioned.GroupVersionKind{Version: "v1", Kind: "Pod"}, "default", "pod1", unversioned.GroupVersionResource{Version: "v1", Resource: "pods"}, "", admission.Create, nil, ) if err := plugin.Admit(attrs); err != nil { t.Fatal(err) } // should attempt resolve the integrated image by tag and allow it attrs = admission.NewAttributesRecord( &kapi.Pod{Spec: kapi.PodSpec{Containers: []kapi.Container{{Image: "integrated.registry/repo/mysql:goodtag"}}}}, nil, unversioned.GroupVersionKind{Version: "v1", Kind: "Pod"}, "default", "pod1", unversioned.GroupVersionResource{Version: "v1", Resource: "pods"}, "", admission.Create, nil, ) if err := plugin.Admit(attrs); err != nil { t.Fatal(err) } // should attempt resolve the integrated image by tag and forbid it attrs = admission.NewAttributesRecord( &kapi.Pod{Spec: kapi.PodSpec{Containers: []kapi.Container{{Image: "integrated.registry/repo/mysql:badtag"}}}}, nil, unversioned.GroupVersionKind{Version: "v1", Kind: "Pod"}, "default", "pod1", unversioned.GroupVersionResource{Version: "v1", Resource: "pods"}, "", admission.Create, nil, ) t.Logf("%#v", plugin.accepter) if err := plugin.Admit(attrs); err == nil || !apierrs.IsInvalid(err) { t.Fatal(err) } // should reject the non-integrated image due to the annotation attrs = admission.NewAttributesRecord( &kapi.Pod{Spec: kapi.PodSpec{Containers: []kapi.Container{{Image: "index.docker.io/mysql@sha256:bad"}}}}, nil, unversioned.GroupVersionKind{Version: "v1", Kind: "Pod"}, "default", "pod1", unversioned.GroupVersionResource{Version: "v1", Resource: "pods"}, "", admission.Create, nil, ) if err := plugin.Admit(attrs); err == nil || !apierrs.IsInvalid(err) { t.Fatal(err) } // should reject the non-integrated image due to the annotation on an init container attrs = admission.NewAttributesRecord( &kapi.Pod{Spec: kapi.PodSpec{InitContainers: []kapi.Container{{Image: "index.docker.io/mysql@sha256:bad"}}}}, nil, unversioned.GroupVersionKind{Version: "v1", Kind: "Pod"}, "default", "pod1", unversioned.GroupVersionResource{Version: "v1", Resource: "pods"}, "", admission.Create, nil, ) if err := plugin.Admit(attrs); err == nil || !apierrs.IsInvalid(err) { t.Fatal(err) } // should reject the non-integrated image due to the annotation for a build attrs = admission.NewAttributesRecord( &buildapi.Build{Spec: buildapi.BuildSpec{CommonSpec: buildapi.CommonSpec{Source: buildapi.BuildSource{Images: []buildapi.ImageSource{ {From: kapi.ObjectReference{Kind: "DockerImage", Name: "index.docker.io/mysql@sha256:bad"}}, }}}}}, nil, unversioned.GroupVersionKind{Version: "v1", Kind: "Build"}, "default", "build1", unversioned.GroupVersionResource{Version: "v1", Resource: "builds"}, "", admission.Create, nil, ) if err := plugin.Admit(attrs); err == nil || !apierrs.IsInvalid(err) { t.Fatal(err) } attrs = admission.NewAttributesRecord( &buildapi.Build{Spec: buildapi.BuildSpec{CommonSpec: buildapi.CommonSpec{Strategy: buildapi.BuildStrategy{DockerStrategy: &buildapi.DockerBuildStrategy{ From: &kapi.ObjectReference{Kind: "DockerImage", Name: "index.docker.io/mysql@sha256:bad"}, }}}}}, nil, unversioned.GroupVersionKind{Version: "v1", Kind: "Build"}, "default", "build1", unversioned.GroupVersionResource{Version: "v1", Resource: "builds"}, "", admission.Create, nil, ) if err := plugin.Admit(attrs); err == nil || !apierrs.IsInvalid(err) { t.Fatal(err) } attrs = admission.NewAttributesRecord( &buildapi.Build{Spec: buildapi.BuildSpec{CommonSpec: buildapi.CommonSpec{Strategy: buildapi.BuildStrategy{SourceStrategy: &buildapi.SourceBuildStrategy{ From: kapi.ObjectReference{Kind: "DockerImage", Name: "index.docker.io/mysql@sha256:bad"}, }}}}}, nil, unversioned.GroupVersionKind{Version: "v1", Kind: "Build"}, "default", "build1", unversioned.GroupVersionResource{Version: "v1", Resource: "builds"}, "", admission.Create, nil, ) if err := plugin.Admit(attrs); err == nil || !apierrs.IsInvalid(err) { t.Fatal(err) } attrs = admission.NewAttributesRecord( &buildapi.Build{Spec: buildapi.BuildSpec{CommonSpec: buildapi.CommonSpec{Strategy: buildapi.BuildStrategy{CustomStrategy: &buildapi.CustomBuildStrategy{ From: kapi.ObjectReference{Kind: "DockerImage", Name: "index.docker.io/mysql@sha256:bad"}, }}}}}, nil, unversioned.GroupVersionKind{Version: "v1", Kind: "Build"}, "default", "build1", unversioned.GroupVersionResource{Version: "v1", Resource: "builds"}, "", admission.Create, nil, ) if err := plugin.Admit(attrs); err == nil || !apierrs.IsInvalid(err) { t.Fatal(err) } // should allow the non-integrated image due to the annotation for a build config because it's not in the list, even though it has // a valid spec attrs = admission.NewAttributesRecord( &buildapi.BuildConfig{Spec: buildapi.BuildConfigSpec{CommonSpec: buildapi.CommonSpec{Source: buildapi.BuildSource{Images: []buildapi.ImageSource{ {From: kapi.ObjectReference{Kind: "DockerImage", Name: "index.docker.io/mysql@sha256:bad"}}, }}}}}, nil, unversioned.GroupVersionKind{Version: "v1", Kind: "BuildConfig"}, "default", "build1", unversioned.GroupVersionResource{Version: "v1", Resource: "buildconfigs"}, "", admission.Create, nil, ) if err := plugin.Admit(attrs); err != nil { t.Fatal(err) } // should hit the cache on the previously good image and continue to allow it (the copy in cache was previously safe) goodImage.Annotations = map[string]string{"images.openshift.io/deny-execution": "true"} attrs = admission.NewAttributesRecord( &kapi.Pod{Spec: kapi.PodSpec{Containers: []kapi.Container{{Image: "index.docker.io/mysql@sha256:good"}}}}, nil, unversioned.GroupVersionKind{Version: "v1", Kind: "Pod"}, "default", "pod1", unversioned.GroupVersionResource{Version: "v1", Resource: "pods"}, "", admission.Create, nil, ) if err := plugin.Admit(attrs); err != nil { t.Fatal(err) } // moving 2 minutes in the future should bypass the cache and deny the image now = func() time.Time { return time.Unix(1, 0).Add(2 * time.Minute) } attrs = admission.NewAttributesRecord( &kapi.Pod{Spec: kapi.PodSpec{Containers: []kapi.Container{{Image: "index.docker.io/mysql@sha256:good"}}}}, nil, unversioned.GroupVersionKind{Version: "v1", Kind: "Pod"}, "default", "pod1", unversioned.GroupVersionResource{Version: "v1", Resource: "pods"}, "", admission.Create, nil, ) if err := plugin.Admit(attrs); err == nil || !apierrs.IsInvalid(err) { t.Fatal(err) } // setting a namespace annotation should allow the rule to be skipped immediately store.Add(&kapi.Namespace{ ObjectMeta: kapi.ObjectMeta{ Namespace: "", Name: "default", Annotations: map[string]string{ api.IgnorePolicyRulesAnnotation: "execution-denied", }, }, }) attrs = admission.NewAttributesRecord( &kapi.Pod{Spec: kapi.PodSpec{Containers: []kapi.Container{{Image: "index.docker.io/mysql@sha256:good"}}}}, nil, unversioned.GroupVersionKind{Version: "v1", Kind: "Pod"}, "default", "pod1", unversioned.GroupVersionResource{Version: "v1", Resource: "pods"}, "", admission.Create, nil, ) if err := plugin.Admit(attrs); err != nil { t.Fatal(err) } }
func TestOAuthLDAP(t *testing.T) { var ( randomSuffix = string(kutil.NewUUID()) providerName = "myldapprovider" bindDN = "uid=admin,ou=company,ou=" + randomSuffix bindPassword = "******" + randomSuffix searchDN = "ou=company,ou=" + randomSuffix searchAttr = "myuid" + randomSuffix searchScope = "one" // must be "one","sub", or "base" searchFilter = "(myAttr=myValue)" // must be a valid LDAP filter format nameAttr1 = "missing-name-attr" nameAttr2 = "a-display-name" + randomSuffix idAttr1 = "missing-id-attr" idAttr2 = "dn" // "dn" is a special value, so don't add a random suffix to make sure we handle it correctly emailAttr1 = "missing-attr" emailAttr2 = "c-mail" + randomSuffix loginAttr1 = "missing-attr" loginAttr2 = "d-mylogin" + randomSuffix myUserUID = "myuser" myUserName = "******" myUserEmail = "*****@*****.**" myUserDN = searchAttr + "=" + myUserUID + "," + searchDN myUserPassword = "******" + randomSuffix ) expectedAttributes := [][]byte{} for _, attr := range sets.NewString(searchAttr, nameAttr1, nameAttr2, idAttr1, idAttr2, emailAttr1, emailAttr2, loginAttr1, loginAttr2).List() { expectedAttributes = append(expectedAttributes, []byte(attr)) } expectedSearchRequest := ldapserver.SearchRequest{ BaseObject: []byte(searchDN), Scope: ldapserver.SearchRequestSingleLevel, DerefAliases: 0, SizeLimit: 2, TimeLimit: 0, TypesOnly: false, Attributes: expectedAttributes, Filter: fmt.Sprintf("(&%s(%s=%s))", searchFilter, searchAttr, myUserUID), } // Start LDAP server ldapAddress, err := testserver.FindAvailableBindAddress(8389, 8400) if err != nil { t.Fatalf("could not allocate LDAP bind address: %v", err) } ldapServer := testutil.NewTestLDAPServer() ldapServer.SetPassword(bindDN, bindPassword) ldapServer.Start(ldapAddress) defer ldapServer.Stop() testutil.RequireEtcd(t) defer testutil.DumpEtcdOnFailure(t) masterOptions, err := testserver.DefaultMasterOptions() if err != nil { t.Fatalf("unexpected error: %v", err) } // Generate an encrypted file/keyfile to contain the bindPassword bindPasswordFile, err := ioutil.TempFile("", "bindPassword") if err != nil { t.Fatalf("unexpected error: %v", err) } defer os.Remove(bindPasswordFile.Name()) bindPasswordKeyFile, err := ioutil.TempFile("", "bindPasswordKey") if err != nil { t.Fatalf("unexpected error: %v", err) } defer os.Remove(bindPasswordKeyFile.Name()) encryptOpts := &admin.EncryptOptions{ CleartextData: []byte(bindPassword), EncryptedFile: bindPasswordFile.Name(), GenKeyFile: bindPasswordKeyFile.Name(), } if err := encryptOpts.Encrypt(); err != nil { t.Fatalf("unexpected error: %v", err) } masterOptions.OAuthConfig.IdentityProviders[0] = configapi.IdentityProvider{ Name: providerName, UseAsChallenger: true, UseAsLogin: true, MappingMethod: "claim", Provider: &configapi.LDAPPasswordIdentityProvider{ URL: fmt.Sprintf("ldap://%s/%s?%s?%s?%s", ldapAddress, searchDN, searchAttr, searchScope, searchFilter), BindDN: bindDN, BindPassword: configapi.StringSource{ StringSourceSpec: configapi.StringSourceSpec{ File: bindPasswordFile.Name(), KeyFile: bindPasswordKeyFile.Name(), }, }, Insecure: true, CA: "", Attributes: configapi.LDAPAttributeMapping{ ID: []string{idAttr1, idAttr2}, PreferredUsername: []string{loginAttr1, loginAttr2}, Name: []string{nameAttr1, nameAttr2}, Email: []string{emailAttr1, emailAttr2}, }, }, } // serialize to YAML to make sure a complex StringSource survives a round-trip serializedOptions, err := configapilatest.WriteYAML(masterOptions) if err != nil { t.Fatalf("unexpected error: %v", err) } // read back in deserializedObject, err := configapilatest.ReadYAML(bytes.NewBuffer(serializedOptions)) if err != nil { t.Fatalf("unexpected error: %v", err) } // assert type and proceed, using the deserialized version as our config if deserializedOptions, ok := deserializedObject.(*configapi.MasterConfig); !ok { t.Fatalf("unexpected object: %v", deserializedObject) } else { masterOptions = deserializedOptions } clusterAdminKubeConfig, err := testserver.StartConfiguredMaster(masterOptions) if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) if err != nil { t.Errorf("unexpected error: %v", err) } clusterAdminClient, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig) if err != nil { t.Errorf("unexpected error: %v", err) } // Use the server and CA info anonConfig := restclient.Config{} anonConfig.Host = clusterAdminClientConfig.Host anonConfig.CAFile = clusterAdminClientConfig.CAFile anonConfig.CAData = clusterAdminClientConfig.CAData // Make sure we can't authenticate as a missing user ldapServer.ResetRequests() if _, err := tokencmd.RequestToken(&anonConfig, nil, myUserUID, myUserPassword); err == nil { t.Error("Expected error, got none") } if len(ldapServer.BindRequests) != 1 { t.Errorf("Expected a single bind request for the search phase, got %d:\n%#v", len(ldapServer.BindRequests), ldapServer.BindRequests) } if len(ldapServer.SearchRequests) != 1 { t.Errorf("Expected a single search request, got %d:\n%#v", len(ldapServer.BindRequests), ldapServer.BindRequests) } // Add user ldapServer.SetPassword(myUserDN, myUserPassword) ldapServer.AddSearchResult(myUserDN, map[string]string{emailAttr2: myUserEmail, nameAttr2: myUserName, loginAttr2: myUserUID}) // Make sure we can't authenticate with a bad password ldapServer.ResetRequests() if _, err := tokencmd.RequestToken(&anonConfig, nil, myUserUID, "badpassword"); err == nil { t.Error("Expected error, got none") } if len(ldapServer.BindRequests) != 2 { t.Errorf("Expected a bind request for the search phase and a failed bind request for the auth phase, got %d:\n%#v", len(ldapServer.BindRequests), ldapServer.BindRequests) } if len(ldapServer.SearchRequests) != 1 { t.Errorf("Expected a single search request, got %d:\n%#v", len(ldapServer.BindRequests), ldapServer.BindRequests) } // Make sure we can get a token with a good password ldapServer.ResetRequests() accessToken, err := tokencmd.RequestToken(&anonConfig, nil, myUserUID, myUserPassword) if err != nil { t.Fatalf("Unexpected error: %v", err) } if len(accessToken) == 0 { t.Errorf("Expected access token, got none") } if len(ldapServer.BindRequests) != 2 { t.Errorf("Expected a bind request for the search phase and a failed bind request for the auth phase, got %d:\n%#v", len(ldapServer.BindRequests), ldapServer.BindRequests) } if len(ldapServer.SearchRequests) != 1 { t.Errorf("Expected a single search request, got %d:\n%#v", len(ldapServer.BindRequests), ldapServer.BindRequests) } if !reflect.DeepEqual(expectedSearchRequest.BaseObject, ldapServer.SearchRequests[0].BaseObject) { t.Errorf("Expected search base DN\n\t%#v\ngot\n\t%#v", string(expectedSearchRequest.BaseObject), string(ldapServer.SearchRequests[0].BaseObject), ) } if !reflect.DeepEqual(expectedSearchRequest.Filter, ldapServer.SearchRequests[0].Filter) { t.Errorf("Expected search filter\n\t%#v\ngot\n\t%#v", string(expectedSearchRequest.Filter), string(ldapServer.SearchRequests[0].Filter), ) } { expectedAttrs := []string{} for _, a := range expectedSearchRequest.Attributes { expectedAttrs = append(expectedAttrs, string(a)) } actualAttrs := []string{} for _, a := range ldapServer.SearchRequests[0].Attributes { actualAttrs = append(actualAttrs, string(a)) } if !reflect.DeepEqual(expectedAttrs, actualAttrs) { t.Errorf("Expected search attributes\n\t%#v\ngot\n\t%#v", expectedAttrs, actualAttrs) } } // Make sure we can use the token, and it represents who we expect userConfig := anonConfig userConfig.BearerToken = accessToken userClient, err := client.New(&userConfig) if err != nil { t.Fatalf("Unexpected error: %v", err) } user, err := userClient.Users().Get("~") if err != nil { t.Fatalf("Unexpected error: %v", err) } if user.Name != myUserUID { t.Fatalf("Expected %s as the user, got %v", myUserUID, user) } // Make sure the identity got created and contained the mapped attributes identity, err := clusterAdminClient.Identities().Get(fmt.Sprintf("%s:%s", providerName, myUserDN)) if err != nil { t.Fatalf("Unexpected error: %v", err) } if identity.ProviderUserName != myUserDN { t.Errorf("Expected %q, got %q", myUserDN, identity.ProviderUserName) } if v := identity.Extra[authapi.IdentityDisplayNameKey]; v != myUserName { t.Errorf("Expected %q, got %q", myUserName, v) } if v := identity.Extra[authapi.IdentityPreferredUsernameKey]; v != myUserUID { t.Errorf("Expected %q, got %q", myUserUID, v) } if v := identity.Extra[authapi.IdentityEmailKey]; v != myUserEmail { t.Errorf("Expected %q, got %q", myUserEmail, v) } }