Merge branch 'main' into identity-fed-routes

This commit is contained in:
Deepak Prabhakara 2024-05-06 12:41:13 +01:00
commit 55d8f14d40
20 changed files with 1446 additions and 2571 deletions

View File

@ -111,7 +111,7 @@ jobs:
ports:
- '8000:8000'
mocksaml:
image: boxyhq/mock-saml:1.2.0
image: boxyhq/mock-saml:1.3.9
ports:
- 4000:4000
env:

View File

@ -0,0 +1,2 @@
export { Portal } from './portal';
export { SSOPage } from './sso-page';

View File

@ -0,0 +1,20 @@
import { Locator, Page, expect } from '@playwright/test';
export class Portal {
userAvatarLocator: Locator;
constructor(public readonly page: Page) {
this.userAvatarLocator = this.page.getByTestId('user-avatar');
}
async doCredentialsLogin() {
await this.page.goto('/admin/auth/login');
await this.page.getByPlaceholder('Email').fill('super@boxyhq.com');
await this.page.getByPlaceholder('Password').fill('999login');
await this.page.getByRole('button', { name: 'Sign In' }).click();
}
async isLoggedIn() {
// assert login state
await expect(this.userAvatarLocator).toBeVisible();
}
}

View File

@ -0,0 +1,178 @@
import type { Page, Locator } from '@playwright/test';
import { adminPortalSSODefaults } from '@lib/env';
const ADMIN_PORTAL_TENANT = adminPortalSSODefaults.tenant;
const ADMIN_PORTAL_PRODUCT = adminPortalSSODefaults.product;
const MOCKSAML_ORIGIN = process.env.MOCKSAML_ORIGIN || 'https://mocksaml.com';
const MOCKSAML_SIGNIN_BUTTON_NAME = 'Sign In';
const MOCKLAB_ORIGIN = 'https://oauth.wiremockapi.cloud';
const MOCKLAB_CLIENT_ID = 'mocklab_oauth2';
const MOCKLAB_CLIENT_SECRET = 'mocklab_secret';
const MOCKLAB_SIGNIN_BUTTON_NAME = 'Login';
const MOCKLAB_DISCOVERY_ENDPOINT = 'https://oauth.wiremockapi.cloud/.well-known/openid-configuration';
export class SSOPage {
private readonly createConnection: Locator;
private readonly nameInput: Locator;
private readonly tenantInput: Locator;
private readonly productInput: Locator;
private readonly redirectURLSInput: Locator;
private readonly defaultRedirectURLInput: Locator;
private readonly metadataUrlInput: Locator;
private readonly oidcDiscoveryUrlInput: Locator;
private readonly oidcClientIdInput: Locator;
private readonly oidcClientSecretInput: Locator;
private readonly saveConnection: Locator;
private readonly deleteButton: Locator;
private readonly confirmButton: Locator;
private readonly toggleConnectionStatusCheckbox: Locator;
private readonly toggleConnectionStatusLabel: Locator;
private connections: string[];
constructor(public readonly page: Page) {
this.connections = [];
this.createConnection = this.page.getByTestId('create-connection');
this.nameInput = this.page.getByLabel('Connection name (Optional)');
this.tenantInput = this.page.getByLabel('Tenant');
this.productInput = this.page.getByLabel('Product');
this.redirectURLSInput = page
.getByRole('group')
.filter({ hasText: 'Allowed redirect URLs' })
.locator(page.getByRole('textbox').first());
this.defaultRedirectURLInput = this.page.getByLabel('Default redirect URL');
this.metadataUrlInput = this.page.getByLabel('Metadata URL');
this.oidcDiscoveryUrlInput = this.page.getByLabel('Well-known URL of OpenID Provider');
this.oidcClientIdInput = this.page.getByLabel('Client ID');
this.oidcClientSecretInput = this.page.getByLabel('Client Secret');
this.saveConnection = this.page.getByRole('button', { name: /save/i });
this.toggleConnectionStatusCheckbox = this.page.getByRole('checkbox', { name: 'Active' });
this.toggleConnectionStatusLabel = this.page.locator('label').filter({ hasText: 'Active' });
this.deleteButton = this.page.getByRole('button', { name: 'Delete' });
this.confirmButton = this.page.getByRole('button', { name: 'Confirm' });
}
async goto() {
const url = new URL(this.page.url());
if (url.pathname !== '/admin/sso-connection') {
await this.page.goto('/admin/sso-connection');
}
}
async addSSOConnection({
name,
type = 'saml',
baseURL,
}: {
name: string;
type: 'saml' | 'oidc';
baseURL: string;
}) {
const connectionIndex = this.connections.length + 1;
const ssoName = `${name}-${connectionIndex}`;
// Find the new connection button and click on it
await this.createConnection.click();
if (type === 'oidc') {
// Toggle connection type to OIDC
await this.page.getByLabel('OIDC').check();
}
// Fill the name for the connection
await this.nameInput.fill(ssoName);
// Fill the tenant for the connection
await this.tenantInput.fill(ADMIN_PORTAL_TENANT);
// Fill the product for the connection
await this.productInput.fill(ADMIN_PORTAL_PRODUCT);
// Fill the Allowed redirect URLs for the connection
await this.redirectURLSInput.fill(baseURL!);
// Fill the default redirect URLs for the connection
await this.defaultRedirectURLInput.fill(`${baseURL}/admin/auth/idp-login`);
if (type === 'saml') {
// Enter the metadata url for mocksaml in the form
await this.metadataUrlInput.fill(`${MOCKSAML_ORIGIN}/api/namespace/${ssoName}/saml/metadata`);
}
if (type === 'oidc') {
// Enter the OIDC client credentials for mocklab in the form
await this.oidcClientIdInput.fill(`${MOCKLAB_CLIENT_ID}-${connectionIndex}`);
await this.oidcClientSecretInput.fill(`${MOCKLAB_CLIENT_SECRET}-${connectionIndex}`);
// Enter the OIDC discovery url for mocklab in the form
await this.oidcDiscoveryUrlInput.fill(MOCKLAB_DISCOVERY_ENDPOINT);
}
// submit the form
await this.saveConnection.click();
this.connections = [...this.connections, ssoName];
}
async gotoEditView(name: string) {
await this.goto();
const editButton = this.page.getByText(name).locator('xpath=..').getByLabel('Edit');
await editButton.click();
}
async toggleConnectionStatus(newStatus: boolean) {
const isChecked = await this.toggleConnectionStatusCheckbox.isChecked();
if (isChecked && !newStatus) {
await this.toggleConnectionStatusLabel.click();
await this.confirmButton.click();
} else if (!isChecked && newStatus) {
await this.toggleConnectionStatusLabel.click();
await this.confirmButton.click();
}
}
async updateSSOConnection({ name, url, newStatus }: { name: string; url: string; newStatus?: boolean }) {
await this.gotoEditView(name);
await this.redirectURLSInput.fill(url);
await this.saveConnection.click();
if (typeof newStatus === 'boolean') {
await this.gotoEditView(name);
await this.toggleConnectionStatus(newStatus);
}
}
async deleteSSOConnection(name: string) {
await this.gotoEditView(name);
// click the delete and confirm deletion
await this.deleteButton.click();
await this.confirmButton.click();
}
async deleteAllSSOConnections() {
let _connection;
while ((_connection = this.connections.shift())) {
await this.deleteSSOConnection(_connection);
}
}
async logout() {
const userAvatarLocator = this.page.getByTestId('user-avatar');
// Logout from the magic link authentication
await userAvatarLocator.click();
await this.page.getByTestId('logout').click();
}
async signInWithSSO() {
await this.page.getByTestId('sso-login-button').click();
}
async selectIdP(name: string) {
const idpSelectionTitle = 'Select an Identity Provider to continue';
await this.page.getByText(idpSelectionTitle).waitFor();
await this.page.getByRole('button', { name }).click();
}
async signInWithMockSAML() {
// Perform sign in at mocksaml
await this.page.waitForURL((url) => url.origin === MOCKSAML_ORIGIN);
await this.page.getByPlaceholder('jackson').fill('bob');
await this.page.getByRole('button', { name: MOCKSAML_SIGNIN_BUTTON_NAME }).click();
}
async signInWithMockLab() {
// Perform sign in at mocklab
await this.page.waitForURL((url) => url.origin === MOCKLAB_ORIGIN);
await this.page.getByPlaceholder('yours@example.com').fill('bob@oidc.com');
await this.page.getByRole('button', { name: MOCKLAB_SIGNIN_BUTTON_NAME }).click();
}
}

