mirror of
https://github.com/netbirdio/dashboard.git
synced 2026-01-26 01:21:04 +00:00
Add setting to change dashboard view for regular users (#362)
This commit is contained in:
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,12 @@
|
||||
|
||||
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";
|
||||
@@ -10,6 +15,7 @@ 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");
|
||||
@@ -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
|
||||
@@ -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>
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 = {
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -10,5 +10,6 @@ export interface Account {
|
||||
jwt_groups_enabled: boolean;
|
||||
jwt_groups_claim_name: string;
|
||||
jwt_allow_groups: string[];
|
||||
regular_users_view_blocked: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
3
src/interfaces/Permission.ts
Normal file
3
src/interfaces/Permission.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export interface Permission {
|
||||
dashboard_view: "limited" | "full" | "blocked";
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
import { Permission } from "@/interfaces/Permission";
|
||||
|
||||
export interface User {
|
||||
id: string;
|
||||
email?: string;
|
||||
@@ -9,6 +11,7 @@ export interface User {
|
||||
is_service_user?: boolean;
|
||||
is_blocked?: boolean;
|
||||
last_login?: Date;
|
||||
permissions: Permission;
|
||||
}
|
||||
|
||||
export enum Role {
|
||||
|
||||
@@ -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();
|
||||
@@ -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>
|
||||
|
||||
@@ -40,7 +40,7 @@ export default function NavbarWithDropdown() {
|
||||
|
||||
const { toggleMobileNav } = useApplicationContext();
|
||||
const { bannerHeight } = useAnnouncement();
|
||||
const { isUser } = useLoggedInUser();
|
||||
const { permission } = useLoggedInUser();
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -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}
|
||||
|
||||
100
src/modules/settings/PermissionsTab.tsx
Normal file
100
src/modules/settings/PermissionsTab.tsx
Normal file
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -100,7 +100,11 @@ export function ServiceUserModalContent({ onSuccess }: ModalProps) {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<UserRoleSelector value={role as Role} onChange={setRole} />
|
||||
<UserRoleSelector
|
||||
value={role as Role}
|
||||
onChange={setRole}
|
||||
hideOwner={true}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user