mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-13 00:29:53 +00:00
feat: battle of ts - unlimited edition
This commit is contained in:
@@ -10,6 +10,7 @@ import {
|
||||
} from "@app/hooks/api/roles/queries";
|
||||
|
||||
import { OrgPermissionSet } from "./types";
|
||||
import { useCallback } from "react";
|
||||
|
||||
export const useOrgPermission = () => {
|
||||
const organizationId = useRouteContext({
|
||||
@@ -17,16 +18,24 @@ export const useOrgPermission = () => {
|
||||
select: (el) => el.organizationId
|
||||
});
|
||||
|
||||
const { data } = useSuspenseQuery({
|
||||
const {
|
||||
data: { permission, memberships = [] }
|
||||
} = useSuspenseQuery({
|
||||
queryKey: roleQueryKeys.getUserOrgPermissions({ orgId: organizationId }),
|
||||
queryFn: () => fetchUserOrgPermissions({ orgId: organizationId }),
|
||||
select: (res) => {
|
||||
const rule = unpackRules<RawRuleOf<MongoAbility<OrgPermissionSet>>>(res.permissions);
|
||||
const ability = createMongoAbility<OrgPermissionSet>(rule, { conditionsMatcher });
|
||||
return { permission: ability, membership: res.membership };
|
||||
return { permission: ability, memberships: res.memberships };
|
||||
},
|
||||
staleTime: Infinity
|
||||
});
|
||||
|
||||
return data;
|
||||
const hasOrgRole = useCallback(
|
||||
(role: string) =>
|
||||
memberships?.some((membership) => membership.roles.some((el) => role === el.role)),
|
||||
[]
|
||||
);
|
||||
|
||||
return { permission, memberships, hasOrgRole };
|
||||
};
|
||||
|
||||
@@ -20,7 +20,7 @@ export const useProjectPermission = () => {
|
||||
}
|
||||
|
||||
const {
|
||||
data: { permission, membership, assumedPrivilegeDetails }
|
||||
data: { permission, memberships, assumedPrivilegeDetails }
|
||||
} = useSuspenseQuery({
|
||||
queryKey: roleQueryKeys.getUserProjectPermissions({ projectId }),
|
||||
queryFn: () => fetchUserProjectPermissions({ projectId }),
|
||||
@@ -31,18 +31,16 @@ export const useProjectPermission = () => {
|
||||
return {
|
||||
permission: ability,
|
||||
assumedPrivilegeDetails: data.assumedPrivilegeDetails,
|
||||
membership: {
|
||||
...data.membership,
|
||||
roles: data.membership.roles.map(({ role }) => role)
|
||||
}
|
||||
memberships: data.memberships
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
const hasProjectRole = useCallback(
|
||||
(role: string) => membership?.roles?.includes(role) || false,
|
||||
(role: string) =>
|
||||
memberships?.some((membership) => membership.roles.some((el) => role === el.role)),
|
||||
[]
|
||||
);
|
||||
|
||||
return { permission, membership, hasProjectRole, assumedPrivilegeDetails };
|
||||
return { permission, memberships, hasProjectRole, assumedPrivilegeDetails };
|
||||
};
|
||||
|
||||
@@ -11,7 +11,7 @@ import { groupBy } from "@app/lib/fn/array";
|
||||
import { omit } from "@app/lib/fn/object";
|
||||
|
||||
import { ActorType } from "../auditLogs/enums";
|
||||
import { OrgUser, TProjectMembership } from "../users/types";
|
||||
import { TUserMembership } from "../users/types";
|
||||
import {
|
||||
TGetUserOrgPermissionsDTO,
|
||||
TGetUserProjectPermissionDTO,
|
||||
@@ -109,11 +109,11 @@ export const useGetOrgRole = (orgId: string, roleId: string) =>
|
||||
});
|
||||
|
||||
export const fetchUserOrgPermissions = async ({ orgId }: TGetUserOrgPermissionsDTO) => {
|
||||
if (orgId === "") return { permissions: [], membership: null };
|
||||
if (orgId === "") return { permissions: [], memberships: [] };
|
||||
|
||||
const { data } = await apiRequest.get<{
|
||||
permissions: PackRule<RawRuleOf<MongoAbility<OrgPermissionSet>>>[];
|
||||
membership: OrgUser;
|
||||
memberships: Array<TUserMembership & { roles: { role: string }[] }>;
|
||||
}>(`/api/v1/organization/${orgId}/permissions`);
|
||||
|
||||
return data;
|
||||
@@ -127,7 +127,7 @@ export const useGetUserOrgPermissions = ({ orgId }: TGetUserOrgPermissionsDTO) =
|
||||
select: (data) => {
|
||||
const rule = unpackRules<RawRuleOf<MongoAbility<OrgPermissionSet>>>(data.permissions);
|
||||
const ability = createMongoAbility<OrgPermissionSet>(rule, { conditionsMatcher });
|
||||
return { permission: ability, membership: data.membership };
|
||||
return { permission: ability, memberships: data.memberships };
|
||||
}
|
||||
});
|
||||
|
||||
@@ -135,7 +135,7 @@ export const fetchUserProjectPermissions = async ({ projectId }: TGetUserProject
|
||||
const { data } = await apiRequest.get<{
|
||||
data: {
|
||||
permissions: PackRule<RawRuleOf<MongoAbility<OrgPermissionSet>>>[];
|
||||
membership: Omit<TProjectMembership, "roles"> & { roles: { role: string }[] };
|
||||
memberships: Array<TUserMembership & { roles: { role: string }[] }>;
|
||||
assumedPrivilegeDetails?: {
|
||||
actorId: string;
|
||||
actorType: ActorType;
|
||||
@@ -181,11 +181,7 @@ export const useGetUserProjectPermissions = ({ projectId }: TGetUserProjectPermi
|
||||
};
|
||||
}
|
||||
});
|
||||
const membership = {
|
||||
...data.membership,
|
||||
roles: data.membership.roles.map(({ role }) => role)
|
||||
};
|
||||
|
||||
return { permission: ability, membership };
|
||||
return { permission: ability, memberships: data.memberships };
|
||||
}
|
||||
});
|
||||
|
||||
@@ -72,6 +72,13 @@ export type OrgUser = {
|
||||
lastLoginTime?: string;
|
||||
};
|
||||
|
||||
export type TUserMembership = {
|
||||
id: string;
|
||||
scope: string;
|
||||
scopeOrgId: string;
|
||||
actorUserId: string;
|
||||
};
|
||||
|
||||
export type TProjectMembership = {
|
||||
id: string;
|
||||
createdAt: string;
|
||||
|
||||
+4
-3
@@ -7,6 +7,7 @@ import { createNotification } from "@app/components/notifications";
|
||||
import { Button, Checkbox, Modal, ModalContent, Tooltip } from "@app/components/v2";
|
||||
import { useOrgPermission } from "@app/context";
|
||||
import { useUpgradePrivilegeSystem } from "@app/hooks/api";
|
||||
import { OrgMembershipRole } from "@app/helpers/roles";
|
||||
|
||||
const formSchema = z.object({
|
||||
isProjectPrivilegesUpdated: z.literal(true),
|
||||
@@ -21,7 +22,7 @@ type Props = {
|
||||
};
|
||||
|
||||
export const UpgradePrivilegeSystemModal = ({ isOpen, onOpenChange }: Props) => {
|
||||
const { membership } = useOrgPermission();
|
||||
const { hasOrgRole } = useOrgPermission();
|
||||
const [step, setStep] = useState<"info" | "upgrade">("info");
|
||||
|
||||
const {
|
||||
@@ -65,7 +66,7 @@ export const UpgradePrivilegeSystemModal = ({ isOpen, onOpenChange }: Props) =>
|
||||
setStep("info");
|
||||
};
|
||||
|
||||
const isAdmin = membership?.role === "admin";
|
||||
const isAdmin = hasOrgRole(OrgMembershipRole.Admin);
|
||||
|
||||
return (
|
||||
<Modal isOpen={isOpen} onOpenChange={handleClose}>
|
||||
@@ -274,7 +275,7 @@ export const UpgradePrivilegeSystemModal = ({ isOpen, onOpenChange }: Props) =>
|
||||
<Tooltip
|
||||
content={
|
||||
!isAdmin
|
||||
? `You cannot perform this upgrade because you are not an organization admin. (Your current role: ${membership?.role ?? "Unknown"})`
|
||||
? `You cannot perform this upgrade because you are not an organization admin.`
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
|
||||
+3
-3
@@ -4,12 +4,12 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { Button } from "@app/components/v2";
|
||||
import { useOrgPermission } from "@app/context";
|
||||
import { usePopUp } from "@app/hooks";
|
||||
import { ProjectMembershipRole } from "@app/hooks/api/roles/types";
|
||||
|
||||
import { SelectImportFromPlatformModal } from "./components/SelectImportFromPlatformModal";
|
||||
import { OrgMembershipRole } from "@app/helpers/roles";
|
||||
|
||||
export const ExternalMigrationsTab = () => {
|
||||
const { membership } = useOrgPermission();
|
||||
const { hasOrgRole } = useOrgPermission();
|
||||
|
||||
const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp(["selectImportPlatform"] as const);
|
||||
|
||||
@@ -41,7 +41,7 @@ export const ExternalMigrationsTab = () => {
|
||||
onClick={() => {
|
||||
handlePopUpOpen("selectImportPlatform");
|
||||
}}
|
||||
isDisabled={membership?.role !== ProjectMembershipRole.Admin}
|
||||
isDisabled={hasOrgRole(OrgMembershipRole.Admin)}
|
||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||
>
|
||||
Import
|
||||
|
||||
+3
-2
@@ -6,12 +6,13 @@ import { useOrganization, useOrgPermission } from "@app/context";
|
||||
import { useDeleteOrgById } from "@app/hooks/api";
|
||||
import { clearSession } from "@app/hooks/api/users/queries";
|
||||
import { usePopUp } from "@app/hooks/usePopUp";
|
||||
import { OrgMembershipRole } from "@app/helpers/roles";
|
||||
|
||||
export const OrgDeleteSection = () => {
|
||||
const navigate = useNavigate();
|
||||
const { currentOrg } = useOrganization();
|
||||
|
||||
const { membership } = useOrgPermission();
|
||||
const { hasOrgRole } = useOrgPermission();
|
||||
|
||||
const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp(["deleteOrg"] as const);
|
||||
|
||||
@@ -52,7 +53,7 @@ export const OrgDeleteSection = () => {
|
||||
variant="outline_bg"
|
||||
type="submit"
|
||||
onClick={() => handlePopUpOpen("deleteOrg")}
|
||||
isDisabled={Boolean(membership && membership.role !== "admin")}
|
||||
isDisabled={Boolean(!hasOrgRole(OrgMembershipRole.Admin))}
|
||||
>
|
||||
{`Delete ${currentOrg?.name}`}
|
||||
</Button>
|
||||
|
||||
+3
-2
@@ -3,14 +3,15 @@ import { useOrgPermission } from "@app/context";
|
||||
import { OrgDeleteSection } from "../OrgDeleteSection";
|
||||
import { OrgIncidentContactsSection } from "../OrgIncidentContactsSection";
|
||||
import { OrgNameChangeSection } from "../OrgNameChangeSection";
|
||||
import { OrgMembershipRole } from "@app/helpers/roles";
|
||||
|
||||
export const OrgGeneralTab = () => {
|
||||
const { membership } = useOrgPermission();
|
||||
const { hasOrgRole } = useOrgPermission();
|
||||
return (
|
||||
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-6">
|
||||
<OrgNameChangeSection />
|
||||
<OrgIncidentContactsSection />
|
||||
{membership && membership.role === "admin" && <OrgDeleteSection />}
|
||||
{hasOrgRole(OrgMembershipRole.Admin) && <OrgDeleteSection />}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
+2
-2
@@ -20,7 +20,7 @@ export const AuditLogsRetentionSection = () => {
|
||||
const { mutateAsync: updateAuditLogsRetention } = useUpdateWorkspaceAuditLogsRetention();
|
||||
|
||||
const { currentProject } = useProject();
|
||||
const { membership } = useProjectPermission();
|
||||
const { hasProjectRole } = useProjectPermission();
|
||||
const { subscription } = useSubscription();
|
||||
const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp(["upgradePlan"] as const);
|
||||
|
||||
@@ -82,7 +82,7 @@ export const AuditLogsRetentionSection = () => {
|
||||
return null;
|
||||
}
|
||||
|
||||
const isAdmin = membership.roles.includes(ProjectMembershipRole.Admin);
|
||||
const isAdmin = hasProjectRole(ProjectMembershipRole.Admin);
|
||||
return (
|
||||
<>
|
||||
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
||||
|
||||
+7
-5
@@ -10,7 +10,8 @@ import {
|
||||
ProjectPermissionSub,
|
||||
useOrganization,
|
||||
useProject,
|
||||
useProjectPermission
|
||||
useProjectPermission,
|
||||
useUser
|
||||
} from "@app/context";
|
||||
import { useToggle } from "@app/hooks";
|
||||
import { useDeleteWorkspace, useGetWorkspaceUsers, useLeaveProject } from "@app/hooks/api";
|
||||
@@ -24,8 +25,9 @@ export const DeleteProjectSection = () => {
|
||||
"leaveWorkspace"
|
||||
] as const);
|
||||
|
||||
const { user } = useUser();
|
||||
const { currentOrg } = useOrganization();
|
||||
const { hasProjectRole, membership } = useProjectPermission();
|
||||
const { hasProjectRole } = useProjectPermission();
|
||||
const { currentProject } = useProject();
|
||||
const [isDeleting, setIsDeleting] = useToggle();
|
||||
const [isLeaving, setIsLeaving] = useToggle();
|
||||
@@ -39,14 +41,14 @@ export const DeleteProjectSection = () => {
|
||||
const isNoAccessMember = hasProjectRole("no-access");
|
||||
|
||||
const isOnlyAdminMember = useMemo(() => {
|
||||
if (!members || !membership || !hasProjectRole("admin")) return false;
|
||||
if (!members || !hasProjectRole("admin")) return false;
|
||||
|
||||
const adminMembers = members.filter(
|
||||
(member) => member.roles.map((r) => r.role).includes("admin") && member.id !== membership.id // exclude the current user
|
||||
(member) => member.roles.map((r) => r.role).includes("admin") && member.user.id !== user.id // exclude the current user
|
||||
);
|
||||
|
||||
return !adminMembers.length;
|
||||
}, [members, membership]);
|
||||
}, [members, user]);
|
||||
|
||||
const handleDeleteWorkspaceSubmit = async () => {
|
||||
setIsDeleting.on();
|
||||
|
||||
+2
-2
@@ -38,7 +38,7 @@ export const SecretV2MigrationSection = () => {
|
||||
currentProject?.upgradeStatus === ProjectUpgradeStatus.InProgress ? 2000 : false
|
||||
}
|
||||
);
|
||||
const { membership } = useProjectPermission();
|
||||
const { hasProjectRole } = useProjectPermission();
|
||||
const migrateProjectToV3 = useMigrateProjectToV3();
|
||||
const { handleSubmit, control, reset } = useForm({ resolver: zodResolver(formSchema) });
|
||||
useEffect(() => {
|
||||
@@ -81,7 +81,7 @@ export const SecretV2MigrationSection = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const isAdmin = membership?.roles.includes(ProjectMembershipRole.Admin);
|
||||
const isAdmin = hasProjectRole(ProjectMembershipRole.Admin);
|
||||
return (
|
||||
<div className="mt-4 flex max-w-2xl flex-col rounded-lg border border-primary/50 bg-primary/10 px-6 py-5">
|
||||
{isUpgrading && (
|
||||
|
||||
+2
-2
@@ -6,7 +6,7 @@ import { ProjectMembershipRole } from "@app/hooks/api/roles/types";
|
||||
|
||||
export const BackfillSecretReferenceSecretion = () => {
|
||||
const { currentProject } = useProject();
|
||||
const { membership } = useProjectPermission();
|
||||
const { hasProjectRole } = useProjectPermission();
|
||||
const backfillSecretReferences = useBackfillSecretReference();
|
||||
|
||||
if (!currentProject) return null;
|
||||
@@ -21,7 +21,7 @@ export const BackfillSecretReferenceSecretion = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const isAdmin = membership.roles.includes(ProjectMembershipRole.Admin);
|
||||
const isAdmin = hasProjectRole(ProjectMembershipRole.Admin);
|
||||
return (
|
||||
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
||||
<div className="flex w-full items-center justify-between">
|
||||
|
||||
+2
-2
@@ -18,7 +18,7 @@ export const PointInTimeVersionLimitSection = () => {
|
||||
const { mutateAsync: updateProject } = useUpdateProject();
|
||||
|
||||
const { currentProject, projectId } = useProject();
|
||||
const { membership } = useProjectPermission();
|
||||
const { hasProjectRole } = useProjectPermission();
|
||||
|
||||
const {
|
||||
control,
|
||||
@@ -52,7 +52,7 @@ export const PointInTimeVersionLimitSection = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const isAdmin = membership.roles.includes(ProjectMembershipRole.Admin);
|
||||
const isAdmin = hasProjectRole(ProjectMembershipRole.Admin);
|
||||
return (
|
||||
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
||||
<div className="flex w-full items-center justify-between">
|
||||
|
||||
+2
-2
@@ -24,7 +24,7 @@ type TForm = z.infer<typeof formSchema>;
|
||||
|
||||
export const SecretDetectionIgnoreValuesSection = () => {
|
||||
const { currentProject } = useProject();
|
||||
const { membership } = useProjectPermission();
|
||||
const { hasProjectRole } = useProjectPermission();
|
||||
const { mutateAsync: updateProject } = useUpdateProject();
|
||||
|
||||
const {
|
||||
@@ -73,7 +73,7 @@ export const SecretDetectionIgnoreValuesSection = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const isAdmin = membership.roles.includes(ProjectMembershipRole.Admin);
|
||||
const isAdmin = hasProjectRole(ProjectMembershipRole.Admin);
|
||||
|
||||
if (!currentProject) return null;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user