View File

@ -0,0 +1,110 @@
import { test as baseTest, expect } from '@playwright/test';
import { Portal, SSOPage } from 'e2e/support/fixtures';
type MyFixtures = {
ssoPage: SSOPage;
portal: Portal;
};
export const test = baseTest.extend<MyFixtures>({
portal: async ({ page }, use) => {
const portal = new Portal(page);
await use(portal);
},
ssoPage: async ({ page, portal }, use) => {
const ssoPage = new SSOPage(page);
await ssoPage.goto();
await use(ssoPage);
await portal.doCredentialsLogin();
await portal.isLoggedIn();
await ssoPage.deleteAllSSOConnections();
},
});
test('OAuth2 wrapper + SAML provider + wrong redirectUrl', async ({ ssoPage, page, baseURL }, testInfo) => {
const ssoName = `saml-${testInfo.workerIndex}`;
await ssoPage.addSSOConnection({ name: ssoName, type: 'saml', baseURL: baseURL! });
// check if the first added connection appears in the connection list
await expect(page.getByText(`${ssoName}-1`)).toBeVisible();
await ssoPage.updateSSOConnection({
name: `${ssoName}-1`,
url: 'https://invalid-url.com',
});
// Logout of magic link login
await ssoPage.logout();
await ssoPage.signInWithSSO();
// Wait for browser to redirect to error page
await page.waitForURL((url) => url.origin === baseURL && url.pathname === '/error');
// Assert error text
await expect(page.getByText(`SSO error: Redirect URL is not allowed.`)).toBeVisible();
});
test('OAuth2 wrapper + SAML provider + inactive connection', async ({ ssoPage, page, baseURL }, testInfo) => {
const ssoName = `saml-${testInfo.workerIndex}`;
await ssoPage.addSSOConnection({ name: ssoName, type: 'saml', baseURL: baseURL! });
// check if the first added connection appears in the connection list
await expect(page.getByText(`${ssoName}-1`)).toBeVisible();
await ssoPage.updateSSOConnection({
name: `${ssoName}-1`,
url: baseURL!,
newStatus: false,
});
// Confirm connection label inactive is displayed
await expect(
page.getByText(`${ssoName}-1`).locator('xpath=..').getByRole('cell', { name: 'Inactive', exact: true })
).toBeVisible();
// Logout and try to sign in with connection
// Logout of magic link login
await ssoPage.logout();
await ssoPage.signInWithSSO();
// Wait for browser to redirect to error page
await page.waitForURL((url) => url.origin === baseURL && url.pathname === '/error');
// Assert error text
await expect(
page.getByText('SSO error: SSO connection is deactivated. Please contact your administrator.')
).toBeVisible();
});
test('OAuth2 wrapper + OIDC provider + wrong redirectUrl', async ({ ssoPage, page, baseURL }, testInfo) => {
const ssoName = `oidc-${testInfo.workerIndex}`;
await ssoPage.addSSOConnection({ name: ssoName, type: 'oidc', baseURL: baseURL! });
// check if the oidc connection appears in the connection list
await expect(page.getByText(`${ssoName}-1`)).toBeVisible();
await ssoPage.updateSSOConnection({
name: `${ssoName}-1`,
url: 'https://invalid-url.com',
});
// Logout of magic link login
await ssoPage.logout();
await ssoPage.signInWithSSO();
// Wait for browser to redirect to error page
await page.waitForURL((url) => url.origin === baseURL && url.pathname === '/error');
// Assert error text
await expect(page.getByText('SSO error: Redirect URL is not allowed.')).toBeVisible();
});
test('OAuth2 wrapper + OIDC provider + inactive connection', async ({ ssoPage, page, baseURL }, testInfo) => {
const ssoName = `oidc-${testInfo.workerIndex}`;
await ssoPage.addSSOConnection({ name: ssoName, type: 'oidc', baseURL: baseURL! });
// check if the oidc connection appears in the connection list
await expect(page.getByText(`${ssoName}-1`)).toBeVisible();
await ssoPage.updateSSOConnection({
name: `${ssoName}-1`,
url: baseURL!,
newStatus: false,
});
// Confirm connection label inactive is displayed
await expect(
page.getByText(`${ssoName}-1`).locator('xpath=..').getByRole('cell', { name: 'Inactive', exact: true })
).toBeVisible();
// Logout and try to sign in with connection
// Logout of magic link login
await ssoPage.logout();
await ssoPage.signInWithSSO();
// Wait for browser to redirect to error page
await page.waitForURL((url) => url.origin === baseURL && url.pathname === '/error');
// Assert error text
await expect(
page.getByText('SSO error: SSO connection is deactivated. Please contact your administrator.')
).toBeVisible();
});

