// TestProjectMustExist verifies that content cannot be added in a project that does not exist func TestProjectMustExist(t *testing.T) { testutil.RequireEtcd(t) _, clusterAdminKubeConfig, err := testserver.StartTestMasterAPI() if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClient, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminKubeClient, err := testutil.GetClusterAdminKubeClient(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } pod := &kapi.Pod{ ObjectMeta: kapi.ObjectMeta{Name: "pod"}, Spec: kapi.PodSpec{ Containers: []kapi.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, RestartPolicy: kapi.RestartPolicyAlways, DNSPolicy: kapi.DNSClusterFirst, }, } _, err = clusterAdminKubeClient.Pods("test").Create(pod) if err == nil { t.Errorf("Expected an error on creation of a Kubernetes resource because namespace does not exist") } build := &buildapi.Build{ ObjectMeta: kapi.ObjectMeta{Name: "buildid", Namespace: "default"}, Spec: buildapi.BuildSpec{ Source: buildapi.BuildSource{ Git: &buildapi.GitBuildSource{ URI: "http://github.com/my/repository", }, ContextDir: "context", }, Strategy: buildapi.BuildStrategy{ DockerStrategy: &buildapi.DockerBuildStrategy{}, }, Output: buildapi.BuildOutput{ To: &kapi.ObjectReference{ Kind: "DockerImage", Name: "repository/data", }, }, }, Status: buildapi.BuildStatus{ Phase: buildapi.BuildPhaseNew, }, } _, err = clusterAdminClient.Builds("test").Create(build) if err == nil { t.Errorf("Expected an error on creation of a Origin resource because namespace does not exist") } }
func setupImageStreamAdmissionTest(t *testing.T) (*kclient.Client, *client.Client) { testutil.RequireEtcd(t) _, clusterAdminKubeConfig, err := testserver.StartTestMasterAPI() if err != nil { t.Fatalf("unexpected error: %v", err) } kClient, err := testutil.GetClusterAdminKubeClient(clusterAdminKubeConfig) if err != nil { t.Errorf("unexpected error: %v", err) } client, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig) if err != nil { t.Errorf("unexpected error: %v", err) } err = testutil.CreateNamespace(clusterAdminKubeConfig, testutil.Namespace()) if err != nil { t.Errorf("unexpected error: %v", err) } _, err = client.ImageStreams(testutil.Namespace()).Create(&imageapi.ImageStream{ ObjectMeta: kapi.ObjectMeta{ Name: "src", }, }) if err != nil { t.Fatal(err) } return kClient, client }
func TestUnprivilegedNewProjectDenied(t *testing.T) { testutil.RequireEtcd(t) defer testutil.DumpEtcdOnFailure(t) _, clusterAdminKubeConfig, err := testserver.StartTestMasterAPI() if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClient, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } role, err := clusterAdminClient.ClusterRoles().Get(bootstrappolicy.SelfProvisionerRoleName) if err != nil { t.Fatalf("unexpected error: %v", err) } role.Rules = []authorizationapi.PolicyRule{} if _, err := clusterAdminClient.ClusterRoles().Update(role); err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } valerieClientConfig := *clusterAdminClientConfig valerieClientConfig.Username = "" valerieClientConfig.Password = "" valerieClientConfig.BearerToken = "" valerieClientConfig.CertFile = "" valerieClientConfig.KeyFile = "" valerieClientConfig.CertData = nil valerieClientConfig.KeyData = nil accessToken, err := tokencmd.RequestToken(&valerieClientConfig, nil, "valerie", "security!") if err != nil { t.Fatalf("unexpected error: %v", err) } valerieClientConfig.BearerToken = accessToken valerieOpenshiftClient, err := client.New(&valerieClientConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } if err := testutil.WaitForClusterPolicyUpdate(valerieOpenshiftClient, "create", projectapi.Resource("projectrequests"), false); err != nil { t.Fatalf("unexpected error: %v", err) } // confirm that we have access to request the project _, err = valerieOpenshiftClient.ProjectRequests().List(kapi.ListOptions{}) if err == nil { t.Fatalf("expected error: %v", err) } expectedError := `You may not request a new project via this API.` if (err != nil) && (err.Error() != expectedError) { t.Fatalf("expected\n\t%v\ngot\n\t%v", expectedError, err.Error()) } }
func TestBootstrapPolicySelfSubjectAccessReviews(t *testing.T) { testutil.RequireEtcd(t) defer testutil.DumpEtcdOnFailure(t) _, clusterAdminKubeConfig, err := testserver.StartTestMasterAPI() if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) if err != nil { t.Errorf("unexpected error: %v", err) } valerieClientConfig := *clusterAdminClientConfig valerieClientConfig.Username = "" valerieClientConfig.Password = "" valerieClientConfig.BearerToken = "" valerieClientConfig.CertFile = "" valerieClientConfig.KeyFile = "" valerieClientConfig.CertData = nil valerieClientConfig.KeyData = nil accessToken, err := tokencmd.RequestToken(&valerieClientConfig, nil, "valerie", "security!") if err != nil { t.Fatalf("unexpected error: %v", err) } valerieClientConfig.BearerToken = accessToken valerieOpenshiftClient, err := client.New(&valerieClientConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } // can I get a subjectaccessreview on myself even if I have no rights to do it generally askCanICreatePolicyBindings := &authorizationapi.LocalSubjectAccessReview{ Action: authorizationapi.Action{Verb: "create", Resource: "policybindings"}, } subjectAccessReviewTest{ localInterface: valerieOpenshiftClient.LocalSubjectAccessReviews("openshift"), localReview: askCanICreatePolicyBindings, response: authorizationapi.SubjectAccessReviewResponse{ Allowed: false, Reason: `User "valerie" cannot create policybindings in project "openshift"`, Namespace: "openshift", }, }.run(t) // I shouldn't be allowed to ask whether someone else can perform an action askCanClusterAdminsCreateProject := &authorizationapi.LocalSubjectAccessReview{ Groups: sets.NewString("system:cluster-admins"), Action: authorizationapi.Action{Verb: "create", Resource: "projects"}, } subjectAccessReviewTest{ localInterface: valerieOpenshiftClient.LocalSubjectAccessReviews("openshift"), localReview: askCanClusterAdminsCreateProject, err: `User "valerie" cannot create localsubjectaccessreviews in project "openshift"`, }.run(t) }
func setupImageStreamAdmissionTest(t *testing.T) (*kclient.Client, *client.Client) { testutil.RequireEtcd(t) _, clusterAdminKubeConfig, err := testserver.StartTestMasterAPI() if err != nil { t.Fatalf("unexpected error: %v", err) } kClient, err := testutil.GetClusterAdminKubeClient(clusterAdminKubeConfig) if err != nil { t.Errorf("unexpected error: %v", err) } client, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig) if err != nil { t.Errorf("unexpected error: %v", err) } err = testutil.CreateNamespace(clusterAdminKubeConfig, testutil.Namespace()) if err != nil { t.Errorf("unexpected error: %v", err) } for { _, err = client.ImageStreams(testutil.Namespace()).Create(newImageStreamWithSpecTags("src", nil)) t.Logf("initing: %v", err) if err != nil { if errForbiddenWithRetry(err) { t.Logf("waiting for limit ranger to catch up: %v", err) continue } t.Fatalf("err: %#v", err) } break } return kClient, client }
func TestGroupCommands(t *testing.T) { testutil.RequireEtcd(t) defer testutil.DumpEtcdOnFailure(t) _, clusterAdminKubeConfig, err := testserver.StartTestMasterAPI() if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClient, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } newGroup := &groupscmd.NewGroupOptions{ GroupClient: clusterAdminClient.Groups(), Group: "group1", Users: []string{"first", "second", "third", "first"}, Printer: func(runtime.Object, io.Writer) error { return nil }, } if err := newGroup.AddGroup(); err != nil { t.Fatalf("unexpected error: %v", err) } group1, err := clusterAdminClient.Groups().Get("group1") if err != nil { t.Fatalf("unexpected error: %v", err) } if e, a := []string{"first", "second", "third"}, group1.Users; !reflect.DeepEqual(e, a) { t.Errorf("expected %v, actual %v", e, a) } modifyUsers := &groupscmd.GroupModificationOptions{ GroupClient: clusterAdminClient.Groups(), Group: "group1", Users: []string{"second", "fourth", "fifth"}, } if err := modifyUsers.AddUsers(); err != nil { t.Fatalf("unexpected error: %v", err) } group1, err = clusterAdminClient.Groups().Get("group1") if err != nil { t.Fatalf("unexpected error: %v", err) } if e, a := []string{"first", "second", "third", "fourth", "fifth"}, group1.Users; !reflect.DeepEqual(e, a) { t.Errorf("expected %v, actual %v", e, a) } if err := modifyUsers.RemoveUsers(); err != nil { t.Fatalf("unexpected error: %v", err) } group1, err = clusterAdminClient.Groups().Get("group1") if err != nil { t.Fatalf("unexpected error: %v", err) } if e, a := []string{"first", "third"}, group1.Users; !reflect.DeepEqual(e, a) { t.Errorf("expected %v, actual %v", e, a) } }
func TestCLIGetToken(t *testing.T) { testutil.RequireEtcd(t) _, clusterAdminKubeConfig, err := testserver.StartTestMasterAPI() checkErr(t, err) clusterAdminClientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) checkErr(t, err) anonymousConfig := clientcmd.AnonymousClientConfig(clusterAdminClientConfig) reader := bytes.NewBufferString("user\npass") accessToken, err := tokencmd.RequestToken(&anonymousConfig, reader, "", "") if err != nil { t.Errorf("Unexpected error: %v", err) } if len(accessToken) == 0 { t.Error("Expected accessToken, but did not get one") } clientConfig := clientcmd.AnonymousClientConfig(clusterAdminClientConfig) clientConfig.BearerToken = accessToken osClient, err := client.New(&clientConfig) checkErr(t, err) user, err := osClient.Users().Get("~") checkErr(t, err) if user.Name != "user" { t.Errorf("expected %v, got %v", "user", user.Name) } }
func TestBootstrapPolicyAuthenticatedUsersAgainstOpenshiftNamespace(t *testing.T) { testutil.RequireEtcd(t) defer testutil.DumpEtcdOnFailure(t) _, clusterAdminKubeConfig, err := testserver.StartTestMasterAPI() if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) if err != nil { t.Errorf("unexpected error: %v", err) } valerieClientConfig := *clusterAdminClientConfig valerieClientConfig.Username = "" valerieClientConfig.Password = "" valerieClientConfig.BearerToken = "" valerieClientConfig.CertFile = "" valerieClientConfig.KeyFile = "" valerieClientConfig.CertData = nil valerieClientConfig.KeyData = nil accessToken, err := tokencmd.RequestToken(&valerieClientConfig, nil, "valerie", "security!") if err != nil { t.Fatalf("unexpected error: %v", err) } valerieClientConfig.BearerToken = accessToken valerieOpenshiftClient, err := client.New(&valerieClientConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } openshiftSharedResourcesNamespace := "openshift" if _, err := valerieOpenshiftClient.Templates(openshiftSharedResourcesNamespace).List(kapi.ListOptions{}); err != nil { t.Errorf("unexpected error: %v", err) } if _, err := valerieOpenshiftClient.Templates(kapi.NamespaceDefault).List(kapi.ListOptions{}); err == nil || !kapierror.IsForbidden(err) { t.Errorf("unexpected error: %v", err) } if _, err := valerieOpenshiftClient.ImageStreams(openshiftSharedResourcesNamespace).List(kapi.ListOptions{}); err != nil { t.Errorf("unexpected error: %v", err) } if _, err := valerieOpenshiftClient.ImageStreams(kapi.NamespaceDefault).List(kapi.ListOptions{}); err == nil || !kapierror.IsForbidden(err) { t.Errorf("unexpected error: %v", err) } if _, err := valerieOpenshiftClient.ImageStreamTags(openshiftSharedResourcesNamespace).Get("name", "tag"); !kapierror.IsNotFound(err) { t.Errorf("unexpected error: %v", err) } if _, err := valerieOpenshiftClient.ImageStreamTags(kapi.NamespaceDefault).Get("name", "tag"); err == nil || !kapierror.IsForbidden(err) { t.Errorf("unexpected error: %v", err) } }
func TestRootRedirect(t *testing.T) { testutil.RequireEtcd(t) defer testutil.DumpEtcdOnFailure(t) masterConfig, _, err := testserver.StartTestMasterAPI() if err != nil { t.Fatalf("unexpected error: %v", err) } transport := knet.SetTransportDefaults(&http.Transport{ TLSClientConfig: &tls.Config{ InsecureSkipVerify: true, }, }) req, err := http.NewRequest("GET", masterConfig.AssetConfig.MasterPublicURL, nil) req.Header.Set("Accept", "*/*") resp, err := transport.RoundTrip(req) if err != nil { t.Fatalf("Unexpected error: %v", err) } if resp.StatusCode != http.StatusOK { t.Fatalf("Expected %d, got %d", http.StatusOK, resp.StatusCode) } if resp.Header.Get("Content-Type") != "application/json" { t.Fatalf("Expected %s, got %s", "application/json", resp.Header.Get("Content-Type")) } type result struct { Paths []string } body, err := ioutil.ReadAll(resp.Body) if err != nil { t.Fatalf("Unexpected error reading the body: %v", err) } var got result json.Unmarshal(body, &got) sort.Strings(got.Paths) if !reflect.DeepEqual(got.Paths, expectedIndex) { t.Fatalf("Unexpected index: got=%v, expected=%v", got, expectedIndex) } req, err = http.NewRequest("GET", masterConfig.AssetConfig.MasterPublicURL, nil) req.Header.Set("Accept", "text/html") resp, err = transport.RoundTrip(req) if err != nil { t.Errorf("Unexpected error: %v", err) } if resp.StatusCode != http.StatusFound { t.Errorf("Expected %d, got %d", http.StatusFound, resp.StatusCode) } if resp.Header.Get("Location") != masterConfig.AssetConfig.PublicURL { t.Errorf("Expected %s, got %s", masterConfig.AssetConfig.PublicURL, resp.Header.Get("Location")) } // TODO add a test for when asset config is nil, the redirect should not occur in this case even when // accept header contains text/html }
func TestAuthorizationRestrictedAccessForProjectAdmins(t *testing.T) { testutil.RequireEtcd(t) defer testutil.DumpEtcdOnFailure(t) _, clusterAdminKubeConfig, err := testserver.StartTestMasterAPI() if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClient, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } haroldClient, err := testserver.CreateNewProject(clusterAdminClient, *clusterAdminClientConfig, "hammer-project", "harold") if err != nil { t.Fatalf("unexpected error: %v", err) } markClient, err := testserver.CreateNewProject(clusterAdminClient, *clusterAdminClientConfig, "mallet-project", "mark") if err != nil { t.Fatalf("unexpected error: %v", err) } _, err = haroldClient.DeploymentConfigs("hammer-project").List(kapi.ListOptions{}) if err != nil { t.Fatalf("unexpected error: %v", err) } _, err = markClient.DeploymentConfigs("hammer-project").List(kapi.ListOptions{}) if (err == nil) || !kapierror.IsForbidden(err) { t.Fatalf("unexpected error: %v", err) } // projects are a special case where a get of a project actually sets a namespace. Make sure that // the namespace is properly special cased and set for authorization rules _, err = haroldClient.Projects().Get("hammer-project") if err != nil { t.Fatalf("unexpected error: %v", err) } _, err = markClient.Projects().Get("hammer-project") if (err == nil) || !kapierror.IsForbidden(err) { t.Fatalf("unexpected error: %v", err) } // wait for the project authorization cache to catch the change. It is on a one second period waitForProject(t, haroldClient, "hammer-project", 1*time.Second, 10) waitForProject(t, markClient, "mallet-project", 1*time.Second, 10) }
func TestBootstrapPolicyOverwritePolicyCommand(t *testing.T) { testutil.RequireEtcd(t) masterConfig, clusterAdminKubeConfig, err := testserver.StartTestMasterAPI() if err != nil { t.Fatalf("unexpected error: %v", err) } client, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig) if err != nil { t.Errorf("unexpected error: %v", err) } if err := client.ClusterPolicies().Delete(authorizationapi.PolicyName); err != nil { t.Errorf("unexpected error: %v", err) } // after the policy is deleted, we must wait for it to be cleared from the policy cache err = wait.Poll(10*time.Millisecond, 10*time.Second, func() (bool, error) { _, err := client.ClusterPolicies().List(kapi.ListOptions{}) if err == nil { return false, nil } if !kapierror.IsForbidden(err) { t.Errorf("unexpected error: %v", err) } return true, nil }) if err != nil { t.Errorf("timeout: %v", err) } etcdClient, err := etcd.MakeNewEtcdClient(masterConfig.EtcdClientInfo) if err != nil { t.Errorf("unexpected error: %v", err) } storageVersion := unversioned.GroupVersion{Group: "", Version: masterConfig.EtcdStorageConfig.OpenShiftStorageVersion} etcdHelper, err := origin.NewEtcdStorage(etcdClient, storageVersion, masterConfig.EtcdStorageConfig.OpenShiftStoragePrefix) if err != nil { t.Errorf("unexpected error: %v", err) } if err := admin.OverwriteBootstrapPolicy(etcdHelper, masterConfig.PolicyConfig.BootstrapPolicyFile, admin.CreateBootstrapPolicyFileFullCommand, true, ioutil.Discard); err != nil { t.Errorf("unexpected error: %v", err) } if _, err := client.ClusterPolicies().List(kapi.ListOptions{}); err != nil { t.Errorf("unexpected error: %v", err) } }
func TestGroupCommands(t *testing.T) { _, clusterAdminKubeConfig, err := testserver.StartTestMasterAPI() if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClient, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } newGroup := &groupscmd.NewGroupOptions{clusterAdminClient.Groups(), "group1", []string{"first", "second", "third", "first"}} if err := newGroup.AddGroup(); err != nil { t.Fatalf("unexpected error: %v", err) } group1, err := clusterAdminClient.Groups().Get("group1") if err != nil { t.Fatalf("unexpected error: %v", err) } if e, a := []string{"first", "second", "third"}, group1.Users; !reflect.DeepEqual(e, a) { t.Errorf("expected %v, actual %v", e, a) } modifyUsers := &groupscmd.GroupModificationOptions{clusterAdminClient.Groups(), "group1", []string{"second", "fourth", "fifth"}} if err := modifyUsers.AddUsers(); err != nil { t.Fatalf("unexpected error: %v", err) } group1, err = clusterAdminClient.Groups().Get("group1") if err != nil { t.Fatalf("unexpected error: %v", err) } if e, a := []string{"first", "second", "third", "fourth", "fifth"}, group1.Users; !reflect.DeepEqual(e, a) { t.Errorf("expected %v, actual %v", e, a) } if err := modifyUsers.RemoveUsers(); err != nil { t.Fatalf("unexpected error: %v", err) } group1, err = clusterAdminClient.Groups().Get("group1") if err != nil { t.Fatalf("unexpected error: %v", err) } if e, a := []string{"first", "third"}, group1.Users; !reflect.DeepEqual(e, a) { t.Errorf("expected %v, actual %v", e, a) } }
func TestImageStreamCreate(t *testing.T) { testutil.RequireEtcd(t) defer testutil.DumpEtcdOnFailure(t) _, clusterAdminKubeConfig, err := testserver.StartTestMasterAPI() if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClient, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig) if err != nil { t.Errorf("unexpected error: %v", err) } err = testutil.CreateNamespace(clusterAdminKubeConfig, testutil.Namespace()) if err != nil { t.Errorf("unexpected error: %v", err) } stream := mockImageStream() if _, err := clusterAdminClient.ImageStreams(testutil.Namespace()).Create(&imageapi.ImageStream{}); err == nil || !errors.IsInvalid(err) { t.Fatalf("Unexpected error: %v", err) } expected, err := clusterAdminClient.ImageStreams(testutil.Namespace()).Create(stream) if err != nil { t.Fatalf("Unexpected error: %v", err) } if expected.Name == "" { t.Errorf("Unexpected empty image Name %v", expected) } actual, err := clusterAdminClient.ImageStreams(testutil.Namespace()).Get(stream.Name) if err != nil { t.Fatalf("Unexpected error: %v", err) } if !reflect.DeepEqual(expected, actual) { t.Errorf("unexpected object: %s", diff.ObjectDiff(expected, actual)) } streams, err := clusterAdminClient.ImageStreams(testutil.Namespace()).List(kapi.ListOptions{}) if err != nil { t.Fatalf("Unexpected error %v", err) } if len(streams.Items) != 1 { t.Errorf("Expected one image, got %#v", streams.Items) } }
// launchAPI launches an api server and returns clients configured to // access it. func launchApi() (osclient.Interface, kclient.Interface, error) { _, clusterAdminKubeConfig, err := testserver.StartTestMasterAPI() if err != nil { return nil, nil, err } kc, err := testutil.GetClusterAdminKubeClient(clusterAdminKubeConfig) if err != nil { return nil, nil, err } oc, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig) if err != nil { return nil, nil, err } return oc, kc, nil }
func TestSelfSubjectAccessReviewsNonExistingNamespace(t *testing.T) { testutil.RequireEtcd(t) defer testutil.DumpEtcdOnFailure(t) _, clusterAdminKubeConfig, err := testserver.StartTestMasterAPI() if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) if err != nil { t.Errorf("unexpected error: %v", err) } valerieClientConfig := *clusterAdminClientConfig valerieClientConfig.Username = "" valerieClientConfig.Password = "" valerieClientConfig.BearerToken = "" valerieClientConfig.CertFile = "" valerieClientConfig.KeyFile = "" valerieClientConfig.CertData = nil valerieClientConfig.KeyData = nil valerieOpenshiftClient, _, _, err := testutil.GetClientForUser(valerieClientConfig, "valerie") if err != nil { t.Fatalf("unexpected error: %v", err) } // ensure that a SAR for a non-exisitng namespace gives a SAR response and not a // namespace doesn't exist response from admisison. askCanICreatePodsInNonExistingNamespace := &authorizationapi.LocalSubjectAccessReview{ Action: authorizationapi.Action{Namespace: "foo", Verb: "create", Resource: "pods"}, } subjectAccessReviewTest{ description: "ensure SAR for non-existing namespace does not leak namespace info", localInterface: valerieOpenshiftClient.LocalSubjectAccessReviews("foo"), localReview: askCanICreatePodsInNonExistingNamespace, response: authorizationapi.SubjectAccessReviewResponse{ Allowed: false, Reason: `User "valerie" cannot create pods in project "foo"`, Namespace: "foo", }, }.run(t) }
func TestScopedImpersonation(t *testing.T) { testutil.RequireEtcd(t) defer testutil.DumpEtcdOnFailure(t) _, clusterAdminKubeConfig, err := testserver.StartTestMasterAPI() if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClient, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } projectName := "hammer-project" userName := "******" if _, err := testserver.CreateNewProject(clusterAdminClient, *clusterAdminClientConfig, projectName, userName); err != nil { t.Fatalf("unexpected error: %v", err) } err = clusterAdminClient.Get(). SetHeader(authenticationapi.ImpersonateUserHeader, "harold"). SetHeader(authenticationapi.ImpersonateUserScopeHeader, "user:info"). Namespace(projectName).Resource("builds").Name("name").Do().Into(&buildapi.Build{}) if !kapierrors.IsForbidden(err) { t.Fatalf("unexpected error: %v", err) } user := &userapi.User{} err = clusterAdminClient.Get(). SetHeader(authenticationapi.ImpersonateUserHeader, "harold"). SetHeader(authenticationapi.ImpersonateUserScopeHeader, "user:info"). Resource("users").Name("~").Do().Into(user) if err != nil { t.Fatalf("unexpected error: %v", err) } if user.Name != "harold" { t.Fatalf("expected %v, got %v", "harold", user.Name) } }
func TestBootstrapPolicyOverwritePolicyCommand(t *testing.T) { testutil.RequireEtcd(t) defer testutil.DumpEtcdOnFailure(t) masterConfig, clusterAdminKubeConfig, err := testserver.StartTestMasterAPI() if err != nil { t.Fatalf("unexpected error: %v", err) } client, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig) if err != nil { t.Errorf("unexpected error: %v", err) } if err := client.ClusterPolicies().Delete(authorizationapi.PolicyName); err != nil { t.Errorf("unexpected error: %v", err) } // after the policy is deleted, we must wait for it to be cleared from the policy cache err = wait.Poll(10*time.Millisecond, 10*time.Second, func() (bool, error) { _, err := client.ClusterPolicies().List(kapi.ListOptions{}) if err == nil { return false, nil } if !kapierror.IsForbidden(err) { t.Errorf("unexpected error: %v", err) } return true, nil }) if err != nil { t.Errorf("timeout: %v", err) } optsGetter := restoptions.NewConfigGetter(*masterConfig) if err := admin.OverwriteBootstrapPolicy(optsGetter, masterConfig.PolicyConfig.BootstrapPolicyFile, admin.CreateBootstrapPolicyFileFullCommand, true, ioutil.Discard); err != nil { t.Errorf("unexpected error: %v", err) } if _, err := client.ClusterPolicies().List(kapi.ListOptions{}); err != nil { t.Errorf("unexpected error: %v", err) } }
func TestDiscoveryGroupVersions(t *testing.T) { testutil.RequireEtcd(t) defer testutil.DumpEtcdOnFailure(t) _, clusterAdminKubeConfig, err := testserver.StartTestMasterAPI() if err != nil { t.Fatalf("unexpected error starting test master: %v", err) } clusterAdminKubeClient, err := testutil.GetClusterAdminKubeClient(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } resources, err := clusterAdminKubeClient.Discovery().ServerResources() if err != nil { t.Fatalf("unexpected error: %v", err) } for _, resource := range resources { gv, err := unversioned.ParseGroupVersion(resource.GroupVersion) if err != nil { continue } allowedVersions := sets.NewString(configapi.KubeAPIGroupsToAllowedVersions[gv.Group]...) if !allowedVersions.Has(gv.Version) { t.Errorf("Disallowed group/version found in discovery: %#v", gv) } } expectedGroupVersions := sets.NewString() for group, versions := range configapi.KubeAPIGroupsToAllowedVersions { for _, version := range versions { expectedGroupVersions.Insert(unversioned.GroupVersion{Group: group, Version: version}.String()) } } discoveredGroupVersions := sets.StringKeySet(resources) if !reflect.DeepEqual(discoveredGroupVersions, expectedGroupVersions) { t.Fatalf("Expected %#v, got %#v", expectedGroupVersions.List(), discoveredGroupVersions.List()) } }
func TestRootRedirect(t *testing.T) { testutil.RequireEtcd(t) masterConfig, _, err := testserver.StartTestMasterAPI() if err != nil { t.Fatalf("unexpected error: %v", err) } transport := &http.Transport{ TLSClientConfig: &tls.Config{ InsecureSkipVerify: true, }, } req, err := http.NewRequest("GET", masterConfig.AssetConfig.MasterPublicURL, nil) req.Header.Set("Accept", "*/*") resp, err := transport.RoundTrip(req) if err != nil { t.Errorf("Unexpected error: %v", err) } if resp.StatusCode != http.StatusOK { t.Errorf("Expected %d, got %d", http.StatusOK, resp.StatusCode) } if resp.Header.Get("Content-Type") != "application/json" { t.Errorf("Expected %s, got %s", "application/json", resp.Header.Get("Content-Type")) } req, err = http.NewRequest("GET", masterConfig.AssetConfig.MasterPublicURL, nil) req.Header.Set("Accept", "text/html") resp, err = transport.RoundTrip(req) if err != nil { t.Errorf("Unexpected error: %v", err) } if resp.StatusCode != http.StatusFound { t.Errorf("Expected %d, got %d", http.StatusFound, resp.StatusCode) } if resp.Header.Get("Location") != masterConfig.AssetConfig.PublicURL { t.Errorf("Expected %s, got %s", masterConfig.AssetConfig.PublicURL, resp.Header.Get("Location")) } // TODO add a test for when asset config is nil, the redirect should not occur in this case even when // accept header contains text/html }
func TestBasicUserBasedGroupManipulation(t *testing.T) { testutil.RequireEtcd(t) defer testutil.DumpEtcdOnFailure(t) _, clusterAdminKubeConfig, err := testserver.StartTestMasterAPI() 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.Fatalf("unexpected error: %v", err) } valerieOpenshiftClient, _, _, err := testutil.GetClientForUser(*clusterAdminClientConfig, "valerie") if err != nil { t.Fatalf("unexpected error: %v", err) } // make sure we don't get back system groups firstValerie, err := clusterAdminClient.Users().Get("valerie") if err != nil { t.Fatalf("unexpected error: %v", err) } if len(firstValerie.Groups) != 0 { t.Errorf("unexpected groups: %v", firstValerie.Groups) } // make sure that user/~ returns groups for unbacked users expectedClusterAdminGroups := []string{"system:cluster-admins"} clusterAdminUser, err := clusterAdminClient.Users().Get("~") if err != nil { t.Fatalf("unexpected error: %v", err) } if !reflect.DeepEqual(clusterAdminUser.Groups, expectedClusterAdminGroups) { t.Errorf("expected %v, got %v", clusterAdminUser.Groups, expectedClusterAdminGroups) } valerieGroups := []string{"theGroup"} firstValerie.Groups = append(firstValerie.Groups, valerieGroups...) _, err = clusterAdminClient.Users().Update(firstValerie) if err != nil { t.Errorf("unexpected error: %v", err) } // make sure that user/~ doesn't get back system groups when it merges secondValerie, err := valerieOpenshiftClient.Users().Get("~") if err != nil { t.Fatalf("unexpected error: %v", err) } if !reflect.DeepEqual(secondValerie.Groups, valerieGroups) { t.Errorf("expected %v, got %v", secondValerie.Groups, valerieGroups) } _, err = valerieOpenshiftClient.Projects().Get("empty") if err == nil { t.Fatalf("expected error") } emptyProject := &projectapi.Project{} emptyProject.Name = "empty" _, err = clusterAdminClient.Projects().Create(emptyProject) if err != nil { t.Fatalf("unexpected error: %v", err) } roleBinding := &authorizationapi.RoleBinding{} roleBinding.Name = "admins" roleBinding.RoleRef.Name = "admin" roleBinding.Subjects = authorizationapi.BuildSubjects([]string{}, valerieGroups, uservalidation.ValidateUserName, uservalidation.ValidateGroupName) _, err = clusterAdminClient.RoleBindings("empty").Create(roleBinding) if err != nil { t.Fatalf("unexpected error: %v", err) } if err := testutil.WaitForPolicyUpdate(valerieOpenshiftClient, "empty", "get", kapi.Resource("pods"), true); err != nil { t.Error(err) } // make sure that user groups are respected for policy _, err = valerieOpenshiftClient.Projects().Get("empty") if err != nil { t.Fatalf("unexpected error: %v", err) } }
func TestBasicGroupManipulation(t *testing.T) { testutil.RequireEtcd(t) defer testutil.DumpEtcdOnFailure(t) _, clusterAdminKubeConfig, err := testserver.StartTestMasterAPI() 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.Fatalf("unexpected error: %v", err) } valerieOpenshiftClient, _, _, err := testutil.GetClientForUser(*clusterAdminClientConfig, "valerie") if err != nil { t.Fatalf("unexpected error: %v", err) } theGroup := &userapi.Group{} theGroup.Name = "thegroup" theGroup.Users = append(theGroup.Users, "valerie", "victor") _, err = clusterAdminClient.Groups().Create(theGroup) if err != nil { t.Errorf("unexpected error: %v", err) } _, err = valerieOpenshiftClient.Projects().Get("empty") if err == nil { t.Fatalf("expected error") } emptyProject := &projectapi.Project{} emptyProject.Name = "empty" _, err = clusterAdminClient.Projects().Create(emptyProject) if err != nil { t.Fatalf("unexpected error: %v", err) } roleBinding := &authorizationapi.RoleBinding{} roleBinding.Name = "admins" roleBinding.RoleRef.Name = "admin" roleBinding.Subjects = authorizationapi.BuildSubjects([]string{}, []string{theGroup.Name}, uservalidation.ValidateUserName, uservalidation.ValidateGroupName) _, err = clusterAdminClient.RoleBindings("empty").Create(roleBinding) if err != nil { t.Fatalf("unexpected error: %v", err) } if err := testutil.WaitForPolicyUpdate(valerieOpenshiftClient, "empty", "get", kapi.Resource("pods"), true); err != nil { t.Error(err) } // make sure that user groups are respected for policy _, err = valerieOpenshiftClient.Projects().Get("empty") if err != nil { t.Errorf("unexpected error: %v", err) } victorOpenshiftClient, _, _, err := testutil.GetClientForUser(*clusterAdminClientConfig, "victor") if err != nil { t.Fatalf("unexpected error: %v", err) } _, err = victorOpenshiftClient.Projects().Get("empty") if err != nil { t.Errorf("unexpected error: %v", err) } }
func TestAuthorizationSubjectAccessReview(t *testing.T) { testutil.RequireEtcd(t) defer testutil.DumpEtcdOnFailure(t) _, clusterAdminKubeConfig, err := testserver.StartTestMasterAPI() if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClient, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } haroldClient, err := testserver.CreateNewProject(clusterAdminClient, *clusterAdminClientConfig, "hammer-project", "harold") if err != nil { t.Fatalf("unexpected error: %v", err) } markClient, err := testserver.CreateNewProject(clusterAdminClient, *clusterAdminClientConfig, "mallet-project", "mark") if err != nil { t.Fatalf("unexpected error: %v", err) } dannyClient, _, dannyConfig, err := testutil.GetClientForUser(*clusterAdminClientConfig, "danny") if err != nil { t.Fatalf("error requesting token: %v", err) } anonymousConfig := clientcmd.AnonymousClientConfig(clusterAdminClientConfig) anonymousClient, err := client.New(&anonymousConfig) if err != nil { t.Fatalf("error getting anonymous client: %v", err) } addAnonymous := &policy.RoleModificationOptions{ RoleNamespace: "", RoleName: bootstrappolicy.EditRoleName, RoleBindingAccessor: policy.NewLocalRoleBindingAccessor("hammer-project", clusterAdminClient), Users: []string{"system:anonymous"}, } if err := addAnonymous.AddRole(); err != nil { t.Errorf("unexpected error: %v", err) } addDanny := &policy.RoleModificationOptions{ RoleNamespace: "", RoleName: bootstrappolicy.ViewRoleName, RoleBindingAccessor: policy.NewLocalRoleBindingAccessor("default", clusterAdminClient), Users: []string{"danny"}, } if err := addDanny.AddRole(); err != nil { t.Errorf("unexpected error: %v", err) } askCanDannyGetProject := &authorizationapi.SubjectAccessReview{ User: "******", Action: authorizationapi.Action{Verb: "get", Resource: "projects"}, } subjectAccessReviewTest{ description: "cluster admin told danny can get project default", localInterface: clusterAdminClient.LocalSubjectAccessReviews("default"), localReview: &authorizationapi.LocalSubjectAccessReview{ User: "******", Action: authorizationapi.Action{Verb: "get", Resource: "projects"}, }, response: authorizationapi.SubjectAccessReviewResponse{ Allowed: true, Reason: "allowed by rule in default", Namespace: "default", }, }.run(t) subjectAccessReviewTest{ description: "cluster admin told danny cannot get projects cluster-wide", clusterInterface: clusterAdminClient.SubjectAccessReviews(), clusterReview: askCanDannyGetProject, response: authorizationapi.SubjectAccessReviewResponse{ Allowed: false, Reason: `User "danny" cannot get projects at the cluster scope`, Namespace: "", }, }.run(t) subjectAccessReviewTest{ description: "as danny, can I make cluster subject access reviews", clusterInterface: dannyClient.SubjectAccessReviews(), clusterReview: askCanDannyGetProject, err: `User "danny" cannot create subjectaccessreviews at the cluster scope`, }.run(t) subjectAccessReviewTest{ description: "as anonymous, can I make cluster subject access reviews", clusterInterface: anonymousClient.SubjectAccessReviews(), clusterReview: askCanDannyGetProject, err: `User "system:anonymous" cannot create subjectaccessreviews at the cluster scope`, }.run(t) addValerie := &policy.RoleModificationOptions{ RoleNamespace: "", RoleName: bootstrappolicy.ViewRoleName, RoleBindingAccessor: policy.NewLocalRoleBindingAccessor("hammer-project", haroldClient), Users: []string{"valerie"}, } if err := addValerie.AddRole(); err != nil { t.Errorf("unexpected error: %v", err) } addEdgar := &policy.RoleModificationOptions{ RoleNamespace: "", RoleName: bootstrappolicy.EditRoleName, RoleBindingAccessor: policy.NewLocalRoleBindingAccessor("mallet-project", markClient), Users: []string{"edgar"}, } if err := addEdgar.AddRole(); err != nil { t.Fatalf("unexpected error: %v", err) } askCanValerieGetProject := &authorizationapi.LocalSubjectAccessReview{ User: "******", Action: authorizationapi.Action{Verb: "get", Resource: "projects"}, } subjectAccessReviewTest{ description: "harold told valerie can get project hammer-project", localInterface: haroldClient.LocalSubjectAccessReviews("hammer-project"), localReview: askCanValerieGetProject, response: authorizationapi.SubjectAccessReviewResponse{ Allowed: true, Reason: "allowed by rule in hammer-project", Namespace: "hammer-project", }, }.run(t) subjectAccessReviewTest{ description: "mark told valerie cannot get project mallet-project", localInterface: markClient.LocalSubjectAccessReviews("mallet-project"), localReview: askCanValerieGetProject, response: authorizationapi.SubjectAccessReviewResponse{ Allowed: false, Reason: `User "valerie" cannot get projects in project "mallet-project"`, Namespace: "mallet-project", }, }.run(t) askCanEdgarDeletePods := &authorizationapi.LocalSubjectAccessReview{ User: "******", Action: authorizationapi.Action{Verb: "delete", Resource: "pods"}, } subjectAccessReviewTest{ description: "mark told edgar can delete pods in mallet-project", localInterface: markClient.LocalSubjectAccessReviews("mallet-project"), localReview: askCanEdgarDeletePods, response: authorizationapi.SubjectAccessReviewResponse{ Allowed: true, Reason: "allowed by rule in mallet-project", Namespace: "mallet-project", }, }.run(t) // ensure unprivileged users cannot check other users' access subjectAccessReviewTest{ description: "harold denied ability to run subject access review in project mallet-project", localInterface: haroldClient.LocalSubjectAccessReviews("mallet-project"), localReview: askCanEdgarDeletePods, err: `User "harold" cannot create localsubjectaccessreviews in project "mallet-project"`, }.run(t) subjectAccessReviewTest{ description: "system:anonymous denied ability to run subject access review in project mallet-project", localInterface: anonymousClient.LocalSubjectAccessReviews("mallet-project"), localReview: askCanEdgarDeletePods, err: `User "system:anonymous" cannot create localsubjectaccessreviews in project "mallet-project"`, }.run(t) // ensure message does not leak whether the namespace exists or not subjectAccessReviewTest{ description: "harold denied ability to run subject access review in project nonexistent-project", localInterface: haroldClient.LocalSubjectAccessReviews("nonexistent-project"), localReview: askCanEdgarDeletePods, err: `User "harold" cannot create localsubjectaccessreviews in project "nonexistent-project"`, }.run(t) subjectAccessReviewTest{ description: "system:anonymous denied ability to run subject access review in project nonexistent-project", localInterface: anonymousClient.LocalSubjectAccessReviews("nonexistent-project"), localReview: askCanEdgarDeletePods, err: `User "system:anonymous" cannot create localsubjectaccessreviews in project "nonexistent-project"`, }.run(t) askCanHaroldUpdateProject := &authorizationapi.LocalSubjectAccessReview{ User: "******", Action: authorizationapi.Action{Verb: "update", Resource: "projects"}, } subjectAccessReviewTest{ description: "harold told harold can update project hammer-project", localInterface: haroldClient.LocalSubjectAccessReviews("hammer-project"), localReview: askCanHaroldUpdateProject, response: authorizationapi.SubjectAccessReviewResponse{ Allowed: true, Reason: "allowed by rule in hammer-project", Namespace: "hammer-project", }, }.run(t) askCanClusterAdminsCreateProject := &authorizationapi.SubjectAccessReview{ Groups: sets.NewString("system:cluster-admins"), Action: authorizationapi.Action{Verb: "create", Resource: "projects"}, } subjectAccessReviewTest{ description: "cluster admin told cluster admins can create projects", clusterInterface: clusterAdminClient.SubjectAccessReviews(), clusterReview: askCanClusterAdminsCreateProject, response: authorizationapi.SubjectAccessReviewResponse{ Allowed: true, Reason: "allowed by cluster rule", Namespace: "", }, }.run(t) subjectAccessReviewTest{ description: "harold denied ability to run cluster subject access review", clusterInterface: haroldClient.SubjectAccessReviews(), clusterReview: askCanClusterAdminsCreateProject, err: `User "harold" cannot create subjectaccessreviews at the cluster scope`, }.run(t) askCanICreatePods := &authorizationapi.LocalSubjectAccessReview{ Action: authorizationapi.Action{Verb: "create", Resource: "pods"}, } subjectAccessReviewTest{ description: "harold told he can create pods in project hammer-project", localInterface: haroldClient.LocalSubjectAccessReviews("hammer-project"), localReview: askCanICreatePods, response: authorizationapi.SubjectAccessReviewResponse{ Allowed: true, Reason: "allowed by rule in hammer-project", Namespace: "hammer-project", }, }.run(t) subjectAccessReviewTest{ description: "system:anonymous told he can create pods in project hammer-project", localInterface: anonymousClient.LocalSubjectAccessReviews("hammer-project"), localReview: askCanICreatePods, response: authorizationapi.SubjectAccessReviewResponse{ Allowed: true, Reason: "allowed by rule in hammer-project", Namespace: "hammer-project", }, }.run(t) // test checking self permissions when denied subjectAccessReviewTest{ description: "harold told he cannot create pods in project mallet-project", localInterface: haroldClient.LocalSubjectAccessReviews("mallet-project"), localReview: askCanICreatePods, response: authorizationapi.SubjectAccessReviewResponse{ Allowed: false, Reason: `User "harold" cannot create pods in project "mallet-project"`, Namespace: "mallet-project", }, }.run(t) subjectAccessReviewTest{ description: "system:anonymous told he cannot create pods in project mallet-project", localInterface: anonymousClient.LocalSubjectAccessReviews("mallet-project"), localReview: askCanICreatePods, response: authorizationapi.SubjectAccessReviewResponse{ Allowed: false, Reason: `User "system:anonymous" cannot create pods in project "mallet-project"`, Namespace: "mallet-project", }, }.run(t) // test checking self-permissions doesn't leak whether namespace exists or not subjectAccessReviewTest{ description: "harold told he cannot create pods in project nonexistent-project", localInterface: haroldClient.LocalSubjectAccessReviews("nonexistent-project"), localReview: askCanICreatePods, response: authorizationapi.SubjectAccessReviewResponse{ Allowed: false, Reason: `User "harold" cannot create pods in project "nonexistent-project"`, Namespace: "nonexistent-project", }, }.run(t) subjectAccessReviewTest{ description: "system:anonymous told he cannot create pods in project nonexistent-project", localInterface: anonymousClient.LocalSubjectAccessReviews("nonexistent-project"), localReview: askCanICreatePods, response: authorizationapi.SubjectAccessReviewResponse{ Allowed: false, Reason: `User "system:anonymous" cannot create pods in project "nonexistent-project"`, Namespace: "nonexistent-project", }, }.run(t) askCanICreatePolicyBindings := &authorizationapi.LocalSubjectAccessReview{ Action: authorizationapi.Action{Verb: "create", Resource: "policybindings"}, } subjectAccessReviewTest{ description: "harold told he can create policybindings in project hammer-project", localInterface: haroldClient.LocalSubjectAccessReviews("hammer-project"), localReview: askCanICreatePolicyBindings, response: authorizationapi.SubjectAccessReviewResponse{ Allowed: false, Reason: `User "harold" cannot create policybindings in project "hammer-project"`, Namespace: "hammer-project", }, }.run(t) // impersonate SAR tests // impersonated empty token SAR shouldn't be allowed at all // impersonated danny token SAR shouldn't be allowed to see pods in hammer or in cluster // impersonated danny token SAR should be allowed to see pods in default // we need a token client for overriding if err != nil { t.Fatalf("unexpected error: %v", err) } otherAdminClient, _, _, err := testutil.GetClientForUser(*clusterAdminClientConfig, "other-admin") if err != nil { t.Fatalf("error requesting token: %v", err) } addOtherAdmin := &policy.RoleModificationOptions{ RoleNamespace: "", RoleName: bootstrappolicy.ClusterAdminRoleName, RoleBindingAccessor: policy.NewClusterRoleBindingAccessor(clusterAdminClient), Users: []string{"other-admin"}, } if err := addOtherAdmin.AddRole(); err != nil { t.Errorf("unexpected error: %v", err) } subjectAccessReviewTest{ description: "empty token impersonate can't see pods in namespace", localInterface: otherAdminClient.ImpersonateLocalSubjectAccessReviews("hammer-project", ""), localReview: &authorizationapi.LocalSubjectAccessReview{ Action: authorizationapi.Action{Verb: "list", Resource: "pods"}, }, err: `impersonating token may not be empty`, }.run(t) subjectAccessReviewTest{ description: "empty token impersonate can't see pods in cluster", clusterInterface: otherAdminClient.ImpersonateSubjectAccessReviews(""), clusterReview: &authorizationapi.SubjectAccessReview{ Action: authorizationapi.Action{Verb: "list", Resource: "pods"}, }, err: `impersonating token may not be empty`, }.run(t) subjectAccessReviewTest{ description: "danny impersonate can't see pods in hammer namespace", localInterface: otherAdminClient.ImpersonateLocalSubjectAccessReviews("hammer-project", dannyConfig.BearerToken), localReview: &authorizationapi.LocalSubjectAccessReview{ Action: authorizationapi.Action{Verb: "list", Resource: "pods"}, }, response: authorizationapi.SubjectAccessReviewResponse{ Allowed: false, Reason: `User "danny" cannot list pods in project "hammer-project"`, Namespace: "hammer-project", }, }.run(t) subjectAccessReviewTest{ description: "danny impersonate can't see pods in cluster", clusterInterface: otherAdminClient.ImpersonateSubjectAccessReviews(dannyConfig.BearerToken), clusterReview: &authorizationapi.SubjectAccessReview{ Action: authorizationapi.Action{Verb: "list", Resource: "pods"}, }, response: authorizationapi.SubjectAccessReviewResponse{ Allowed: false, Reason: `User "danny" cannot list all pods in the cluster`, }, }.run(t) subjectAccessReviewTest{ description: "danny impersonate can see pods in default", localInterface: otherAdminClient.ImpersonateLocalSubjectAccessReviews("default", dannyConfig.BearerToken), localReview: &authorizationapi.LocalSubjectAccessReview{ Action: authorizationapi.Action{Verb: "list", Resource: "pods"}, }, response: authorizationapi.SubjectAccessReviewResponse{ Allowed: true, Reason: `allowed by rule in default`, Namespace: "default", }, }.run(t) }
func TestAuthorizationResourceAccessReview(t *testing.T) { testutil.RequireEtcd(t) defer testutil.DumpEtcdOnFailure(t) _, clusterAdminKubeConfig, err := testserver.StartTestMasterAPI() if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClient, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } haroldClient, err := testserver.CreateNewProject(clusterAdminClient, *clusterAdminClientConfig, "hammer-project", "harold") if err != nil { t.Fatalf("unexpected error: %v", err) } markClient, err := testserver.CreateNewProject(clusterAdminClient, *clusterAdminClientConfig, "mallet-project", "mark") if err != nil { t.Fatalf("unexpected error: %v", err) } addValerie := &policy.RoleModificationOptions{ RoleNamespace: "", RoleName: bootstrappolicy.ViewRoleName, RoleBindingAccessor: policy.NewLocalRoleBindingAccessor("hammer-project", haroldClient), Users: []string{"valerie"}, } if err := addValerie.AddRole(); err != nil { t.Fatalf("unexpected error: %v", err) } addEdgar := &policy.RoleModificationOptions{ RoleNamespace: "", RoleName: bootstrappolicy.EditRoleName, RoleBindingAccessor: policy.NewLocalRoleBindingAccessor("mallet-project", markClient), Users: []string{"edgar"}, } if err := addEdgar.AddRole(); err != nil { t.Fatalf("unexpected error: %v", err) } requestWhoCanViewDeploymentConfigs := &authorizationapi.ResourceAccessReview{ Action: authorizationapi.Action{Verb: "get", Resource: "deploymentconfigs"}, } localRequestWhoCanViewDeploymentConfigs := &authorizationapi.LocalResourceAccessReview{ Action: authorizationapi.Action{Verb: "get", Resource: "deploymentconfigs"}, } { test := localResourceAccessReviewTest{ description: "who can view deploymentconfigs in hammer by harold", clientInterface: haroldClient.LocalResourceAccessReviews("hammer-project"), review: localRequestWhoCanViewDeploymentConfigs, response: authorizationapi.ResourceAccessReviewResponse{ Users: sets.NewString("harold", "valerie"), Groups: sets.NewString(), Namespace: "hammer-project", }, } test.response.Users.Insert(globalClusterReaderUsers.List()...) test.response.Groups.Insert(globalClusterReaderGroups.List()...) test.run(t) } { test := localResourceAccessReviewTest{ description: "who can view deploymentconfigs in mallet by mark", clientInterface: markClient.LocalResourceAccessReviews("mallet-project"), review: localRequestWhoCanViewDeploymentConfigs, response: authorizationapi.ResourceAccessReviewResponse{ Users: sets.NewString("mark", "edgar"), Groups: sets.NewString(), Namespace: "mallet-project", }, } test.response.Users.Insert(globalClusterReaderUsers.List()...) test.response.Groups.Insert(globalClusterReaderGroups.List()...) test.run(t) } // mark should not be able to make global access review requests { test := resourceAccessReviewTest{ description: "who can view deploymentconfigs in all by mark", clientInterface: markClient.ResourceAccessReviews(), review: requestWhoCanViewDeploymentConfigs, err: "cannot ", } test.run(t) } // a cluster-admin should be able to make global access review requests { test := resourceAccessReviewTest{ description: "who can view deploymentconfigs in all by cluster-admin", clientInterface: clusterAdminClient.ResourceAccessReviews(), review: requestWhoCanViewDeploymentConfigs, response: authorizationapi.ResourceAccessReviewResponse{ Users: sets.NewString(), Groups: sets.NewString(), }, } test.response.Users.Insert(globalClusterReaderUsers.List()...) test.response.Groups.Insert(globalClusterReaderGroups.List()...) test.run(t) } { if err := clusterAdminClient.ClusterRoles().Delete(bootstrappolicy.AdminRoleName); err != nil { t.Errorf("unexpected error: %v", err) } test := localResourceAccessReviewTest{ description: "who can view deploymentconfigs in mallet by cluster-admin", clientInterface: clusterAdminClient.LocalResourceAccessReviews("mallet-project"), review: localRequestWhoCanViewDeploymentConfigs, response: authorizationapi.ResourceAccessReviewResponse{ Users: sets.NewString("edgar"), Groups: sets.NewString(), Namespace: "mallet-project", EvaluationError: `role "admin" not found`, }, } test.response.Users.Insert(globalClusterReaderUsers.List()...) test.response.Groups.Insert(globalClusterReaderGroups.List()...) test.run(t) } }
func TestClusterReaderCoverage(t *testing.T) { testutil.RequireEtcd(t) defer testutil.DumpEtcdOnFailure(t) _, clusterAdminKubeConfig, err := testserver.StartTestMasterAPI() if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClient, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } discoveryClient := client.NewDiscoveryClient(clusterAdminClient.RESTClient) // (map[string]*unversioned.APIResourceList, error) allResourceList, err := discoveryClient.ServerResources() if err != nil { t.Fatalf("unexpected error: %v", err) } allResources := map[unversioned.GroupResource]bool{} for _, resources := range allResourceList { version, err := unversioned.ParseGroupVersion(resources.GroupVersion) if err != nil { t.Fatalf("unexpected error: %v", err) } for _, resource := range resources.APIResources { allResources[version.WithResource(resource.Name).GroupResource()] = true } } escalatingResources := map[unversioned.GroupResource]bool{ oauthapi.Resource("oauthauthorizetokens"): true, oauthapi.Resource("oauthaccesstokens"): true, oauthapi.Resource("oauthclients"): true, imageapi.Resource("imagestreams/secrets"): true, kapi.Resource("secrets"): true, kapi.Resource("pods/exec"): true, kapi.Resource("pods/proxy"): true, kapi.Resource("pods/portforward"): true, kapi.Resource("nodes/proxy"): true, kapi.Resource("services/proxy"): true, } readerRole, err := clusterAdminClient.ClusterRoles().Get(bootstrappolicy.ClusterReaderRoleName) if err != nil { t.Fatalf("unexpected error: %v", err) } for _, rule := range readerRole.Rules { for _, group := range rule.APIGroups { for resource := range rule.Resources { gr := unversioned.GroupResource{Group: group, Resource: resource} if escalatingResources[gr] { t.Errorf("cluster-reader role has escalating resource %v. Check pkg/cmd/server/bootstrappolicy/policy.go.", gr) } delete(allResources, gr) } } } // remove escalating resources that cluster-reader should not have access to for resource := range escalatingResources { delete(allResources, resource) } // remove resources without read APIs nonreadingResources := []unversioned.GroupResource{ buildapi.Resource("buildconfigs/instantiatebinary"), buildapi.Resource("buildconfigs/instantiate"), buildapi.Resource("builds/clone"), deployapi.Resource("deploymentconfigrollbacks"), deployapi.Resource("generatedeploymentconfigs"), deployapi.Resource("deploymentconfigs/rollback"), imageapi.Resource("imagestreamimports"), imageapi.Resource("imagestreammappings"), extensionsapi.Resource("deployments/rollback"), kapi.Resource("pods/attach"), kapi.Resource("namespaces/finalize"), } for _, resource := range nonreadingResources { delete(allResources, resource) } // anything left in the map is missing from the permissions if len(allResources) > 0 { t.Errorf("cluster-reader role is missing %v. Check pkg/cmd/server/bootstrappolicy/policy.go.", allResources) } }
func TestAuthorizationResolution(t *testing.T) { testutil.RequireEtcd(t) defer testutil.DumpEtcdOnFailure(t) _, clusterAdminKubeConfig, err := testserver.StartTestMasterAPI() if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClient, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } addValerie := &policy.RoleModificationOptions{ RoleNamespace: "", RoleName: bootstrappolicy.ViewRoleName, RoleBindingAccessor: policy.NewClusterRoleBindingAccessor(clusterAdminClient), Users: []string{"valerie"}, } if err := addValerie.AddRole(); err != nil { t.Fatalf("unexpected error: %v", err) } if err = clusterAdminClient.ClusterRoles().Delete(bootstrappolicy.ViewRoleName); err != nil { t.Fatalf("unexpected error: %v", err) } addEdgar := &policy.RoleModificationOptions{ RoleNamespace: "", RoleName: bootstrappolicy.EditRoleName, RoleBindingAccessor: policy.NewClusterRoleBindingAccessor(clusterAdminClient), Users: []string{"edgar"}, } if err := addEdgar.AddRole(); err != nil { t.Fatalf("unexpected error: %v", err) } // try to add Valerie to a non-existent role if err := addValerie.AddRole(); !kapierror.IsNotFound(err) { t.Fatalf("unexpected error: %v", err) } roleWithGroup := &authorizationapi.ClusterRole{} roleWithGroup.Name = "with-group" roleWithGroup.Rules = append(roleWithGroup.Rules, authorizationapi.PolicyRule{ Verbs: sets.NewString("list"), Resources: sets.NewString("resourcegroup:builds"), }) if _, err := clusterAdminClient.ClusterRoles().Create(roleWithGroup); err != nil { t.Fatalf("unexpected error: %v", err) } addBuildLister := &policy.RoleModificationOptions{ RoleNamespace: "", RoleName: "with-group", RoleBindingAccessor: policy.NewClusterRoleBindingAccessor(clusterAdminClient), Users: []string{"build-lister"}, } if err := addBuildLister.AddRole(); err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } buildListerClient, _, _, err := testutil.GetClientForUser(*clusterAdminConfig, "build-lister") if err != nil { t.Fatalf("unexpected error: %v", err) } if _, err := buildListerClient.Builds(kapi.NamespaceDefault).List(kapi.ListOptions{}); err != nil { t.Fatalf("unexpected error: %v", err) } if _, err := buildListerClient.DeploymentConfigs(kapi.NamespaceDefault).List(kapi.ListOptions{}); !kapierror.IsForbidden(err) { t.Errorf("expected forbidden, got %v", err) } }
// TestOldLocalResourceAccessReviewEndpoint checks to make sure that the old resource access review endpoint still functions properly // this is needed to support old who-can client func TestOldLocalResourceAccessReviewEndpoint(t *testing.T) { testutil.RequireEtcd(t) defer testutil.DumpEtcdOnFailure(t) _, clusterAdminKubeConfig, err := testserver.StartTestMasterAPI() if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClient, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } haroldClient, err := testserver.CreateNewProject(clusterAdminClient, *clusterAdminClientConfig, "hammer-project", "harold") if err != nil { t.Fatalf("unexpected error: %v", err) } namespace := "hammer-project" // simple check { rar := &authorizationapi.ResourceAccessReview{ Action: authorizationapi.Action{ Verb: "get", Resource: "imagestreams/layers", }, } actualResponse := &authorizationapi.ResourceAccessReviewResponse{} err := haroldClient.Post().Namespace(namespace).Resource("resourceAccessReviews").Body(rar).Do().Into(actualResponse) if err != nil { t.Errorf("unexpected error: %v", err) } expectedResponse := &authorizationapi.ResourceAccessReviewResponse{ Namespace: namespace, Users: sets.NewString("harold", "system:serviceaccount:hammer-project:builder", "system:serviceaccount:openshift-infra:namespace-controller", "system:admin"), Groups: sets.NewString("system:cluster-admins", "system:masters", "system:cluster-readers", "system:serviceaccounts:hammer-project"), } if (actualResponse.Namespace != expectedResponse.Namespace) || !reflect.DeepEqual(actualResponse.Users.List(), expectedResponse.Users.List()) || !reflect.DeepEqual(actualResponse.Groups.List(), expectedResponse.Groups.List()) { t.Errorf("review\n\t%#v\nexpected\n\t%#v\ngot\n\t%#v", rar, expectedResponse, actualResponse) } } // namespace forced to allowed namespace so we can't trick the server into leaking { rar := &authorizationapi.ResourceAccessReview{ Action: authorizationapi.Action{ Namespace: "sneaky-user", Verb: "get", Resource: "imagestreams/layers", }, } actualResponse := &authorizationapi.ResourceAccessReviewResponse{} err := haroldClient.Post().Namespace(namespace).Resource("resourceAccessReviews").Body(rar).Do().Into(actualResponse) if err != nil { t.Errorf("unexpected error: %v", err) } expectedResponse := &authorizationapi.ResourceAccessReviewResponse{ Namespace: namespace, Users: sets.NewString("harold", "system:serviceaccount:hammer-project:builder", "system:serviceaccount:openshift-infra:namespace-controller", "system:admin"), Groups: sets.NewString("system:cluster-admins", "system:masters", "system:cluster-readers", "system:serviceaccounts:hammer-project"), } if (actualResponse.Namespace != expectedResponse.Namespace) || !reflect.DeepEqual(actualResponse.Users.List(), expectedResponse.Users.List()) || !reflect.DeepEqual(actualResponse.Groups.List(), expectedResponse.Groups.List()) { t.Errorf("review\n\t%#v\nexpected\n\t%#v\ngot\n\t%#v", rar, expectedResponse, actualResponse) } } }
// TestOldLocalSubjectAccessReviewEndpoint checks to make sure that the old subject access review endpoint still functions properly // this is needed to support old docker registry images func TestOldLocalSubjectAccessReviewEndpoint(t *testing.T) { testutil.RequireEtcd(t) defer testutil.DumpEtcdOnFailure(t) _, clusterAdminKubeConfig, err := testserver.StartTestMasterAPI() if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClient, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } haroldClient, err := testserver.CreateNewProject(clusterAdminClient, *clusterAdminClientConfig, "hammer-project", "harold") if err != nil { t.Fatalf("unexpected error: %v", err) } namespace := "hammer-project" // simple check { sar := &authorizationapi.SubjectAccessReview{ Action: authorizationapi.Action{ Verb: "get", Resource: "imagestreams/layers", }, } actualResponse := &authorizationapi.SubjectAccessReviewResponse{} err := haroldClient.Post().Namespace(namespace).Resource("subjectAccessReviews").Body(sar).Do().Into(actualResponse) if err != nil { t.Errorf("unexpected error: %v", err) } expectedResponse := &authorizationapi.SubjectAccessReviewResponse{ Allowed: true, Reason: `allowed by rule in hammer-project`, Namespace: namespace, } if (actualResponse.Namespace != expectedResponse.Namespace) || (actualResponse.Allowed != expectedResponse.Allowed) || (!strings.HasPrefix(actualResponse.Reason, expectedResponse.Reason)) { t.Errorf("review\n\t%#v\nexpected\n\t%#v\ngot\n\t%#v", sar, expectedResponse, actualResponse) } } // namespace forced to allowed namespace so we can't trick the server into leaking { sar := &authorizationapi.SubjectAccessReview{ Action: authorizationapi.Action{ Namespace: "sneaky-user", Verb: "get", Resource: "imagestreams/layers", }, } actualResponse := &authorizationapi.SubjectAccessReviewResponse{} err := haroldClient.Post().Namespace(namespace).Resource("subjectAccessReviews").Body(sar).Do().Into(actualResponse) if err != nil { t.Errorf("unexpected error: %v", err) } expectedResponse := &authorizationapi.SubjectAccessReviewResponse{ Allowed: true, Reason: `allowed by rule in hammer-project`, Namespace: namespace, } if (actualResponse.Namespace != expectedResponse.Namespace) || (actualResponse.Allowed != expectedResponse.Allowed) || (!strings.HasPrefix(actualResponse.Reason, expectedResponse.Reason)) { t.Errorf("review\n\t%#v\nexpected\n\t%#v\ngot\n\t%#v", sar, expectedResponse, actualResponse) } } // harold should be able to issue a self SAR against any project with the OLD policy { otherNamespace := "chisel-project" // we need a real project for this to make it past admission. // TODO, this is an information leaking problem. This admission plugin leaks knowledge of which projects exist via SARs if _, err := testserver.CreateNewProject(clusterAdminClient, *clusterAdminClientConfig, otherNamespace, "charlie"); err != nil { t.Fatalf("unexpected error: %v", err) } // remove the new permission for localSAR basicUserRole, err := clusterAdminClient.ClusterRoles().Get(bootstrappolicy.BasicUserRoleName) if err != nil { t.Fatalf("unexpected error: %v", err) } for i := range basicUserRole.Rules { basicUserRole.Rules[i].Resources.Delete("localsubjectaccessreviews") } if _, err := clusterAdminClient.ClusterRoles().Update(basicUserRole); err != nil { t.Fatalf("unexpected error: %v", err) } sar := &authorizationapi.SubjectAccessReview{ Action: authorizationapi.Action{ Verb: "get", Resource: "imagestreams/layers", }, } actualResponse := &authorizationapi.SubjectAccessReviewResponse{} err = haroldClient.Post().Namespace(otherNamespace).Resource("subjectAccessReviews").Body(sar).Do().Into(actualResponse) if err != nil { t.Errorf("unexpected error: %v", err) } expectedResponse := &authorizationapi.SubjectAccessReviewResponse{ Allowed: false, Reason: `User "harold" cannot get imagestreams/layers in project "chisel-project"`, Namespace: otherNamespace, } if (actualResponse.Namespace != expectedResponse.Namespace) || (actualResponse.Allowed != expectedResponse.Allowed) || (!strings.HasPrefix(actualResponse.Reason, expectedResponse.Reason)) { t.Errorf("review\n\t%#v\nexpected\n\t%#v\ngot\n\t%#v", sar, expectedResponse, actualResponse) } } }
func TestUserInitialization(t *testing.T) { testutil.RequireEtcd(t) defer testutil.DumpEtcdOnFailure(t) masterConfig, clusterAdminKubeConfig, err := testserver.StartTestMasterAPI() if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClient, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } optsGetter := originrest.StorageOptions(*masterConfig) userStorage, err := useretcd.NewREST(optsGetter) if err != nil { t.Fatalf("unexpected error: %v", err) } userRegistry := userregistry.NewRegistry(userStorage) identityStorage, err := identityetcd.NewREST(optsGetter) if err != nil { t.Fatalf("unexpected error: %v", err) } identityRegistry := identityregistry.NewRegistry(identityStorage) lookup, err := identitymapper.NewIdentityUserMapper(identityRegistry, userRegistry, identitymapper.MappingMethodLookup) if err != nil { t.Fatalf("unexpected error: %v", err) } generate, err := identitymapper.NewIdentityUserMapper(identityRegistry, userRegistry, identitymapper.MappingMethodGenerate) if err != nil { t.Fatalf("unexpected error: %v", err) } add, err := identitymapper.NewIdentityUserMapper(identityRegistry, userRegistry, identitymapper.MappingMethodAdd) if err != nil { t.Fatalf("unexpected error: %v", err) } claim, err := identitymapper.NewIdentityUserMapper(identityRegistry, userRegistry, identitymapper.MappingMethodClaim) if err != nil { t.Fatalf("unexpected error: %v", err) } testcases := map[string]struct { Identity authapi.UserIdentityInfo Mapper authapi.UserIdentityMapper CreateIdentity *api.Identity CreateUser *api.User CreateMapping *api.UserIdentityMapping UpdateUser *api.User ExpectedErr error ExpectedUserName string ExpectedFullName string ExpectedIdentities []string }{ "lookup missing identity": { Identity: makeIdentityInfo("idp", "bob", nil), Mapper: lookup, ExpectedErr: identitymapper.NewLookupError(makeIdentityInfo("idp", "bob", nil), kerrs.NewNotFound(api.Resource("useridentitymapping"), "idp:bob")), }, "lookup existing identity": { Identity: makeIdentityInfo("idp", "bob", nil), Mapper: lookup, CreateUser: makeUser("mappeduser"), CreateIdentity: makeIdentity("idp", "bob"), CreateMapping: makeMapping("mappeduser", "idp:bob"), ExpectedUserName: "******", ExpectedIdentities: []string{"idp:bob"}, }, "generate missing identity and user": { Identity: makeIdentityInfo("idp", "bob", nil), Mapper: generate, ExpectedUserName: "******", ExpectedIdentities: []string{"idp:bob"}, }, "generate missing identity and user with preferred username and display name": { Identity: makeIdentityInfo("idp", "bob", map[string]string{authapi.IdentityDisplayNameKey: "Bob, Sr.", authapi.IdentityPreferredUsernameKey: "admin"}), Mapper: generate, ExpectedUserName: "******", ExpectedFullName: "Bob, Sr.", ExpectedIdentities: []string{"idp:bob"}, }, "generate missing identity for existing user": { Identity: makeIdentityInfo("idp", "bob", nil), Mapper: generate, CreateUser: makeUser("bob", "idp:bob"), ExpectedUserName: "******", ExpectedIdentities: []string{"idp:bob"}, }, "generate missing identity with conflicting user": { Identity: makeIdentityInfo("idp", "bob", nil), Mapper: generate, CreateUser: makeUser("bob"), ExpectedUserName: "******", ExpectedIdentities: []string{"idp:bob"}, }, "generate missing identity with conflicting user and preferred username": { Identity: makeIdentityInfo("idp", "bob", map[string]string{authapi.IdentityPreferredUsernameKey: "admin"}), Mapper: generate, CreateUser: makeUser("admin"), ExpectedUserName: "******", ExpectedIdentities: []string{"idp:bob"}, }, "generate with existing unmapped identity": { Identity: makeIdentityInfo("idp", "bob", nil), Mapper: generate, CreateIdentity: makeIdentity("idp", "bob"), ExpectedErr: kerrs.NewNotFound(api.Resource("useridentitymapping"), "idp:bob"), }, "generate with existing mapped identity with invalid user UID": { Identity: makeIdentityInfo("idp", "bob", nil), Mapper: generate, CreateUser: makeUser("mappeduser"), CreateIdentity: makeIdentityWithUserReference("idp", "bob", "mappeduser", "invalidUID"), ExpectedErr: kerrs.NewNotFound(api.Resource("useridentitymapping"), "idp:bob"), ExpectedIdentities: []string{"idp:bob"}, }, "generate with existing mapped identity without user backreference": { Identity: makeIdentityInfo("idp", "bob", nil), Mapper: generate, CreateUser: makeUser("mappeduser"), CreateIdentity: makeIdentity("idp", "bob"), CreateMapping: makeMapping("mappeduser", "idp:bob"), // Update user to a version which does not reference the identity UpdateUser: makeUser("mappeduser"), ExpectedErr: kerrs.NewNotFound(api.Resource("useridentitymapping"), "idp:bob"), }, "generate returns existing mapping": { Identity: makeIdentityInfo("idp", "bob", nil), Mapper: generate, CreateUser: makeUser("mappeduser"), CreateIdentity: makeIdentity("idp", "bob"), CreateMapping: makeMapping("mappeduser", "idp:bob"), ExpectedUserName: "******", ExpectedIdentities: []string{"idp:bob"}, }, "add missing identity and user": { Identity: makeIdentityInfo("idp", "bob", nil), Mapper: add, ExpectedUserName: "******", ExpectedIdentities: []string{"idp:bob"}, }, "add missing identity and user with preferred username and display name": { Identity: makeIdentityInfo("idp", "bob", map[string]string{authapi.IdentityDisplayNameKey: "Bob, Sr.", authapi.IdentityPreferredUsernameKey: "admin"}), Mapper: add, ExpectedUserName: "******", ExpectedFullName: "Bob, Sr.", ExpectedIdentities: []string{"idp:bob"}, }, "add missing identity for existing user": { Identity: makeIdentityInfo("idp", "bob", nil), Mapper: add, CreateUser: makeUser("bob", "idp:bob"), ExpectedUserName: "******", ExpectedIdentities: []string{"idp:bob"}, }, "add missing identity with conflicting user": { Identity: makeIdentityInfo("idp", "bob", nil), Mapper: add, CreateUser: makeUser("bob", "otheridp:otheruser"), ExpectedUserName: "******", ExpectedIdentities: []string{"otheridp:otheruser", "idp:bob"}, }, "add missing identity with conflicting user and preferred username": { Identity: makeIdentityInfo("idp", "bob", map[string]string{authapi.IdentityPreferredUsernameKey: "admin"}), Mapper: add, CreateUser: makeUser("admin", "otheridp:otheruser"), ExpectedUserName: "******", ExpectedIdentities: []string{"otheridp:otheruser", "idp:bob"}, }, "add with existing unmapped identity": { Identity: makeIdentityInfo("idp", "bob", nil), Mapper: add, CreateIdentity: makeIdentity("idp", "bob"), ExpectedErr: kerrs.NewNotFound(api.Resource("useridentitymapping"), "idp:bob"), }, "add with existing mapped identity with invalid user UID": { Identity: makeIdentityInfo("idp", "bob", nil), Mapper: add, CreateUser: makeUser("mappeduser"), CreateIdentity: makeIdentityWithUserReference("idp", "bob", "mappeduser", "invalidUID"), ExpectedErr: kerrs.NewNotFound(api.Resource("useridentitymapping"), "idp:bob"), }, "add with existing mapped identity without user backreference": { Identity: makeIdentityInfo("idp", "bob", nil), Mapper: add, CreateUser: makeUser("mappeduser"), CreateIdentity: makeIdentity("idp", "bob"), CreateMapping: makeMapping("mappeduser", "idp:bob"), // Update user to a version which does not reference the identity UpdateUser: makeUser("mappeduser"), ExpectedErr: kerrs.NewNotFound(api.Resource("useridentitymapping"), "idp:bob"), }, "add returns existing mapping": { Identity: makeIdentityInfo("idp", "bob", nil), Mapper: add, CreateUser: makeUser("mappeduser"), CreateIdentity: makeIdentity("idp", "bob"), CreateMapping: makeMapping("mappeduser", "idp:bob"), ExpectedUserName: "******", ExpectedIdentities: []string{"idp:bob"}, }, "claim missing identity and user": { Identity: makeIdentityInfo("idp", "bob", nil), Mapper: claim, ExpectedUserName: "******", ExpectedIdentities: []string{"idp:bob"}, }, "claim missing identity and user with preferred username and display name": { Identity: makeIdentityInfo("idp", "bob", map[string]string{authapi.IdentityDisplayNameKey: "Bob, Sr.", authapi.IdentityPreferredUsernameKey: "admin"}), Mapper: claim, ExpectedUserName: "******", ExpectedFullName: "Bob, Sr.", ExpectedIdentities: []string{"idp:bob"}, }, "claim missing identity for existing user": { Identity: makeIdentityInfo("idp", "bob", nil), Mapper: claim, CreateUser: makeUser("bob", "idp:bob"), ExpectedUserName: "******", ExpectedIdentities: []string{"idp:bob"}, }, "claim missing identity with existing available user": { Identity: makeIdentityInfo("idp", "bob", nil), Mapper: claim, CreateUser: makeUser("bob"), ExpectedUserName: "******", ExpectedIdentities: []string{"idp:bob"}, }, "claim missing identity with conflicting user": { Identity: makeIdentityInfo("idp", "bob", nil), Mapper: claim, CreateUser: makeUser("bob", "otheridp:otheruser"), ExpectedErr: identitymapper.NewClaimError(makeUser("bob", "otheridp:otheruser"), makeIdentity("idp", "bob")), }, "claim missing identity with conflicting user and preferred username": { Identity: makeIdentityInfo("idp", "bob", map[string]string{authapi.IdentityPreferredUsernameKey: "admin"}), Mapper: claim, CreateUser: makeUser("admin", "otheridp:otheruser"), ExpectedErr: identitymapper.NewClaimError(makeUser("admin", "otheridp:otheruser"), makeIdentity("idp", "bob")), }, "claim with existing unmapped identity": { Identity: makeIdentityInfo("idp", "bob", nil), Mapper: claim, CreateIdentity: makeIdentity("idp", "bob"), ExpectedErr: kerrs.NewNotFound(api.Resource("useridentitymapping"), "idp:bob"), }, "claim with existing mapped identity with invalid user UID": { Identity: makeIdentityInfo("idp", "bob", nil), Mapper: claim, CreateUser: makeUser("mappeduser"), CreateIdentity: makeIdentityWithUserReference("idp", "bob", "mappeduser", "invalidUID"), ExpectedErr: kerrs.NewNotFound(api.Resource("useridentitymapping"), "idp:bob"), }, "claim with existing mapped identity without user backreference": { Identity: makeIdentityInfo("idp", "bob", nil), Mapper: claim, CreateUser: makeUser("mappeduser"), CreateIdentity: makeIdentity("idp", "bob"), CreateMapping: makeMapping("mappeduser", "idp:bob"), // Update user to a version which does not reference the identity UpdateUser: makeUser("mappeduser"), ExpectedErr: kerrs.NewNotFound(api.Resource("useridentitymapping"), "idp:bob"), }, "claim returns existing mapping": { Identity: makeIdentityInfo("idp", "bob", nil), Mapper: claim, CreateUser: makeUser("mappeduser"), CreateIdentity: makeIdentity("idp", "bob"), CreateMapping: makeMapping("mappeduser", "idp:bob"), ExpectedUserName: "******", ExpectedIdentities: []string{"idp:bob"}, }, } oldEtcdClient, err := etcd.MakeNewEtcdClient(masterConfig.EtcdClientInfo) if err != nil { t.Fatalf("unexpected error: %v", err) } etcdClient := etcdclient.NewKeysAPI(oldEtcdClient) for k, testcase := range testcases { // Cleanup if _, err := etcdClient.Delete(context.Background(), path.Join(masterConfig.EtcdStorageConfig.OpenShiftStoragePrefix, "/users"), &etcdclient.DeleteOptions{Recursive: true}); err != nil && !etcdutil.IsEtcdNotFound(err) { t.Fatalf("Could not clean up users: %v", err) } if _, err := etcdClient.Delete(context.Background(), path.Join(masterConfig.EtcdStorageConfig.OpenShiftStoragePrefix, "/identities"), &etcdclient.DeleteOptions{Recursive: true}); err != nil && !etcdutil.IsEtcdNotFound(err) { t.Fatalf("Could not clean up identities: %v", err) } // Pre-create items if testcase.CreateUser != nil { _, err := clusterAdminClient.Users().Create(testcase.CreateUser) if err != nil { t.Errorf("%s: Could not create user: %v", k, err) continue } } if testcase.CreateIdentity != nil { _, err := clusterAdminClient.Identities().Create(testcase.CreateIdentity) if err != nil { t.Errorf("%s: Could not create identity: %v", k, err) continue } } if testcase.CreateMapping != nil { _, err := clusterAdminClient.UserIdentityMappings().Update(testcase.CreateMapping) if err != nil { t.Errorf("%s: Could not create mapping: %v", k, err) continue } } if testcase.UpdateUser != nil { if testcase.UpdateUser.ResourceVersion == "" { existingUser, err := clusterAdminClient.Users().Get(testcase.UpdateUser.Name) if err != nil { t.Errorf("%s: Could not get user to update: %v", k, err) continue } testcase.UpdateUser.ResourceVersion = existingUser.ResourceVersion } _, err := clusterAdminClient.Users().Update(testcase.UpdateUser) if err != nil { t.Errorf("%s: Could not update user: %v", k, err) continue } } // Spawn 5 simultaneous mappers to test race conditions var wg sync.WaitGroup for i := 0; i < 5; i++ { wg.Add(1) go func() { defer wg.Done() userInfo, err := testcase.Mapper.UserFor(testcase.Identity) if err != nil { if testcase.ExpectedErr == nil { t.Errorf("%s: Expected success, got error '%v'", k, err) } else if err.Error() != testcase.ExpectedErr.Error() { t.Errorf("%s: Expected error %v, got '%v'", k, testcase.ExpectedErr.Error(), err) } return } if err == nil && testcase.ExpectedErr != nil { t.Errorf("%s: Expected error '%v', got none", k, testcase.ExpectedErr) return } if userInfo.GetName() != testcase.ExpectedUserName { t.Errorf("%s: Expected username %s, got %s", k, testcase.ExpectedUserName, userInfo.GetName()) return } user, err := clusterAdminClient.Users().Get(userInfo.GetName()) if err != nil { t.Errorf("%s: Error getting user: %v", k, err) } if user.FullName != testcase.ExpectedFullName { t.Errorf("%s: Expected full name %s, got %s", k, testcase.ExpectedFullName, user.FullName) } if !reflect.DeepEqual(user.Identities, testcase.ExpectedIdentities) { t.Errorf("%s: Expected identities %v, got %v", k, testcase.ExpectedIdentities, user.Identities) } }() } wg.Wait() } }
func TestImageStreamMappingCreate(t *testing.T) { testutil.RequireEtcd(t) _, clusterAdminKubeConfig, err := testserver.StartTestMasterAPI() if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClient, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig) if err != nil { t.Errorf("unexpected error: %v", err) } err = testutil.CreateNamespace(clusterAdminKubeConfig, testutil.Namespace()) if err != nil { t.Errorf("unexpected error: %v", err) } stream := mockImageStream() expected, err := clusterAdminClient.ImageStreams(testutil.Namespace()).Create(stream) if err != nil { t.Fatalf("Unexpected error: %v", err) } if expected.Name == "" { t.Errorf("Unexpected empty image Name %v", expected) } // create a mapping to an image that doesn't exist mapping := &imageapi.ImageStreamMapping{ ObjectMeta: kapi.ObjectMeta{Name: stream.Name}, Tag: "newer", Image: imageapi.Image{ ObjectMeta: kapi.ObjectMeta{ Name: "image1", }, DockerImageReference: "some/other/name", }, } if err := clusterAdminClient.ImageStreamMappings(testutil.Namespace()).Create(mapping); err != nil { t.Fatalf("unexpected error: %v", err) } // verify we can tag a second time with the same data, and nothing changes if err := clusterAdminClient.ImageStreamMappings(testutil.Namespace()).Create(mapping); err != nil { t.Fatalf("unexpected non-error or type: %v", err) } // create an image directly image := &imageapi.Image{ ObjectMeta: kapi.ObjectMeta{Name: "image2"}, DockerImageMetadata: imageapi.DockerImage{ Config: &imageapi.DockerConfig{ Env: []string{"A=B"}, }, }, } if _, err := clusterAdminClient.Images().Create(image); err == nil { t.Error("unexpected non-error") } image.DockerImageReference = "some/other/name" // can reuse references across multiple images actual, err := clusterAdminClient.Images().Create(image) if err != nil { t.Fatalf("unexpected error: %v", err) } if actual == nil || actual.Name != image.Name { t.Errorf("unexpected object: %#v", actual) } // verify that image stream mappings cannot mutate / overwrite the image (images are immutable) mapping = &imageapi.ImageStreamMapping{ ObjectMeta: kapi.ObjectMeta{Name: stream.Name}, Tag: "newest", Image: *image, } mapping.Image.DockerImageReference = "different" if err := clusterAdminClient.ImageStreamMappings(testutil.Namespace()).Create(mapping); err != nil { t.Fatalf("unexpected error: %v", err) } image, err = clusterAdminClient.Images().Get(image.Name) if err != nil { t.Fatalf("unexpected error: %v", err) } if image.DockerImageReference != "some/other/name" { t.Fatalf("image was unexpectedly mutated: %#v", image) } // ensure the correct tags are set updated, err := clusterAdminClient.ImageStreams(testutil.Namespace()).Get(stream.Name) if err != nil { t.Fatalf("Unexpected error: %v", err) } if updated.Spec.Tags != nil && len(updated.Spec.Tags) > 0 { t.Errorf("unexpected object: %#v", updated.Spec.Tags) } fromTag, err := clusterAdminClient.ImageStreamTags(testutil.Namespace()).Get(stream.Name, "newer") if err != nil { t.Fatalf("Unexpected error: %v", err) } if fromTag.Name != "test:newer" || fromTag.Image.UID == "" || fromTag.Image.DockerImageReference != "some/other/name" { t.Errorf("unexpected object: %#v", fromTag) } fromTag, err = clusterAdminClient.ImageStreamTags(testutil.Namespace()).Get(stream.Name, "newest") if err != nil { t.Fatalf("Unexpected error: %v", err) } if fromTag.Name != "test:newest" || fromTag.Image.UID == "" || fromTag.Image.DockerImageReference != "different" { t.Errorf("unexpected object: %#v", fromTag) } // verify that image stream mappings can use the same image for different tags image.ResourceVersion = "" mapping = &imageapi.ImageStreamMapping{ ObjectMeta: kapi.ObjectMeta{Name: stream.Name}, Tag: "anothertag", Image: *image, } if err := clusterAdminClient.ImageStreamMappings(testutil.Namespace()).Create(mapping); err != nil { t.Fatalf("unexpected error: %v", err) } // ensure the correct tags are set updated, err = clusterAdminClient.ImageStreams(testutil.Namespace()).Get(stream.Name) if err != nil { t.Fatalf("Unexpected error: %v", err) } if updated.Spec.Tags != nil && len(updated.Spec.Tags) > 0 { t.Errorf("unexpected object: %#v", updated.Spec.Tags) } if _, err := clusterAdminClient.ImageStreamTags(testutil.Namespace()).Get(stream.Name, "doesnotexist"); err == nil || !errors.IsNotFound(err) { t.Fatalf("Unexpected error: %v", err) } fromTag, err = clusterAdminClient.ImageStreamTags(testutil.Namespace()).Get(stream.Name, "newer") if err != nil { t.Fatalf("Unexpected error: %v", err) } if fromTag.Name != "test:newer" || fromTag.Image.UID == "" || fromTag.Image.DockerImageReference != "some/other/name" { t.Errorf("unexpected object: %#v", fromTag) } fromTag, err = clusterAdminClient.ImageStreamTags(testutil.Namespace()).Get(stream.Name, "newest") if err != nil { t.Fatalf("Unexpected error: %v", err) } if fromTag.Name != "test:newest" || fromTag.Image.UID == "" || fromTag.Image.DockerImageReference != "different" { t.Errorf("unexpected object: %#v", fromTag) } fromTag, err = clusterAdminClient.ImageStreamTags(testutil.Namespace()).Get(stream.Name, "anothertag") if err != nil { t.Fatalf("Unexpected error: %v", err) } if fromTag.Name != "test:anothertag" || fromTag.Image.UID == "" || fromTag.Image.DockerImageReference != "some/other/name" { t.Errorf("unexpected object: %#v", fromTag) } // try an update with an incorrect resource version if _, err := clusterAdminClient.ImageStreamTags(testutil.Namespace()).Update(&imageapi.ImageStreamTag{ ObjectMeta: kapi.ObjectMeta{Namespace: stream.Namespace, Name: stream.Name + ":brandnew", ResourceVersion: fromTag.ResourceVersion + "0"}, Tag: &imageapi.TagReference{ From: &kapi.ObjectReference{ Kind: "ImageStreamTag", Name: "newest", }, }, }); !errors.IsConflict(err) { t.Fatalf("should have returned conflict error: %v", err) } // update and create a new tag fromTag, err = clusterAdminClient.ImageStreamTags(testutil.Namespace()).Update(&imageapi.ImageStreamTag{ ObjectMeta: kapi.ObjectMeta{Namespace: stream.Namespace, Name: stream.Name + ":brandnew", ResourceVersion: fromTag.ResourceVersion}, Tag: &imageapi.TagReference{ From: &kapi.ObjectReference{ Kind: "ImageStreamTag", Name: "newest", }, }, }) if err != nil { t.Fatalf("should have returned conflict error: %v", err) } if fromTag.Name != "test:brandnew" || fromTag.Image.UID == "" || fromTag.Tag.From.Name != "newest" { t.Errorf("unexpected object: %#v", fromTag) } }
func TestLogin(t *testing.T) { testutil.RequireEtcd(t) defer testutil.DumpEtcdOnFailure(t) _, clusterAdminKubeConfig, err := testserver.StartTestMasterAPI() if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClient, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } username := "******" password := "******" project := "the-singularity-is-near" server := clusterAdminClientConfig.Host loginOptions := newLoginOptions(server, username, password, true) if err := loginOptions.GatherInfo(); err != nil { t.Fatalf("Error trying to determine server info: %v", err) } if loginOptions.Username != username { t.Fatalf("Unexpected user after authentication: %#v", loginOptions) } newProjectOptions := &newproject.NewProjectOptions{ Client: clusterAdminClient, ProjectName: project, AdminRole: bootstrappolicy.AdminRoleName, AdminUser: username, } if err := newProjectOptions.Run(false); err != nil { t.Fatalf("unexpected error, a project is required to continue: %v", err) } oClient, _ := client.New(loginOptions.Config) p, err := oClient.Projects().Get(project) if err != nil { t.Errorf("unexpected error: %v", err) } if p.Name != project { t.Fatalf("unexpected project: %#v", p) } // TODO Commented because of incorrectly hitting cache when listing projects. // Should be enabled again when cache eviction is properly fixed. // err = loginOptions.GatherProjectInfo() // if err != nil { // t.Fatalf("unexpected error: %v", err) // } // if loginOptions.Project != project { // t.Fatalf("Expected project %v but got %v", project, loginOptions.Project) // } // configFile, err := ioutil.TempFile("", "openshiftconfig") // if err != nil { // t.Fatalf("unexpected error: %v", err) // } // defer os.Remove(configFile.Name()) // if _, err = loginOptions.SaveConfig(configFile.Name()); err != nil { // t.Fatalf("unexpected error: %v", err) // } userWhoamiOptions := cmd.WhoAmIOptions{UserInterface: oClient.Users(), Out: ioutil.Discard} retrievedUser, err := userWhoamiOptions.WhoAmI() if err != nil { t.Errorf("unexpected error: %v", err) } if retrievedUser.Name != username { t.Errorf("expected %v, got %v", retrievedUser.Name, username) } adminWhoamiOptions := cmd.WhoAmIOptions{UserInterface: clusterAdminClient.Users(), Out: ioutil.Discard} retrievedAdmin, err := adminWhoamiOptions.WhoAmI() if err != nil { t.Errorf("unexpected error: %v", err) } if retrievedAdmin.Name != "system:admin" { t.Errorf("expected %v, got %v", retrievedAdmin.Name, "system:admin") } }