Skip to content

Commit

Permalink
Add Permission tab and ability to limit dashboard view
Browse files Browse the repository at this point in the history
  • Loading branch information
heisbrot committed Mar 25, 2024
1 parent efe6b82 commit 26312e4
Show file tree
Hide file tree
Showing 7 changed files with 147 additions and 10 deletions.
8 changes: 6 additions & 2 deletions src/app/(dashboard)/peers/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,15 @@ import { SetupModalContent } from "@/modules/setup-netbird-modal/SetupModal";
const PeersTable = lazy(() => import("@/modules/peers/PeersTable"));

export default function Peers() {
const { isUser } = useLoggedInUser();
const { permission } = useLoggedInUser();

return (
<PageContainer>
<PeersView />
{permission?.dashboard_view === "blocked" ? (
<PeersDefaultView />
) : (
<PeersView />
)}
</PageContainer>
);
}
Expand Down
13 changes: 12 additions & 1 deletion src/app/(dashboard)/settings/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,20 @@

import { RestrictedAccess } from "@components/ui/RestrictedAccess";
import { VerticalTabs } from "@components/VerticalTabs";
import { AlertOctagonIcon, FolderGit2Icon, ShieldIcon } from "lucide-react";
import {
AlertOctagonIcon,
FolderGit2Icon,
LockIcon,
ShieldIcon,
} from "lucide-react";
import React, { useState } from "react";
import { useLoggedInUser } from "@/contexts/UsersProvider";
import PageContainer from "@/layouts/PageContainer";
import { useAccount } from "@/modules/account/useAccount";
import AuthenticationTab from "@/modules/settings/AuthenticationTab";
import DangerZoneTab from "@/modules/settings/DangerZoneTab";
import GroupsTab from "@/modules/settings/GroupsTab";
import PermissionsTab from "@/modules/settings/PermissionsTab";