View File

@ -0,0 +1,57 @@
import { test as baseTest, expect } from '@playwright/test';
import { Portal, SSOPage } from 'e2e/support/fixtures';
type MyFixtures = {
ssoPage: SSOPage;
portal: Portal;
};
export const test = baseTest.extend<MyFixtures>({
ssoPage: async ({ page, baseURL }, use, testInfo) => {
const ssoPage = new SSOPage(page);
const ssoName = `oidc-${testInfo.workerIndex}`;
await ssoPage.goto();
await ssoPage.addSSOConnection({ name: ssoName, type: 'oidc', baseURL: baseURL! });
await use(ssoPage);
await ssoPage.deleteAllSSOConnections();
},
portal: async ({ page }, use) => {
const portal = new Portal(page);
await use(portal);
},
});
test('OAuth2 wrapper + OIDC provider', async ({ ssoPage, portal, page, baseURL }, testInfo) => {
// check if the first added connection appears in the connection list
await expect(page.getByText(`oidc-${testInfo.workerIndex}-1`)).toBeVisible();
// Logout of magic link login
await ssoPage.logout();
await ssoPage.signInWithSSO();
// Login using MockLab
await ssoPage.signInWithMockLab();
// Wait for browser to redirect back to admin portal
await page.waitForURL((url) => url.origin === baseURL);
// Assert logged in state
await portal.isLoggedIn();
});
test('OAuth2 wrapper + 2 OIDC providers', async ({ ssoPage, portal, page, baseURL }, testInfo) => {
const ssoName = `oidc-${testInfo.workerIndex}`;
// check if the first added connection appears in the connection list
await expect(page.getByText(`${ssoName}-1`)).toBeVisible();
// Add second OIDC connection
await ssoPage.addSSOConnection({ name: ssoName, type: 'oidc', baseURL: baseURL! });
// check if the second added connection appears in the connection list
await expect(page.getByText(`${ssoName}-2`)).toBeVisible();
// Logout of magic link login
await ssoPage.logout();
// Login using MockLab
await ssoPage.signInWithSSO();
// Select IdP from selection screen
await ssoPage.selectIdP(`${ssoName}-2`);
await ssoPage.signInWithMockLab();
// Wait for browser to redirect back to admin portal
await page.waitForURL((url) => url.origin === baseURL);
// Assert logged in state
await portal.isLoggedIn();
});

