feat(rbac): added org permission context for pulling permission from api

This commit is contained in:
Akhil Mohan
2023-09-08 21:21:43 +05:30
parent e342e88499
commit 34fb7be1c4
11 changed files with 166 additions and 18 deletions
@@ -13,8 +13,10 @@ import {
CreateRoleSchema, CreateRoleSchema,
DeleteRoleSchema, DeleteRoleSchema,
GetRoleSchema, GetRoleSchema,
GetUserPermission,
UpdateRoleSchema UpdateRoleSchema
} from "../../validation"; } from "../../validation";
import { packRules } from "@casl/ability/extra";
export const createRole = async (req: Request, res: Response) => { export const createRole = async (req: Request, res: Response) => {
const { 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)
}
});
};
+7
View File
@@ -17,4 +17,11 @@ router.delete(
router.get("/", requireAuth({ acceptedAuthModes: [AuthMode.JWT] }), roleController.getRoles); 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; export default router;
+9 -3
View File
@@ -40,13 +40,19 @@ export const UpdateRoleSchema = z.object({
export const DeleteRoleSchema = z.object({ export const DeleteRoleSchema = z.object({
params: z.object({ params: z.object({
id: z.string() id: z.string().trim()
}) })
}); });
export const GetRoleSchema = z.object({ export const GetRoleSchema = z.object({
query: z.object({ query: z.object({
workspaceId: z.string().optional(), workspaceId: z.string().trim().optional(),
orgId: z.string() 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;
};
@@ -0,0 +1 @@
export { OrgPermissionProvider, useOrgPermission } from "./OrgPermissionContext";
@@ -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
View File
@@ -1,5 +1,6 @@
export { AuthProvider } from "./AuthContext"; export { AuthProvider } from "./AuthContext";
export { OrgProvider, useOrganization } from "./OrganizationContext"; export { OrgProvider, useOrganization } from "./OrganizationContext";
export { OrgPermissionProvider,useOrgPermission } from "./OrgPermissionContext";
export { SubscriptionProvider, useSubscription } from "./SubscriptionContext"; export { SubscriptionProvider, useSubscription } from "./SubscriptionContext";
export { UserProvider, useUser } from "./UserContext"; export { UserProvider, useUser } from "./UserContext";
export { useWorkspace, WorkspaceProvider } from "./WorkspaceContext"; export { useWorkspace, WorkspaceProvider } from "./WorkspaceContext";
+2 -2
View File
@@ -1,2 +1,2 @@
export { useCreateRole, useDeleteRole,useUpdateRole } from "./mutation"; export { useCreateRole, useDeleteRole, useUpdateRole } from "./mutation";
export { useGetRoles } from "./queries"; export { useGetRoles, useGetUserOrgPermissions } from "./queries";
+27 -2
View File
@@ -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 { useQuery } from "@tanstack/react-query";
import { apiRequest } from "@app/config/request"; 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 = { 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) => { const getRoles = async ({ orgId, workspaceId }: TGetRolesDTO) => {
@@ -25,3 +30,23 @@ export const useGetRoles = ({ orgId, workspaceId }: TGetRolesDTO) =>
queryFn: () => getRoles({ orgId, workspaceId }), queryFn: () => getRoles({ orgId, workspaceId }),
enabled: Boolean(orgId) 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;
}
});
+4
View File
@@ -49,3 +49,7 @@ export type TDeleteRoleDTO = {
id: string; id: string;
workspaceId?: string; workspaceId?: string;
}; };
export type TGetUserOrgPermissionsDTO = {
orgId: string;
};
+14 -11
View File
@@ -17,6 +17,7 @@ import { TooltipProvider } from "@app/components/v2";
import { publicPaths } from "@app/const"; import { publicPaths } from "@app/const";
import { import {
AuthProvider, AuthProvider,
OrgPermissionProvider,
OrgProvider, OrgProvider,
SubscriptionProvider, SubscriptionProvider,
UserProvider, UserProvider,
@@ -95,17 +96,19 @@ const App = ({ Component, pageProps, ...appProps }: NextAppProp): JSX.Element =>
<TooltipProvider> <TooltipProvider>
<AuthProvider> <AuthProvider>
<OrgProvider> <OrgProvider>
<WorkspaceProvider> <OrgPermissionProvider>
<SubscriptionProvider> <WorkspaceProvider>
<UserProvider> <SubscriptionProvider>
<NotificationProvider> <UserProvider>
<AppLayout> <NotificationProvider>
<Component {...pageProps} /> <AppLayout>
</AppLayout> <Component {...pageProps} />
</NotificationProvider> </AppLayout>
</UserProvider> </NotificationProvider>
</SubscriptionProvider> </UserProvider>
</WorkspaceProvider> </SubscriptionProvider>
</WorkspaceProvider>
</OrgPermissionProvider>
</OrgProvider> </OrgProvider>
</AuthProvider> </AuthProvider>
</TooltipProvider> </TooltipProvider>