test: e2e oidc (#11129)

Co-authored-by: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com>
pull/11356/head^2
Shivam Kalra 2023-09-19 00:46:32 +05:30 committed by GitHub
parent c820a3937a
commit fd507f1d12
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 105 additions and 5 deletions

View File

@ -229,3 +229,18 @@ AUTH_BEARER_TOKEN_VERCEL=
# Used for E2E tests on Apple Calendar
E2E_TEST_APPLE_CALENDAR_EMAIL=""
E2E_TEST_APPLE_CALENDAR_PASSWORD=""
# - OIDC E2E TEST *******************************************************************************************
# Ensure this ADMIN EMAIL is present in the SAML_ADMINS list
E2E_TEST_SAML_ADMIN_EMAIL=
E2E_TEST_SAML_ADMIN_PASSWORD=
E2E_TEST_OIDC_CLIENT_ID=
E2E_TEST_OIDC_CLIENT_SECRET=
E2E_TEST_OIDC_PROVIDER_DOMAIN=
E2E_TEST_OIDC_USER_EMAIL=
E2E_TEST_OIDC_USER_PASSWORD=
# ***********************************************************************************************************

View File

@ -438,7 +438,7 @@ const createUserFixture = (user: UserWithIncludes, page: Page) => {
type SupportedTestEventTypes = PrismaType.EventTypeCreateInput & {
_bookings?: PrismaType.BookingCreateInput[];
};
type CustomUserOptsKeys = "username" | "password" | "completedOnboarding" | "locale" | "name";
type CustomUserOptsKeys = "username" | "password" | "completedOnboarding" | "locale" | "name" | "email";
type CustomUserOpts = Partial<Pick<Prisma.User, CustomUserOptsKeys>> & {
timeZone?: TimeZoneEnum;
eventTypes?: SupportedTestEventTypes[];
@ -457,7 +457,7 @@ const createUser = (workerInfo: WorkerInfo, opts?: CustomUserOpts | null): Prism
return {
username: uname,
name: opts?.name,
email: `${uname}@example.com`,
email: opts?.email ?? `${uname}@example.com`,
password: hashPassword(uname),
emailVerified: new Date(),
completedOnboarding: opts?.completedOnboarding ?? true,

View File

@ -0,0 +1,71 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import { test } from "./lib/fixtures";
const SAML_DATABASE_URL = process.env.SAML_DATABASE_URL!;
const SAML_ADMINS = process.env.SAML_ADMINS!;
const SAML_ADMIN_EMAIL = process.env.E2E_TEST_SAML_ADMIN_EMAIL!;
const SAML_ADMIN_PASSWORD = process.env.E2E_TEST_SAML_ADMIN_PASSWORD!;
const OIDC_CLIENT_ID = process.env.E2E_TEST_OIDC_CLIENT_ID!;
const OIDC_CLIENT_SECRET = process.env.E2E_TEST_OIDC_CLIENT_SECRET!;
const OIDC_PROVIDER_DOMAIN = process.env.E2E_TEST_OIDC_PROVIDER_DOMAIN!;
const OIDC_USER_EMAIL = process.env.E2E_TEST_OIDC_USER_EMAIL!;
const OIDC_USER_PASSWORD = process.env.E2E_TEST_OIDC_USER_PASSWORD!;
const SHOULD_SKIP_TESTS =
!SAML_DATABASE_URL ||
!SAML_ADMINS ||
!SAML_ADMIN_EMAIL ||
!SAML_ADMIN_PASSWORD ||
!OIDC_CLIENT_ID ||
!OIDC_CLIENT_SECRET ||
!OIDC_PROVIDER_DOMAIN ||
!OIDC_USER_EMAIL ||
!OIDC_USER_PASSWORD;
test.afterEach(({ users }) => users.deleteAll());
// TODO: Cleanup the OIDC connection after the tests with fixtures
test.describe("OIDC", () => {
// eslint-disable-next-line playwright/no-skipped-test
test.skip(SHOULD_SKIP_TESTS, "Skipping due to missing the testing variables");
test("Setup with SAML admin and login", async ({ page, users }) => {
// Add the admin user provided in the environment variables to the db
const samlAdminUser = await users.create({ email: SAML_ADMIN_EMAIL, password: SAML_ADMIN_PASSWORD });
await samlAdminUser.apiLogin();
await test.step("Connect with OIDC Provider", async () => {
await page.goto("/settings/security/sso");
await page.click('[data-testid="sso-oidc-configure"]');
await page.fill('[data-testid="sso-oidc-client-id"]', OIDC_CLIENT_ID);
await page.fill('[data-testid="sso-oidc-client-secret"]', OIDC_CLIENT_SECRET);
await page.fill(
'[data-testid="sso-oidc-well-known-url"]',
`https://${OIDC_PROVIDER_DOMAIN}/.well-known/openid-configuration`
);
await page.click('[data-testid="sso-oidc-save"]');
await page.waitForSelector('[data-testid="toast-success"]');
});
// Logout the SAML Admin
await samlAdminUser.logout();
await test.step("Login using the OIDC provider", async () => {
// Login a user using the OIDC provider.
// The credentials are handled by the provider, so we don't need to create a user in the db.
await page.goto("/auth/login");
await page.click('[data-testid="saml"]');
// Redirected outide of the app, the user would be redirected to the OIDC provider.
await page.waitForURL(/https:\/\/[^/]+\/oauth2\/v1\/authorize\?.*/);
await page.getByRole("textbox", { name: "Username" }).fill(OIDC_USER_EMAIL);
await page.getByRole("textbox", { name: "Password" }).fill(OIDC_USER_PASSWORD);
await page.getByRole("button", { name: "Sign in" }).click();
// The user is redirected back to the app.
await page.waitForURL("getting-started", { waitUntil: "load" });
});
// Logout the user.
await page.goto("/auth/logout");
await test.step("Disconnect OIDC Provider", async () => {
samlAdminUser.apiLogin();
await page.goto("/settings/security/sso", { waitUntil: "load" });
await page.getByTestId("delete-oidc-sso-connection").click();
await page.getByRole("button", { name: "Yes, delete OIDC configuration" }).click();
await page.waitForSelector('[data-testid="toast-success"]');
});
});
});

View File

@ -57,7 +57,11 @@ export default function ConnectionInfo({
<Dialog>
<div>
<DialogTrigger asChild>
<Button color="destructive">{t("delete_sso_configuration", { connectionType })}</Button>
<Button
color="destructive"
data-testid={`delete-${connectionType === "OIDC" ? "oidc" : "saml"}-sso-connection`}>
{t("delete_sso_configuration", { connectionType })}
</Button>
</DialogTrigger>
</div>
<ConfirmationDialogContent

View File

@ -33,7 +33,7 @@ export default function OIDCConnection({
</div>
{!connection && (
<div className="flex-shrink-0 pt-3 sm:ml-auto sm:pl-3 sm:pt-0">
<Button color="secondary" onClick={() => setOpenModal(true)}>
<Button data-testid="sso-oidc-configure" color="secondary" onClick={() => setOpenModal(true)}>
{t("configure")}
</Button>
</div>
@ -100,6 +100,7 @@ const CreateConnectionDialog = ({
<TextField
name="clientId"
label="Client id"
data-testid="sso-oidc-client-id"
value={value}
onChange={(e) => {
form.setValue("clientId", e?.target.value);
@ -116,6 +117,7 @@ const CreateConnectionDialog = ({
<TextField
name="clientSecret"
label="Client secret"
data-testid="sso-oidc-client-secret"
value={value}
onChange={(e) => {
form.setValue("clientSecret", e?.target.value);
@ -132,6 +134,7 @@ const CreateConnectionDialog = ({
<TextField
name="wellKnownUrl"
label="Well-Known URL"
data-testid="sso-oidc-well-known-url"
value={value}
onChange={(e) => {
form.setValue("wellKnownUrl", e?.target.value);
@ -152,7 +155,7 @@ const CreateConnectionDialog = ({
tabIndex={-1}>
{t("cancel")}
</Button>
<Button type="submit" loading={form.formState.isSubmitting}>
<Button type="submit" data-testid="sso-oidc-save" loading={form.formState.isSubmitting}>
{t("save")}
</Button>
</DialogFooter>

View File

@ -211,6 +211,13 @@
"E2E_TEST_APPLE_CALENDAR_EMAIL",
"E2E_TEST_APPLE_CALENDAR_PASSWORD",
"E2E_TEST_MAILHOG_ENABLED",
"E2E_TEST_OIDC_CLIENT_ID",
"E2E_TEST_OIDC_CLIENT_SECRET",
"E2E_TEST_OIDC_PROVIDER_DOMAIN",
"E2E_TEST_OIDC_USER_EMAIL",
"E2E_TEST_OIDC_USER_PASSWORD",
"E2E_TEST_SAML_ADMIN_EMAIL",
"E2E_TEST_SAML_ADMIN_PASSWORD",
"EMAIL_FROM",
"EMAIL_SERVER_HOST",
"EMAIL_SERVER_PASSWORD",