diff --git a/cmd/main_test.go b/cmd/main_test.go index 940fd3e..b6a2db9 100644 --- a/cmd/main_test.go +++ b/cmd/main_test.go @@ -2,11 +2,8 @@ package main import ( "fmt" - "io" - "log" - "net/http" + // "net/http" "os/exec" - "strings" "github.com/labstack/echo/v4" k8sapi "k8s.io/api/core/v1" @@ -16,9 +13,10 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/selection" "k8s.io/client-go/kubernetes" + authorizationv1Client "k8s.io/client-go/kubernetes/typed/authorization/v1" "context" - "net/http/httptest" + // "net/http/httptest" "testing" crt "github.com/codeready-toolchain/api/api/v1alpha1" @@ -34,16 +32,6 @@ import ( clientgoscheme "k8s.io/client-go/kubernetes/scheme" ) -type HTTPResponse struct { - Body string - StatusCode int -} - -type HTTPheader struct { - name string - value string -} - type NamespaceRoleBinding struct { Namespace string Role string @@ -53,63 +41,6 @@ type NamespaceRoleBinding struct { var k8sClient client.Client var testEnv *envtest.Environment -func createRole(k8sClient client.Client, nsName string, roleName string, verbs []string) { - role := &rbacv1.Role{ - ObjectMeta: metav1.ObjectMeta{ - Name: roleName, - Namespace: nsName, - }, - Rules: []rbacv1.PolicyRule{ - { - APIGroups: []string{"appstudio.redhat.com"}, - Resources: []string{"applications", "components"}, - Verbs: verbs, - }, - }, - } - err := k8sClient.Create(context.Background(), role) - Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("Error creating 'Role' resource: %v", err)) -} - -func createRoleBinding(k8sClient client.Client, bindingName string, nsName string, userName string, roleName string) { - roleBinding := &rbacv1.RoleBinding{ - ObjectMeta: metav1.ObjectMeta{ - Name: bindingName, - Namespace: nsName, - }, - Subjects: []rbacv1.Subject{ - { - Kind: "User", - Name: userName, - APIGroup: "rbac.authorization.k8s.io", - }, - }, - RoleRef: rbacv1.RoleRef{ - Kind: "Role", - Name: roleName, - APIGroup: "rbac.authorization.k8s.io", - }, - } - err := k8sClient.Create(context.Background(), roleBinding) - Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("Error creating 'roleBinding' resource: %v", err)) -} - -func createNamespace(k8sClient client.Client, name string) (k8sapi.Namespace, error) { - namespaced := &k8sapi.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Labels: map[string]string{ - "konflux.ci/type": "user", - "kubernetes.io/metadata.name": name, - }, - }, - } - if err := k8sClient.Create(context.Background(), namespaced); err != nil { - return k8sapi.Namespace{}, fmt.Errorf("Error creating 'Namespace' resource: %v", err) - } - return *namespaced, nil -} - func deleteRole(k8sClient client.Client, nsName string, roleName string) { role := &rbacv1.Role{ ObjectMeta: metav1.ObjectMeta{ @@ -142,115 +73,11 @@ func deleteNamespace(k8sClient client.Client, nsName string) { Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("Error deleting the namespace: %s: %v\n", nsName, err)) } -func performHTTPGetCall(url string, header HTTPheader) (*HTTPResponse, error) { - req, err := http.NewRequest("GET", url, nil) - if err != nil { - log.Printf("Error creating request: %s", err) - return nil, err - } - if header.name != "" { - req.Header.Add(header.name, header.value) - } - client := &http.Client{} - resp, err := client.Do(req) - if err != nil { - log.Printf("Error making request: %s", err) - return nil, err - } - defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) - if err != nil { - log.Printf("Error reading response body: %s", err) - return nil, err - } - response := &HTTPResponse{ - Body: string(body), - StatusCode: resp.StatusCode, - } - return response, nil -} - func TestCmd(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "Main Suite") } -var _ = Describe("Signup endpoint", func() { - Context("Calling the signup endpoint with GET", func() { - It("responds with ready and signedup", func() { - url := "http://localhost:5000/api/v1/signup" - expectedCode := http.StatusOK - expectedBody := `{"status":{"ready":true,"reason":"SignedUp"}}` - - resp, err := performHTTPGetCall(url, HTTPheader{}) - Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("Unexpected error testing the \"%s\" endpoint: %v", url, err)) - Expect(resp.StatusCode).To(Equal(expectedCode)) - Expect(strings.TrimSpace(expectedBody)).To(Equal(strings.TrimSpace(resp.Body))) - }) - }) -}) - -var _ = DescribeTable("Workspace endpoint", func(header HTTPheader, expectedCode int, expectedBody string) { - url := "http://localhost:5000/workspaces" - resp, err := performHTTPGetCall(url, header) - Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("Unexpected error testing the \"%s\" endpoint: %v", url, err)) - Expect(resp.StatusCode).To(Equal(expectedCode)) - Expect(strings.TrimSpace(expectedBody)).To(Equal(strings.TrimSpace(resp.Body))) -}, - Entry( - "Calling the workspace endpoint for user1 responds only with the 'test-tenant' workspace info", - HTTPheader{"X-Email", "user1@konflux.dev"}, - http.StatusOK, - `{"kind":"WorkspaceList","apiVersion":"toolchain.dev.openshift.com/v1alpha1","metadata":{},`+ - `"items":[{"kind":"Workspace","apiVersion":"toolchain.dev.openshift.com/v1alpha1",`+ - `"metadata":{"name":"test-tenant","creationTimestamp":null},"status":`+ - `{"namespaces":[{"name":"test-tenant","type":"default"}]}}]}`), - Entry( - "Workspace endpoint for user2 responds with 2 namespaces info", - HTTPheader{"X-Email", "user2@konflux.dev"}, - http.StatusOK, - `{"kind":"WorkspaceList","apiVersion":"toolchain.dev.openshift.com/v1alpha1","metadata":{},`+ - `"items":[{"kind":"Workspace","apiVersion":"toolchain.dev.openshift.com/v1alpha1",`+ - `"metadata":{"name":"test-tenant","creationTimestamp":null},"status":{"namespaces":`+ - `[{"name":"test-tenant","type":"default"}]}},{"kind":"Workspace","apiVersion":`+ - `"toolchain.dev.openshift.com/v1alpha1","metadata":{"name":"test-tenant-2",`+ - `"creationTimestamp":null},"status":{"namespaces":[{"name":"test-tenant-2",`+ - `"type":"default"}]}}]}`), - Entry( - "Workspace endpoint for user3 responds with no namespaces", - HTTPheader{"X-Email", "user3@konflux.dev"}, - http.StatusOK, - `{"kind":"WorkspaceList","apiVersion":"toolchain.dev.openshift.com/v1alpha1","metadata":{},"items":null}`), - Entry( - "Workspace endpoint with no header", - HTTPheader{}, - 500, - `{"message":"Internal Server Error"}`), -) - -var _ = DescribeTable("Specific workspace endpoint", func(endpoint string, header HTTPheader, expectedCode int, expectedBody string) { - url := "http://localhost:5000/workspaces/" + endpoint - resp, err := performHTTPGetCall(url, header) - Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("Unexpected error testing the \"%s\" endpoint: %v", url, err)) - Expect(resp.StatusCode).To(Equal(expectedCode)) - Expect(strings.TrimSpace(expectedBody)).To(Equal(strings.TrimSpace(resp.Body))) -}, - Entry( - "Calling the workspace endpoint for the test-tenant workspace for user2", - "test-tenant", - HTTPheader{"X-Email", "user2@konflux.dev"}, - http.StatusOK, - `{"kind":"Workspace","apiVersion":"toolchain.dev.openshift.com/v1alpha1","metadata":`+ - `{"name":"test-tenant","creationTimestamp":null},"status":{"namespaces":`+ - `[{"name":"test-tenant","type":"default"}]}}`), - Entry( - "Specific workspace endpoint for test-tenant-2 for user1 only", - "test-tenant-2", - HTTPheader{"X-Email", "user1@konflux.dev"}, - 404, - `{"message":"Not Found"}`), -) - var serverProcess *exec.Cmd var serverCancelFunc context.CancelFunc @@ -262,19 +89,6 @@ var _ = BeforeSuite(func() { serverProcess, serverCancelFunc = utils.CreateWorkspaceManagerServer("main.go", nil, "") utils.WaitForWorkspaceManagerServerToServe() - - user1 := "user1@konflux.dev" - user2 := "user2@konflux.dev" - namespaceNames := []string{"test-tenant", "test-tenant-2", "test-tenant-3"} - for _, name := range namespaceNames { - _, err := createNamespace(k8sClient, name) - Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("Error while creating the namespace %s: %v", name, err)) - } - createRole(k8sClient, "test-tenant", "namespace-access", []string{"create", "list", "watch", "delete"}) - createRole(k8sClient, "test-tenant-2", "namespace-access-2", []string{"create", "list", "watch", "delete"}) - createRoleBinding(k8sClient, "namespace-access-user-binding", "test-tenant", user1, "namespace-access") - createRoleBinding(k8sClient, "namespace-access-user-binding-2", "test-tenant", user2, "namespace-access") - createRoleBinding(k8sClient, "namespace-access-user-binding-3", "test-tenant-2", user2, "namespace-access-2") }) var _ = AfterSuite(func() { @@ -282,39 +96,75 @@ var _ = AfterSuite(func() { utils.StopEnvTest(testEnv) }) -var _ = DescribeTable("TestRunAccessCheck", func(user string, namespace string, resource string, verb string, expectedResult bool) { - cfg, _ := config.GetConfig() - clientset, _ := kubernetes.NewForConfig(cfg) - authCl := clientset.AuthorizationV1() - - boolresult, err := runAccessCheck(authCl, user, namespace, "appstudio.redhat.com", resource, verb) - Expect(boolresult).To(Equal(expectedResult)) - Expect(err).NotTo(HaveOccurred(), "Unexpected error testing RunAccessCheck") -}, - Entry( - "A user that has access to the resource should return true (user2 has permission to 'create' on test-tenant-1)", - "user2@konflux.dev", - "test-tenant", - "applications", - "create", - true), - Entry( - "A user that does not have any premissions on the namespace should return false (user1 doesn't have access to test-tenant-2)", - "user1@konflux.dev", - "test-tenant-2", - "applications", - "create", - false), - Entry( - "A user that does not have the permissions to perform the specific action on the namespace should return false (user1 doesn't have permission to 'patch' on test-tenant-1)", - "user1@konflux.dev", - "test-tenant-1", - "applications", - "patch", - false), -) +var _ = Describe("TestRunAccessCheck", func() { + var authCl authorizationv1Client.AuthorizationV1Interface + + BeforeEach(func() { + // Set up Kubernetes client + cfg, err := config.GetConfig() + Expect(err).NotTo(HaveOccurred(), "Unexpected error getting Kubernetes config") + clientset, err := kubernetes.NewForConfig(cfg) + Expect(err).NotTo(HaveOccurred(), "Unexpected error creating Kubernetes clientset") + authCl = clientset.AuthorizationV1() + }) + + Context("When a user has access to the resource", func() { + It("should return true for a user with 'create' permission on test-tenant", func() { + user := "user3@konflux.dev" + namespace := "test-tenant" + resource := "applications" + verb := "create" + expectedResult := true + _, err := utils.CreateNamespace(k8sClient, namespace) + Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("Error while creating the namespace %s: %v", namespace, err)) + utils.CreateRole(k8sClient, "test-tenant", "namespace-access", []string{"create", "list", "watch", "delete"}) + utils.CreateRoleBinding(k8sClient, "namespace-access-user-binding", "test-tenant", user, "namespace-access") + boolresult, err := runAccessCheck(authCl, user, namespace, "appstudio.redhat.com", resource, verb) + Expect(boolresult).To(Equal(expectedResult)) + Expect(err).NotTo(HaveOccurred(), "Unexpected error testing RunAccessCheck") + }) + }) + + Context("When a user does not have any permissions on the namespace", func() { + It("should return false for a user without access to test-tenant-2", func() { + user := "user4@konflux.dev" + namespace := "test-tenant-2" + resource := "applications" + verb := "create" + expectedResult := false + _, err := utils.CreateNamespace(k8sClient, namespace) + Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("Error while creating the namespace %s: %v", namespace, err)) + + utils.CreateRole(k8sClient, "test-tenant-2", "namespace-access-2", []string{"create", "list", "watch", "delete"}) + utils.CreateRoleBinding(k8sClient, "namespace-access-user-binding-3", "test-tenant-2", user, "namespace-access-2") + boolresult, err := runAccessCheck(authCl, "user3@konflux.dev", namespace, "appstudio.redhat.com", resource, verb) + Expect(boolresult).To(Equal(expectedResult)) + Expect(err).NotTo(HaveOccurred(), "Unexpected error testing RunAccessCheck") + }) + }) + + Context("When a user lacks the specific action permission on the namespace", func() { + It("should return false for a user without 'patch' permission on test-tenant-1", func() { + user := "user5@konflux.dev" + namespace := "test-tenant-1" + resource := "applications" + verb := "patch" + expectedResult := false + _, err := utils.CreateNamespace(k8sClient, namespace) + Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("Error while creating the namespace %s: %v", namespace, err)) + utils.CreateRole(k8sClient, "test-tenant-1", "namespace-access", []string{"create", "list", "watch", "delete"}) + utils.CreateRoleBinding(k8sClient, "namespace-access-user-binding", "test-tenant-1", user, "namespace-access") + boolresult, err := runAccessCheck(authCl, user, namespace, "appstudio.redhat.com", resource, verb) + Expect(boolresult).To(Equal(expectedResult)) + Expect(err).NotTo(HaveOccurred(), "Unexpected error testing RunAccessCheck") + }) + }) +}) -var _ = DescribeTable("GetWorkspacesWithAccess querying for workspaces with access", func(gv string, allNamespaces []k8sapi.Namespace, expectedWorkspaces []crt.Workspace) { +var _ = Describe("GetWorkspacesWithAccess querying for workspaces with access", func() { + var gv string + var allNamespaces []k8sapi.Namespace + var expectedWorkspaces []crt.Workspace e := echo.New() c := e.NewContext(nil, nil) Context("When workspace test-tenant's namespaces has all the necessary permissions", func() { @@ -324,11 +174,11 @@ var _ = DescribeTable("GetWorkspacesWithAccess querying for workspaces with acce BeforeEach(func() { gv = "v1alpha1" for i, name := range namespaceNames { - ns, err := createNamespace(k8sClient, name) + ns, err := utils.CreateNamespace(k8sClient, name) Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("Error while creating the namespace %s: %v", name, err)) allNamespaces = append(allNamespaces, ns) - createRole(k8sClient, name, roleNames[i], []string{"create", "list", "watch", "delete"}) - createRoleBinding(k8sClient, roleBindings[i], name, "user1@konflux.dev", roleNames[i]) + utils.CreateRole(k8sClient, name, roleNames[i], []string{"create", "list", "watch", "delete"}) + utils.CreateRoleBinding(k8sClient, roleBindings[i], name, "user1@konflux.dev", roleNames[i]) } expectedWorkspaces = []crt.Workspace{ { @@ -388,12 +238,12 @@ var _ = DescribeTable("GetWorkspacesWithAccess querying for workspaces with acce BeforeEach(func() { gv = "v1alpha1" for _, name := range namespaceNames { - ns, err := createNamespace(k8sClient, name) + ns, err := utils.CreateNamespace(k8sClient, name) Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("Error while creating the namespace %s: %v", name, err)) allNamespaces = append(allNamespaces, ns) } - createRole(k8sClient, "ws-test-tenant-1", "ws-namespace-access-1", []string{"create", "list", "watch", "delete"}) - createRoleBinding(k8sClient, "ws-namespace-access-user-binding-1", "ws-test-tenant-1", "user1@konflux.dev", "ws-namespace-access-1") + utils.CreateRole(k8sClient, "ws-test-tenant-1", "ws-namespace-access-1", []string{"create", "list", "watch", "delete"}) + utils.CreateRoleBinding(k8sClient, "ws-namespace-access-user-binding-1", "ws-test-tenant-1", "user1@konflux.dev", "ws-namespace-access-1") expectedWorkspaces = []crt.Workspace{ { TypeMeta: metav1.TypeMeta{ @@ -435,12 +285,12 @@ var _ = DescribeTable("GetWorkspacesWithAccess querying for workspaces with acce BeforeEach(func() { gv = "v1alpha1" for _, name := range namespaceNames { - ns, err := createNamespace(k8sClient, name) + ns, err := utils.CreateNamespace(k8sClient, name) Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("Error while creating the namespace %s: %v", name, err)) allNamespaces = append(allNamespaces, ns) } - createRole(k8sClient, "ws-test-tenant-1", "ws-namespace-access-1", []string{"create", "list"}) - createRoleBinding(k8sClient, "ws-namespace-access-user-binding-1", "ws-test-tenant-1", "user1@konflux.dev", "ws-namespace-access-1") + utils.CreateRole(k8sClient, "ws-test-tenant-1", "ws-namespace-access-1", []string{"create", "list"}) + utils.CreateRoleBinding(k8sClient, "ws-namespace-access-user-binding-1", "ws-test-tenant-1", "user1@konflux.dev", "ws-namespace-access-1") expectedWorkspaces = []crt.Workspace{} }) @@ -460,128 +310,128 @@ var _ = DescribeTable("GetWorkspacesWithAccess querying for workspaces with acce }) }) -var _ = DescribeTable("TestGetNamespacesWithAccess", func(allNamespaces []k8sapi.Namespace, - expectedNs []k8sapi.Namespace, actualNs []k8sapi.Namespace, err error) { - e := echo.New() - cfg, _ := config.GetConfig() - clientset, _ := kubernetes.NewForConfig(cfg) - req := httptest.NewRequest(http.MethodGet, "/", nil) - rec := httptest.NewRecorder() - c := e.NewContext(req, rec) - c.Request().Header.Set("X-Email", "user1@konflux.dev") - authCl := clientset.AuthorizationV1() - - JustBeforeEach(func() { - actualNs, err = getNamespacesWithAccess(e, c, authCl, allNamespaces) - }) - - Context("When all the namespaces have all the necessary permissions like create, list, watch and delete", func() { - mappings := []NamespaceRoleBinding{ - { - Namespace: "ns-test-tenant-1", - Role: "ns-namespace-access-1", - RoleBinding: "ns-namespace-access-user-binding-1", - }, - { - Namespace: "ns-test-tenant-2", - Role: "ns-namespace-access-2", - RoleBinding: "ns-namespace-access-user-binding-2", - }, - } - BeforeEach(func() { - for _, name := range mappings { - ns, err := createNamespace(k8sClient, name.Namespace) - Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("Error while creating the namespace %s: %v", name.Namespace, err)) - allNamespaces = append(allNamespaces, ns) - createRole(k8sClient, name.Namespace, name.Role, []string{"create", "list", "watch", "delete"}) - createRoleBinding(k8sClient, name.RoleBinding, name.Namespace, "user1@konflux.dev", name.Role) - } - expectedNs = allNamespaces - }) - It("returns all namespaces in the list", func() { - Expect(actualNs).To(Equal(expectedNs)) - Expect(err).NotTo(HaveOccurred(), "Unexpected error testing GetNamespacesWithAccess") - }) - AfterEach(func() { - for _, name := range mappings { - deleteRoleBinding(k8sClient, name.Namespace, name.RoleBinding) - deleteRole(k8sClient, name.Namespace, name.Role) - deleteNamespace(k8sClient, name.Namespace) - } - }) - }) - - Context("When namspace ns3 doesn't have necessary permissions", func() { - BeforeEach(func() { - var ns3 k8sapi.Namespace - ns3, err = createNamespace(k8sClient, "ns-test-tenant-3") - Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("Error while creating the namespace ns3: %v", err)) - allNamespaces = []k8sapi.Namespace{ns3} - expectedNs = []k8sapi.Namespace{} - }) - It("doesn't return any namespace", func() { - Expect(actualNs).To(Equal(expectedNs)) - Expect(err).NotTo(HaveOccurred(), "Unexpected error testing GetNamespacesWithAccess") - }) - AfterEach(func() { - deleteNamespace(k8sClient, "ns-test-tenant-3") - }) - }) - - Context("When only namespaces ns-test-tenant-1 and ns-test-tenant-2 has all necessary permissions and other's don't", func() { - mappings := []NamespaceRoleBinding{ - { - Namespace: "ns-test-tenant-1", - Role: "ns-namespace-access-1", - RoleBinding: "ns-namespace-access-user-binding-1", - }, - { - Namespace: "ns-test-tenant-2", - Role: "ns-namespace-access-2", - RoleBinding: "ns-namespace-access-user-binding-2", - }, - { - Namespace: "ns-test-tenant-3", - Role: "ns-namespace-access-3", - RoleBinding: "ns-namespace-access-user-binding-3", - }, - { - Namespace: "ns-test-tenant-4", - Role: "", - RoleBinding: "", - }, - } - BeforeEach(func() { - for _, name := range mappings { - ns, err := createNamespace(k8sClient, name.Namespace) - Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("Error while creating the namespace %s: %v", name.Namespace, err)) - allNamespaces = append(allNamespaces, ns) - } - createRole(k8sClient, "ns-test-tenant-1", "ns-namespace-access-1", []string{"create", "list", "watch", "delete"}) - createRole(k8sClient, "ns-test-tenant-2", "ns-namespace-access-2", []string{"create", "list", "watch", "delete"}) - createRole(k8sClient, "ns-test-tenant-3", "ns-namespace-access-3", []string{"create", "list", "watch"}) - createRoleBinding(k8sClient, "ns-namespace-access-user-binding-1", "ns-test-tenant-1", "user1@konflux.dev", "ns-namespace-access-1") - createRoleBinding(k8sClient, "ns-namespace-access-user-binding-2", "ns-test-tenant-2", "user2@konflux.dev", "ns-namespace-access-2") - createRoleBinding(k8sClient, "ns-namespace-access-user-binding-3", "ns-test-tenant-3", "user3@konflux.dev", "ns-namespace-access-3") - expectedNs = append(expectedNs, allNamespaces[0], allNamespaces[1]) - }) - It("returns only namespaces test-tenant and test-tenant-2", func() { - Expect(actualNs).To(Equal(expectedNs)) - Expect(err).NotTo(HaveOccurred(), "Unexpected error testing GetNamespacesWithAccess") - }) - AfterEach(func() { - deleteRoleBinding(k8sClient, "ns-test-tenant-1", "ns-namespace-access-user-binding-1") - deleteRoleBinding(k8sClient, "ns-test-tenant-2", "ns-namespace-access-user-binding-2") - deleteRoleBinding(k8sClient, "ns-test-tenant-3", "ns-namespace-access-user-binding-3") - deleteRole(k8sClient, "ns-test-tenant-1", "ns-namespace-access-1") - deleteRole(k8sClient, "ns-test-tenant-2", "ns-namespace-access-2") - deleteRole(k8sClient, "ns-test-tenant-3", "ns-namespace-access-3") - for _, name := range mappings { - deleteNamespace(k8sClient, name.Namespace) - } - }) - }) -}) +// var _ = Describe("TestGetNamespacesWithAccess", func() { +// var allNamespaces, expectedNs, actualNs []k8sapi.Namespace +// var err error +// e := echo.New() +// cfg, _ := config.GetConfig() +// clientset, _ := kubernetes.NewForConfig(cfg) +// req := httptest.NewRequest(http.MethodGet, "/", nil) +// rec := httptest.NewRecorder() +// c := e.NewContext(req, rec) +// c.Request().Header.Set("X-Email", "user1@konflux.dev") +// authCl := clientset.AuthorizationV1() + +// Context("When all the namespaces have all the necessary permissions like create, list, watch and delete", func() { +// mappings := []NamespaceRoleBinding{ +// { +// Namespace: "ns-test-tenant-1", +// Role: "ns-namespace-access-1", +// RoleBinding: "ns-namespace-access-user-binding-1", +// }, +// { +// Namespace: "ns-test-tenant-2", +// Role: "ns-namespace-access-2", +// RoleBinding: "ns-namespace-access-user-binding-2", +// }, +// } +// BeforeEach(func() { +// for _, name := range mappings { +// ns, err := utils.CreateNamespace(k8sClient, name.Namespace) +// Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("Error while creating the namespace %s: %v", name.Namespace, err)) +// allNamespaces = append(allNamespaces, ns) +// utils.CreateRole(k8sClient, name.Namespace, name.Role, []string{"create", "list", "watch", "delete"}) +// utils.CreateRoleBinding(k8sClient, name.RoleBinding, name.Namespace, "user1@konflux.dev", name.Role) +// } +// expectedNs = allNamespaces +// }) +// It("returns all namespaces in the list", func() { +// actualNs, err = getNamespacesWithAccess(e, c, authCl, allNamespaces) +// Expect(actualNs).To(Equal(expectedNs)) +// Expect(err).NotTo(HaveOccurred(), "Unexpected error testing GetNamespacesWithAccess") +// }) +// AfterEach(func() { +// for _, name := range mappings { +// deleteRoleBinding(k8sClient, name.Namespace, name.RoleBinding) +// deleteRole(k8sClient, name.Namespace, name.Role) +// deleteNamespace(k8sClient, name.Namespace) +// } +// }) +// }) + +// Context("When namspace ns3 doesn't have necessary permissions", func() { +// BeforeEach(func() { +// var ns3 k8sapi.Namespace +// ns3, err = utils.CreateNamespace(k8sClient, "ns-test-tenant-3") +// Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("Error while creating the namespace ns3: %v", err)) +// allNamespaces = []k8sapi.Namespace{ns3} +// expectedNs = []k8sapi.Namespace{} +// }) +// It("doesn't return any namespace", func() { +// actualNs, err = getNamespacesWithAccess(e, c, authCl, allNamespaces) +// Expect(actualNs).To(Equal(expectedNs)) +// Expect(err).NotTo(HaveOccurred(), "Unexpected error testing GetNamespacesWithAccess") +// }) +// AfterEach(func() { +// deleteNamespace(k8sClient, "ns-test-tenant-3") +// }) +// }) + +// Context("When only namespaces ns-test-tenant-1 and ns-test-tenant-2 has all necessary permissions and other's don't", func() { +// mappings := []NamespaceRoleBinding{ +// { +// Namespace: "ns-test-tenant-1", +// Role: "ns-namespace-access-1", +// RoleBinding: "ns-namespace-access-user-binding-1", +// }, +// { +// Namespace: "ns-test-tenant-2", +// Role: "ns-namespace-access-2", +// RoleBinding: "ns-namespace-access-user-binding-2", +// }, +// { +// Namespace: "ns-test-tenant-3", +// Role: "ns-namespace-access-3", +// RoleBinding: "ns-namespace-access-user-binding-3", +// }, +// { +// Namespace: "ns-test-tenant-4", +// Role: "", +// RoleBinding: "", +// }, +// } +// BeforeEach(func() { +// for _, name := range mappings { +// ns, err := utils.CreateNamespace(k8sClient, name.Namespace) +// Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("Error while creating the namespace %s: %v", name.Namespace, err)) +// allNamespaces = append(allNamespaces, ns) +// } +// utils.CreateRole(k8sClient, "ns-test-tenant-1", "ns-namespace-access-1", []string{"create", "list", "watch", "delete"}) +// utils.CreateRole(k8sClient, "ns-test-tenant-2", "ns-namespace-access-2", []string{"create", "list", "watch", "delete"}) +// utils.CreateRole(k8sClient, "ns-test-tenant-3", "ns-namespace-access-3", []string{"create", "list", "watch"}) +// utils.CreateRoleBinding(k8sClient, "ns-namespace-access-user-binding-1", "ns-test-tenant-1", "user1@konflux.dev", "ns-namespace-access-1") +// utils.CreateRoleBinding(k8sClient, "ns-namespace-access-user-binding-2", "ns-test-tenant-2", "user2@konflux.dev", "ns-namespace-access-2") +// utils.CreateRoleBinding(k8sClient, "ns-namespace-access-user-binding-3", "ns-test-tenant-3", "user3@konflux.dev", "ns-namespace-access-3") +// expectedNs = append(expectedNs, allNamespaces[0], allNamespaces[1]) +// }) +// It("returns only namespaces test-tenant and test-tenant-2", func() { +// actualNs, err = getNamespacesWithAccess(e, c, authCl, allNamespaces) +// Expect(actualNs).To(Equal(expectedNs)) +// Expect(err).NotTo(HaveOccurred(), "Unexpected error testing GetNamespacesWithAccess") +// }) +// AfterEach(func() { +// deleteRoleBinding(k8sClient, "ns-test-tenant-1", "ns-namespace-access-user-binding-1") +// deleteRoleBinding(k8sClient, "ns-test-tenant-2", "ns-namespace-access-user-binding-2") +// deleteRoleBinding(k8sClient, "ns-test-tenant-3", "ns-namespace-access-user-binding-3") +// deleteRole(k8sClient, "ns-test-tenant-1", "ns-namespace-access-1") +// deleteRole(k8sClient, "ns-test-tenant-2", "ns-namespace-access-2") +// deleteRole(k8sClient, "ns-test-tenant-3", "ns-namespace-access-3") +// for _, name := range mappings { +// deleteNamespace(k8sClient, name.Namespace) +// } +// }) +// }) +// }) var _ = Describe("GetUserNamespaces", func() { var e *echo.Echo @@ -592,7 +442,7 @@ var _ = Describe("GetUserNamespaces", func() { It("Should return all created namespaces", func() { namesToCreate := []string{"test-ns-1", "test-ns-2", "test-ns-3"} for _, name := range namesToCreate { - ns, err := createNamespace(k8sClient, name) + ns, err := utils.CreateNamespace(k8sClient, name) Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("Error creating the namespace %s", name)) createdNamespaces = append(createdNamespaces, ns.Name) } @@ -624,7 +474,7 @@ var _ = Describe("GetUserNamespaces", func() { Context("When querying for specific namespaces using In", func() { It("Should return only the specified namespaces", func() { for _, name := range []string{"in-test-1", "in-test-2", "not-in-test"} { - ns, err := createNamespace(k8sClient, name) + ns, err := utils.CreateNamespace(k8sClient, name) Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("Error creating the namespace %s", name)) createdNamespaces = append(createdNamespaces, ns.Name) } @@ -654,7 +504,7 @@ var _ = Describe("GetUserNamespaces", func() { Context("When querying for namespaces using NotIn", func() { It("Should return namespaces not in the specified list", func() { for _, name := range []string{"ts-keep-1", "ts-keep-2", "ts-exclude-1", "ts-exclude-2"} { - ns, err := createNamespace(k8sClient, name) + ns, err := utils.CreateNamespace(k8sClient, name) Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("Error creating the namespace %s", name)) createdNamespaces = append(createdNamespaces, ns.Name) } diff --git a/pkg/test/provision-test/provision_test.go b/pkg/test/provision-test/provision_test.go index ac09d5f..c60c3fb 100644 --- a/pkg/test/provision-test/provision_test.go +++ b/pkg/test/provision-test/provision_test.go @@ -3,8 +3,11 @@ package provision_test import ( "context" "fmt" + "io" + "log" "net/http" "os/exec" + "strings" "testing" . "github.com/onsi/ginkgo/v2" @@ -18,6 +21,16 @@ import ( "sigs.k8s.io/controller-runtime/pkg/envtest" ) +type HTTPheader struct { + name string + value string +} + +type HTTPResponse struct { + Body string + StatusCode int +} + func TestProvision(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "Provision namespace Suite") @@ -35,6 +48,19 @@ var _ = BeforeSuite(func() { k8sClient = utils.StartTestEnv(schema, testEnv) serverProcess, serverCancelFunc = utils.CreateWorkspaceManagerServer("../../../cmd/main.go", nil, "") utils.WaitForWorkspaceManagerServerToServe() + + user1 := "user1@konflux.dev" + user2 := "user2@konflux.dev" + namespaceNames := []string{"test-tenant", "test-tenant-2", "test-tenant-3"} + for _, name := range namespaceNames { + _, err := utils.CreateNamespace(k8sClient, name) + Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("Error while creating the namespace %s: %v", name, err)) + } + utils.CreateRole(k8sClient, "test-tenant", "namespace-access", []string{"create", "list", "watch", "delete"}) + utils.CreateRole(k8sClient, "test-tenant-2", "namespace-access-2", []string{"create", "list", "watch", "delete"}) + utils.CreateRoleBinding(k8sClient, "namespace-access-user-binding", "test-tenant", user1, "namespace-access") + utils.CreateRoleBinding(k8sClient, "namespace-access-user-binding-2", "test-tenant", user2, "namespace-access") + utils.CreateRoleBinding(k8sClient, "namespace-access-user-binding-3", "test-tenant-2", user2, "namespace-access-2") }) var _ = AfterSuite(func() { @@ -66,3 +92,107 @@ var _ = Describe("simple test", func() { }) }) }) + +func performHTTPGetCall(url string, header HTTPheader) (*HTTPResponse, error) { + req, err := http.NewRequest("GET", url, nil) + if err != nil { + log.Printf("Error creating request: %s", err) + return nil, err + } + if header.name != "" { + req.Header.Add(header.name, header.value) + } + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + log.Printf("Error making request: %s", err) + return nil, err + } + defer resp.Body.Close() + body, err := io.ReadAll(resp.Body) + if err != nil { + log.Printf("Error reading response body: %s", err) + return nil, err + } + response := &HTTPResponse{ + Body: string(body), + StatusCode: resp.StatusCode, + } + return response, nil +} + +var _ = Describe("Signup endpoint", func() { + Context("Calling the signup endpoint with GET", func() { + It("responds with ready and signedup", func() { + url := "http://localhost:5000/api/v1/signup" + expectedCode := http.StatusOK + expectedBody := `{"status":{"ready":true,"reason":"SignedUp"}}` + + resp, err := performHTTPGetCall(url, HTTPheader{}) + Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("Unexpected error testing the \"%s\" endpoint: %v", url, err)) + Expect(resp.StatusCode).To(Equal(expectedCode)) + Expect(strings.TrimSpace(expectedBody)).To(Equal(strings.TrimSpace(resp.Body))) + }) + }) +}) + +var _ = DescribeTable("Workspace endpoint", func(header HTTPheader, expectedCode int, expectedBody string) { + url := "http://localhost:5000/workspaces" + resp, err := performHTTPGetCall(url, header) + Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("Unexpected error testing the \"%s\" endpoint: %v", url, err)) + Expect(resp.StatusCode).To(Equal(expectedCode)) + Expect(strings.TrimSpace(expectedBody)).To(Equal(strings.TrimSpace(resp.Body))) +}, + Entry( + "Calling the workspace endpoint for user1 responds only with the 'test-tenant' workspace info", + HTTPheader{"X-Email", "user1@konflux.dev"}, + http.StatusOK, + `{"kind":"WorkspaceList","apiVersion":"toolchain.dev.openshift.com/v1alpha1","metadata":{},`+ + `"items":[{"kind":"Workspace","apiVersion":"toolchain.dev.openshift.com/v1alpha1",`+ + `"metadata":{"name":"test-tenant","creationTimestamp":null},"status":`+ + `{"namespaces":[{"name":"test-tenant","type":"default"}]}}]}`), + Entry( + "Workspace endpoint for user2 responds with 2 namespaces info", + HTTPheader{"X-Email", "user2@konflux.dev"}, + http.StatusOK, + `{"kind":"WorkspaceList","apiVersion":"toolchain.dev.openshift.com/v1alpha1","metadata":{},`+ + `"items":[{"kind":"Workspace","apiVersion":"toolchain.dev.openshift.com/v1alpha1",`+ + `"metadata":{"name":"test-tenant","creationTimestamp":null},"status":{"namespaces":`+ + `[{"name":"test-tenant","type":"default"}]}},{"kind":"Workspace","apiVersion":`+ + `"toolchain.dev.openshift.com/v1alpha1","metadata":{"name":"test-tenant-2",`+ + `"creationTimestamp":null},"status":{"namespaces":[{"name":"test-tenant-2",`+ + `"type":"default"}]}}]}`), + Entry( + "Workspace endpoint for user3 responds with no namespaces", + HTTPheader{"X-Email", "user3@konflux.dev"}, + http.StatusOK, + `{"kind":"WorkspaceList","apiVersion":"toolchain.dev.openshift.com/v1alpha1","metadata":{},"items":null}`), + Entry( + "Workspace endpoint with no header", + HTTPheader{}, + 500, + `{"message":"Internal Server Error"}`), +) + +var _ = DescribeTable("Specific workspace endpoint", func(endpoint string, header HTTPheader, expectedCode int, expectedBody string) { + url := "http://localhost:5000/workspaces/" + endpoint + resp, err := performHTTPGetCall(url, header) + Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("Unexpected error testing the \"%s\" endpoint: %v", url, err)) + Expect(resp.StatusCode).To(Equal(expectedCode)) + Expect(strings.TrimSpace(expectedBody)).To(Equal(strings.TrimSpace(resp.Body))) +}, + Entry( + "Calling the workspace endpoint for the test-tenant workspace for user2", + "test-tenant", + HTTPheader{"X-Email", "user2@konflux.dev"}, + http.StatusOK, + `{"kind":"Workspace","apiVersion":"toolchain.dev.openshift.com/v1alpha1","metadata":`+ + `{"name":"test-tenant","creationTimestamp":null},"status":{"namespaces":`+ + `[{"name":"test-tenant","type":"default"}]}}`), + Entry( + "Specific workspace endpoint for test-tenant-2 for user1 only", + "test-tenant-2", + HTTPheader{"X-Email", "user1@konflux.dev"}, + 404, + `{"message":"Not Found"}`), +) diff --git a/pkg/test/utils/utils.go b/pkg/test/utils/utils.go index 9027cb6..ecef9e2 100644 --- a/pkg/test/utils/utils.go +++ b/pkg/test/utils/utils.go @@ -9,6 +9,9 @@ import ( "path/filepath" . "github.com/onsi/gomega" + k8sapi "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" @@ -164,3 +167,60 @@ func StopWorkspaceManagerServer(cmd *exec.Cmd, serverCancelFunc context.CancelFu Expect(err).Should(BeAssignableToTypeOf(&exec.ExitError{})) } } + +func CreateRole(k8sClient client.Client, nsName string, roleName string, verbs []string) { + role := &rbacv1.Role{ + ObjectMeta: metav1.ObjectMeta{ + Name: roleName, + Namespace: nsName, + }, + Rules: []rbacv1.PolicyRule{ + { + APIGroups: []string{"appstudio.redhat.com"}, + Resources: []string{"applications", "components"}, + Verbs: verbs, + }, + }, + } + err := k8sClient.Create(context.Background(), role) + Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("Error creating 'Role' resource: %v", err)) +} + +func CreateRoleBinding(k8sClient client.Client, bindingName string, nsName string, userName string, roleName string) { + roleBinding := &rbacv1.RoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: bindingName, + Namespace: nsName, + }, + Subjects: []rbacv1.Subject{ + { + Kind: "User", + Name: userName, + APIGroup: "rbac.authorization.k8s.io", + }, + }, + RoleRef: rbacv1.RoleRef{ + Kind: "Role", + Name: roleName, + APIGroup: "rbac.authorization.k8s.io", + }, + } + err := k8sClient.Create(context.Background(), roleBinding) + Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("Error creating 'roleBinding' resource: %v", err)) +} + +func CreateNamespace(k8sClient client.Client, name string) (k8sapi.Namespace, error) { + namespaced := &k8sapi.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Labels: map[string]string{ + "konflux.ci/type": "user", + "kubernetes.io/metadata.name": name, + }, + }, + } + if err := k8sClient.Create(context.Background(), namespaced); err != nil { + return k8sapi.Namespace{}, fmt.Errorf("Error creating 'Namespace' resource: %v", err) + } + return *namespaced, nil +}