func TestAuthorizationRestrictedAccessForProjectAdmins(t *testing.T) { _, clusterAdminKubeConfig, err := testutil.StartTestMaster() 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 := testutil.CreateNewProject(clusterAdminClient, *clusterAdminClientConfig, "hammer-project", "harold") if err != nil { t.Fatalf("unexpected error: %v", err) } markClient, err := testutil.CreateNewProject(clusterAdminClient, *clusterAdminClientConfig, "mallet-project", "mark") if err != nil { t.Fatalf("unexpected error: %v", err) } _, err = haroldClient.DeploymentConfigs("hammer-project").List(labels.Everything(), fields.Everything()) if err != nil { t.Fatalf("unexpected error: %v", err) } _, err = markClient.DeploymentConfigs("hammer-project").List(labels.Everything(), fields.Everything()) 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 setupBuildStrategyTest(t *testing.T) (clusterAdminClient, projectAdminClient, projectEditorClient *client.Client) { namespace := testutil.Namespace() _, clusterAdminKubeConfig, err := testutil.StartTestMaster() 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) } projectAdminClient, err = testutil.CreateNewProject(clusterAdminClient, *clusterAdminClientConfig, namespace, "harold") if err != nil { t.Fatalf("unexpected error: %v", err) } projectEditorClient, _, _, err = testutil.GetClientForUser(*clusterAdminClientConfig, "joe") if err != nil { t.Fatalf("unexpected error: %v", err) } addJoe := &policy.RoleModificationOptions{ RoleNamespace: "", RoleName: bootstrappolicy.EditRoleName, RoleBindingAccessor: policy.NewLocalRoleBindingAccessor(namespace, projectAdminClient), Users: []string{"joe"}, } if err := addJoe.AddRole(); err != nil { t.Fatalf("unexpected error: %v", err) } if err := testutil.WaitForPolicyUpdate(projectEditorClient, namespace, "create", authorizationapi.DockerBuildResource, true); err != nil { t.Fatalf(err.Error()) } // Create builder image stream and tag imageStream := &imageapi.ImageStream{} imageStream.Name = "builderimage" _, err = clusterAdminClient.ImageStreams(testutil.Namespace()).Create(imageStream) if err != nil { t.Fatalf("Couldn't create ImageStream: %v", err) } // Create image stream mapping imageStreamMapping := &imageapi.ImageStreamMapping{} imageStreamMapping.Name = "builderimage" imageStreamMapping.Tag = "latest" imageStreamMapping.Image.Name = "image-id" imageStreamMapping.Image.DockerImageReference = "test/builderimage:latest" err = clusterAdminClient.ImageStreamMappings(testutil.Namespace()).Create(imageStreamMapping) if err != nil { t.Fatalf("Couldn't create ImageStreamMapping: %v", err) } return }
func setup(t *testing.T) *client.Client { _, clusterAdminKubeConfigFile, err := testutil.StartTestMaster() if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClient, err := testutil.GetClusterAdminClient(clusterAdminKubeConfigFile) if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminKubeConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfigFile) if err != nil { t.Fatalf("unexpected error: %v", err) } projectAdminClient, err := testutil.CreateNewProject(clusterAdminClient, *clusterAdminKubeConfig, testutil.Namespace(), testutil.Namespace()) if err != nil { t.Fatalf("unexpected error: %v", err) } return projectAdminClient }
func TestTriggers_imageChange(t *testing.T) { _, clusterAdminKubeConfig, err := testutil.StartTestMaster() if err != nil { t.Fatalf("error starting master: %v", err) } openshiftClusterAdminClient, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig) if err != nil { t.Fatalf("error getting OpenShift cluster admin client: %v", err) } openshiftClusterAdminClientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) if err != nil { t.Fatalf("error getting cluster admin client config: %v", err) } openshiftProjectAdminClient, err := testutil.CreateNewProject(openshiftClusterAdminClient, *openshiftClusterAdminClientConfig, testutil.Namespace(), "bob") if err != nil { t.Fatalf("error creating project: %v", err) } imageStream := &imageapi.ImageStream{ObjectMeta: kapi.ObjectMeta{Name: "test-image-stream"}} config := deploytest.OkDeploymentConfig(0) config.Namespace = testutil.Namespace() configWatch, err := openshiftProjectAdminClient.DeploymentConfigs(testutil.Namespace()).Watch(labels.Everything(), fields.Everything(), "0") if err != nil { t.Fatalf("Couldn't subscribe to Deployments %v", err) } defer configWatch.Stop() if imageStream, err = openshiftProjectAdminClient.ImageStreams(testutil.Namespace()).Create(imageStream); err != nil { t.Fatalf("Couldn't create ImageStream: %v", err) } imageWatch, err := openshiftProjectAdminClient.ImageStreams(testutil.Namespace()).Watch(labels.Everything(), fields.Everything(), "0") if err != nil { t.Fatalf("Couldn't subscribe to ImageStreams: %s", err) } defer imageWatch.Stop() // Make a function which can create a new tag event for the image stream and // then wait for the stream status to be asynchronously updated. createTagEvent := func(image string) { mapping := &imageapi.ImageStreamMapping{ ObjectMeta: kapi.ObjectMeta{Name: imageStream.Name}, Tag: "latest", Image: imageapi.Image{ ObjectMeta: kapi.ObjectMeta{ Name: image, }, DockerImageReference: fmt.Sprintf("registry:8080/openshift/test-image@%s", image), }, } if err := openshiftProjectAdminClient.ImageStreamMappings(testutil.Namespace()).Create(mapping); err != nil { t.Fatalf("unexpected error: %v", err) } t.Log("Waiting for image stream mapping to be reflected in the IS status...") statusLoop: for { select { case event := <-imageWatch.ResultChan(): stream := event.Object.(*imageapi.ImageStream) if _, ok := stream.Status.Tags["latest"]; ok { t.Logf("ImageStream %s now has Status with tags: %#v", stream.Name, stream.Status.Tags) break statusLoop } else { t.Logf("Still waiting for latest tag status on ImageStream %s", stream.Name) } } } } if config, err = openshiftProjectAdminClient.DeploymentConfigs(testutil.Namespace()).Create(config); err != nil { t.Fatalf("Couldn't create DeploymentConfig: %v", err) } createTagEvent("sha256:00000000000000000000000000000001") var newConfig *deployapi.DeploymentConfig t.Log("Waiting for a new deployment config in response to ImageStream update") waitForNewConfig: for { select { case event := <-configWatch.ResultChan(): if event.Type == watchapi.Modified { newConfig = event.Object.(*deployapi.DeploymentConfig) break waitForNewConfig } } } if e, a := 1, newConfig.LatestVersion; e != a { t.Fatalf("expected config version %d, got %d", e, a) } }
func TestAuthorizationSubjectAccessReview(t *testing.T) { _, clusterAdminKubeConfig, err := testutil.StartTestMaster() 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 := testutil.CreateNewProject(clusterAdminClient, *clusterAdminClientConfig, "hammer-project", "harold") if err != nil { t.Fatalf("unexpected error: %v", err) } markClient, err := testutil.CreateNewProject(clusterAdminClient, *clusterAdminClientConfig, "mallet-project", "mark") if err != nil { t.Fatalf("unexpected error: %v", err) } dannyClient, err := testutil.GetClientForUser(*clusterAdminClientConfig, "danny") if err != nil { t.Fatalf("error requesting token: %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: "******", Verb: "get", Resource: "projects"} subjectAccessReviewTest{ description: "cluster admin told danny can get project default", clientInterface: clusterAdminClient.SubjectAccessReviews("default"), review: askCanDannyGetProject, 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", clientInterface: clusterAdminClient.ClusterSubjectAccessReviews(), review: 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", clientInterface: dannyClient.ClusterSubjectAccessReviews(), review: askCanDannyGetProject, err: `User "danny" 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.SubjectAccessReview{User: "******", Verb: "get", Resource: "projects"} subjectAccessReviewTest{ description: "harold told valerie can get project hammer-project", clientInterface: haroldClient.SubjectAccessReviews("hammer-project"), review: 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", clientInterface: markClient.SubjectAccessReviews("mallet-project"), review: askCanValerieGetProject, response: authorizationapi.SubjectAccessReviewResponse{ Allowed: false, Reason: `User "valerie" cannot get projects in project "mallet-project"`, Namespace: "mallet-project", }, }.run(t) askCanEdgarDeletePods := &authorizationapi.SubjectAccessReview{User: "******", Verb: "delete", Resource: "pods"} subjectAccessReviewTest{ description: "mark told edgar can delete pods in mallet-project", clientInterface: markClient.SubjectAccessReviews("mallet-project"), review: askCanEdgarDeletePods, response: authorizationapi.SubjectAccessReviewResponse{ Allowed: true, Reason: "allowed by rule in mallet-project", Namespace: "mallet-project", }, }.run(t) subjectAccessReviewTest{ description: "harold denied ability to run subject access review in project mallet-project", clientInterface: haroldClient.SubjectAccessReviews("mallet-project"), review: askCanEdgarDeletePods, err: `User "harold" cannot create subjectaccessreviews in project "mallet-project"`, }.run(t) askCanHaroldUpdateProject := &authorizationapi.SubjectAccessReview{User: "******", Verb: "update", Resource: "projects"} subjectAccessReviewTest{ description: "harold told harold can update project hammer-project", clientInterface: haroldClient.SubjectAccessReviews("hammer-project"), review: askCanHaroldUpdateProject, response: authorizationapi.SubjectAccessReviewResponse{ Allowed: true, Reason: "allowed by rule in hammer-project", Namespace: "hammer-project", }, }.run(t) askCanClusterAdminsCreateProject := &authorizationapi.SubjectAccessReview{Groups: util.NewStringSet("system:cluster-admins"), Verb: "create", Resource: "projects"} subjectAccessReviewTest{ description: "cluster admin told cluster admins can create projects", clientInterface: clusterAdminClient.ClusterSubjectAccessReviews(), review: 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", clientInterface: haroldClient.ClusterSubjectAccessReviews(), review: askCanClusterAdminsCreateProject, err: `User "harold" cannot create subjectaccessreviews at the cluster scope`, }.run(t) askCanICreatePods := &authorizationapi.SubjectAccessReview{Verb: "create", Resource: "pods"} subjectAccessReviewTest{ description: "harold told he can create pods in project hammer-project", clientInterface: haroldClient.SubjectAccessReviews("hammer-project"), review: askCanICreatePods, response: authorizationapi.SubjectAccessReviewResponse{ Allowed: true, Reason: "allowed by rule in hammer-project", Namespace: "hammer-project", }, }.run(t) askCanICreatePolicyBindings := &authorizationapi.SubjectAccessReview{Verb: "create", Resource: "policybindings"} subjectAccessReviewTest{ description: "harold told he can create policybindings in project hammer-project", clientInterface: haroldClient.SubjectAccessReviews("hammer-project"), review: askCanICreatePolicyBindings, response: authorizationapi.SubjectAccessReviewResponse{ Allowed: false, Reason: `User "harold" cannot create policybindings in project "hammer-project"`, Namespace: "hammer-project", }, }.run(t) }
func TestAuthorizationResourceAccessReview(t *testing.T) { _, clusterAdminKubeConfig, err := testutil.StartTestMaster() 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 := testutil.CreateNewProject(clusterAdminClient, *clusterAdminClientConfig, "hammer-project", "harold") if err != nil { t.Fatalf("unexpected error: %v", err) } markClient, err := testutil.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) } requestWhoCanViewDeployments := &authorizationapi.ResourceAccessReview{Verb: "get", Resource: "deployments"} { test := resourceAccessReviewTest{ clientInterface: haroldClient.ResourceAccessReviews("hammer-project"), review: requestWhoCanViewDeployments, response: authorizationapi.ResourceAccessReviewResponse{ Users: util.NewStringSet("harold", "valerie"), Groups: globalClusterAdminGroups, Namespace: "hammer-project", }, } test.response.Users.Insert(globalClusterAdminUsers.List()...) test.response.Groups.Insert("system:cluster-readers") test.run(t) } { test := resourceAccessReviewTest{ clientInterface: markClient.ResourceAccessReviews("mallet-project"), review: requestWhoCanViewDeployments, response: authorizationapi.ResourceAccessReviewResponse{ Users: util.NewStringSet("mark", "edgar"), Groups: globalClusterAdminGroups, Namespace: "mallet-project", }, } test.response.Users.Insert(globalClusterAdminUsers.List()...) test.response.Groups.Insert("system:cluster-readers") test.run(t) } // mark should not be able to make global access review requests { test := resourceAccessReviewTest{ clientInterface: markClient.ClusterResourceAccessReviews(), review: requestWhoCanViewDeployments, err: "cannot ", } test.run(t) } // a cluster-admin should be able to make global access review requests { test := resourceAccessReviewTest{ clientInterface: clusterAdminClient.ClusterResourceAccessReviews(), review: requestWhoCanViewDeployments, response: authorizationapi.ResourceAccessReviewResponse{ Users: globalClusterAdminUsers, Groups: globalClusterAdminGroups, }, } test.response.Groups.Insert("system:cluster-readers") test.run(t) } }
// 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) { _, clusterAdminKubeConfig, err := testutil.StartTestMaster() 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 := testutil.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.AuthorizationAttributes{ 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: util.NewStringSet("harold", "system:serviceaccount:hammer-project:builder"), Groups: util.NewStringSet("system:cluster-admins", "system:masters", "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.AuthorizationAttributes{ 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: util.NewStringSet("harold", "system:serviceaccount:hammer-project:builder"), Groups: util.NewStringSet("system:cluster-admins", "system:masters", "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) { _, clusterAdminKubeConfig, err := testutil.StartTestMaster() 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 := testutil.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.AuthorizationAttributes{ 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.AuthorizationAttributes{ 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) } } }
func TestAuthorizationSubjectAccessReview(t *testing.T) { _, clusterAdminKubeConfig, err := testutil.StartTestMaster() 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 := testutil.CreateNewProject(clusterAdminClient, *clusterAdminClientConfig, "hammer-project", "harold") if err != nil { t.Fatalf("unexpected error: %v", err) } markClient, err := testutil.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) } 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.AuthorizationAttributes{Verb: "get", Resource: "projects"}, } subjectAccessReviewTest{ description: "cluster admin told danny can get project default", localInterface: clusterAdminClient.LocalSubjectAccessReviews("default"), localReview: &authorizationapi.LocalSubjectAccessReview{ User: "******", Action: authorizationapi.AuthorizationAttributes{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) 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.AuthorizationAttributes{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.AuthorizationAttributes{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) 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) askCanHaroldUpdateProject := &authorizationapi.LocalSubjectAccessReview{ User: "******", Action: authorizationapi.AuthorizationAttributes{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: util.NewStringSet("system:cluster-admins"), Action: authorizationapi.AuthorizationAttributes{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.AuthorizationAttributes{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) askCanICreatePolicyBindings := &authorizationapi.LocalSubjectAccessReview{ Action: authorizationapi.AuthorizationAttributes{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.AuthorizationAttributes{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.AuthorizationAttributes{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.AuthorizationAttributes{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.AuthorizationAttributes{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.AuthorizationAttributes{Verb: "list", Resource: "pods"}, }, response: authorizationapi.SubjectAccessReviewResponse{ Allowed: true, Reason: `allowed by rule in default`, Namespace: "default", }, }.run(t) }
func TestV2RegistryGetTags(t *testing.T) { _, clusterAdminKubeConfig, err := testutil.StartTestMaster() if err != nil { t.Fatalf("error starting master: %v", err) } clusterAdminClient, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig) if err != nil { t.Fatalf("error getting cluster admin client: %v", err) } clusterAdminClientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) if err != nil { t.Fatalf("error getting cluster admin client config: %v", err) } user := "******" adminClient, err := testutil.CreateNewProject(clusterAdminClient, *clusterAdminClientConfig, testutil.Namespace(), user) if err != nil { t.Fatalf("error creating project: %v", err) } token, err := tokencmd.RequestToken(clusterAdminClientConfig, nil, user, "password") if err != nil { t.Fatalf("error requesting token: %v", err) } config := `version: 0.1 loglevel: debug http: addr: 127.0.0.1:5000 storage: inmemory: {} auth: openshift: middleware: repository: - name: openshift ` os.Setenv("OPENSHIFT_CA_DATA", string(clusterAdminClientConfig.CAData)) os.Setenv("OPENSHIFT_CERT_DATA", string(clusterAdminClientConfig.CertData)) os.Setenv("OPENSHIFT_KEY_DATA", string(clusterAdminClientConfig.KeyData)) os.Setenv("OPENSHIFT_MASTER", clusterAdminClientConfig.Host) os.Setenv("REGISTRY_URL", "127.0.0.1:5000") go dockerregistry.Execute(strings.NewReader(config)) stream := imageapi.ImageStream{ ObjectMeta: kapi.ObjectMeta{ Namespace: testutil.Namespace(), Name: "test", }, } if _, err := adminClient.ImageStreams(testutil.Namespace()).Create(&stream); err != nil { t.Fatalf("error creating image stream: %s", err) } tags, err := getTags(stream.Name, user, token) if err != nil { t.Fatal(err) } if len(tags) > 0 { t.Fatalf("expected 0 tags, got: %#v", tags) } dgst, err := putManifest(stream.Name, user, token) if err != nil { t.Fatal(err) } tags, err = getTags(stream.Name, user, token) if err != nil { t.Fatal(err) } if len(tags) != 1 { t.Fatalf("expected 1 tag, got %d: %v", len(tags), tags) } if tags[0] != imageapi.DefaultImageTag { t.Fatalf("expected latest, got %q", tags[0]) } // test get by tag url := fmt.Sprintf("http://127.0.0.1:5000/v2/%s/%s/manifests/%s", testutil.Namespace(), stream.Name, imageapi.DefaultImageTag) req, err := http.NewRequest("GET", url, nil) if err != nil { t.Fatalf("error creating request: %v", err) } req.SetBasicAuth(user, token) resp, err := http.DefaultClient.Do(req) if err != nil { t.Fatalf("error retrieving manifest from registry: %s", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { t.Fatalf("unexpected status code: %d", resp.StatusCode) } body, err := ioutil.ReadAll(resp.Body) var retrievedManifest manifest.Manifest if err := json.Unmarshal(body, &retrievedManifest); err != nil { t.Fatalf("error unmarshaling retrieved manifest") } if retrievedManifest.Name != fmt.Sprintf("%s/%s", testutil.Namespace(), stream.Name) { t.Fatalf("unexpected manifest name: %s", retrievedManifest.Name) } if retrievedManifest.Tag != imageapi.DefaultImageTag { t.Fatalf("unexpected manifest tag: %s", retrievedManifest.Tag) } // test get by digest url = fmt.Sprintf("http://127.0.0.1:5000/v2/%s/%s/manifests/%s", testutil.Namespace(), stream.Name, dgst.String()) req, err = http.NewRequest("GET", url, nil) if err != nil { t.Fatalf("error creating request: %v", err) } req.SetBasicAuth(user, token) resp, err = http.DefaultClient.Do(req) if err != nil { t.Fatalf("error retrieving manifest from registry: %s", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { t.Fatalf("unexpected status code: %d", resp.StatusCode) } body, err = ioutil.ReadAll(resp.Body) if err := json.Unmarshal(body, &retrievedManifest); err != nil { t.Fatalf("error unmarshaling retrieved manifest") } if retrievedManifest.Name != fmt.Sprintf("%s/%s", testutil.Namespace(), stream.Name) { t.Fatalf("unexpected manifest name: %s", retrievedManifest.Name) } if retrievedManifest.Tag != imageapi.DefaultImageTag { t.Fatalf("unexpected manifest tag: %s", retrievedManifest.Tag) } image, err := adminClient.ImageStreamImages(testutil.Namespace()).Get(stream.Name, dgst.String()) if err != nil { t.Fatalf("error getting imageStreamImage: %s", err) } if e, a := fmt.Sprintf("test@%s", dgst.Hex()[:7]), image.Name; e != a { t.Errorf("image name: expected %q, got %q", e, a) } if e, a := dgst.String(), image.Image.Name; e != a { t.Errorf("image name: expected %q, got %q", e, a) } if e, a := fmt.Sprintf("127.0.0.1:5000/%s/%s@%s", testutil.Namespace(), stream.Name, dgst.String()), image.Image.DockerImageReference; e != a { t.Errorf("image dockerImageReference: expected %q, got %q", e, a) } if e, a := "foo", image.Image.DockerImageMetadata.ID; e != a { t.Errorf("image dockerImageMetadata.ID: expected %q, got %q", e, a) } // test auto provisioning otherStream, err := adminClient.ImageStreams(testutil.Namespace()).Get("otherrepo") t.Logf("otherStream=%#v, err=%v", otherStream, err) if err == nil { t.Fatalf("expected error getting otherrepo") } otherDigest, err := putManifest("otherrepo", user, token) if err != nil { t.Fatal(err) } otherStream, err = adminClient.ImageStreams(testutil.Namespace()).Get("otherrepo") if err != nil { t.Fatalf("unexpected error getting otherrepo: %s", err) } if otherStream == nil { t.Fatalf("unexpected nil otherrepo") } if len(otherStream.Status.Tags) != 1 { t.Errorf("expected 1 tag, got %#v", otherStream.Status.Tags) } history, ok := otherStream.Status.Tags[imageapi.DefaultImageTag] if !ok { t.Fatal("unable to find 'latest' tag") } if len(history.Items) != 1 { t.Errorf("expected 1 tag event, got %#v", history.Items) } if e, a := otherDigest.String(), history.Items[0].Image; e != a { t.Errorf("digest: expected %q, got %q", e, a) } }
func TestPolicyBasedRestrictionOfBuildStrategies(t *testing.T) { const namespace = "hammer" _, clusterAdminKubeConfig, err := testutil.StartTestMaster() 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 := testutil.CreateNewProject(clusterAdminClient, *clusterAdminClientConfig, namespace, "harold") if err != nil { t.Fatalf("unexpected error: %v", err) } joeClient, err := testutil.GetClientForUser(*clusterAdminClientConfig, "joe") if err != nil { t.Fatalf("unexpected error: %v", err) } addJoe := &policy.RoleModificationOptions{ RoleNamespace: "", RoleName: bootstrappolicy.EditRoleName, RoleBindingAccessor: policy.NewLocalRoleBindingAccessor(namespace, haroldClient), Users: []string{"joe"}, } if err := addJoe.AddRole(); err != nil { t.Errorf("unexpected error: %v", err) } if err := testutil.WaitForPolicyUpdate(joeClient, namespace, "create", authorizationapi.DockerBuildResource, true); err != nil { t.Error(err) } // by default admins and editors can create all type of builds _, err = createDockerBuild(t, haroldClient.Builds(namespace)) if err != nil { t.Errorf("unexpected error: %v", err) } _, err = createDockerBuild(t, joeClient.Builds(namespace)) if err != nil { t.Errorf("unexpected error: %v", err) } _, err = createSourceBuild(t, haroldClient.Builds(namespace)) if err != nil { t.Errorf("unexpected error: %v", err) } _, err = createSourceBuild(t, joeClient.Builds(namespace)) if err != nil { t.Errorf("unexpected error: %v", err) } _, err = createCustomBuild(t, haroldClient.Builds(namespace)) if err != nil { t.Errorf("unexpected error: %v", err) } _, err = createCustomBuild(t, joeClient.Builds(namespace)) if err != nil { t.Errorf("unexpected error: %v", err) } // remove resources from role so that certain build strategies are forbidden removeBuildStrategyPrivileges(t, clusterAdminClient.ClusterRoles(), bootstrappolicy.EditRoleName) if err := testutil.WaitForPolicyUpdate(joeClient, namespace, "create", authorizationapi.DockerBuildResource, false); err != nil { t.Error(err) } removeBuildStrategyPrivileges(t, clusterAdminClient.ClusterRoles(), bootstrappolicy.AdminRoleName) if err := testutil.WaitForPolicyUpdate(haroldClient, namespace, "create", authorizationapi.DockerBuildResource, false); err != nil { t.Error(err) } // make sure builds are rejected if _, err = createDockerBuild(t, haroldClient.Builds(namespace)); !kapierror.IsForbidden(err) { t.Errorf("expected forbidden, got %v", err) } if _, err = createDockerBuild(t, joeClient.Builds(namespace)); !kapierror.IsForbidden(err) { t.Errorf("expected forbidden, got %v", err) } if _, err = createSourceBuild(t, haroldClient.Builds(namespace)); !kapierror.IsForbidden(err) { t.Errorf("expected forbidden, got %v", err) } if _, err = createSourceBuild(t, joeClient.Builds(namespace)); !kapierror.IsForbidden(err) { t.Errorf("expected forbidden, got %v", err) } if _, err = createCustomBuild(t, haroldClient.Builds(namespace)); !kapierror.IsForbidden(err) { t.Errorf("expected forbidden, got %v", err) } if _, err = createCustomBuild(t, joeClient.Builds(namespace)); !kapierror.IsForbidden(err) { t.Errorf("expected forbidden, got %v", err) } }
func TestPolicyCommands(t *testing.T) { _, clusterAdminKubeConfig, err := testutil.StartTestMaster() 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) } const projectName = "hammer-project" haroldClient, err := testutil.CreateNewProject(clusterAdminClient, *clusterAdminClientConfig, projectName, "harold") if err != nil { t.Fatalf("unexpected error: %v", err) } addViewer := policy.RoleModificationOptions{ RoleName: bootstrappolicy.ViewRoleName, RoleBindingAccessor: policy.NewLocalRoleBindingAccessor(projectName, haroldClient), Users: []string{"valerie"}, Groups: []string{"my-group"}, } if err := addViewer.AddRole(); err != nil { t.Fatalf("unexpected error: %v", err) } viewers, err := haroldClient.RoleBindings(projectName).Get("view") if err != nil { t.Fatalf("unexpected error: %v", err) } if !viewers.Users.Has("valerie") { t.Errorf("expected valerie in users: %v", viewers.Users) } if !viewers.Groups.Has("my-group") { t.Errorf("expected my-group in groups: %v", viewers.Groups) } removeValerie := policy.RemoveFromProjectOptions{ BindingNamespace: projectName, Client: haroldClient, Users: []string{"valerie"}, Out: ioutil.Discard, } if err := removeValerie.Run(); err != nil { t.Fatalf("unexpected error: %v", err) } viewers, err = haroldClient.RoleBindings(projectName).Get("view") if err != nil { t.Fatalf("unexpected error: %v", err) } if viewers.Users.Has("valerie") { t.Errorf("unexpected valerie in users: %v", viewers.Users) } if !viewers.Groups.Has("my-group") { t.Errorf("expected my-group in groups: %v", viewers.Groups) } removeMyGroup := policy.RemoveFromProjectOptions{ BindingNamespace: projectName, Client: haroldClient, Groups: []string{"my-group"}, Out: ioutil.Discard, } if err := removeMyGroup.Run(); err != nil { t.Fatalf("unexpected error: %v", err) } viewers, err = haroldClient.RoleBindings(projectName).Get("view") if err != nil { t.Fatalf("unexpected error: %v", err) } if viewers.Users.Has("valerie") { t.Errorf("unexpected valerie in users: %v", viewers.Users) } if viewers.Groups.Has("my-group") { t.Errorf("unexpected my-group in groups: %v", viewers.Groups) } }