Repository: Infisical/infisical
File: frontend/src/pages/project/RoleDetailsBySlugPage/components/ProjectRoleModifySection.utils.tsx (TypeScript)

Code before:
      caslCondition[el.lhs][
        el.operator as Exclude<
          PermissionConditionOperators,
          | PermissionConditionOperators.$ALL
          | PermissionConditionOperators.$IN
          | PermissionConditionOperators.$ELEMENTMATCH
        >
      ] = el.rhs;
    }
  });
  return caslCondition;
};

export const formRolePermission2API = (formVal: TFormSchema["permissions"]) => {
  const permissions: TProjectPermission[] = [];
  // other than workspace everything else follows same
  // if in future there is a different follow the above on how workspace is done
  Object.entries(formVal || {}).forEach(([subject, rules]) => {
    rules.forEach((actions) => {
      const caslActions = Object.keys(actions).filter(
        (el) => actions?.[el as keyof typeof actions] && el !== "conditions" && el !== "inverted"
      );
      // Skip permissions with no actions selected (e.g. user added a row but unchecked all actions).
      // Sending empty action arrays would be invalid or redundant for the API.
      if (caslActions.length === 0) return;

      const caslConditions =
        "conditions" in actions
          ? convertFormOperatorToCaslCondition(actions.conditions)
          : undefined;

      permissions.push({
        action: caslActions,
        subject,
        inverted: (actions as { inverted?: boolean })?.inverted,
        conditions: caslConditions
      });
    });
  });
  return permissions;
};

export const EXCLUDED_PERMISSION_SUBS = [ProjectPermissionSub.SecretRollback];

export type TProjectPermissionObject = {
  [K in ProjectPermissionSub]: {
    title: string;
    actions: {
      label: string | ReactNode;
      value: keyof Omit<
        NonNullable<NonNullable<TFormSchema["permissions"]>[K]>[number],

Review comment (suggestion):
I don't think we should do this; it's modifying a form submission without the users awareness and making the assumption that they were going to remove it - for example if I add this and forgot to check `read`, as a user I'm intending to add this, just made a mistake, but now it disappears and saves with no context - it's better to provide the feedback than make an assumption

<img width="3456" height="1912" alt="CleanShot 2026-02-04 at 10 16 25@2x" src="https://github.com/user-attachments/assets/4ae725b5-42f2-46a9-89de-cd273e44bdd6" />



Code after:
      caslCondition[el.lhs][
        el.operator as Exclude<
          PermissionConditionOperators,
          | PermissionConditionOperators.$ALL
          | PermissionConditionOperators.$IN
          | PermissionConditionOperators.$ELEMENTMATCH
        >
      ] = el.rhs;
    }
  });
  return caslCondition;
};

export const formRolePermission2API = (formVal: TFormSchema["permissions"]) => {
  const permissions: TProjectPermission[] = [];
  // other than workspace everything else follows same
  // if in future there is a different follow the above on how workspace is done
  Object.entries(formVal || {}).forEach(([subject, rules]) => {
    rules.forEach((actions) => {
      const caslActions = Object.keys(actions).filter(
        (el) => actions?.[el as keyof typeof actions] && el !== "conditions" && el !== "inverted"
      );

      const caslConditions =
        "conditions" in actions
          ? convertFormOperatorToCaslCondition(actions.conditions)
          : undefined;

      permissions.push({
        action: caslActions,
        subject,
        inverted: (actions as { inverted?: boolean })?.inverted,
        conditions: caslConditions
      });
    });
  });
  return permissions;
};

export const EXCLUDED_PERMISSION_SUBS = [ProjectPermissionSub.SecretRollback];

export type TProjectPermissionObject = {
  [K in ProjectPermissionSub]: {
    title: string;
    actions: {
      label: string | ReactNode;
      value: keyof Omit<
        NonNullable<NonNullable<TFormSchema["permissions"]>[K]>[number],
        "conditions" | "inverted"
      >;
    }[];