View File

@ -0,0 +1,62 @@
import { test as baseTest, expect } from '@playwright/test';
import { Portal, SSOPage } from 'e2e/support/fixtures';
type MyFixtures = {
ssoPage: SSOPage;
portal: Portal;
};
export const test = baseTest.extend<MyFixtures>({
ssoPage: async ({ page, baseURL }, use, testInfo) => {
const ssoPage = new SSOPage(page);
let ssoName = `saml-${testInfo.workerIndex}`;
await ssoPage.goto();
await ssoPage.addSSOConnection({ name: ssoName, type: 'saml', baseURL: baseURL! });
await ssoPage.goto();
ssoName = `oidc-${testInfo.workerIndex}`;
await ssoPage.addSSOConnection({ name: ssoName, type: 'oidc', baseURL: baseURL! });
await use(ssoPage);
await ssoPage.deleteAllSSOConnections();
},
portal: async ({ page }, use) => {
const portal = new Portal(page);
await use(portal);
},
});
test('OAuth2 wrapper + SAML provider + OIDC provider', async ({
ssoPage,
portal,
page,
baseURL,
}, testInfo) => {
// check if the first added connection appears in the connection list
await expect(page.getByText(`saml-${testInfo.workerIndex}-1`)).toBeVisible();
// check if the second added connection appears in the connection list
await expect(page.getByText(`oidc-${testInfo.workerIndex}-2`)).toBeVisible();
// Logout of magic link login
await ssoPage.logout();
// Login using MockSAML
await ssoPage.signInWithSSO();
// Select IdP from selection screen
await ssoPage.selectIdP(`saml-${testInfo.workerIndex}-1`);
// Login using MockSAML
await ssoPage.signInWithMockSAML();
// Wait for browser to redirect back to admin portal
await page.waitForURL((url) => url.origin === baseURL);
// Assert logged in state
await portal.isLoggedIn();
// Logout of SAML login
await ssoPage.logout();
// Login using MockLab
await ssoPage.signInWithSSO();
// Select IdP from selection screen
await ssoPage.selectIdP(`oidc-${testInfo.workerIndex}-2`);
// Login using MockLab
await ssoPage.signInWithMockLab();
// Wait for browser to redirect back to admin portal
await page.waitForURL((url) => url.origin === baseURL);
// Assert logged in state
await portal.isLoggedIn();
});

