From d30244cc2271673b6157fc97cb11683af4a05353 Mon Sep 17 00:00:00 2001 From: Rafa Castelblanque Date: Wed, 2 Feb 2022 12:58:03 +0100 Subject: [PATCH 1/3] Fix bearer token login on E2E tests (#2898) Signed-off-by: Rafa Castelblanque --- integration/tests/utils/kubeapps-login.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/integration/tests/utils/kubeapps-login.js b/integration/tests/utils/kubeapps-login.js index 3e9095339e3..6bac7e567a2 100644 --- a/integration/tests/utils/kubeapps-login.js +++ b/integration/tests/utils/kubeapps-login.js @@ -7,12 +7,14 @@ exports.KubeappsLogin = class KubeappsLogin { constructor(page) { this.page = page; this.oidc = process.env.USE_MULTICLUSTER_OIDC_ENV === "true"; + this.token = ""; } isOidc = () => this.oidc; setToken(token) { - if ((token.match(/Bearer/g)).length > 1) { + let bearerMatch = token.match(/Bearer/g); + if (bearerMatch && bearerMatch.length > 1) { token = token.split(",")[0]; } this.token = token.trim().startsWith("Bearer") ? token.trim().substring(7) : token; @@ -71,8 +73,8 @@ exports.KubeappsLogin = class KubeappsLogin { await this.page.goto(utils.getUrl("/")); await this.page.waitForLoadState("networkidle"); - const formLocator = page.locator("form"); - await formLocator.type("input[name=token]", token); + const inputLocator = this.page.locator("form input[name=token]"); + await inputLocator.type(token); await this.page.click("#login-submit-button"); await this.page.waitForLoadState("networkidle"); From 2e8a04903a0f38c08fe2881f2ecdbe61607683f3 Mon Sep 17 00:00:00 2001 From: Rafa Castelblanque Date: Wed, 2 Feb 2022 18:01:27 +0100 Subject: [PATCH 2/3] Added positive and negative permissions tests for regular user (#4081) Signed-off-by: Rafa Castelblanque --- .../tests/main/01-missing-permissions.spec.js | 104 ++++++++++++------ .../09-user-positive-installation.spec.js | 81 ++++++++++++++ integration/tests/utils/kubeapps-login.js | 8 ++ 3 files changed, 162 insertions(+), 31 deletions(-) create mode 100644 integration/tests/main/09-user-positive-installation.spec.js diff --git a/integration/tests/main/01-missing-permissions.spec.js b/integration/tests/main/01-missing-permissions.spec.js index b3583bf8e99..a25393e60ce 100644 --- a/integration/tests/main/01-missing-permissions.spec.js +++ b/integration/tests/main/01-missing-permissions.spec.js @@ -5,35 +5,77 @@ const { test, expect } = require("@playwright/test"); const { KubeappsLogin } = require("../utils/kubeapps-login"); const utils = require("../utils/util-functions"); -test("Regular user fails to deploy an application due to missing permissions", async ({ page }) => { - test.setTimeout(60000); - - // Log in - const k = new KubeappsLogin(page); - await k.doLogin("kubeapps-user@example.com", "password", process.env.VIEW_TOKEN); - - // Select package to deploy - await page.click('a.nav-link:has-text("Catalog")'); - await page.click('a .card-title:has-text("apache")'); - await page.click('cds-button:has-text("Deploy") >> nth=0'); - - // Deploy package - const releaseNameLocator = page.locator("#releaseName"); - await releaseNameLocator.waitFor(); - await expect(releaseNameLocator).toHaveText(""); - await releaseNameLocator.type(utils.getRandomName("test-01-release")); - await page.locator('cds-button:has-text("Deploy")').click(); - - const errorLocator = page.locator(".alert-items .alert-text"); - await expect(errorLocator).toHaveCount(1); - await page.waitForTimeout(5000); - - // For some reason, UI is showing different error messages randomly - // Custom assertion logic - const errorMsg = await errorLocator.textContent(); - console.log(`Error message on UI = "${errorMsg}"`); - - await page.waitForFunction(msg => { - return msg.indexOf("secrets is forbidden") > -1 || msg.indexOf("unable to read secret") > -1; - }, errorMsg); +test.describe("Limited user simple deployments", () => { + test("Regular user fails to deploy package due to missing permissions", async ({ page }) => { + test.setTimeout(60000); + + // Log in + const k = new KubeappsLogin(page); + await k.doLogin("kubeapps-user@example.com", "password", process.env.VIEW_TOKEN); + + // Select package to deploy + await page.click('a.nav-link:has-text("Catalog")'); + await page.click('a .card-title:has-text("apache")'); + await page.click('cds-button:has-text("Deploy") >> nth=0'); + + // Deploy package + const releaseNameLocator = page.locator("#releaseName"); + await releaseNameLocator.waitFor(); + await expect(releaseNameLocator).toHaveText(""); + await releaseNameLocator.type(utils.getRandomName("test-01-release")); + await page.locator('cds-button:has-text("Deploy")').click(); + + const errorLocator = page.locator(".alert-items .alert-text"); + await expect(errorLocator).toHaveCount(1); + await page.waitForTimeout(5000); + + // For some reason, UI is showing different error messages randomly + // Custom assertion logic + const errorMsg = await errorLocator.textContent(); + console.log(`Error message on UI = "${errorMsg}"`); + + await page.waitForFunction(msg => { + return msg.indexOf("secrets is forbidden") > -1 || msg.indexOf("unable to read secret") > -1; + }, errorMsg); + }); + + test("Regular user fails to deploy package in its own namespace from repos with secret", async ({ + page, + }) => { + // Explanation: User has permissions to deploy in its namespace, but can't actually deploy + // if the package is from a repo that has a secret to which the user doesn't have access + + test.setTimeout(60000); + + // Log in + const k = new KubeappsLogin(page); + await k.doLogin("kubeapps-user@example.com", "password", process.env.VIEW_TOKEN); + + // Change to user's namespace using UI + await page.click(".kubeapps-dropdown .kubeapps-nav-link"); + await page.selectOption('select[name="namespaces"]', "kubeapps-user-namespace"); + await page.click('cds-button:has-text("Change Context")'); + + // Select package to deploy + await page.click('a.nav-link:has-text("Catalog")'); + await page.click('a .card-title:has-text("apache")'); + await page.click('cds-button:has-text("Deploy") >> nth=0'); + + // Deploy package + const releaseNameLocator = page.locator("#releaseName"); + await releaseNameLocator.waitFor(); + await expect(releaseNameLocator).toHaveText(""); + await releaseNameLocator.type(utils.getRandomName("test-01-release")); + await page.locator('cds-button:has-text("Deploy")').click(); + + const errorLocator = page.locator(".alert-items .alert-text"); + await expect(errorLocator).toHaveCount(1); + await page.waitForTimeout(5000); + + // For some reason, UI is showing different error messages randomly + // Custom assertion logic + const errorMsg = await errorLocator.textContent(); + console.log(`Error message on UI = "${errorMsg}"`); + await expect(errorLocator).toContainText("unable to read secret"); + }); }); diff --git a/integration/tests/main/09-user-positive-installation.spec.js b/integration/tests/main/09-user-positive-installation.spec.js new file mode 100644 index 00000000000..ae101acd68c --- /dev/null +++ b/integration/tests/main/09-user-positive-installation.spec.js @@ -0,0 +1,81 @@ +// Copyright 2022 the Kubeapps contributors. +// SPDX-License-Identifier: Apache-2.0 + +const { test, expect } = require("@playwright/test"); +const { KubeappsLogin } = require("../utils/kubeapps-login"); +const utils = require("../utils/util-functions"); + +test.describe("Limited user simple deployments", () => { + test("Regular user can deploy and delete packages in its own namespace from global repo without secrets", async ({ + page, + }) => { + test.setTimeout(120000); + + // Log in as admin to create a repo without password + const k = new KubeappsLogin(page); + await k.doLogin("kubeapps-operator@example.com", "password", process.env.ADMIN_TOKEN); + + // Change namespace using UI + await page.click(".kubeapps-dropdown .kubeapps-nav-link"); + await page.selectOption('select[name="namespaces"]', "kubeapps"); + await page.click('cds-button:has-text("Change Context")'); + + // Go to repos page + await page.click(".dropdown.kubeapps-menu button.kubeapps-nav-link"); + await page.click('a.dropdown-menu-link:has-text("App Repositories")'); + await page.waitForTimeout(3000); + + // Add new repo + const repoName = utils.getRandomName("repo-test-09"); + console.log(`Creating repository "${repoName}"`); + await page.click('cds-button:has-text("Add App Repository")'); + await page.type("input#kubeapps-repo-name", repoName); + await page.type( + "input#kubeapps-repo-url", + "https://prometheus-community.github.io/helm-charts", + ); + await page.click('cds-button:has-text("Install Repo")'); + await page.waitForLoadState("networkidle"); + + // Log out admin and log in regular user + await k.doLogout(); + await k.doLogin("kubeapps-user@example.com", "password", process.env.VIEW_TOKEN); + + // Switch to user's namespace using UI + await page.click(".kubeapps-dropdown .kubeapps-nav-link"); + await page.selectOption('select[name="namespaces"]', "kubeapps-user-namespace"); + await page.click('cds-button:has-text("Change Context")'); + + // Select package to deploy + await page.click('a.nav-link:has-text("Catalog")'); + await page.locator("input#search").type("alertmanager"); + await page.waitForTimeout(3000); + await page.click('a:has-text("alertmanager")'); + await page.click('cds-button:has-text("Deploy") >> nth=0'); + + // Deploy package + const releaseNameLocator = page.locator("#releaseName"); + await releaseNameLocator.waitFor(); + await expect(releaseNameLocator).toHaveText(""); + const releaseName = utils.getRandomName("test-09-release"); + console.log(`Creating release "${releaseName}"`); + await releaseNameLocator.type(releaseName); + await page.locator('cds-button:has-text("Deploy")').click(); + + // Check that package is deployed + await page.waitForSelector("css=.application-status-pie-chart-number >> text=1"); + await page.waitForSelector("css=.application-status-pie-chart-title >> text=Ready"); + + // Delete deployment + await page.locator('cds-button:has-text("Delete")').click(); + await page.locator('cds-modal-actions button:has-text("Delete")').click(); + await page.waitForTimeout(10000); + + // Search for package deployed + await page.click('a.nav-link:has-text("Applications")'); + await page.locator("input#search").type("alertmanager"); + await page.waitForTimeout(3000); + const packageLocator = page.locator('a:has-text("alertmanager")'); + await expect(packageLocator).toHaveCount(0); + }); +}); diff --git a/integration/tests/utils/kubeapps-login.js b/integration/tests/utils/kubeapps-login.js index 6bac7e567a2..327faa8e66a 100644 --- a/integration/tests/utils/kubeapps-login.js +++ b/integration/tests/utils/kubeapps-login.js @@ -38,6 +38,14 @@ exports.KubeappsLogin = class KubeappsLogin { console.log("Logged into Kubeapps!"); } + async doLogout() { + console.log("Logging out of Kubeapps"); + await this.page.click(".dropdown.kubeapps-menu .kubeapps-nav-link"); + await this.page.click('cds-button:has-text("Log out")'); + await this.page.waitForLoadState("networkidle"); + console.log("Logged out of Kubeapps"); + } + async doOidcLogin(username, pwd) { console.log(`Logging in Kubeapps via OIDC in host: ${utils.getUrl("/")}`); From b16c1464292d4eca3b6148a0960bfa015a47f2e3 Mon Sep 17 00:00:00 2001 From: Rafa Castelblanque Date: Thu, 3 Feb 2022 09:02:21 +0100 Subject: [PATCH 3/3] Re-add rolebinding for kubeapps-user in its namespace (#4081) Signed-off-by: Rafa Castelblanque --- .circleci/config.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 8e97b4761e0..d4b352dd124 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -314,7 +314,6 @@ install_cluster: &install_cluster sleep 5 && kubectl --context kind-kubeapps-ci --kubeconfig ${HOME}/.kube/kind-config-kubeapps-ci wait --namespace ingress-nginx --for=condition=ready pod --selector=app.kubernetes.io/component=controller --timeout=120s && - kubectl --context kind-kubeapps-ci --kubeconfig ${HOME}/.kube/kind-config-kubeapps-ci delete rolebinding kubeapps-user -n kubeapps-user-namespace && kubectl --context kind-kubeapps-ci --kubeconfig ${HOME}/.kube/kind-config-kubeapps-ci create rolebinding kubeapps-view-secret-oidc --role view-secrets --user oidc:kubeapps-user@example.com && kubectl --context kind-kubeapps-ci --kubeconfig ${HOME}/.kube/kind-config-kubeapps-ci create clusterrolebinding kubeapps-view-oidc --clusterrole=view --user oidc:kubeapps-user@example.com && echo "Cluster created" @@ -327,7 +326,6 @@ install_cluster: &install_cluster sleep 5 && kubectl wait --namespace ingress-nginx --for=condition=ready pod --selector=app.kubernetes.io/component=controller --timeout=120s && - kubectl --context kind-kubeapps-ci --kubeconfig ${HOME}/.kube/kind-config-kubeapps-ci delete rolebinding kubeapps-user -n kubeapps-user-namespace && kubectl --context kind-kubeapps-ci --kubeconfig ${HOME}/.kube/kind-config-kubeapps-ci create rolebinding kubeapps-view-secret-oidc --role view-secrets --user oidc:kubeapps-user@example.com && kubectl --context kind-kubeapps-ci --kubeconfig ${HOME}/.kube/kind-config-kubeapps-ci create clusterrolebinding kubeapps-view-oidc --clusterrole=view --user oidc:kubeapps-user@example.com && echo "Cluster created"