chore: add e2e test against an external auth provider during workspace creation (#12985)

This commit is contained in:
Danny Kopping 2024-04-18 19:43:10 +02:00 committed by GitHub
parent 75223dfd8b
commit 319fd5bf1d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 134 additions and 21 deletions

View File

@ -31,6 +31,7 @@ import {
type Resource,
Response,
type RichParameter,
type ExternalAuthProviderResource,
} from "./provisionerGenerated";
// requiresEnterpriseLicense will skip the test if we're not running with an enterprise license
@ -49,6 +50,7 @@ export const createWorkspace = async (
templateName: string,
richParameters: RichParameter[] = [],
buildParameters: WorkspaceBuildParameter[] = [],
useExternalAuthProvider: string | undefined = undefined,
): Promise<string> => {
await page.goto(`/templates/${templateName}/workspace`, {
waitUntil: "domcontentloaded",
@ -59,6 +61,25 @@ export const createWorkspace = async (
await page.getByLabel("name").fill(name);
await fillParameters(page, richParameters, buildParameters);
if (useExternalAuthProvider !== undefined) {
// Create a new context for the popup which will be created when clicking the button
const popupPromise = page.waitForEvent("popup");
// Find the "Login with <Provider>" button
const externalAuthLoginButton = page
.getByRole("button")
.getByText("Login with GitHub");
await expect(externalAuthLoginButton).toBeVisible();
// Click it
await externalAuthLoginButton.click();
// Wait for authentication to occur
const popup = await popupPromise;
await popup.waitForSelector("text=You are now authenticated.");
}
await page.getByTestId("form-submit").click();
await expectUrl(page).toHavePathName("/@admin/" + name);
@ -648,6 +669,37 @@ export const echoResponsesWithParameters = (
};
};
export const echoResponsesWithExternalAuth = (
providers: ExternalAuthProviderResource[],
): EchoProvisionerResponses => {
return {
parse: [
{
parse: {},
},
],
plan: [
{
plan: {
externalAuthProviders: providers,
},
},
],
apply: [
{
apply: {
externalAuthProviders: providers,
resources: [
{
name: "example",
},
],
},
},
],
};
};
export const fillParameters = async (
page: Page,
richParameters: RichParameter[] = [],

View File

@ -1,4 +1,6 @@
import type { Page } from "@playwright/test";
import type { BrowserContext, Page } from "@playwright/test";
import http from "http";
import { coderPort, gitAuth } from "./constants";
export const beforeCoderTest = async (page: Page) => {
// eslint-disable-next-line no-console -- Show everything that was printed with console.log()
@ -45,6 +47,41 @@ export const beforeCoderTest = async (page: Page) => {
});
};
export const resetExternalAuthKey = async (context: BrowserContext) => {
// Find the session token so we can destroy the external auth link between tests, to ensure valid authentication happens each time.
const cookies = await context.cookies();
const sessionCookie = cookies.find((c) => c.name === "coder_session_token");
const options = {
method: "DELETE",
hostname: "127.0.0.1",
port: coderPort,
path: `/api/v2/external-auth/${gitAuth.webProvider}?coder_session_token=${sessionCookie?.value}`,
};
const req = http.request(options, (res) => {
let data = "";
res.on("data", (chunk) => {
data += chunk;
});
res.on("end", () => {
// Both 200 (key deleted successfully) and 500 (key was not found) are valid responses.
if (res.statusCode !== 200 && res.statusCode !== 500) {
console.error("failed to delete external auth link", data);
throw new Error(
`failed to delete external auth link: HTTP response ${res.statusCode}`,
);
}
});
});
req.on("error", (err) => {
throw err.message;
});
req.end();
};
const isApiCall = (urlString: string): boolean => {
const url = new URL(urlString);
const apiPath = "/api/v2";

View File

@ -115,7 +115,7 @@ export default defineConfig({
// Tests for Deployment / User Authentication / OIDC
CODER_OIDC_ISSUER_URL: "https://accounts.google.com",
CODER_OIDC_EMAIL_DOMAIN: "coder.com",
CODER_OIDC_CLIENT_ID: "1234567890", // FIXME: https://github.com/coder/coder/issues/12585
CODER_OIDC_CLIENT_ID: "1234567890",
CODER_OIDC_CLIENT_SECRET: "1234567890Secret",
CODER_OIDC_ALLOW_SIGNUPS: "false",
CODER_OIDC_SIGN_IN_TEXT: "Hello",

View File

@ -2,8 +2,37 @@ import type { Endpoints } from "@octokit/types";
import { test } from "@playwright/test";
import type { ExternalAuthDevice } from "api/typesGenerated";
import { gitAuth } from "../constants";
import { Awaiter, createServer } from "../helpers";
import { beforeCoderTest } from "../hooks";
import {
Awaiter,
createServer,
createTemplate,
createWorkspace,
echoResponsesWithExternalAuth,
} from "../helpers";
import { beforeCoderTest, resetExternalAuthKey } from "../hooks";
test.beforeAll(async ({ baseURL }) => {
const srv = await createServer(gitAuth.webPort);
// The GitHub validate endpoint returns the currently authenticated user!
srv.use(gitAuth.validatePath, (req, res) => {
res.write(JSON.stringify(ghUser));
res.end();
});
srv.use(gitAuth.tokenPath, (req, res) => {
const r = (Math.random() + 1).toString(36).substring(7);
res.write(JSON.stringify({ access_token: r }));
res.end();
});
srv.use(gitAuth.authPath, (req, res) => {
res.redirect(
`${baseURL}/external-auth/${gitAuth.webProvider}/callback?code=1234&state=` +
req.query.state,
);
});
});
test.beforeEach(async ({ context }) => resetExternalAuthKey(context));
test.beforeEach(({ page }) => beforeCoderTest(page));
@ -57,23 +86,7 @@ test("external auth device", async ({ page }) => {
await page.waitForSelector("text=1 organization authorized");
});
test("external auth web", async ({ baseURL, page }) => {
const srv = await createServer(gitAuth.webPort);
// The GitHub validate endpoint returns the currently authenticated user!
srv.use(gitAuth.validatePath, (req, res) => {
res.write(JSON.stringify(ghUser));
res.end();
});
srv.use(gitAuth.tokenPath, (req, res) => {
res.write(JSON.stringify({ access_token: "hello-world" }));
res.end();
});
srv.use(gitAuth.authPath, (req, res) => {
res.redirect(
`${baseURL}/external-auth/${gitAuth.webProvider}/callback?code=1234&state=` +
req.query.state,
);
});
test("external auth web", async ({ page }) => {
await page.goto(`/external-auth/${gitAuth.webProvider}`, {
waitUntil: "domcontentloaded",
});
@ -81,6 +94,17 @@ test("external auth web", async ({ baseURL, page }) => {
await page.waitForSelector("text=You've authenticated with GitHub!");
});
test("successful external auth from workspace", async ({ page }) => {
const templateName = await createTemplate(
page,
echoResponsesWithExternalAuth([
{ id: gitAuth.webProvider, optional: false },
]),
);
await createWorkspace(page, templateName, [], [], gitAuth.webProvider);
});
const ghUser: Endpoints["GET /user"]["response"]["data"] = {
login: "kylecarbs",
id: 7122116,