export default function NetBirdSettings() {
const [tab, setTab] = useState("authentication");
Expand All @@ -28,6 +34,10 @@ export default function NetBirdSettings() {
<FolderGit2Icon size={14} />
Groups
</VerticalTabs.Trigger>
<VerticalTabs.Trigger value="permissions">
<LockIcon size={14} />
Permissions
</VerticalTabs.Trigger>
<VerticalTabs.Trigger value="danger-zone" disabled={!isOwner}>
<AlertOctagonIcon size={14} />
Danger zone
Expand All @@ -36,6 +46,7 @@ export default function NetBirdSettings() {
<RestrictedAccess page={"Settings"}>
<div className={"border-l border-nb-gray-930 w-full"}>
{account && <AuthenticationTab account={account} />}
{account && <PermissionsTab account={account} />}
{account && <GroupsTab account={account} />}
{account && <DangerZoneTab account={account} />}
</div>
Expand Down
8 changes: 6 additions & 2 deletions src/contexts/GroupsProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,13 @@ const GroupContext = React.createContext(

export default function GroupsProvider({ children }: Props) {
const path = usePathname();
const { isUser } = useLoggedInUser();
const { permission } = useLoggedInUser();

return <GroupsProviderContent>{children}</GroupsProviderContent>;
return path === "/peers" && permission.dashboard_view == "blocked" ? (
<>{children}</>
) : (
<GroupsProviderContent>{children}</GroupsProviderContent>
);
}

export function GroupsProviderContent({ children }: Props) {
Expand Down
17 changes: 16 additions & 1 deletion src/contexts/UsersProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import FullScreenLoading from "@components/ui/FullScreenLoading";
import useFetchApi from "@utils/api";
import React, { useMemo } from "react";
import { Permission } from "@/interfaces/Permission";
import { User } from "@/interfaces/User";

type Props = {
Expand Down Expand Up @@ -43,5 +44,19 @@ export const useLoggedInUser = () => {
const isAdmin = loggedInUser ? loggedInUser?.role === "admin" : false;
const isUser = !isOwner && !isAdmin;
const isOwnerOrAdmin = isOwner || isAdmin;
return { loggedInUser, isOwner, isAdmin, isUser, isOwnerOrAdmin } as const;

const permission = useMemo(() => {
return {
dashboard_view: loggedInUser?.permissions.dashboard_view || "blocked",
} as Permission;
}, [loggedInUser]);

return {
loggedInUser,
isOwner,
isAdmin,
isUser,
isOwnerOrAdmin,
permission,
} as const;
};
6 changes: 4 additions & 2 deletions src/layouts/DashboardLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ function DashboardPageContent({ children }: { children: React.ReactNode }) {
const { mobileNavOpen, toggleMobileNav } = useApplicationContext();
const isSm = useIsSm();
const isXs = useIsXs();
const { isUser } = useLoggedInUser();
const { permission } = useLoggedInUser();

const navOpenPageWidth = isSm ? "50%" : isXs ? "65%" : "80%";
const { bannerHeight } = useAnnouncement();
Expand Down Expand Up @@ -154,7 +154,9 @@ function DashboardPageContent({ children }: { children: React.ReactNode }) {
height: `calc(100vh - ${headerHeight + bannerHeight}px)`,
}}
>
<Navigation hideOnMobile />
{permission.dashboard_view !== "blocked" && (
<Navigation hideOnMobile />
)}
{children}
</div>
</motion.div>
Expand Down
5 changes: 3 additions & 2 deletions src/layouts/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export default function NavbarWithDropdown() {

const { toggleMobileNav } = useApplicationContext();
const { bannerHeight } = useAnnouncement();
const { isUser } = useLoggedInUser();
const { permission } = useLoggedInUser();

return (
<>
Expand All @@ -62,7 +62,8 @@ export default function NavbarWithDropdown() {
<Button
className={cn(
"!px-3 md:hidden",
isUser && "opacity-0 pointer-events-none",
permission.dashboard_view == "blocked" &&
"opacity-0 pointer-events-none",
)}
variant={"default-outline"}
onClick={toggleMobileNav}
Expand Down
100 changes: 100 additions & 0 deletions src/modules/settings/PermissionsTab.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import Breadcrumbs from "@components/Breadcrumbs";
import Button from "@components/Button";
import FancyToggleSwitch from "@components/FancyToggleSwitch";
import { notify } from "@components/Notification";
import * as Tabs from "@radix-ui/react-tabs";
import { useApiCall } from "@utils/api";
import { GaugeIcon, LockIcon } from "lucide-react";
import React, { useState } from "react";
import { useSWRConfig } from "swr";
import SettingsIcon from "@/assets/icons/SettingsIcon";
import { useHasChanges } from "@/hooks/useHasChanges";
import { Account } from "@/interfaces/Account";

type Props = {
account: Account;
};

export default function PermissionsTab({ account }: Props) {
const { mutate } = useSWRConfig();
const saveRequest = useApiCall<Account>("/accounts/" + account.id);

const [userViewBlocked, setUserViewBlocked] = useState<boolean>(
account?.settings.regular_users_view_blocked ?? false,
);

const { hasChanges, updateRef } = useHasChanges([userViewBlocked]);

const saveChanges = async () => {
notify({
title: "Permission Settings",
description: "Permissions were updated successfully.",
promise: saveRequest
.put({
id: account.id,
settings: {
regular_users_view_blocked: userViewBlocked,
groups_propagation_enabled:
account.settings?.groups_propagation_enabled,
peer_login_expiration_enabled:
account.settings?.peer_login_expiration_enabled,
peer_login_expiration: account.settings?.peer_login_expiration,
jwt_groups_enabled: account.settings?.jwt_groups_enabled,
jwt_groups_claim_name: account.settings?.jwt_groups_claim_name,
jwt_allow_groups: account.settings?.jwt_allow_groups,
},
})
.then(() => {
mutate("/accounts");
updateRef([userViewBlocked]);
}),
loadingMessage: "Updating permissions...",
});
};

return (
<Tabs.Content value={"permissions"} className={"w-full"}>
<div className={"p-default py-6 max-w-xl"}>
<Breadcrumbs>
<Breadcrumbs.Item
href={"/settings"}
label={"Settings"}
icon={<SettingsIcon size={13} />}
/>
<Breadcrumbs.Item
href={"/settings?tab=permissions"}
label={"Permissions"}
icon={<LockIcon size={14} />}
active
/>
</Breadcrumbs>
<div className={"flex items-start justify-between"}>
<h1>Permissions</h1>
<Button
variant={"primary"}
disabled={!hasChanges}
onClick={saveChanges}
>
Save Changes
</Button>
</div>

<div className={"flex flex-col gap-6 mt-8 mb-3"}>
<FancyToggleSwitch
value={userViewBlocked}
onChange={setUserViewBlocked}
label={
<>
<GaugeIcon size={15} />
Restrict dashboard for regular users
</>
}
helpText={
"Access to the dashboard will be limited and regular users will not be able to view any peers."
}
/>
</div>
</div>
</Tabs.Content>
);
}

0 comments on commit 26312e4

Please sign in to comment.