mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
feat(rbac): added org permission context for pulling permission from api
This commit is contained in:
@@ -13,8 +13,10 @@ import {
|
||||
CreateRoleSchema,
|
||||
DeleteRoleSchema,
|
||||
GetRoleSchema,
|
||||
GetUserPermission,
|
||||
UpdateRoleSchema
|
||||
} from "../../validation";
|
||||
import { packRules } from "@casl/ability/extra";
|
||||
|
||||
export const createRole = async (req: Request, res: Response) => {
|
||||
const {
|
||||
@@ -160,3 +162,16 @@ export const getRoles = async (req: Request, res: Response) => {
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const getUserPermissions = async (req: Request, res: Response) => {
|
||||
const {
|
||||
params: { orgId }
|
||||
} = await validateRequest(GetUserPermission, req);
|
||||
const { permission } = await getUserOrgPermissions(req.user.id, orgId);
|
||||
|
||||
res.status(200).json({
|
||||
data: {
|
||||
permissions: packRules(permission.rules)
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -17,4 +17,11 @@ router.delete(
|
||||
|
||||
router.get("/", requireAuth({ acceptedAuthModes: [AuthMode.JWT] }), roleController.getRoles);
|
||||
|
||||
// get a user permissions in an org
|
||||
router.get(
|
||||
"/:orgId/permissions",
|
||||
requireAuth({ acceptedAuthModes: [AuthMode.JWT] }),
|
||||
roleController.getUserPermissions
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
||||
@@ -40,13 +40,19 @@ export const UpdateRoleSchema = z.object({
|
||||
|
||||
export const DeleteRoleSchema = z.object({
|
||||
params: z.object({
|
||||
id: z.string()
|
||||
id: z.string().trim()
|
||||
})
|
||||
});
|
||||
|
||||
export const GetRoleSchema = z.object({
|
||||
query: z.object({
|
||||
workspaceId: z.string().optional(),
|
||||
orgId: z.string()
|
||||
workspaceId: z.string().trim().optional(),
|
||||
orgId: z.string().trim()
|
||||
})
|
||||
});
|
||||
|
||||
export const GetUserPermission = z.object({
|
||||
params: z.object({
|
||||
orgId: z.string().trim()
|
||||
})
|
||||
});
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
import { createContext, ReactNode, useContext } from "react";
|
||||
|
||||
import { useGetUserOrgPermissions } from "@app/hooks/api";
|
||||
|
||||
import { useOrganization } from "../OrganizationContext";
|
||||
import { TPermission } from "./types";
|
||||
|
||||
type Props = {
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
const PermissionContext = createContext<null | TPermission>(null);
|
||||
|
||||
export const OrgPermissionProvider = ({ children }: Props): JSX.Element => {
|
||||
const { currentOrg } = useOrganization();
|
||||
const orgId = currentOrg?._id || "";
|
||||
const { data: permission, isLoading } = useGetUserOrgPermissions({ orgId });
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center w-screen h-screen bg-bunker-800">
|
||||
<img
|
||||
src="/images/loading/loading.gif"
|
||||
height={70}
|
||||
width={120}
|
||||
alt="infisical loading indicator"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!permission) {
|
||||
return (
|
||||
<div className="flex items-center justify-center w-screen h-screen bg-bunker-800">
|
||||
Failed to load user permissions
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return <PermissionContext.Provider value={permission}>{children}</PermissionContext.Provider>;
|
||||
};
|
||||
|
||||
export const useOrgPermission = () => {
|
||||
const ctx = useContext(PermissionContext);
|
||||
if (!ctx) {
|
||||
throw new Error("useOrgPermission to be used within <OrgPermissionProvider>");
|
||||
}
|
||||
|
||||
return ctx;
|
||||
};
|
||||
1
frontend/src/context/OrgPermissionContext/index.tsx
Normal file
1
frontend/src/context/OrgPermissionContext/index.tsx
Normal file
@@ -0,0 +1 @@
|
||||
export { OrgPermissionProvider, useOrgPermission } from "./OrgPermissionContext";
|
||||
36
frontend/src/context/OrgPermissionContext/types.ts
Normal file
36
frontend/src/context/OrgPermissionContext/types.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { MongoAbility } from "@casl/ability";
|
||||
|
||||
export enum GeneralPermissionActions {
|
||||
Read = "read",
|
||||
Create = "create",
|
||||
Edit = "edit",
|
||||
Delete = "delete"
|
||||
}
|
||||
|
||||
export enum WorkspacePermissionActions {
|
||||
Read = "read",
|
||||
Create = "create"
|
||||
}
|
||||
|
||||
export enum OrgPermissionSubjects {
|
||||
Workspace = "workspace",
|
||||
Role = "role",
|
||||
Member = "member",
|
||||
Settings = "settings",
|
||||
IncidentAccount = "incident-contact",
|
||||
Sso = "sso",
|
||||
Billing = "billing",
|
||||
SecretScanning = "secret-scanning"
|
||||
}
|
||||
|
||||
export type OrgPermissionSet =
|
||||
| [WorkspacePermissionActions, OrgPermissionSubjects.Workspace]
|
||||
| [GeneralPermissionActions, OrgPermissionSubjects.Role]
|
||||
| [GeneralPermissionActions, OrgPermissionSubjects.Member]
|
||||
| [GeneralPermissionActions, OrgPermissionSubjects.Settings]
|
||||
| [GeneralPermissionActions, OrgPermissionSubjects.IncidentAccount]
|
||||
| [GeneralPermissionActions, OrgPermissionSubjects.Sso]
|
||||
| [GeneralPermissionActions, OrgPermissionSubjects.SecretScanning]
|
||||
| [GeneralPermissionActions, OrgPermissionSubjects.Billing];
|
||||
|
||||
export type TPermission = MongoAbility<OrgPermissionSet>;
|
||||
@@ -1,5 +1,6 @@
|
||||
export { AuthProvider } from "./AuthContext";
|
||||
export { OrgProvider, useOrganization } from "./OrganizationContext";
|
||||
export { OrgPermissionProvider,useOrgPermission } from "./OrgPermissionContext";
|
||||
export { SubscriptionProvider, useSubscription } from "./SubscriptionContext";
|
||||
export { UserProvider, useUser } from "./UserContext";
|
||||
export { useWorkspace, WorkspaceProvider } from "./WorkspaceContext";
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export { useCreateRole, useDeleteRole,useUpdateRole } from "./mutation";
|
||||
export { useGetRoles } from "./queries";
|
||||
export { useCreateRole, useDeleteRole, useUpdateRole } from "./mutation";
|
||||
export { useGetRoles, useGetUserOrgPermissions } from "./queries";
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
import { createMongoAbility, MongoAbility, RawRuleOf } from "@casl/ability";
|
||||
import { PackRule, unpackRules } from "@casl/ability/extra";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
|
||||
import { apiRequest } from "@app/config/request";
|
||||
import { OrgPermissionSet } from "@app/context/OrgPermissionContext/types";
|
||||
|
||||
import { TGetRolesDTO, TRole } from "./types";
|
||||
import { TGetRolesDTO, TGetUserOrgPermissionsDTO, TRole } from "./types";
|
||||
|
||||
export const roleQueryKeys = {
|
||||
getRoles: ({ orgId, workspaceId }: TGetRolesDTO) => ["roles", { orgId, workspaceId }] as const
|
||||
getRoles: ({ orgId, workspaceId }: TGetRolesDTO) => ["roles", { orgId, workspaceId }] as const,
|
||||
getUserOrgPermissions: ({ orgId }: TGetUserOrgPermissionsDTO) =>
|
||||
["user-permissions", { orgId }] as const
|
||||
};
|
||||
|
||||
const getRoles = async ({ orgId, workspaceId }: TGetRolesDTO) => {
|
||||
@@ -25,3 +30,23 @@ export const useGetRoles = ({ orgId, workspaceId }: TGetRolesDTO) =>
|
||||
queryFn: () => getRoles({ orgId, workspaceId }),
|
||||
enabled: Boolean(orgId)
|
||||
});
|
||||
|
||||
const getUserOrgPermissions = async ({ orgId }: TGetUserOrgPermissionsDTO) => {
|
||||
const { data } = await apiRequest.get<{
|
||||
data: { permissions: PackRule<RawRuleOf<MongoAbility<OrgPermissionSet>>>[] };
|
||||
}>(`/api/v1/roles/${orgId}/permissions`, {});
|
||||
|
||||
return data.data.permissions;
|
||||
};
|
||||
|
||||
export const useGetUserOrgPermissions = ({ orgId }: TGetUserOrgPermissionsDTO) =>
|
||||
useQuery({
|
||||
queryKey: roleQueryKeys.getUserOrgPermissions({ orgId }),
|
||||
queryFn: () => getUserOrgPermissions({ orgId }),
|
||||
enabled: Boolean(orgId),
|
||||
select: (data) => {
|
||||
const rule = unpackRules<RawRuleOf<MongoAbility<OrgPermissionSet>>>(data);
|
||||
const ability = createMongoAbility<OrgPermissionSet>(rule);
|
||||
return ability;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -49,3 +49,7 @@ export type TDeleteRoleDTO = {
|
||||
id: string;
|
||||
workspaceId?: string;
|
||||
};
|
||||
|
||||
export type TGetUserOrgPermissionsDTO = {
|
||||
orgId: string;
|
||||
};
|
||||
|
||||
@@ -17,6 +17,7 @@ import { TooltipProvider } from "@app/components/v2";
|
||||
import { publicPaths } from "@app/const";
|
||||
import {
|
||||
AuthProvider,
|
||||
OrgPermissionProvider,
|
||||
OrgProvider,
|
||||
SubscriptionProvider,
|
||||
UserProvider,
|
||||
@@ -95,17 +96,19 @@ const App = ({ Component, pageProps, ...appProps }: NextAppProp): JSX.Element =>
|
||||
<TooltipProvider>
|
||||
<AuthProvider>
|
||||
<OrgProvider>
|
||||
<WorkspaceProvider>
|
||||
<SubscriptionProvider>
|
||||
<UserProvider>
|
||||
<NotificationProvider>
|
||||
<AppLayout>
|
||||
<Component {...pageProps} />
|
||||
</AppLayout>
|
||||
</NotificationProvider>
|
||||
</UserProvider>
|
||||
</SubscriptionProvider>
|
||||
</WorkspaceProvider>
|
||||
<OrgPermissionProvider>
|
||||
<WorkspaceProvider>
|
||||
<SubscriptionProvider>
|
||||
<UserProvider>
|
||||
<NotificationProvider>
|
||||
<AppLayout>
|
||||
<Component {...pageProps} />
|
||||
</AppLayout>
|
||||
</NotificationProvider>
|
||||
</UserProvider>
|
||||
</SubscriptionProvider>
|
||||
</WorkspaceProvider>
|
||||
</OrgPermissionProvider>
|
||||
</OrgProvider>
|
||||
</AuthProvider>
|
||||
</TooltipProvider>
|
||||
|
||||
Reference in New Issue
Block a user