mirror of
https://github.com/fosrl/pangolin.git
synced 2026-09-04 02:09:43 +02:00
show warning when changing idp identifier mapping
This commit is contained in:
@@ -46,6 +46,7 @@ import { AxiosResponse } from "axios";
|
||||
import { ListRolesResponse } from "@server/routers/role";
|
||||
import AutoProvisionConfigWidget from "@app/components/AutoProvisionConfigWidget";
|
||||
import IdpAutoProvisionUsersDescription from "@app/components/IdpAutoProvisionUsersDescription";
|
||||
import IdpIdentifierChangeDialog from "@app/components/IdpIdentifierChangeDialog";
|
||||
import { PaidFeaturesAlert } from "@app/components/PaidFeaturesAlert";
|
||||
import { tierMatrix } from "@server/lib/billing/tierMatrix";
|
||||
import {
|
||||
@@ -75,6 +76,12 @@ export default function GeneralPage() {
|
||||
>([createMappingBuilderRule()]);
|
||||
const [rawRoleExpression, setRawRoleExpression] = useState("");
|
||||
const [variant, setVariant] = useState<"oidc" | "google" | "azure">("oidc");
|
||||
const [originalIdentifierPath, setOriginalIdentifierPath] = useState("");
|
||||
const [identifierConfirmOpen, setIdentifierConfirmOpen] = useState(false);
|
||||
const [pendingPayload, setPendingPayload] = useState<Record<
|
||||
string,
|
||||
unknown
|
||||
> | null>(null);
|
||||
|
||||
const dashboardRedirectUrl = `${env.app.dashboardUrl}/auth/idp/${idpId}/oidc/callback`;
|
||||
const [redirectUrl, setRedirectUrl] = useState(
|
||||
@@ -184,6 +191,9 @@ export default function GeneralPage() {
|
||||
const data = res.data.data;
|
||||
const roleMapping = data.idpOrg.roleMapping;
|
||||
const idpVariant = data.idpOidcConfig?.variant || "oidc";
|
||||
setOriginalIdentifierPath(
|
||||
data.idpOidcConfig?.identifierPath ?? "sub"
|
||||
);
|
||||
setRedirectUrl(res.data.data.redirectUrl);
|
||||
|
||||
// Set the variant
|
||||
@@ -378,18 +388,56 @@ export default function GeneralPage() {
|
||||
};
|
||||
}
|
||||
|
||||
const res = await api.post(
|
||||
`/org/${orgId}/idp/${idpId}/oidc`,
|
||||
payload
|
||||
);
|
||||
const nextIdentifierPath =
|
||||
variant === "oidc"
|
||||
? (data as OidcFormValues).identifierPath
|
||||
: undefined;
|
||||
|
||||
if (res.status === 200) {
|
||||
toast({
|
||||
title: t("success"),
|
||||
description: t("idpUpdatedDescription")
|
||||
});
|
||||
router.refresh();
|
||||
if (
|
||||
typeof nextIdentifierPath === "string" &&
|
||||
nextIdentifierPath !== originalIdentifierPath
|
||||
) {
|
||||
setPendingPayload(payload);
|
||||
setIdentifierConfirmOpen(true);
|
||||
return;
|
||||
}
|
||||
|
||||
await persistIdp(payload);
|
||||
} catch (e) {
|
||||
toast({
|
||||
title: t("error"),
|
||||
description: formatAxiosError(e),
|
||||
variant: "destructive"
|
||||
});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
async function persistIdp(payload: Record<string, unknown>) {
|
||||
const res = await api.post(`/org/${orgId}/idp/${idpId}/oidc`, payload);
|
||||
|
||||
if (res.status === 200) {
|
||||
if (typeof payload.identifierPath === "string") {
|
||||
setOriginalIdentifierPath(payload.identifierPath);
|
||||
}
|
||||
toast({
|
||||
title: t("success"),
|
||||
description: t("idpUpdatedDescription")
|
||||
});
|
||||
router.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
async function confirmIdentifierChange() {
|
||||
if (!pendingPayload) {
|
||||
return;
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
try {
|
||||
await persistIdp(pendingPayload);
|
||||
setPendingPayload(null);
|
||||
} catch (e) {
|
||||
toast({
|
||||
title: t("error"),
|
||||
@@ -407,6 +455,16 @@ export default function GeneralPage() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<IdpIdentifierChangeDialog
|
||||
open={identifierConfirmOpen}
|
||||
setOpen={(open) => {
|
||||
setIdentifierConfirmOpen(open);
|
||||
if (!open) {
|
||||
setPendingPayload(null);
|
||||
}
|
||||
}}
|
||||
onConfirm={confirmIdentifierChange}
|
||||
/>
|
||||
<SettingsContainer>
|
||||
<SettingsSection>
|
||||
<SettingsSectionHeader>
|
||||
|
||||
@@ -41,6 +41,7 @@ import {
|
||||
} from "@app/components/InfoSection";
|
||||
import CopyToClipboard from "@app/components/CopyToClipboard";
|
||||
import IdpTypeBadge from "@app/components/IdpTypeBadge";
|
||||
import IdpIdentifierChangeDialog from "@app/components/IdpIdentifierChangeDialog";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
export default function GeneralPage() {
|
||||
@@ -51,6 +52,12 @@ export default function GeneralPage() {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [initialLoading, setInitialLoading] = useState(true);
|
||||
const [variant, setVariant] = useState<"oidc" | "google" | "azure">("oidc");
|
||||
const [originalIdentifierPath, setOriginalIdentifierPath] = useState("");
|
||||
const [identifierConfirmOpen, setIdentifierConfirmOpen] = useState(false);
|
||||
const [pendingPayload, setPendingPayload] = useState<Record<
|
||||
string,
|
||||
unknown
|
||||
> | null>(null);
|
||||
|
||||
const redirectUrl = `${env.app.dashboardUrl}/auth/idp/${idpId}/oidc/callback`;
|
||||
const t = useTranslations();
|
||||
@@ -141,6 +148,9 @@ export default function GeneralPage() {
|
||||
| "google"
|
||||
| "azure") || "oidc";
|
||||
setVariant(idpVariant);
|
||||
setOriginalIdentifierPath(
|
||||
data.idpOidcConfig?.identifierPath ?? "sub"
|
||||
);
|
||||
|
||||
let tenantId = "";
|
||||
if (idpVariant === "azure" && data.idpOidcConfig?.authUrl) {
|
||||
@@ -258,15 +268,56 @@ export default function GeneralPage() {
|
||||
};
|
||||
}
|
||||
|
||||
const res = await api.post(`/idp/${idpId}/oidc`, payload);
|
||||
const nextIdentifierPath =
|
||||
variant === "oidc"
|
||||
? (data as OidcFormValues).identifierPath
|
||||
: undefined;
|
||||
|
||||
if (res.status === 200) {
|
||||
toast({
|
||||
title: t("success"),
|
||||
description: t("idpUpdatedDescription")
|
||||
});
|
||||
router.refresh();
|
||||
if (
|
||||
typeof nextIdentifierPath === "string" &&
|
||||
nextIdentifierPath !== originalIdentifierPath
|
||||
) {
|
||||
setPendingPayload(payload);
|
||||
setIdentifierConfirmOpen(true);
|
||||
return;
|
||||
}
|
||||
|
||||
await persistIdp(payload);
|
||||
} catch (e) {
|
||||
toast({
|
||||
title: t("error"),
|
||||
description: formatAxiosError(e),
|
||||
variant: "destructive"
|
||||
});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
async function persistIdp(payload: Record<string, unknown>) {
|
||||
const res = await api.post(`/idp/${idpId}/oidc`, payload);
|
||||
|
||||
if (res.status === 200) {
|
||||
if (typeof payload.identifierPath === "string") {
|
||||
setOriginalIdentifierPath(payload.identifierPath);
|
||||
}
|
||||
toast({
|
||||
title: t("success"),
|
||||
description: t("idpUpdatedDescription")
|
||||
});
|
||||
router.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
async function confirmIdentifierChange() {
|
||||
if (!pendingPayload) {
|
||||
return;
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
try {
|
||||
await persistIdp(pendingPayload);
|
||||
setPendingPayload(null);
|
||||
} catch (e) {
|
||||
toast({
|
||||
title: t("error"),
|
||||
@@ -284,6 +335,16 @@ export default function GeneralPage() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<IdpIdentifierChangeDialog
|
||||
open={identifierConfirmOpen}
|
||||
setOpen={(open) => {
|
||||
setIdentifierConfirmOpen(open);
|
||||
if (!open) {
|
||||
setPendingPayload(null);
|
||||
}
|
||||
}}
|
||||
onConfirm={confirmIdentifierChange}
|
||||
/>
|
||||
<SettingsContainer>
|
||||
<SettingsSection>
|
||||
<SettingsSectionHeader>
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
"use client";
|
||||
|
||||
import ConfirmDeleteDialog from "@app/components/ConfirmDeleteDialog";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
type IdpIdentifierChangeDialogProps = {
|
||||
open: boolean;
|
||||
setOpen: (open: boolean) => void;
|
||||
onConfirm: () => Promise<void>;
|
||||
};
|
||||
|
||||
export default function IdpIdentifierChangeDialog({
|
||||
open,
|
||||
setOpen,
|
||||
onConfirm
|
||||
}: IdpIdentifierChangeDialogProps) {
|
||||
const t = useTranslations();
|
||||
|
||||
return (
|
||||
<ConfirmDeleteDialog
|
||||
open={open}
|
||||
setOpen={setOpen}
|
||||
dialog={
|
||||
<div className="space-y-2">
|
||||
<p>{t("idpIdentifierChangeDescription")}</p>
|
||||
</div>
|
||||
}
|
||||
buttonText={t("saveGeneralSettings")}
|
||||
onConfirm={onConfirm}
|
||||
string={t("idpIdentifierChangeConfirmMessage")}
|
||||
title={t("idpIdentifierChangeTitle")}
|
||||
warningText={t("idpIdentifierChangeWarningText")}
|
||||
/>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user