View File

@ -0,0 +1,57 @@
import { test as baseTest, expect } from '@playwright/test';
import { Portal, SSOPage } from 'e2e/support/fixtures';
type MyFixtures = {
ssoPage: SSOPage;
portal: Portal;
};
export const test = baseTest.extend<MyFixtures>({
ssoPage: async ({ page, baseURL }, use, testInfo) => {
const ssoPage = new SSOPage(page);
const ssoName = `saml-${testInfo.workerIndex}`;
await ssoPage.goto();
await ssoPage.addSSOConnection({ name: ssoName, type: 'saml', baseURL: baseURL! });
await use(ssoPage);
await ssoPage.deleteAllSSOConnections();
},
portal: async ({ page }, use) => {
const portal = new Portal(page);
await use(portal);
},
});
test('OAuth2 wrapper + SAML provider', async ({ ssoPage, portal, page, baseURL }, testInfo) => {
// check if the first added connection appears in the connection list
await expect(page.getByText(`saml-${testInfo.workerIndex}-1`)).toBeVisible();
// Logout of magic link login
await ssoPage.logout();
await ssoPage.signInWithSSO();
// Login using MockSAML
await ssoPage.signInWithMockSAML();
// Wait for browser to redirect back to admin portal
await page.waitForURL((url) => url.origin === baseURL);
// Assert logged in state
await portal.isLoggedIn();
});
test('OAuth2 wrapper + 2 SAML providers', async ({ ssoPage, portal, page, baseURL }, testInfo) => {
const ssoName = `saml-${testInfo.workerIndex}`;
// check if the first added connection appears in the connection list
await expect(page.getByText(`${ssoName}-1`)).toBeVisible();
// Add second SAML connection
await ssoPage.addSSOConnection({ name: ssoName, type: 'saml', baseURL: baseURL! });
// check if the second added connection appears in the connection list
await expect(page.getByText(`${ssoName}-2`)).toBeVisible();
// Logout of magic link login
await ssoPage.logout();
// Login using MockSAML
await ssoPage.signInWithSSO();
// Select IdP from selection screen
await ssoPage.selectIdP(`${ssoName}-2`);
await ssoPage.signInWithMockSAML();
// Wait for browser to redirect back to admin portal
await page.waitForURL((url) => url.origin === baseURL);
// Assert logged in state
await portal.isLoggedIn();
});

View File

