mirror of https://github.com/boxyhq/jackson.git
Compare commits
17 Commits
0439806021
...
e3ead0cdc8
Author | SHA1 | Date |
---|---|---|
Deepak Prabhakara | e3ead0cdc8 | |
dependabot[bot] | 886b855098 | |
Deepak Prabhakara | 64d9b38ef7 | |
Deepak Prabhakara | 8fbc1e8db7 | |
Utkarsh Mehta | b98ccc68bc | |
dependabot[bot] | 1eb2147802 | |
dependabot[bot] | d18e92a20c | |
dependabot[bot] | fcb746576f | |
dependabot[bot] | 5cb196ea1d | |
dependabot[bot] | 8d875ce14b | |
dependabot[bot] | 2b491b67ba | |
dependabot[bot] | 61d8b0c2b0 | |
dependabot[bot] | ade4466d26 | |
dependabot[bot] | f3290bb0f7 | |
Deepak Prabhakara | 12d6742dce | |
Deepak Prabhakara | cdd984c64a | |
Deepak Prabhakara | 0be094d76d |
|
@ -1,4 +1,4 @@
|
|||
ARG NODEJS_IMAGE=node:20.12.1-alpine3.19
|
||||
ARG NODEJS_IMAGE=node:20.12.2-alpine3.19
|
||||
FROM --platform=$BUILDPLATFORM $NODEJS_IMAGE AS base
|
||||
|
||||
# Install dependencies only when needed
|
||||
|
|
|
@ -136,7 +136,12 @@ const Branding = ({ hasValidLicense }: { hasValidLicense: boolean }) => {
|
|||
<label className='label'>
|
||||
<span className='label-text'>{t('bui-shared-primary-color')}</span>
|
||||
</label>
|
||||
<input type='color' id='primaryColor' onChange={onChange} value={branding.primaryColor || ''} />
|
||||
<input
|
||||
type='color'
|
||||
id='primaryColor'
|
||||
onChange={onChange}
|
||||
value={branding.primaryColor || '#25c2a0'}
|
||||
/>
|
||||
<label className='label'>
|
||||
<span className='label-text-alt'>{t('bui-shared-primary-color-desc')}</span>
|
||||
</label>
|
||||
|
|
|
@ -3,6 +3,7 @@ import type { NextApiRequest, NextApiResponse } from 'next';
|
|||
import jackson from '@lib/jackson';
|
||||
import { defaultHandler } from '@lib/api';
|
||||
import { parsePaginateApiParams } from '@lib/utils';
|
||||
import { validateDevelopmentModeLimits } from '@lib/development-mode';
|
||||
|
||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
await defaultHandler(req, res, {
|
||||
|
@ -15,6 +16,12 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
|||
const handlePOST = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const { samlFederatedController } = await jackson();
|
||||
|
||||
await validateDevelopmentModeLimits(
|
||||
req.body.product,
|
||||
'samlFederation',
|
||||
'Maximum number of federation apps reached'
|
||||
);
|
||||
|
||||
const app = await samlFederatedController.app.create(req.body);
|
||||
|
||||
res.status(201).json({ data: app });
|
||||
|
|
|
@ -2,36 +2,28 @@ import { AppRequestParams } from '@boxyhq/saml-jackson';
|
|||
import { NextApiRequest, NextApiResponse } from 'next';
|
||||
|
||||
import jackson from '@lib/jackson';
|
||||
import { validateDevelopmentModeLimits } from '@lib/development-mode';
|
||||
import { defaultHandler } from '@lib/api';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
switch (req.method) {
|
||||
case 'POST':
|
||||
await handlePOST(req, res);
|
||||
break;
|
||||
case 'GET':
|
||||
await handleGET(req, res);
|
||||
break;
|
||||
case 'PATCH':
|
||||
await handlePATCH(req, res);
|
||||
break;
|
||||
case 'DELETE':
|
||||
await handleDELETE(req, res);
|
||||
break;
|
||||
default:
|
||||
res.setHeader('Allow', 'POST, GET, PATCH, DELETE');
|
||||
res.status(405).json({ error: { message: `Method ${req.method} Not Allowed` } });
|
||||
}
|
||||
} catch (error: any) {
|
||||
const { message, statusCode = 500 } = error;
|
||||
res.status(statusCode).json({ error: { message } });
|
||||
}
|
||||
await defaultHandler(req, res, {
|
||||
POST: handlePOST,
|
||||
GET: handleGET,
|
||||
PATCH: handlePATCH,
|
||||
DELETE: handleDELETE,
|
||||
});
|
||||
}
|
||||
|
||||
// Create a SAML federated app
|
||||
const handlePOST = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const { samlFederatedController } = await jackson();
|
||||
|
||||
await validateDevelopmentModeLimits(
|
||||
req.body.product,
|
||||
'samlFederation',
|
||||
'Maximum number of federation apps reached'
|
||||
);
|
||||
|
||||
const app = await samlFederatedController.app.create(req.body);
|
||||
|
||||
res.status(201).json({ data: app });
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -27,12 +27,12 @@
|
|||
"devDependencies": {
|
||||
"@rollup/plugin-typescript": "11.1.6",
|
||||
"@types/node": "20.12.7",
|
||||
"@types/react": "18.3.0",
|
||||
"@typescript-eslint/eslint-plugin": "7.7.1",
|
||||
"@typescript-eslint/parser": "7.7.1",
|
||||
"@types/react": "18.3.1",
|
||||
"@typescript-eslint/eslint-plugin": "7.8.0",
|
||||
"@typescript-eslint/parser": "7.8.0",
|
||||
"@vitejs/plugin-react": "4.2.1",
|
||||
"eslint": "8.57.0",
|
||||
"eslint-plugin-react-hooks": "4.6.1",
|
||||
"eslint-plugin-react-hooks": "4.6.2",
|
||||
"eslint-plugin-react-refresh": "0.4.6",
|
||||
"prettier": "3.2.5",
|
||||
"react-daisyui": "5.0.0",
|
||||
|
@ -40,7 +40,7 @@
|
|||
"vite": "5.2.10"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@rollup/rollup-linux-x64-gnu": "4.16.4"
|
||||
"@rollup/rollup-linux-x64-gnu": "4.17.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@boxyhq/react-ui": ">=3.3.42",
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
import jackson from './jackson';
|
||||
import { IndexNames } from 'npm/src/controller/utils';
|
||||
|
||||
type Module = 'sso' | 'dsync' | 'samlFederation';
|
||||
|
||||
export const validateDevelopmentModeLimits = async (
|
||||
productId: string,
|
||||
type: Module,
|
||||
message: string = 'Maximum number of connections reached'
|
||||
) => {
|
||||
if (productId) {
|
||||
const { productController, connectionAPIController, directorySyncController, samlFederatedController } =
|
||||
await jackson();
|
||||
|
||||
const getController = async (type: Module) => {
|
||||
switch (type) {
|
||||
case 'sso':
|
||||
return connectionAPIController;
|
||||
case 'dsync':
|
||||
return directorySyncController.directories;
|
||||
case 'samlFederation':
|
||||
return samlFederatedController.app;
|
||||
default:
|
||||
return {
|
||||
getCount: () => null,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const product = await productController.get(productId);
|
||||
if (product?.development) {
|
||||
const controller = await getController(type);
|
||||
const count = await controller.getCount({
|
||||
name: IndexNames.Product,
|
||||
value: productId,
|
||||
});
|
||||
if (count) {
|
||||
if (count >= 3) {
|
||||
throw { message, statusCode: 400 };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
File diff suppressed because it is too large
Load Diff
|
@ -39,9 +39,9 @@
|
|||
"coverage-map": "map.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-dynamodb": "3.563.0",
|
||||
"@aws-sdk/credential-providers": "3.563.0",
|
||||
"@aws-sdk/util-dynamodb": "3.563.0",
|
||||
"@aws-sdk/client-dynamodb": "3.565.0",
|
||||
"@aws-sdk/credential-providers": "3.565.0",
|
||||
"@aws-sdk/util-dynamodb": "3.565.0",
|
||||
"@boxyhq/error-code-mnemonic": "0.1.1",
|
||||
"@boxyhq/metrics": "0.2.6",
|
||||
"@boxyhq/saml20": "1.5.0",
|
||||
|
|
|
@ -8,6 +8,7 @@ import type {
|
|||
Records,
|
||||
GetByProductParams,
|
||||
AppRequestParams,
|
||||
Index,
|
||||
} from '../../typings';
|
||||
import { fedAppID, clientIDFederatedPrefix } from '../../controller/utils';
|
||||
import { JacksonError } from '../../controller/error';
|
||||
|
@ -633,4 +634,8 @@ export class App {
|
|||
x509cert: publicKey,
|
||||
};
|
||||
}
|
||||
|
||||
public async getCount(idx?: Index) {
|
||||
return await this.store.getCount(idx);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -622,4 +622,5 @@ export interface ProductConfig {
|
|||
faviconUrl: string | null;
|
||||
companyName: string | null;
|
||||
ory: OryConfig | null;
|
||||
development?: boolean;
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
23
package.json
23
package.json
|
@ -66,9 +66,9 @@
|
|||
"@boxyhq/react-ui": "3.3.43",
|
||||
"@boxyhq/saml-jackson": "file:npm",
|
||||
"@heroicons/react": "2.1.3",
|
||||
"@retracedhq/logs-viewer": "2.7.2",
|
||||
"@retracedhq/logs-viewer": "2.7.3",
|
||||
"@retracedhq/retraced": "0.7.9",
|
||||
"@tailwindcss/typography": "0.5.12",
|
||||
"@tailwindcss/typography": "0.5.13",
|
||||
"axios": "1.6.8",
|
||||
"blockly": "10.4.3",
|
||||
"chroma-js": "2.4.2",
|
||||
|
@ -77,7 +77,7 @@
|
|||
"cross-env": "7.0.3",
|
||||
"daisyui": "4.10.2",
|
||||
"formik": "2.4.6",
|
||||
"i18next": "23.11.2",
|
||||
"i18next": "23.11.3",
|
||||
"medium-zoom": "1.1.0",
|
||||
"micromatch": "4.0.5",
|
||||
"next": "14.2.3",
|
||||
|
@ -86,9 +86,9 @@
|
|||
"next-mdx-remote": "4.4.1",
|
||||
"nodemailer": "6.9.13",
|
||||
"raw-body": "2.5.2",
|
||||
"react": "18.2.0",
|
||||
"react": "18.3.1",
|
||||
"react-daisyui": "5.0.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-dom": "18.3.1",
|
||||
"react-i18next": "14.1.1",
|
||||
"react-syntax-highlighter": "15.5.0",
|
||||
"react-tagsinput": "3.20.3",
|
||||
|
@ -100,17 +100,17 @@
|
|||
"@playwright/test": "1.43.1",
|
||||
"@types/cors": "2.8.17",
|
||||
"@types/micromatch": "4.0.7",
|
||||
"@types/node": "20.11.30",
|
||||
"@types/react": "18.2.67",
|
||||
"@typescript-eslint/eslint-plugin": "7.3.1",
|
||||
"@typescript-eslint/parser": "7.3.1",
|
||||
"@types/node": "20.12.7",
|
||||
"@types/react": "18.3.1",
|
||||
"@typescript-eslint/eslint-plugin": "7.8.0",
|
||||
"@typescript-eslint/parser": "7.8.0",
|
||||
"autoprefixer": "10.4.19",
|
||||
"env-cmd": "10.1.0",
|
||||
"eslint": "8.57.0",
|
||||
"eslint-config-next": "14.2.3",
|
||||
"eslint-config-prettier": "9.1.0",
|
||||
"eslint-plugin-i18next": "6.0.3",
|
||||
"jose": "5.2.3",
|
||||
"jose": "5.2.4",
|
||||
"postcss": "8.4.38",
|
||||
"prettier": "3.2.5",
|
||||
"prettier-plugin-tailwindcss": "0.5.14",
|
||||
|
@ -119,8 +119,9 @@
|
|||
"tailwindcss": "3.4.3",
|
||||
"ts-node": "10.9.2",
|
||||
"tsconfig-paths": "4.2.0",
|
||||
"typescript": "5.4.2"
|
||||
"typescript": "5.4.5"
|
||||
},
|
||||
"overrides": {},
|
||||
"engines": {
|
||||
"node": ">=18.14.2",
|
||||
"npm": ">=10"
|
||||
|
|
|
@ -5,6 +5,7 @@ import { oidcMetadataParse, parsePaginateApiParams, strategyChecker } from '@lib
|
|||
import { adminPortalSSODefaults } from '@lib/env';
|
||||
import { defaultHandler } from '@lib/api';
|
||||
import { ApiError } from '@lib/error';
|
||||
import { validateDevelopmentModeLimits } from '@lib/development-mode';
|
||||
|
||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
await defaultHandler(req, res, {
|
||||
|
@ -55,6 +56,8 @@ const handlePOST = async (req: NextApiRequest, res: NextApiResponse) => {
|
|||
|
||||
const { isSAML, isOIDC } = strategyChecker(req);
|
||||
|
||||
await validateDevelopmentModeLimits(req.body.product, 'sso');
|
||||
|
||||
if (!isSAML && !isOIDC) {
|
||||
throw new ApiError('Missing SSO connection params', 400);
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import jackson from '@lib/jackson';
|
|||
import { defaultHandler } from '@lib/api';
|
||||
import { ApiError } from '@lib/error';
|
||||
import { parsePaginateApiParams } from '@lib/utils';
|
||||
import { validateDevelopmentModeLimits } from '@lib/development-mode';
|
||||
|
||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
await defaultHandler(req, res, {
|
||||
|
@ -17,6 +18,8 @@ const handlePOST = async (req: NextApiRequest, res: NextApiResponse) => {
|
|||
|
||||
const { name, tenant, product, type, webhook_url, webhook_secret, google_domain } = req.body;
|
||||
|
||||
await validateDevelopmentModeLimits(product, 'dsync');
|
||||
|
||||
const { data, error } = await directorySyncController.directories.create({
|
||||
name,
|
||||
tenant,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import type { SetupLink } from '@boxyhq/saml-jackson';
|
||||
import jackson from '@lib/jackson';
|
||||
import { validateDevelopmentModeLimits } from '@lib/development-mode';
|
||||
|
||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const { setupLinkController } = await jackson();
|
||||
|
@ -33,6 +34,8 @@ const handlePOST = async (req: NextApiRequest, res: NextApiResponse, setupLink:
|
|||
|
||||
const { type, google_domain } = req.body;
|
||||
|
||||
await validateDevelopmentModeLimits(setupLink.product, 'dsync');
|
||||
|
||||
const directory = {
|
||||
type,
|
||||
google_domain,
|
||||
|
|
|
@ -1,39 +1,23 @@
|
|||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import jackson from '@lib/jackson';
|
||||
import { oidcMetadataParse, strategyChecker } from '@lib/utils';
|
||||
import type { SetupLink } from '@boxyhq/saml-jackson';
|
||||
import { validateDevelopmentModeLimits } from '@lib/development-mode';
|
||||
import { defaultHandler } from '@lib/api';
|
||||
|
||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const { setupLinkController } = await jackson();
|
||||
|
||||
const { method } = req;
|
||||
const { token } = req.query as { token: string };
|
||||
|
||||
try {
|
||||
const setupLink = await setupLinkController.getByToken(token);
|
||||
|
||||
switch (method) {
|
||||
case 'GET':
|
||||
return await handleGET(req, res, setupLink);
|
||||
case 'POST':
|
||||
return await handlePOST(req, res, setupLink);
|
||||
case 'PATCH':
|
||||
return await handlePATCH(req, res);
|
||||
case 'DELETE':
|
||||
return await handleDELETE(req, res);
|
||||
default:
|
||||
res.setHeader('Allow', 'GET, POST, PATCH, DELETE');
|
||||
res.status(405).json({ error: { message: `Method ${method} Not Allowed` } });
|
||||
}
|
||||
} catch (error: any) {
|
||||
const { message, statusCode = 500 } = error;
|
||||
|
||||
return res.status(statusCode).json({ error: { message } });
|
||||
}
|
||||
await defaultHandler(req, res, {
|
||||
GET: handleGET,
|
||||
POST: handlePOST,
|
||||
PATCH: handlePATCH,
|
||||
DELETE: handleDELETE,
|
||||
});
|
||||
};
|
||||
|
||||
const handleGET = async (req: NextApiRequest, res: NextApiResponse, setupLink: SetupLink) => {
|
||||
const { connectionAPIController } = await jackson();
|
||||
const handleGET = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const { token } = req.query as { token: string };
|
||||
const { connectionAPIController, setupLinkController } = await jackson();
|
||||
|
||||
const setupLink = await setupLinkController.getByToken(token);
|
||||
|
||||
const connections = await connectionAPIController.getConnections({
|
||||
tenant: setupLink.tenant,
|
||||
|
@ -68,14 +52,19 @@ const handleGET = async (req: NextApiRequest, res: NextApiResponse, setupLink: S
|
|||
res.json(_connections);
|
||||
};
|
||||
|
||||
const handlePOST = async (req: NextApiRequest, res: NextApiResponse, setupLink: SetupLink) => {
|
||||
const { connectionAPIController } = await jackson();
|
||||
const handlePOST = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const { token } = req.query as { token: string };
|
||||
const { connectionAPIController, setupLinkController } = await jackson();
|
||||
|
||||
const setupLink = await setupLinkController.getByToken(token);
|
||||
|
||||
const body = {
|
||||
...req.body,
|
||||
...setupLink,
|
||||
};
|
||||
|
||||
await validateDevelopmentModeLimits(body.product, 'sso');
|
||||
|
||||
const { isSAML, isOIDC } = strategyChecker(req);
|
||||
|
||||
if (isSAML) {
|
||||
|
|
|
@ -1,18 +1,13 @@
|
|||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import jackson from '@lib/jackson';
|
||||
import { validateDevelopmentModeLimits } from '@lib/development-mode';
|
||||
import { defaultHandler } from '@lib/api';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const { method } = req;
|
||||
|
||||
switch (method) {
|
||||
case 'GET':
|
||||
return await handleGET(req, res);
|
||||
case 'POST':
|
||||
return await handlePOST(req, res);
|
||||
default:
|
||||
res.setHeader('Allow', 'GET, POST');
|
||||
res.status(405).json({ error: { message: `Method ${method} Not Allowed` } });
|
||||
}
|
||||
await defaultHandler(req, res, {
|
||||
GET: handleGET,
|
||||
POST: handlePOST,
|
||||
});
|
||||
}
|
||||
|
||||
// Get the configuration
|
||||
|
@ -41,6 +36,8 @@ const handleGET = async (req: NextApiRequest, res: NextApiResponse) => {
|
|||
const handlePOST = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const { directorySyncController } = await jackson();
|
||||
|
||||
await validateDevelopmentModeLimits(req.body.product, 'dsync');
|
||||
|
||||
const { data, error } = await directorySyncController.directories.create(req.body);
|
||||
|
||||
if (error) {
|
||||
|
|
|
@ -2,29 +2,16 @@ import jackson from '@lib/jackson';
|
|||
import { oidcMetadataParse, strategyChecker } from '@lib/utils';
|
||||
import { NextApiRequest, NextApiResponse } from 'next';
|
||||
import type { DelConnectionsQuery } from '@boxyhq/saml-jackson';
|
||||
import { validateDevelopmentModeLimits } from '@lib/development-mode';
|
||||
import { defaultHandler } from '@lib/api';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const { method } = req;
|
||||
|
||||
try {
|
||||
switch (method) {
|
||||
case 'GET':
|
||||
return await handleGET(req, res);
|
||||
case 'POST':
|
||||
return await handlePOST(req, res);
|
||||
case 'PATCH':
|
||||
return await handlePATCH(req, res);
|
||||
case 'DELETE':
|
||||
return await handleDELETE(req, res);
|
||||
default:
|
||||
res.setHeader('Allow', 'GET, POST, PATCH, DELETE');
|
||||
res.status(405).json({ error: { message: `Method ${method} Not Allowed` } });
|
||||
}
|
||||
} catch (error: any) {
|
||||
const { message, statusCode = 500 } = error;
|
||||
|
||||
return res.status(statusCode).json({ error: { message } });
|
||||
}
|
||||
await defaultHandler(req, res, {
|
||||
GET: handleGET,
|
||||
POST: handlePOST,
|
||||
PATCH: handlePATCH,
|
||||
DELETE: handleDELETE,
|
||||
});
|
||||
}
|
||||
|
||||
// Get all connections
|
||||
|
@ -52,6 +39,8 @@ const handlePOST = async (req: NextApiRequest, res: NextApiResponse) => {
|
|||
throw { message: 'Missing SSO connection params', statusCode: 400 };
|
||||
}
|
||||
|
||||
await validateDevelopmentModeLimits(req.body.product, 'sso');
|
||||
|
||||
// Create SAML connection
|
||||
if (isSAML) {
|
||||
const connection = await connectionAPIController.createSAMLConnection(req.body);
|
||||
|
|
|
@ -21,11 +21,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||
}
|
||||
|
||||
const handlePOST = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const { connectionAPIController, directorySyncController } = await jackson();
|
||||
const { connectionAPIController, directorySyncController, samlFederatedController } = await jackson();
|
||||
|
||||
// Products must be an array of strings
|
||||
const products = req.body.products as string[];
|
||||
const type = req.body.type ? (req.body.type as 'sso' | 'dsync') : undefined;
|
||||
const type = req.body.type ? (req.body.type as 'sso' | 'dsync' | 'samlFederation') : undefined;
|
||||
|
||||
// Validate products
|
||||
if (!products) {
|
||||
|
@ -36,9 +36,9 @@ const handlePOST = async (req: NextApiRequest, res: NextApiResponse) => {
|
|||
throw { message: 'Products must not exceed 50', statusCode: 400 };
|
||||
} else {
|
||||
// Get counts for product
|
||||
// If type is not provided, get counts for both sso and dsync
|
||||
let sso_connections_count = 0;
|
||||
let dsync_connections_count = 0;
|
||||
let saml_federation_count = 0;
|
||||
|
||||
for (const product of products) {
|
||||
if (product) {
|
||||
|
@ -54,6 +54,14 @@ const handlePOST = async (req: NextApiRequest, res: NextApiResponse) => {
|
|||
});
|
||||
dsync_connections_count += count || 0;
|
||||
}
|
||||
|
||||
if (!type || type === 'samlFederation') {
|
||||
const count = await samlFederatedController.app.getCount({
|
||||
name: IndexNames.Product,
|
||||
value: product,
|
||||
});
|
||||
saml_federation_count += count || 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -61,6 +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,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue