From 759a89bb0c8832a42dd154bd95a86f744c15eda5 Mon Sep 17 00:00:00 2001 From: Kiran K Date: Wed, 19 Oct 2022 02:04:32 +0530 Subject: [PATCH] Reintroduce SAML SSO (#4938) * wip reintroduce SAML SSO * Fix the imports * wip * Some tweaks * Fix the type * Reduce the textarea height * Cleanup * Fix the access issues * Make the SAML SSO active on the sidebar * Add SP's instructions * Remove the console.log * Add the condition to check SAML SSO is enabled * Replace SAML SSO with Single Sign-On * Update to SAML feature * Upgrade the @boxyhq/saml-jackson * Fix the SAML part and other cleanup * Tweaks to SAML SSO setup * Fix the type * Fix the import path * Remove samlLoginUrl * Import fixes * Simplifies endpoints Co-authored-by: zomars --- apps/web/lib/jackson.ts | 40 - apps/web/lib/saml.ts | 59 -- apps/web/package.json | 2 +- apps/web/pages/api/auth/[...nextauth].tsx | 8 +- apps/web/pages/api/auth/saml/authorize.ts | 33 +- apps/web/pages/api/auth/saml/callback.ts | 31 +- apps/web/pages/api/auth/saml/token.ts | 28 +- apps/web/pages/api/auth/saml/userinfo.ts | 59 +- apps/web/pages/auth/login.tsx | 2 +- apps/web/pages/auth/sso/[provider].tsx | 8 +- apps/web/pages/settings/security/sso.tsx | 1 + apps/web/pages/settings/teams/[id]/sso.tsx | 1 + apps/web/pages/signup.tsx | 2 +- apps/web/playwright/saml.e2e.ts | 2 +- apps/web/public/static/locales/en/common.json | 34 +- .../common/components/SamlConfiguration.tsx | 164 ---- .../ee/sso/components/ConfigDialogForm.tsx | 88 ++ .../ee/sso/components/SAMLConfiguration.tsx | 175 ++++ packages/features/ee/sso/lib/jackson.ts | 53 ++ packages/{ => features/ee/sso}/lib/saml.ts | 61 +- .../features/ee/sso/page/teams-sso-view.tsx | 39 + .../features/ee/sso/page/user-sso-view.tsx | 22 + packages/lib/jackson.ts | 40 - packages/trpc/server/routers/viewer.tsx | 107 +-- packages/trpc/server/routers/viewer/saml.tsx | 121 +++ .../ui/v2/core/layouts/SettingsLayout.tsx | 25 +- yarn.lock | 802 +++++++----------- 27 files changed, 990 insertions(+), 1017 deletions(-) delete mode 100644 apps/web/lib/jackson.ts delete mode 100644 apps/web/lib/saml.ts create mode 100644 apps/web/pages/settings/security/sso.tsx create mode 100644 apps/web/pages/settings/teams/[id]/sso.tsx delete mode 100644 packages/features/ee/common/components/SamlConfiguration.tsx create mode 100644 packages/features/ee/sso/components/ConfigDialogForm.tsx create mode 100644 packages/features/ee/sso/components/SAMLConfiguration.tsx create mode 100644 packages/features/ee/sso/lib/jackson.ts rename packages/{ => features/ee/sso}/lib/saml.ts (50%) create mode 100644 packages/features/ee/sso/page/teams-sso-view.tsx create mode 100644 packages/features/ee/sso/page/user-sso-view.tsx delete mode 100644 packages/lib/jackson.ts create mode 100644 packages/trpc/server/routers/viewer/saml.tsx diff --git a/apps/web/lib/jackson.ts b/apps/web/lib/jackson.ts deleted file mode 100644 index 076ed57c06..0000000000 --- a/apps/web/lib/jackson.ts +++ /dev/null @@ -1,40 +0,0 @@ -import jackson, { IAPIController, IOAuthController, JacksonOption } from "@boxyhq/saml-jackson"; - -import { BASE_URL } from "@lib/config/constants"; -import { samlDatabaseUrl } from "@lib/saml"; - -// Set the required options. Refer to https://github.com/boxyhq/jackson#configuration for the full list -const opts: JacksonOption = { - externalUrl: BASE_URL, - samlPath: "/api/auth/saml/callback", - db: { - engine: "sql", - type: "postgres", - url: samlDatabaseUrl, - encryptionKey: process.env.CALENDSO_ENCRYPTION_KEY, - }, - samlAudience: "https://saml.cal.com", -}; - -let apiController: IAPIController; -let oauthController: IOAuthController; - -const g = global as any; - -export default async function init() { - if (!g.apiController || !g.oauthController) { - const ret = await jackson(opts); - apiController = ret.apiController; - oauthController = ret.oauthController; - g.apiController = apiController; - g.oauthController = oauthController; - } else { - apiController = g.apiController; - oauthController = g.oauthController; - } - - return { - apiController, - oauthController, - }; -} diff --git a/apps/web/lib/saml.ts b/apps/web/lib/saml.ts deleted file mode 100644 index 6af24b61c9..0000000000 --- a/apps/web/lib/saml.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { PrismaClient } from "@prisma/client"; - -import { TRPCError } from "@calcom/trpc/server"; - -import { BASE_URL } from "@lib/config/constants"; - -export const samlDatabaseUrl = process.env.SAML_DATABASE_URL || ""; -export const samlLoginUrl = BASE_URL; - -export const isSAMLLoginEnabled = samlDatabaseUrl.length > 0; - -export const samlTenantID = "Cal.com"; -export const samlProductID = "Cal.com"; - -const samlAdmins = (process.env.SAML_ADMINS || "").split(","); -export const hostedCal = BASE_URL === "https://app.cal.com"; -export const tenantPrefix = "team-"; - -export const isSAMLAdmin = (email: string) => { - for (const admin of samlAdmins) { - if (admin.toLowerCase() === email.toLowerCase() && admin.toUpperCase() === email.toUpperCase()) { - return true; - } - } - - return false; -}; - -export const samlTenantProduct = async (prisma: PrismaClient, email: string) => { - const user = await prisma.user.findUnique({ - where: { - email, - }, - select: { - id: true, - invitedTo: true, - }, - }); - - if (!user) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "Unauthorized Request", - }); - } - - if (!user.invitedTo) { - throw new TRPCError({ - code: "BAD_REQUEST", - message: - "Could not find a SAML Identity Provider for your email. Please contact your admin to ensure you have been given access to Cal", - }); - } - - return { - tenant: tenantPrefix + user.invitedTo, - product: samlProductID, - }; -}; diff --git a/apps/web/package.json b/apps/web/package.json index 94ba52b067..0e2c39a1e7 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -23,7 +23,7 @@ "yarn": ">=1.19.0 < 2.0.0" }, "dependencies": { - "@boxyhq/saml-jackson": "0.3.6", + "@boxyhq/saml-jackson": "1.3.1", "@calcom/app-store": "*", "@calcom/app-store-cli": "*", "@calcom/core": "*", diff --git a/apps/web/pages/api/auth/[...nextauth].tsx b/apps/web/pages/api/auth/[...nextauth].tsx index 720c5e8270..30c97c7647 100644 --- a/apps/web/pages/api/auth/[...nextauth].tsx +++ b/apps/web/pages/api/auth/[...nextauth].tsx @@ -12,6 +12,7 @@ import path from "path"; import checkLicense from "@calcom/features/ee/common/server/checkLicense"; import ImpersonationProvider from "@calcom/features/ee/impersonation/lib/ImpersonationProvider"; +import { hostedCal, isSAMLLoginEnabled } from "@calcom/features/ee/sso/lib/saml"; import { WEBAPP_URL } from "@calcom/lib/constants"; import { symmetricDecrypt } from "@calcom/lib/crypto"; import { defaultCookies } from "@calcom/lib/default-cookies"; @@ -22,7 +23,6 @@ import prisma from "@calcom/prisma"; import { ErrorCode, verifyPassword } from "@lib/auth"; import CalComAdapter from "@lib/auth/next-auth-custom-adapter"; import { randomString } from "@lib/random"; -import { hostedCal, isSAMLLoginEnabled, samlLoginUrl } from "@lib/saml"; import slugify from "@lib/slugify"; import { GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, IS_GOOGLE_LOGIN_ENABLED } from "@server/lib/constants"; @@ -135,7 +135,7 @@ if (isSAMLLoginEnabled) { version: "2.0", checks: ["pkce", "state"], authorization: { - url: `${samlLoginUrl}/api/auth/saml/authorize`, + url: `${WEBAPP_URL}/api/auth/saml/authorize`, params: { scope: "", response_type: "code", @@ -143,10 +143,10 @@ if (isSAMLLoginEnabled) { }, }, token: { - url: `${samlLoginUrl}/api/auth/saml/token`, + url: `${WEBAPP_URL}/api/auth/saml/token`, params: { grant_type: "authorization_code" }, }, - userinfo: `${samlLoginUrl}/api/auth/saml/userinfo`, + userinfo: `${WEBAPP_URL}/api/auth/saml/userinfo`, profile: (profile) => { return { id: profile.id || "", diff --git a/apps/web/pages/api/auth/saml/authorize.ts b/apps/web/pages/api/auth/saml/authorize.ts index 5fd4ac9393..7dd9acd865 100644 --- a/apps/web/pages/api/auth/saml/authorize.ts +++ b/apps/web/pages/api/auth/saml/authorize.ts @@ -1,25 +1,24 @@ -import { OAuthReqBody } from "@boxyhq/saml-jackson"; +import { OAuthReq } from "@boxyhq/saml-jackson"; import { NextApiRequest, NextApiResponse } from "next"; -import { HttpError } from "@calcom/lib/http-error"; +import jackson from "@calcom/features/ee/sso/lib/jackson"; -import jackson from "@lib/jackson"; +import { HttpError } from "@lib/core/http/error"; export default async function handler(req: NextApiRequest, res: NextApiResponse) { - try { - if (req.method !== "GET") { - throw new Error("Method not allowed"); - } + const { oauthController } = await jackson(); - const { oauthController } = await jackson(); - const { redirect_url } = await oauthController.authorize(req.query as unknown as OAuthReqBody); - res.redirect(302, redirect_url); - } catch (err: unknown) { - if (err instanceof HttpError) { - console.error("authorize error:", err); - const { message, statusCode = 500 } = err; - return res.status(statusCode).send(message); - } - return res.status(500).send("Unknown error"); + if (req.method !== "GET") { + return res.status(400).send("Method not allowed"); + } + + try { + const { redirect_url } = await oauthController.authorize(req.query as unknown as OAuthReq); + + return res.redirect(302, redirect_url as string); + } catch (err) { + const { message, statusCode = 500 } = err as HttpError; + + return res.status(statusCode).send(message); } } diff --git a/apps/web/pages/api/auth/saml/callback.ts b/apps/web/pages/api/auth/saml/callback.ts index 5221198d51..f7c91928ff 100644 --- a/apps/web/pages/api/auth/saml/callback.ts +++ b/apps/web/pages/api/auth/saml/callback.ts @@ -1,25 +1,14 @@ import { NextApiRequest, NextApiResponse } from "next"; -import { HttpError } from "@calcom/lib/http-error"; +import jackson from "@calcom/features/ee/sso/lib/jackson"; +import { defaultHandler, defaultResponder } from "@calcom/lib/server"; -import jackson from "@lib/jackson"; - -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - try { - if (req.method !== "POST") { - throw new Error("Method not allowed"); - } - - const { oauthController } = await jackson(); - const { redirect_url } = await oauthController.samlResponse(req.body); - - res.redirect(302, redirect_url); - } catch (err: unknown) { - if (err instanceof HttpError) { - console.error("callback error:", err); - const { message, statusCode = 500 } = err; - return res.status(statusCode).send(message); - } - return res.status(500).send("Unknown error"); - } +async function postHandler(req: NextApiRequest, res: NextApiResponse) { + const { oauthController } = await jackson(); + const { redirect_url } = await oauthController.samlResponse(req.body); + if (redirect_url) return res.redirect(302, redirect_url); } + +export default defaultHandler({ + POST: Promise.resolve({ default: defaultResponder(postHandler) }), +}); diff --git a/apps/web/pages/api/auth/saml/token.ts b/apps/web/pages/api/auth/saml/token.ts index 3a49c4dff7..703a010850 100644 --- a/apps/web/pages/api/auth/saml/token.ts +++ b/apps/web/pages/api/auth/saml/token.ts @@ -1,21 +1,13 @@ -import { NextApiRequest, NextApiResponse } from "next"; +import { NextApiRequest } from "next"; -import jackson from "@lib/jackson"; +import jackson from "@calcom/features/ee/sso/lib/jackson"; +import { defaultHandler, defaultResponder } from "@calcom/lib/server"; -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - try { - if (req.method !== "POST") { - throw new Error("Method not allowed"); - } - - const { oauthController } = await jackson(); - const result = await oauthController.token(req.body); - - res.json(result); - } catch (err: any) { - console.error("token error:", err); - const { message, statusCode = 500 } = err; - - res.status(statusCode).send(message); - } +async function postHandler(req: NextApiRequest) { + const { oauthController } = await jackson(); + return await oauthController.token(req.body); } + +export default defaultHandler({ + POST: Promise.resolve({ default: defaultResponder(postHandler) }), +}); diff --git a/apps/web/pages/api/auth/saml/userinfo.ts b/apps/web/pages/api/auth/saml/userinfo.ts index 9c5da6ba9b..e408357a37 100644 --- a/apps/web/pages/api/auth/saml/userinfo.ts +++ b/apps/web/pages/api/auth/saml/userinfo.ts @@ -1,53 +1,34 @@ -import { NextApiRequest, NextApiResponse } from "next"; +import { NextApiRequest } from "next"; import z from "zod"; -import jackson from "@lib/jackson"; +import jackson from "@calcom/features/ee/sso/lib/jackson"; +import { HttpError } from "@calcom/lib/http-error"; +import { defaultHandler, defaultResponder } from "@calcom/lib/server"; const extractAuthToken = (req: NextApiRequest) => { const authHeader = req.headers["authorization"]; const parts = (authHeader || "").split(" "); - if (parts.length > 1) { - return parts[1]; - } + if (parts.length > 1) return parts[1]; - return null; + // check for query param + let arr: string[] = []; + const { access_token } = requestQuery.parse(req.query); + arr = arr.concat(access_token); + if (arr[0].length > 0) return arr[0]; + + throw new HttpError({ statusCode: 401, message: "Unauthorized" }); }; const requestQuery = z.object({ access_token: z.string(), }); -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - try { - if (req.method !== "GET") { - throw new Error("Method not allowed"); - } - - const { oauthController } = await jackson(); - let token: string | null = extractAuthToken(req); - - // check for query param - if (!token) { - let arr: string[] = []; - const { access_token } = requestQuery.parse(req.query); - arr = arr.concat(access_token); - if (arr[0].length > 0) { - token = arr[0]; - } - } - - if (!token) { - res.status(401).json({ message: "Unauthorized" }); - return; - } - - const profile = await oauthController.userInfo(token); - - res.json(profile); - } catch (err: any) { - console.error("userinfo error:", err); - const { message, statusCode = 500 } = err; - - res.status(statusCode).json({ message }); - } +async function getHandler(req: NextApiRequest) { + const { oauthController } = await jackson(); + const token = extractAuthToken(req); + return await oauthController.userInfo(token); } + +export default defaultHandler({ + GET: Promise.resolve({ default: defaultResponder(getHandler) }), +}); diff --git a/apps/web/pages/auth/login.tsx b/apps/web/pages/auth/login.tsx index 4fa4f9cd1c..e147b71623 100644 --- a/apps/web/pages/auth/login.tsx +++ b/apps/web/pages/auth/login.tsx @@ -7,6 +7,7 @@ import { useState } from "react"; import { useForm } from "react-hook-form"; import { FaGoogle } from "react-icons/fa"; +import { hostedCal, isSAMLLoginEnabled, samlProductID, samlTenantID } from "@calcom/features/ee/sso/lib/saml"; import { getSafeRedirectUrl } from "@calcom/lib/getSafeRedirectUrl"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry"; @@ -18,7 +19,6 @@ import SAMLLogin from "@calcom/ui/v2/modules/auth/SAMLLogin"; import { ErrorCode, getSession } from "@lib/auth"; import { WEBAPP_URL, WEBSITE_URL } from "@lib/config/constants"; -import { hostedCal, isSAMLLoginEnabled, samlProductID, samlTenantID } from "@lib/saml"; import { inferSSRProps } from "@lib/types/inferSSRProps"; import AddToHomescreen from "@components/AddToHomescreen"; diff --git a/apps/web/pages/auth/sso/[provider].tsx b/apps/web/pages/auth/sso/[provider].tsx index bef4fbcdb9..fbfd258e1c 100644 --- a/apps/web/pages/auth/sso/[provider].tsx +++ b/apps/web/pages/auth/sso/[provider].tsx @@ -5,12 +5,18 @@ import { useEffect } from "react"; import { getPremiumPlanPrice } from "@calcom/app-store/stripepayment/lib/utils"; import stripe from "@calcom/features/ee/payments/server/stripe"; +import { + hostedCal, + isSAMLLoginEnabled, + samlProductID, + samlTenantID, + samlTenantProduct, +} from "@calcom/features/ee/sso/lib/saml"; import { checkUsername } from "@calcom/lib/server/checkUsername"; import prisma from "@calcom/prisma"; import { asStringOrNull } from "@lib/asStringOrNull"; import { getSession } from "@lib/auth"; -import { hostedCal, isSAMLLoginEnabled, samlProductID, samlTenantID, samlTenantProduct } from "@lib/saml"; import { inferSSRProps } from "@lib/types/inferSSRProps"; import { ssrInit } from "@server/lib/ssr"; diff --git a/apps/web/pages/settings/security/sso.tsx b/apps/web/pages/settings/security/sso.tsx new file mode 100644 index 0000000000..7b9d48b68e --- /dev/null +++ b/apps/web/pages/settings/security/sso.tsx @@ -0,0 +1 @@ +export { default } from "@calcom/features/ee/sso/page/user-sso-view"; diff --git a/apps/web/pages/settings/teams/[id]/sso.tsx b/apps/web/pages/settings/teams/[id]/sso.tsx new file mode 100644 index 0000000000..afb0724157 --- /dev/null +++ b/apps/web/pages/settings/teams/[id]/sso.tsx @@ -0,0 +1 @@ +export { default } from "@calcom/features/ee/sso/page/teams-sso-view"; diff --git a/apps/web/pages/signup.tsx b/apps/web/pages/signup.tsx index 7a85866e27..57cd44ac06 100644 --- a/apps/web/pages/signup.tsx +++ b/apps/web/pages/signup.tsx @@ -4,6 +4,7 @@ import { useRouter } from "next/router"; import { FormProvider, SubmitHandler, useForm } from "react-hook-form"; import LicenseRequired from "@calcom/features/ee/common/components/v2/LicenseRequired"; +import { isSAMLLoginEnabled } from "@calcom/features/ee/sso/lib/saml"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { inferSSRProps } from "@calcom/types/inferSSRProps"; import { Alert } from "@calcom/ui/Alert"; @@ -13,7 +14,6 @@ import { HeadSeo } from "@calcom/web/components/seo/head-seo"; import { asStringOrNull } from "@calcom/web/lib/asStringOrNull"; import { WEBAPP_URL } from "@calcom/web/lib/config/constants"; import prisma from "@calcom/web/lib/prisma"; -import { isSAMLLoginEnabled } from "@calcom/web/lib/saml"; import { IS_GOOGLE_LOGIN_ENABLED } from "@calcom/web/server/lib/constants"; import { ssrInit } from "@calcom/web/server/lib/ssr"; diff --git a/apps/web/playwright/saml.e2e.ts b/apps/web/playwright/saml.e2e.ts index d1fcfb5dc0..794e277133 100644 --- a/apps/web/playwright/saml.e2e.ts +++ b/apps/web/playwright/saml.e2e.ts @@ -10,7 +10,7 @@ test.describe("SAML tests", () => { // eslint-disable-next-line playwright/no-skipped-test test.skip(!IS_SAML_LOGIN_ENABLED, "It should only run if SAML is enabled"); // Try to go Security page - await page.goto("/settings/security"); + await page.goto("/settings/security/sso"); // It should redirect you to the event-types page // await page.waitForSelector("[data-testid=saml_config]"); }); diff --git a/apps/web/public/static/locales/en/common.json b/apps/web/public/static/locales/en/common.json index 1474809f58..e452d055f5 100644 --- a/apps/web/public/static/locales/en/common.json +++ b/apps/web/public/static/locales/en/common.json @@ -746,22 +746,11 @@ "user_away_description": "The person you are trying to book has set themselves to away, and therefore is not accepting new bookings.", "meet_people_with_the_same_tokens": "Meet people with the same tokens", "only_book_people_and_allow": "Only book and allow bookings from people who share the same tokens, DAOs, or NFTs.", - "saml_config_deleted_successfully": "SAML configuration deleted successfully", "account_created_with_identity_provider": "Your account was created using an Identity Provider.", "account_managed_by_identity_provider": "Your account is managed by {{provider}}", "account_managed_by_identity_provider_description": "To change your email, password, enable two-factor authentication and more, please visit your {{provider}} account settings.", "signin_with_google": "Sign in with Google", "signin_with_saml": "Sign in with SAML", - "saml_configuration": "SAML configuration", - "delete_saml_configuration": "Delete SAML configuration", - "delete_saml_configuration_confirmation_message": "Are you sure you want to delete the SAML configuration? Your team members who use SAML login will no longer be able to access Cal.com.", - "confirm_delete_saml_configuration": "Yes, delete SAML configuration", - "saml_not_configured_yet": "SAML not configured yet", - "saml_configuration_description": "Please paste the SAML metadata from your Identity Provider in the textbox below to update your SAML configuration.", - "saml_configuration_placeholder": "Please paste the SAML metadata from your Identity Provider here", - "saml_configuration_update_failed": "SAML configuration update failed", - "saml_configuration_delete_failed": "SAML configuration delete failed", - "saml_email_required": "Please enter an email so we can find your SAML Identity Provider", "you_will_need_to_generate": "You will need to generate an access token from your old scheduling tool.", "import": "Import", "import_from": "Import from", @@ -1241,7 +1230,6 @@ "theme_dark": "Dark", "theme_system": "System default", "add_a_team": "Add a team", - "saml_config": "SAML Config", "add_webhook_description": "Receive meeting data in real-time when something happens in Cal.com", "triggers_when": "Triggers when", "test_webhook": "Please ping test before creating.", @@ -1315,5 +1303,25 @@ "no_available_slots": "No Available slots", "time_available": "Time available", "install_new_calendar_app": "Install new calendar app", - "make_phone_number_required": "Make phone number required for booking event" + "make_phone_number_required": "Make phone number required for booking event", + "dont_have_permission": "You don't have permission to access this resource.", + "saml_config": "Single Sign-On", + "saml_description": "Allow team members to login using an Identity Provider", + "saml_config_deleted_successfully": "SAML configuration deleted successfully", + "saml_config_updated_successfully": "SAML configuration updated successfully", + "saml_configuration": "SAML configuration", + "delete_saml_configuration": "Delete SAML configuration", + "delete_saml_configuration_confirmation_message": "Are you sure you want to delete the SAML configuration? Your team members who use SAML login will no longer be able to access Cal.com.", + "confirm_delete_saml_configuration": "Yes, delete SAML configuration", + "saml_not_configured_yet": "SAML not configured yet", + "saml_configuration_description": "Please paste the SAML metadata from your Identity Provider in the textbox below to update your SAML configuration.", + "saml_configuration_placeholder": "Please paste the SAML metadata from your Identity Provider here", + "saml_email_required": "Please enter an email so we can find your SAML Identity Provider", + "saml_sp_title": "Service Provider Details", + "saml_sp_description": "Your Identity Provider (IdP) will ask you for the following details to complete the SAML application configuration.", + "saml_sp_acs_url": "ACS URL", + "saml_sp_entity_id": "SP Entity ID", + "saml_sp_acs_url_copied": "ACS URL copied!", + "saml_sp_entity_id_copied": "SP Entity ID copied!", + "saml_btn_configure": "Configure" } diff --git a/packages/features/ee/common/components/SamlConfiguration.tsx b/packages/features/ee/common/components/SamlConfiguration.tsx deleted file mode 100644 index 47f19e84d5..0000000000 --- a/packages/features/ee/common/components/SamlConfiguration.tsx +++ /dev/null @@ -1,164 +0,0 @@ -import React, { useEffect, useRef, useState } from "react"; - -import { useLocale } from "@calcom/lib/hooks/useLocale"; -import showToast from "@calcom/lib/notification"; -import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry"; -import { trpc } from "@calcom/trpc/react"; -import { Alert } from "@calcom/ui/Alert"; -import Badge from "@calcom/ui/Badge"; -import Button from "@calcom/ui/Button"; -import ConfirmationDialogContent from "@calcom/ui/ConfirmationDialogContent"; -import { Dialog, DialogTrigger } from "@calcom/ui/Dialog"; -import { TextArea } from "@calcom/ui/form/fields"; - -import LicenseRequired from "./LicenseRequired"; - -export default function SAMLConfiguration({ - teamsView, - teamId, -}: { - teamsView: boolean; - teamId: null | undefined | number; -}) { - const [isSAMLLoginEnabled, setIsSAMLLoginEnabled] = useState(false); - const [samlConfig, setSAMLConfig] = useState(null); - - const query = trpc.useQuery(["viewer.showSAMLView", { teamsView, teamId }]); - - const telemetry = useTelemetry(); - - useEffect(() => { - const data = query.data; - setIsSAMLLoginEnabled(data?.isSAMLLoginEnabled ?? false); - setSAMLConfig(data?.provider ?? null); - }, [query.data]); - - const mutation = trpc.useMutation("viewer.updateSAMLConfig", { - onSuccess: (data: { provider: string | undefined }) => { - showToast(t("saml_config_updated_successfully"), "success"); - setHasErrors(false); // dismiss any open errors - setSAMLConfig(data?.provider ?? null); - samlConfigRef.current.value = ""; - }, - onError: () => { - setHasErrors(true); - setErrorMessage(t("saml_configuration_update_failed")); - document?.getElementsByTagName("main")[0]?.scrollTo({ top: 0, behavior: "smooth" }); - }, - }); - - const deleteMutation = trpc.useMutation("viewer.deleteSAMLConfig", { - onSuccess: () => { - showToast(t("saml_config_deleted_successfully"), "success"); - setHasErrors(false); // dismiss any open errors - setSAMLConfig(null); - samlConfigRef.current.value = ""; - }, - onError: () => { - setHasErrors(true); - setErrorMessage(t("saml_configuration_delete_failed")); - document?.getElementsByTagName("main")[0]?.scrollTo({ top: 0, behavior: "smooth" }); - }, - }); - - const samlConfigRef = useRef() as React.MutableRefObject; - - const [hasErrors, setHasErrors] = useState(false); - const [errorMessage, setErrorMessage] = useState(""); - - async function updateSAMLConfigHandler(event: React.FormEvent) { - event.preventDefault(); - - const rawMetadata = samlConfigRef.current.value; - - // track Google logins. Without personal data/payload - telemetry.event(telemetryEventTypes.samlConfig, collectPageParameters()); - - mutation.mutate({ - encodedRawMetadata: Buffer.from(rawMetadata).toString("base64"), - teamId, - }); - } - - async function deleteSAMLConfigHandler(event: React.MouseEvent) { - event.preventDefault(); - - deleteMutation.mutate({ - teamId, - }); - } - - const { t } = useLocale(); - return ( - <> - {isSAMLLoginEnabled ? ( - -
-
-

- {t("saml_configuration")} - - {samlConfig ? t("enabled") : t("disabled")} - - {samlConfig ? ( - <> - - {samlConfig ? samlConfig : ""} - - - ) : null} -

-
- - {samlConfig ? ( -
- - - - - - {t("delete_saml_configuration_confirmation_message")} - - -
- ) : ( -

{!samlConfig ? t("saml_not_configured_yet") : ""}

- )} - -

{t("saml_configuration_description")}

- -
- {hasErrors && } - -