@ -75,7 +75,7 @@ test.describe('Admin Portal SSO - SAML', () => {
test('delete the SAML SSO connection', async ({ page }) => {
await page.goto('/admin/settings');
// select the row of the connection list table, then locate the edit button
const editButton = page.getByText(TEST_SAML_SSO_CONNECTION_NAME).locator('..').getByLabel('Edit');
const editButton = page.getByText(TEST_SAML_SSO_CONNECTION_NAME).locator('xpath=..').getByLabel('Edit');
await editButton.click();
// click the delete and confirm deletion
await page.getByRole('button', { name: 'Delete' }).click();
@ -138,7 +138,7 @@ test.describe('Admin Portal SSO - OIDC', () => {
await page.getByTestId('logout').click();
// Click on login with sso button
await page.getByTestId('sso-login-button').click();
// Perform sign in at mocksaml
// Perform sign in at mocklab
await page.waitForURL((url) => url.origin === MOCKLAB_ORIGIN);
await page.getByPlaceholder('yours@example.com').fill('bob@oidc.com');
await page.getByRole('button', { name: MOCKLAB_SIGNIN_BUTTON_NAME }).click();

File diff suppressed because it is too large Load Diff

View File

@ -26,7 +26,7 @@
},
"devDependencies": {
"@rollup/plugin-typescript": "11.1.6",
"@types/node": "20.12.7",
"@types/node": "20.12.8",
"@types/react": "18.3.1",
"@typescript-eslint/eslint-plugin": "7.8.0",
"@typescript-eslint/parser": "7.8.0",
@ -37,7 +37,7 @@
"prettier": "3.2.5",
"react-daisyui": "5.0.0",
"typescript": "5.4.5",
"vite": "5.2.10"
"vite": "5.2.11"
},
"optionalDependencies": {
"@rollup/rollup-linux-x64-gnu": "4.17.2"

View File

@ -22,4 +22,4 @@ patches:
images:
- name: boxyhq/jackson
newTag: 1.23.5
newTag: 1.23.7

View File

@ -22,4 +22,4 @@ patches:
images:
- name: boxyhq/jackson
newTag: 1.23.5
newTag: 1.23.7

716
npm/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -39,19 +39,19 @@
"coverage-map": "map.js"
},
"dependencies": {
"@aws-sdk/client-dynamodb": "3.565.0",
"@aws-sdk/credential-providers": "3.565.0",
"@aws-sdk/util-dynamodb": "3.565.0",
"@aws-sdk/client-dynamodb": "3.569.0",
"@aws-sdk/credential-providers": "3.569.0",
"@aws-sdk/util-dynamodb": "3.569.0",
"@boxyhq/error-code-mnemonic": "0.1.1",
"@boxyhq/metrics": "0.2.6",
"@boxyhq/saml20": "1.5.0",
"@googleapis/admin": "17.0.0",
"@boxyhq/metrics": "0.2.7",
"@boxyhq/saml20": "1.5.1",
"@googleapis/admin": "18.0.0",
"axios": "1.6.8",
"encoding": "0.1.13",
"jose": "5.2.4",
"lodash": "4.17.21",
"mixpanel": "0.18.0",
"mongodb": "6.5.0",
"mongodb": "6.6.0",
"mssql": "10.0.2",
"mysql2": "3.9.7",
"node-forge": "1.3.1",
@ -64,8 +64,8 @@
},
"devDependencies": {
"@faker-js/faker": "8.4.1",
"@types/lodash": "4.17.0",
"@types/node": "20.12.7",
"@types/lodash": "4.17.1",
"@types/node": "20.12.8",
"@types/sinon": "17.0.3",
"@types/tap": "15.0.11",
"cross-env": "7.0.3",

814
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "jackson",
"version": "1.23.6",
"version": "1.23.7",
"private": true,
"description": "SAML 2.0 service",
"keywords": [
@ -66,8 +66,8 @@
"@boxyhq/react-ui": "3.3.43",
"@boxyhq/saml-jackson": "file:npm",
"@heroicons/react": "2.1.3",
"@retracedhq/logs-viewer": "2.7.3",
"@retracedhq/retraced": "0.7.9",
"@retracedhq/logs-viewer": "2.7.4",
"@retracedhq/retraced": "0.7.10",
"@tailwindcss/typography": "0.5.13",
"axios": "1.6.8",
"blockly": "10.4.3",
@ -75,7 +75,7 @@
"classnames": "2.5.1",
"cors": "2.8.5",
"cross-env": "7.0.3",
"daisyui": "4.10.2",
"daisyui": "4.10.5",
"formik": "2.4.6",
"i18next": "23.11.3",
"medium-zoom": "1.1.0",

View File

@ -20,15 +20,17 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
}
const handleGET = async (req: NextApiRequest, res: NextApiResponse) => {
const { connectionAPIController, directorySyncController } = await jackson();
const { connectionAPIController, directorySyncController, samlFederatedController } = await jackson();
const sso_connections_count = await connectionAPIController.getCount();
const dsync_connections_count = await directorySyncController.directories.getCount();
const identity_federation_count = await samlFederatedController.app.getCount();
return res.json({
data: {
sso_connections: sso_connections_count,
dsync_connections: dsync_connections_count,
identity_federation_apps: identity_federation_count,
},
});
};

View File

@ -38,7 +38,7 @@ const handlePOST = async (req: NextApiRequest, res: NextApiResponse) => {
// Get counts for product
let sso_connections_count = 0;
let dsync_connections_count = 0;
let saml_federation_count = 0;
let identity_federation_count = 0;
for (const product of products) {
if (product) {
@ -60,7 +60,7 @@ const handlePOST = async (req: NextApiRequest, res: NextApiResponse) => {
name: IndexNames.Product,
value: product,
});
saml_federation_count += count || 0;
identity_federation_count += count || 0;
}
}
}
@ -69,7 +69,7 @@ const handlePOST = async (req: NextApiRequest, res: NextApiResponse) => {
data: {
sso_connections: sso_connections_count,
dsync_connections: dsync_connections_count,
saml_federation: saml_federation_count,
identity_federation_apps: identity_federation_count,
},
});
}

View File

@ -22,7 +22,8 @@ const config: PlaywrightTestConfig = {
timeout: 60 * 1000,
reuseExistingServer: !process.env.CI,
env: {
NODE_ENV: 'test',
DEBUG: 'pw:webserver',
NEXTAUTH_ADMIN_CREDENTIALS: 'super@boxyhq.com:999login',
},
},