From f80e212b07c4eaafde4c9b4aef938f3b441a7a16 Mon Sep 17 00:00:00 2001 From: Fred KISSIE Date: Wed, 11 Mar 2026 00:27:27 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=A7=20wip?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/auth/actions.ts | 2 +- server/routers/external.ts | 6 +- server/routers/integration.ts | 6 +- ...olicies.ts => getDefaultResourcePolicy.ts} | 28 +- server/routers/resource/index.ts | 2 +- .../proxy/[niceId]/authentication/page.tsx | 586 +----------------- .../EditPolicyAuthMethodsSectionForm.tsx | 30 +- .../resource-policy/EditPolicyForm.tsx | 13 +- .../EditPolicyNameSectionForm.tsx | 6 +- .../EditPolicyOtpEmailSectionForm.tsx | 29 +- .../EditPolicyRulesSectionForm.tsx | 46 +- .../EditPolicyUserRolesSectionForm.tsx | 12 +- src/lib/queries.ts | 8 +- 13 files changed, 156 insertions(+), 618 deletions(-) rename server/routers/resource/{getResourcePolicies.ts => getDefaultResourcePolicy.ts} (77%) diff --git a/server/auth/actions.ts b/server/auth/actions.ts index 5549380ab..82ceda2f3 100644 --- a/server/auth/actions.ts +++ b/server/auth/actions.ts @@ -147,7 +147,7 @@ export enum ActionsEnum { setResourcePolicyHeaderAuth = "setResourcePolicyHeaderAuth", setResourcePolicyWhitelist = "setResourcePolicyWhitelist", setResourcePolicyRules = "setResourcePolicyRules", - getResourcePolicies = "getResourcePolicies" + } export async function checkUserActionPermission( diff --git a/server/routers/external.ts b/server/routers/external.ts index 130ebbbb4..a23ec54ad 100644 --- a/server/routers/external.ts +++ b/server/routers/external.ts @@ -637,10 +637,10 @@ authenticated.get( ); authenticated.get( - "/resource/:resourceId/policies", + "/resource/:resourceId/default-policy", verifyResourceAccess, - verifyUserHasAction(ActionsEnum.getResourcePolicies), - resource.getResourcePolicies + verifyUserHasAction(ActionsEnum.getResourcePolicy), + resource.getDefaultResourcePolicy ); authenticated.put( diff --git a/server/routers/integration.ts b/server/routers/integration.ts index cadda13cb..56dec9dee 100644 --- a/server/routers/integration.ts +++ b/server/routers/integration.ts @@ -454,10 +454,10 @@ authenticated.get( ); authenticated.get( - "/resource/:resourceId/policies", + "/resource/:resourceId/default-policy", verifyApiKeyResourceAccess, - verifyApiKeyHasAction(ActionsEnum.getResourcePolicies), - resource.getResourcePolicies + verifyApiKeyHasAction(ActionsEnum.getResourcePolicy), + resource.getDefaultResourcePolicy ); authenticated.post( diff --git a/server/routers/resource/getResourcePolicies.ts b/server/routers/resource/getDefaultResourcePolicy.ts similarity index 77% rename from server/routers/resource/getResourcePolicies.ts rename to server/routers/resource/getDefaultResourcePolicy.ts index c51e6f1fd..39621c2ea 100644 --- a/server/routers/resource/getResourcePolicies.ts +++ b/server/routers/resource/getDefaultResourcePolicy.ts @@ -17,13 +17,11 @@ const getResourcePoliciesParamsSchema = z.strictObject({ resourceId: z.string().transform(Number).pipe(z.int().positive()) }); -export type GetResourcePoliciesResponse = { - defaultPolicy: GetResourcePolicyResponse | null; -}; +export type GetDefaultResourcePolicyResponse = GetResourcePolicyResponse; registry.registerPath({ method: "get", - path: "/resource/{resourceId}/policies", + path: "/resource/{resourceId}/default-policy", description: "Get the default policy for a resource.", tags: [OpenAPITags.PublicResource, OpenAPITags.Policy], request: { @@ -32,7 +30,7 @@ registry.registerPath({ responses: {} }); -export async function getResourcePolicies( +export async function getDefaultResourcePolicy( req: Request, res: Response, next: NextFunction @@ -66,14 +64,20 @@ export async function getResourcePolicies( ); } - const defaultPolicy = resource.defaultResourcePolicyId - ? await queryResourcePolicy({ - resourcePolicyId: resource.defaultResourcePolicyId - }) - : null; + if (!resource.defaultResourcePolicyId) { + return next( + createHttpError( + HttpCode.NOT_FOUND, + "Resource has no default policy" + ) + ); + } - return response(res, { - data: { defaultPolicy }, + const defaultPolicy = await queryResourcePolicy({ + resourcePolicyId: resource.defaultResourcePolicyId + }); + return response(res, { + data: defaultPolicy, success: true, error: false, message: "Resource policies retrieved successfully", diff --git a/server/routers/resource/index.ts b/server/routers/resource/index.ts index 78803105f..e602932f0 100644 --- a/server/routers/resource/index.ts +++ b/server/routers/resource/index.ts @@ -31,4 +31,4 @@ export * from "./addUserToResource"; export * from "./removeUserFromResource"; export * from "./listAllResourceNames"; export * from "./removeEmailFromResourceWhitelist"; -export * from "./getResourcePolicies"; +export * from "./getDefaultResourcePolicy"; diff --git a/src/app/[orgId]/settings/resources/proxy/[niceId]/authentication/page.tsx b/src/app/[orgId]/settings/resources/proxy/[niceId]/authentication/page.tsx index e2e315d35..9609d9da1 100644 --- a/src/app/[orgId]/settings/resources/proxy/[niceId]/authentication/page.tsx +++ b/src/app/[orgId]/settings/resources/proxy/[niceId]/authentication/page.tsx @@ -1,5 +1,6 @@ "use client"; +import { EditPolicyForm } from "@app/components/resource-policy/EditPolicyForm"; import SetResourceHeaderAuthForm from "@app/components/SetResourceHeaderAuthForm"; import SetResourcePincodeForm from "@app/components/SetResourcePincodeForm"; import { @@ -46,7 +47,10 @@ import { toast } from "@app/hooks/useToast"; import { createApiClient, formatAxiosError } from "@app/lib/api"; import { getUserDisplayName } from "@app/lib/getUserDisplayName"; import { orgQueries, resourceQueries } from "@app/lib/queries"; -import { ResourcePolicyContext } from "@app/providers/ResourcePolicyProvider"; +import { + ResourcePolicyContext, + ResourcePolicyProvider +} from "@app/providers/ResourcePolicyProvider"; import { zodResolver } from "@hookform/resolvers/zod"; import { build } from "@server/build"; import { tierMatrix } from "@server/lib/billing/tierMatrix"; @@ -64,32 +68,19 @@ import { useState, useTransition } from "react"; -import { useForm } from "react-hook-form"; +import { useForm, useWatch } from "react-hook-form"; import { z } from "zod"; -const UsersRolesFormSchema = z.object({ - roles: z.array( +const resourceTypeSchema = z + .object({ + type: z.literal("inline") + }) + .or( z.object({ - id: z.string(), - text: z.string() + type: z.literal("shared"), + resourcePolicyId: z.number() }) - ), - users: z.array( - z.object({ - id: z.string(), - text: z.string() - }) - ) -}); - -const whitelistSchema = z.object({ - emails: z.array( - z.object({ - id: z.string(), - text: z.string() - }) - ) -}); + ); type ResourcePolicyType = StrategyOption<"inline" | "shared">; @@ -106,114 +97,14 @@ export default function ResourceAuthenticationPage() { const { isPaidUser } = usePaidStatus(); - const queryClient = useQueryClient(); - const { data: resourceRoles = [], isLoading: isLoadingResourceRoles } = - useQuery( - resourceQueries.resourceRoles({ - resourceId: resource.resourceId - }) - ); - const { data: resourceUsers = [], isLoading: isLoadingResourceUsers } = - useQuery( - resourceQueries.resourceUsers({ - resourceId: resource.resourceId - }) - ); - - const { data: whitelist = [], isLoading: isLoadingWhiteList } = useQuery( - resourceQueries.resourceWhitelist({ - resourceId: resource.resourceId - }) - ); - const { data: policies, isLoading: isLoadingPolicies } = useQuery( - resourceQueries.policies({ + const { data: defaultPolicy, isLoading: isLoadingPolicies } = useQuery( + resourceQueries.defaultPolicy({ resourceId: resource.resourceId }) ); - const { data: orgRoles = [], isLoading: isLoadingOrgRoles } = useQuery( - orgQueries.roles({ - orgId: org.org.orgId - }) - ); - const { data: orgUsers = [], isLoading: isLoadingOrgUsers } = useQuery( - orgQueries.users({ - orgId: org.org.orgId - }) - ); - const { data: orgIdps = [], isLoading: isLoadingOrgIdps } = useQuery( - orgQueries.identityProviders({ - orgId: org.org.orgId, - useOrgOnlyIdp: env.app.identityProviderMode === "org" - }) - ); + const pageLoading = isLoadingPolicies || !defaultPolicy; - const pageLoading = - isLoadingOrgRoles || - isLoadingOrgUsers || - isLoadingResourceRoles || - isLoadingResourceUsers || - isLoadingWhiteList || - isLoadingOrgIdps || - isLoadingPolicies; - - const allRoles = useMemo(() => { - return orgRoles - .map((role) => ({ - id: role.roleId.toString(), - text: role.name - })) - .filter((role) => role.text !== "Admin"); - }, [orgRoles]); - - const allUsers = useMemo(() => { - return orgUsers.map((user) => ({ - id: user.id.toString(), - text: `${getUserDisplayName({ - email: user.email, - username: user.username - })}${user.type !== UserType.Internal ? ` (${user.idpName})` : ""}` - })); - }, [orgUsers]); - - const allIdps = useMemo(() => { - if (build === "saas") { - if (isPaidUser(tierMatrix.orgOidc)) { - return orgIdps.map((idp) => ({ - id: idp.idpId, - text: idp.name - })); - } - } else { - return orgIdps.map((idp) => ({ - id: idp.idpId, - text: idp.name - })); - } - return []; - }, [orgIdps]); - - const [activeRolesTagIndex, setActiveRolesTagIndex] = useState< - number | null - >(null); - const [activeUsersTagIndex, setActiveUsersTagIndex] = useState< - number | null - >(null); - - const [ssoEnabled, setSsoEnabled] = useState(resource.sso ?? false); - - useEffect(() => { - setSsoEnabled(resource.sso ?? false); - }, [resource.sso]); - - const [selectedIdpId, setSelectedIdpId] = useState( - resource.skipToIdpId || null - ); - - const [loadingRemoveResourcePassword, setLoadingRemoveResourcePassword] = - useState(false); - const [loadingRemoveResourcePincode, setLoadingRemoveResourcePincode] = - useState(false); const [ loadingRemoveResourceHeaderAuth, setLoadingRemoveResourceHeaderAuth @@ -223,209 +114,6 @@ export default function ResourceAuthenticationPage() { const [isSetPincodeOpen, setIsSetPincodeOpen] = useState(false); const [isSetHeaderAuthOpen, setIsSetHeaderAuthOpen] = useState(false); - const usersRolesForm = useForm({ - resolver: zodResolver(UsersRolesFormSchema), - defaultValues: { roles: [], users: [] } - }); - - const whitelistForm = useForm({ - resolver: zodResolver(whitelistSchema), - defaultValues: { emails: [] } - }); - - const hasInitializedRef = useRef(false); - - useEffect(() => { - if (pageLoading || hasInitializedRef.current) return; - - usersRolesForm.setValue( - "roles", - resourceRoles - .map((i) => ({ - id: i.roleId.toString(), - text: i.name - })) - .filter((role) => role.text !== "Admin") - ); - usersRolesForm.setValue( - "users", - resourceUsers.map((i) => ({ - id: i.userId.toString(), - text: `${getUserDisplayName({ - email: i.email, - username: i.username - })}${i.type !== UserType.Internal ? ` (${i.idpName})` : ""}` - })) - ); - - whitelistForm.setValue( - "emails", - whitelist.map((w) => ({ - id: w.email, - text: w.email - })) - ); - hasInitializedRef.current = true; - }, [pageLoading, resourceRoles, resourceUsers, whitelist, orgIdps]); - - const [, submitUserRolesForm, loadingSaveUsersRoles] = useActionState( - onSubmitUsersRoles, - null - ); - - async function onSubmitUsersRoles() { - const isValid = usersRolesForm.trigger(); - if (!isValid) return; - - const data = usersRolesForm.getValues(); - - try { - const jobs = [ - api.post(`/resource/${resource.resourceId}/roles`, { - roleIds: data.roles.map((i) => parseInt(i.id)) - }), - api.post(`/resource/${resource.resourceId}/users`, { - userIds: data.users.map((i) => i.id) - }), - api.post(`/resource/${resource.resourceId}`, { - sso: ssoEnabled, - skipToIdpId: selectedIdpId - }) - ]; - - await Promise.all(jobs); - - updateResource({ - sso: ssoEnabled, - skipToIdpId: selectedIdpId - }); - - updateAuthInfo({ - sso: ssoEnabled - }); - - toast({ - title: t("resourceAuthSettingsSave"), - description: t("resourceAuthSettingsSaveDescription") - }); - // invalidate resource queries - await queryClient.invalidateQueries( - resourceQueries.resourceUsers({ - resourceId: resource.resourceId - }) - ); - await queryClient.invalidateQueries( - resourceQueries.resourceRoles({ - resourceId: resource.resourceId - }) - ); - - router.refresh(); - } catch (e) { - console.error(e); - toast({ - variant: "destructive", - title: t("resourceErrorUsersRolesSave"), - description: formatAxiosError( - e, - t("resourceErrorUsersRolesSaveDescription") - ) - }); - } - } - - function removeResourcePassword() { - setLoadingRemoveResourcePassword(true); - - api.post(`/resource/${resource.resourceId}/password`, { - password: null - }) - .then(() => { - toast({ - title: t("resourcePasswordRemove"), - description: t("resourcePasswordRemoveDescription") - }); - - updateAuthInfo({ - password: false - }); - router.refresh(); - }) - .catch((e) => { - toast({ - variant: "destructive", - title: t("resourceErrorPasswordRemove"), - description: formatAxiosError( - e, - t("resourceErrorPasswordRemoveDescription") - ) - }); - }) - .finally(() => setLoadingRemoveResourcePassword(false)); - } - - function removeResourcePincode() { - setLoadingRemoveResourcePincode(true); - - api.post(`/resource/${resource.resourceId}/pincode`, { - pincode: null - }) - .then(() => { - toast({ - title: t("resourcePincodeRemove"), - description: t("resourcePincodeRemoveDescription") - }); - - updateAuthInfo({ - pincode: false - }); - router.refresh(); - }) - .catch((e) => { - toast({ - variant: "destructive", - title: t("resourceErrorPincodeRemove"), - description: formatAxiosError( - e, - t("resourceErrorPincodeRemoveDescription") - ) - }); - }) - .finally(() => setLoadingRemoveResourcePincode(false)); - } - - function removeResourceHeaderAuth() { - setLoadingRemoveResourceHeaderAuth(true); - - api.post(`/resource/${resource.resourceId}/header-auth`, { - user: null, - password: null, - extendedCompatibility: null - }) - .then(() => { - toast({ - title: t("resourceHeaderAuthRemove"), - description: t("resourceHeaderAuthRemoveDescription") - }); - - updateAuthInfo({ - headerAuth: false - }); - router.refresh(); - }) - .catch((e) => { - toast({ - variant: "destructive", - title: t("resourceErrorHeaderAuthRemove"), - description: formatAxiosError( - e, - t("resourceErrorHeaderAuthRemoveDescription") - ) - }); - }) - .finally(() => setLoadingRemoveResourceHeaderAuth(false)); - } - const resourcePolicyTypes: Array = [ { id: "inline", @@ -439,8 +127,17 @@ export default function ResourceAuthenticationPage() { } ]; - const [selectedResourceType, setSelectedResourceType] = - useState("inline"); + const form = useForm({ + resolver: zodResolver(resourceTypeSchema), + defaultValues: { + type: "inline" + } + }); + + const selectedResourceType = useWatch({ + control: form.control, + name: "type" + }); if (pageLoading) { return <>; @@ -503,13 +200,9 @@ export default function ResourceAuthenticationPage() { { - // baseForm.setValue( - // "http", - // value === "http" - // ); - // // Update method default when switching resource type + form.setValue("type", value); }} cols={2} /> @@ -524,223 +217,10 @@ export default function ResourceAuthenticationPage() { - {/* - - */} + + + ); } - -type OneTimePasswordFormSectionProps = Pick< - ResourceContextType, - "resource" | "updateResource" -> & { - whitelist: Array<{ email: string }>; - isLoadingWhiteList: boolean; -}; - -function OneTimePasswordFormSection({ - resource, - updateResource, - whitelist, - isLoadingWhiteList -}: OneTimePasswordFormSectionProps) { - const { env } = useEnvContext(); - const [whitelistEnabled, setWhitelistEnabled] = useState( - resource.emailWhitelistEnabled ?? false - ); - - useEffect(() => { - setWhitelistEnabled(resource.emailWhitelistEnabled); - }, [resource.emailWhitelistEnabled]); - - const queryClient = useQueryClient(); - - const [loadingSaveWhitelist, startTransition] = useTransition(); - const whitelistForm = useForm({ - resolver: zodResolver(whitelistSchema), - defaultValues: { emails: [] } - }); - const api = createApiClient({ env }); - const router = useRouter(); - const t = useTranslations(); - - const [activeEmailTagIndex, setActiveEmailTagIndex] = useState< - number | null - >(null); - - useEffect(() => { - if (isLoadingWhiteList) return; - - whitelistForm.setValue( - "emails", - whitelist.map((w) => ({ - id: w.email, - text: w.email - })) - ); - }, [isLoadingWhiteList, whitelist, whitelistForm]); - - async function saveWhitelist() { - try { - await api.post(`/resource/${resource.resourceId}`, { - emailWhitelistEnabled: whitelistEnabled - }); - - if (whitelistEnabled) { - await api.post(`/resource/${resource.resourceId}/whitelist`, { - emails: whitelistForm.getValues().emails.map((i) => i.text) - }); - } - - updateResource({ - emailWhitelistEnabled: whitelistEnabled - }); - - toast({ - title: t("resourceWhitelistSave"), - description: t("resourceWhitelistSaveDescription") - }); - router.refresh(); - await queryClient.invalidateQueries( - resourceQueries.resourceWhitelist({ - resourceId: resource.resourceId - }) - ); - } catch (e) { - console.error(e); - toast({ - variant: "destructive", - title: t("resourceErrorWhitelistSave"), - description: formatAxiosError( - e, - t("resourceErrorWhitelistSaveDescription") - ) - }); - } - } - - return ( - - - - {t("otpEmailTitle")} - - - {t("otpEmailTitleDescription")} - - - - - {!env.email.emailEnabled && ( - - - - {t("otpEmailSmtpRequired")} - - - {t("otpEmailSmtpRequiredDescription")} - - - )} - - - {whitelistEnabled && env.email.emailEnabled && ( -
- - ( - - - - - - {/* @ts-ignore */} - { - return z - .email() - .or( - z - .string() - .regex( - /^\*@[\w.-]+\.[a-zA-Z]{2,}$/, - { - message: - t( - "otpEmailErrorInvalid" - ) - } - ) - ) - .safeParse(tag) - .success; - }} - setActiveTagIndex={ - setActiveEmailTagIndex - } - placeholder={t( - "otpEmailEnter" - )} - tags={ - whitelistForm.getValues() - .emails - } - setTags={(newRoles) => { - whitelistForm.setValue( - "emails", - newRoles as [ - Tag, - ...Tag[] - ] - ); - }} - allowDuplicates={false} - sortTags={true} - /> - - - {t("otpEmailEnterDescription")} - - - )} - /> - - - )} -
-
- - - -
- ); -} diff --git a/src/components/resource-policy/EditPolicyAuthMethodsSectionForm.tsx b/src/components/resource-policy/EditPolicyAuthMethodsSectionForm.tsx index 57f37c958..f739a737b 100644 --- a/src/components/resource-policy/EditPolicyAuthMethodsSectionForm.tsx +++ b/src/components/resource-policy/EditPolicyAuthMethodsSectionForm.tsx @@ -73,7 +73,11 @@ const setHeaderAuthSchema = z.object({ extendedCompatibility: z.boolean() }); -export function EditPolicyAuthMethodsSectionForm() { +export function EditPolicyAuthMethodsSectionForm({ + readonly +}: { + readonly?: boolean; +}) { const { policy } = useResourcePolicyContext(); const router = useRouter(); @@ -132,6 +136,7 @@ export function EditPolicyAuthMethodsSectionForm() { const [, formAction, isSubmitting] = useActionState(onSubmit, null); async function onSubmit() { + if (readonly) return; const isValid = await form.trigger(); if (!isValid) return; @@ -237,14 +242,16 @@ export function EditPolicyAuthMethodsSectionForm() { - + {!readonly && ( + + )} ); @@ -541,6 +548,7 @@ export function EditPolicyAuthMethodsSectionForm() { type="button" variant="secondary" size="sm" + disabled={readonly} onClick={ hasPassword ? () => @@ -579,6 +587,7 @@ export function EditPolicyAuthMethodsSectionForm() { type="button" variant="secondary" size="sm" + disabled={readonly} onClick={ hasPincode ? () => @@ -619,6 +628,7 @@ export function EditPolicyAuthMethodsSectionForm() { type="button" variant="secondary" size="sm" + disabled={readonly} onClick={ hasHeaderAuth ? () => @@ -644,7 +654,7 @@ export function EditPolicyAuthMethodsSectionForm() { diff --git a/src/components/resource-policy/EditPolicyForm.tsx b/src/components/resource-policy/EditPolicyForm.tsx index 076e726dd..d0594752f 100644 --- a/src/components/resource-policy/EditPolicyForm.tsx +++ b/src/components/resource-policy/EditPolicyForm.tsx @@ -28,9 +28,13 @@ import { EditPolicyRulesSectionForm } from "./EditPolicyRulesSectionForm"; export type EditPolicyFormProps = { hidePolicyNameForm?: boolean; + readonly?: boolean; }; -export function EditPolicyForm({ hidePolicyNameForm }: EditPolicyFormProps) { +export function EditPolicyForm({ + hidePolicyNameForm, + readonly +}: EditPolicyFormProps) { const { org } = useOrgContext(); const t = useTranslations(); const { env } = useEnvContext(); @@ -100,23 +104,26 @@ export function EditPolicyForm({ hidePolicyNameForm }: EditPolicyFormProps) { return ( - {!hidePolicyNameForm && } + {!hidePolicyNameForm && } - + ); diff --git a/src/components/resource-policy/EditPolicyNameSectionForm.tsx b/src/components/resource-policy/EditPolicyNameSectionForm.tsx index adedcb7c1..e3a2a156f 100644 --- a/src/components/resource-policy/EditPolicyNameSectionForm.tsx +++ b/src/components/resource-policy/EditPolicyNameSectionForm.tsx @@ -40,7 +40,7 @@ import { useForm } from "react-hook-form"; // ─── PolicyNameSection ────────────────────────────────────────────────── -export function EditPolicyNameSectionForm() { +export function EditPolicyNameSectionForm({ readonly }: { readonly?: boolean }) { const t = useTranslations(); const api = createApiClient(useEnvContext()); const router = useRouter(); @@ -61,6 +61,7 @@ export function EditPolicyNameSectionForm() { const [, formAction, isSubmitting] = useActionState(onSubmit, null); async function onSubmit() { + if (readonly) return; const isValid = await form.trigger(); if (!isValid) return; @@ -125,6 +126,7 @@ export function EditPolicyNameSectionForm() { {t("saveSettings")} diff --git a/src/components/resource-policy/EditPolicyOtpEmailSectionForm.tsx b/src/components/resource-policy/EditPolicyOtpEmailSectionForm.tsx index 3a117bd8a..cb9b92a6b 100644 --- a/src/components/resource-policy/EditPolicyOtpEmailSectionForm.tsx +++ b/src/components/resource-policy/EditPolicyOtpEmailSectionForm.tsx @@ -46,10 +46,12 @@ import { useResourcePolicyContext } from "@app/providers/ResourcePolicyProvider" type PolicyOtpEmailSectionProps = { emailEnabled: boolean; + readonly?: boolean; }; export function EditPolicyOtpEmailSectionForm({ - emailEnabled + emailEnabled, + readonly }: PolicyOtpEmailSectionProps) { const t = useTranslations(); @@ -87,6 +89,7 @@ export function EditPolicyOtpEmailSectionForm({ const [, formAction, isSubmitting] = useActionState(onSubmit, null); async function onSubmit() { + if (readonly) return; const isValid = await form.trigger(); if (!isValid) return; @@ -141,14 +144,16 @@ export function EditPolicyOtpEmailSectionForm({ - + {!readonly && ( + + )} ); @@ -186,7 +191,7 @@ export function EditPolicyOtpEmailSectionForm({ onCheckedChange={(val) => { form.setValue("emailWhitelistEnabled", val); }} - disabled={!emailEnabled} + disabled={readonly || !emailEnabled} /> {whitelistEnabled && emailEnabled && ( @@ -268,7 +273,9 @@ export function EditPolicyOtpEmailSectionForm({ diff --git a/src/components/resource-policy/EditPolicyRulesSectionForm.tsx b/src/components/resource-policy/EditPolicyRulesSectionForm.tsx index cd6f39fe1..e034f0d59 100644 --- a/src/components/resource-policy/EditPolicyRulesSectionForm.tsx +++ b/src/components/resource-policy/EditPolicyRulesSectionForm.tsx @@ -108,11 +108,13 @@ type LocalRule = { type PolicyRulesSectionProps = { isMaxmindAvailable: boolean; isMaxmindAsnAvailable: boolean; + readonly?: boolean; }; export function EditPolicyRulesSectionForm({ isMaxmindAvailable, - isMaxmindAsnAvailable + isMaxmindAsnAvailable, + readonly }: PolicyRulesSectionProps) { const t = useTranslations(); @@ -331,6 +333,7 @@ export function EditPolicyRulesSectionForm({ defaultValue={row.original.priority} className="w-[75px]" type="number" + disabled={readonly} onClick={(e) => e.currentTarget.focus()} onBlur={(e) => { const parsed = z.coerce @@ -361,6 +364,7 @@ export function EditPolicyRulesSectionForm({ cell: ({ row }) => ( @@ -439,6 +444,7 @@ export function EditPolicyRulesSectionForm({ + {!readonly && ( + + )}
); @@ -721,6 +736,7 @@ export function EditPolicyRulesSectionForm({ onCheckedChange={(val) => { form.setValue("applyRules", val); }} + disabled={readonly} /> @@ -741,6 +757,7 @@ export function EditPolicyRulesSectionForm({ ) : ( - + )} @@ -1053,7 +1073,7 @@ export function EditPolicyRulesSectionForm({ @@ -1134,7 +1154,7 @@ export function EditPolicyRulesSectionForm({ diff --git a/src/components/resource-policy/EditPolicyUserRolesSectionForm.tsx b/src/components/resource-policy/EditPolicyUserRolesSectionForm.tsx index d4d9b2de2..29fe486be 100644 --- a/src/components/resource-policy/EditPolicyUserRolesSectionForm.tsx +++ b/src/components/resource-policy/EditPolicyUserRolesSectionForm.tsx @@ -53,12 +53,14 @@ type PolicyUsersRolesSectionProps = { allRoles: { id: string; text: string }[]; allUsers: { id: string; text: string }[]; allIdps: { id: number; text: string }[]; + readonly?: boolean; }; export function EditPolicyUsersRolesSectionForm({ allRoles, allUsers, - allIdps + allIdps, + readonly }: PolicyUsersRolesSectionProps) { const t = useTranslations(); @@ -106,6 +108,8 @@ export function EditPolicyUsersRolesSectionForm({ const [, formAction, isSubmitting] = useActionState(onSubmit, null); async function onSubmit() { + if (readonly) return; + const isValid = await form.trigger(); if (!isValid) return; @@ -172,6 +176,7 @@ export function EditPolicyUsersRolesSectionForm({ console.log(`form.setValue("sso", ${val})`); form.setValue("sso", val); }} + disabled={readonly} /> {ssoEnabled && ( @@ -221,6 +226,7 @@ export function EditPolicyUsersRolesSectionForm({ true } sortTags={true} + disabled={readonly} /> @@ -277,6 +283,7 @@ export function EditPolicyUsersRolesSectionForm({ true } sortTags={true} + disabled={readonly} /> @@ -292,6 +299,7 @@ export function EditPolicyUsersRolesSectionForm({ {t("defaultIdentityProvider")}