mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-05 13:26:31 +00:00
feat: added hoc, resolved dropdown type issue
This commit is contained in:
@@ -14,7 +14,7 @@ export type DropdownMenuTriggerProps = DropdownMenuPrimitive.DropdownMenuTrigger
|
|||||||
export const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
|
export const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
|
||||||
|
|
||||||
// item container
|
// item container
|
||||||
export type DropdownMenuContentProps = DropdownMenuPrimitive.MenuContentProps;
|
export type DropdownMenuContentProps = DropdownMenuPrimitive.DropdownMenuContentProps;
|
||||||
export const DropdownMenuContent = forwardRef<HTMLDivElement, DropdownMenuContentProps>(
|
export const DropdownMenuContent = forwardRef<HTMLDivElement, DropdownMenuContentProps>(
|
||||||
({ children, className, ...props }, forwardedRef) => {
|
({ children, className, ...props }, forwardedRef) => {
|
||||||
return (
|
return (
|
||||||
@@ -38,7 +38,7 @@ export const DropdownMenuContent = forwardRef<HTMLDivElement, DropdownMenuConten
|
|||||||
DropdownMenuContent.displayName = "DropdownMenuContent";
|
DropdownMenuContent.displayName = "DropdownMenuContent";
|
||||||
|
|
||||||
// item container
|
// item container
|
||||||
export type DropdownSubMenuContentProps = DropdownMenuPrimitive.MenuSubContentProps;
|
export type DropdownSubMenuContentProps = DropdownMenuPrimitive.DropdownMenuSubContentProps;
|
||||||
export const DropdownSubMenuContent = forwardRef<HTMLDivElement, DropdownSubMenuContentProps>(
|
export const DropdownSubMenuContent = forwardRef<HTMLDivElement, DropdownSubMenuContentProps>(
|
||||||
({ children, className, ...props }, forwardedRef) => {
|
({ children, className, ...props }, forwardedRef) => {
|
||||||
return (
|
return (
|
||||||
@@ -62,7 +62,7 @@ export const DropdownSubMenuContent = forwardRef<HTMLDivElement, DropdownSubMenu
|
|||||||
DropdownSubMenuContent.displayName = "DropdownMenuContent";
|
DropdownSubMenuContent.displayName = "DropdownMenuContent";
|
||||||
|
|
||||||
// item label component
|
// item label component
|
||||||
export type DropdownLabelProps = DropdownMenuPrimitive.MenuLabelProps;
|
export type DropdownLabelProps = DropdownMenuPrimitive.DropdownMenuLabelProps;
|
||||||
export const DropdownMenuLabel = ({ className, ...props }: DropdownLabelProps) => (
|
export const DropdownMenuLabel = ({ className, ...props }: DropdownLabelProps) => (
|
||||||
<DropdownMenuPrimitive.Label
|
<DropdownMenuPrimitive.Label
|
||||||
{...props}
|
{...props}
|
||||||
@@ -72,7 +72,7 @@ export const DropdownMenuLabel = ({ className, ...props }: DropdownLabelProps) =
|
|||||||
|
|
||||||
// dropdown items
|
// dropdown items
|
||||||
export type DropdownMenuItemProps<T extends ElementType> =
|
export type DropdownMenuItemProps<T extends ElementType> =
|
||||||
DropdownMenuPrimitive.MenuContentProps & {
|
DropdownMenuPrimitive.DropdownMenuContentProps & {
|
||||||
icon?: ReactNode;
|
icon?: ReactNode;
|
||||||
as?: T;
|
as?: T;
|
||||||
inputRef?: Ref<T>;
|
inputRef?: Ref<T>;
|
||||||
|
|||||||
@@ -50,10 +50,10 @@ export const OrgPermissionProvider = ({ children }: Props): JSX.Element => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const useOrgPermission = () => {
|
export const useOrgPermission = () => {
|
||||||
const { permission } = useRouteContext({
|
const ctx = useRouteContext({
|
||||||
from: "/_authenticate/_org_details",
|
from: "/_authenticate/_org_details",
|
||||||
select: (el) => el.orgPermission
|
select: (el) => el.orgPermission
|
||||||
});
|
});
|
||||||
|
|
||||||
return permission;
|
return ctx;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export { withPermission } from "./withPermission";
|
||||||
|
export { withProjectPermission } from "./withProjectPermission";
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { withPermission } from "./withPermission";
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
import { ComponentType } from "react";
|
||||||
|
import { Abilities, AbilityTuple, Generics, SubjectType } from "@casl/ability";
|
||||||
|
import { faLock } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
import { twMerge } from "tailwind-merge";
|
||||||
|
|
||||||
|
import { TOrgPermission, useOrgPermission } from "@app/context";
|
||||||
|
|
||||||
|
type Props<T extends Abilities> = (T extends AbilityTuple
|
||||||
|
? {
|
||||||
|
action: T[0];
|
||||||
|
subject: Extract<T[1], SubjectType>;
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
action: string;
|
||||||
|
subject: string;
|
||||||
|
}) & { className?: string; containerClassName?: string };
|
||||||
|
|
||||||
|
export const withPermission = <T extends object, J extends TOrgPermission>(
|
||||||
|
Component: ComponentType<T>,
|
||||||
|
{ action, subject, className, containerClassName }: Props<Generics<J>["abilities"]>
|
||||||
|
) => {
|
||||||
|
const HOC = (hocProps: T) => {
|
||||||
|
const { permission } = useOrgPermission();
|
||||||
|
|
||||||
|
// akhilmhdh: Set as any due to casl/react ts type bug
|
||||||
|
// REASON: casl due to its type checking can't seem to union even if union intersection is applied
|
||||||
|
if (permission.cannot(action as any, subject)) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={twMerge(
|
||||||
|
"container mx-auto flex h-full items-center justify-center",
|
||||||
|
containerClassName
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={twMerge(
|
||||||
|
"flex items-end space-x-12 rounded-md bg-mineshaft-800 p-16 text-bunker-300",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<FontAwesomeIcon icon={faLock} size="6x" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div className="mb-2 text-4xl font-medium">Access Restricted</div>
|
||||||
|
<div className="text-sm">
|
||||||
|
Your role has limited permissions, please <br /> contact your admin to gain access
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return <Component {...hocProps} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
HOC.displayName = "WithPermission";
|
||||||
|
return HOC;
|
||||||
|
};
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { withProjectPermission } from "./withProjectPermission";
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
import { ComponentType } from "react";
|
||||||
|
import { AbilityTuple } from "@casl/ability";
|
||||||
|
import { faLock } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
import { twMerge } from "tailwind-merge";
|
||||||
|
|
||||||
|
import { useProjectPermission } from "@app/context";
|
||||||
|
import { ProjectPermissionSet } from "@app/context/ProjectPermissionContext";
|
||||||
|
|
||||||
|
type Props<T extends AbilityTuple> = {
|
||||||
|
className?: string;
|
||||||
|
containerClassName?: string;
|
||||||
|
action: T[0];
|
||||||
|
subject: T[1];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const withProjectPermission = <T extends object>(
|
||||||
|
Component: ComponentType<Omit<Props<ProjectPermissionSet>, "action" | "subject"> & T>,
|
||||||
|
{ action, subject, className, containerClassName }: Props<ProjectPermissionSet>
|
||||||
|
) => {
|
||||||
|
const HOC = (hocProps: Omit<Props<ProjectPermissionSet>, "action" | "subject"> & T) => {
|
||||||
|
const { permission } = useProjectPermission();
|
||||||
|
|
||||||
|
// akhilmhdh: Set as any due to casl/react ts type bug
|
||||||
|
// REASON: casl due to its type checking can't seem to union even if union intersection is applied
|
||||||
|
if (permission.cannot(action as any, subject as any)) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={twMerge(
|
||||||
|
"container mx-auto flex h-full items-center justify-center",
|
||||||
|
containerClassName
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={twMerge(
|
||||||
|
"flex items-end space-x-12 rounded-md bg-mineshaft-800 p-16 text-bunker-300",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<FontAwesomeIcon icon={faLock} size="6x" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div className="mb-2 text-4xl font-medium">Permission Denied</div>
|
||||||
|
<div className="text-sm">
|
||||||
|
You do not have permission to this page. <br /> Kindly contact your organization
|
||||||
|
administrator
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return <Component {...hocProps} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
HOC.displayName = "WithProjectPermission";
|
||||||
|
return HOC;
|
||||||
|
};
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
export enum TabSections {
|
||||||
|
Member = "members",
|
||||||
|
Groups = "groups",
|
||||||
|
Roles = "roles",
|
||||||
|
Identities = "identities"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user