diff --git a/messages/en-US.json b/messages/en-US.json
index 937032c18..bee581c73 100644
--- a/messages/en-US.json
+++ b/messages/en-US.json
@@ -872,6 +872,7 @@
"resourcePolicyOtpEmpty": "No one time password",
"resourcePolicyReadOnly": "This policy is Read only",
"resourcePolicyReadOnlyDescription": "This resource policy is shared accross multiple resources, you cannot edit it on this page.",
+ "editSharedPolicy": "Edit Shared Policy",
"resourcePolicyTypeSave": "Save Resource type",
"resourcePolicySelect": "Select resource policy",
"resourcePolicySelectError": "Select a resource policy",
@@ -918,7 +919,7 @@
"resourcePolicyInline": "Inline Resource Policy",
"resourcePolicyInlineDescription": "Access Policy scoped to only this resource",
"resourcePolicyShared": "Shared Resource Policy",
- "resourcePolicySharedDescription": "Access Policy shared accross multiple resources",
+ "resourcePolicySharedDescription": "This resource uses a shared policy. Policy-level settings (auth methods, email whitelist) are locked. You can add resource-specific rules, roles, and users below.",
"resourceUsersRoles": "Access Controls",
"resourceUsersRolesDescription": "Configure which users and roles can visit this resource",
"resourceUsersRolesSubmit": "Save Access Controls",
diff --git a/src/app/[orgId]/settings/resources/public/[niceId]/authentication/page.tsx b/src/app/[orgId]/settings/resources/public/[niceId]/authentication/page.tsx
index 760205ec8..ba55ce833 100644
--- a/src/app/[orgId]/settings/resources/public/[niceId]/authentication/page.tsx
+++ b/src/app/[orgId]/settings/resources/public/[niceId]/authentication/page.tsx
@@ -315,13 +315,13 @@ export default function ResourceAuthenticationPage() {
key={policies.sharedPolicy.resourcePolicyId}
>
}
description={t(
- "resourcePolicyReadOnlyDescription"
+ "resourcePolicySharedDescription"
)}
actions={
}
/>
diff --git a/src/components/resource-policy/EditPolicyForm.tsx b/src/components/resource-policy/EditPolicyForm.tsx
index 043ab1852..bed4a4647 100644
--- a/src/components/resource-policy/EditPolicyForm.tsx
+++ b/src/components/resource-policy/EditPolicyForm.tsx
@@ -44,6 +44,11 @@ export function EditPolicyForm({
const router = useRouter();
+ // In overlay mode (resourceId provided), policy-level sections are locked.
+ // Rules and users/roles sections handle their own hybrid logic via resourceId.
+ const isOverlay = resourceId !== undefined;
+ const policyLevelReadonly = readonly || isOverlay;
+
const isMaxmindAvailable = !!(
env.server.maxmind_db_path && env.server.maxmind_db_path.length > 0
);
@@ -79,7 +84,7 @@ export function EditPolicyForm({
return (
{!hidePolicyNameForm && (
-
+
)}
-
+
!policyRoleLockedIds.has(r.id))
- .map((r) => r.id)
- );
- const currentResourceUserIds = new Set(
- combinedUsers
- .filter((u) => !policyUserLockedIds.has(u.id))
- .map((u) => u.id)
- );
-
- const initialRoleIds = initialResourceRoleIdsRef.current;
- const initialUserIds = initialResourceUserIdsRef.current;
-
- const addedRoleIds = [...currentResourceRoleIds].filter(
- (id) => !initialRoleIds.has(id)
- );
- const removedRoleIds = [...initialRoleIds].filter(
- (id) => !currentResourceRoleIds.has(id)
- );
- const addedUserIds = [...currentResourceUserIds].filter(
- (id) => !initialUserIds.has(id)
- );
- const removedUserIds = [...initialUserIds].filter(
- (id) => !currentResourceUserIds.has(id)
- );
+ const currentResourceRoleIds = combinedRoles
+ .filter((r) => !policyRoleLockedIds.has(r.id))
+ .map((r) => Number(r.id));
+ const currentResourceUserIds = combinedUsers
+ .filter((u) => !policyUserLockedIds.has(u.id))
+ .map((u) => u.id);
+ // Use bulk-set endpoints (session-authenticated) which replace
+ // all resource-specific roles/users in one call
await Promise.all([
- ...addedRoleIds.map((id) =>
- api.post(`/resource/${resourceId}/roles/add`, {
- roleId: Number(id)
- })
- ),
- ...removedRoleIds.map((id) =>
- api.post(`/resource/${resourceId}/roles/remove`, {
- roleId: Number(id)
- })
- ),
- ...addedUserIds.map((id) =>
- api.post(`/resource/${resourceId}/users/add`, {
- userId: id
- })
- ),
- ...removedUserIds.map((id) =>
- api.post(`/resource/${resourceId}/users/remove`, {
- userId: id
- })
- )
+ api.post(`/resource/${resourceId}/roles`, {
+ roleIds: currentResourceRoleIds
+ }),
+ api.post(`/resource/${resourceId}/users`, {
+ userIds: currentResourceUserIds
+ })
]);
// Update refs to reflect new state
- initialResourceRoleIdsRef.current = currentResourceRoleIds;
- initialResourceUserIdsRef.current = currentResourceUserIds;
+ initialResourceRoleIdsRef.current = new Set(
+ currentResourceRoleIds.map(String)
+ );
+ initialResourceUserIdsRef.current = new Set(currentResourceUserIds);
toast({
title: t("success"),