Compare commits

...
Author SHA1 Message Date
Eduard Gert 7c7f0a0f10 Add network range (#482) 2025-08-13 14:18:59 +02:00
braginini 76541c701c Update LICENSE 2025-08-05 11:19:13 +02:00
Eduard Gert d2046fee21 Add resource search, unidirectional policies for all/icmp and dns0 template (#479) 2025-07-30 17:01:49 +02:00
Eduard Gert 8e2cbe1d2a Add support for port ranges (#475) 2025-06-20 10:26:53 +02:00
Eduard Gert 8a08583225 Do not redirect on same page (#471) 2025-06-05 19:30:25 +02:00
Eduard GertandViktor Liu 1defac4e34 Update wording for dns domain, macOS and Windows install steps (#470)
* Update wording for dns domain, macOS and Windows install steps

* Update src/modules/settings/NetworkSettingsTab.tsx

Co-authored-by: Viktor Liu <17948409+lixmal@users.noreply.github.com>

---------

Co-authored-by: Viktor Liu <17948409+lixmal@users.noreply.github.com>
2025-06-05 13:16:42 +02:00
Eduard Gert fa68f98cd0 Remove permission for add peer button (#469) 2025-06-05 13:12:38 +02:00
Eduard Gert 3f6e4c4e4f Add lazy connection setting (#465) 2025-06-04 11:54:18 +02:00
Eduard GertandMaycon Santos 0e2661caea Merge cloud changes to public (#462)
* Add changes from dashboard cloud

* Add changes from dashboard cloud

* Update next.js version

* Small formatting changes

* remove unknown permission check

---------

Co-authored-by: Maycon Santos <mlsmaycon@gmail.com>
2025-05-05 15:30:28 +02:00
218 changed files with 8733 additions and 2995 deletions

No files matched your search

+655 -7
View File
File diff suppressed because it is too large. Load diff
+2 -1
View File
@@ -12,5 +12,6 @@
"tokenSource": "$NETBIRD_TOKEN_SOURCE",
"dragQueryParams": "$NETBIRD_DRAG_QUERY_PARAMS",
"hotjarTrackID": "$NETBIRD_HOTJAR_TRACK_ID",
"googleAnalyticsID": "$NETBIRD_GOOGLE_ANALYTICS_ID"
"googleAnalyticsID": "$NETBIRD_GOOGLE_ANALYTICS_ID",
"googleTagManagerID": "$NETBIRD_GOOGLE_TAG_MANAGER_ID"
}
+2 -1
View File
@@ -58,13 +58,14 @@ export NETBIRD_MGMT_API_ENDPOINT=$(echo $NETBIRD_MGMT_API_ENDPOINT | sed -E 's/(
export NETBIRD_MGMT_GRPC_API_ENDPOINT=${NETBIRD_MGMT_GRPC_API_ENDPOINT}
export NETBIRD_HOTJAR_TRACK_ID=${NETBIRD_HOTJAR_TRACK_ID}
export NETBIRD_GOOGLE_ANALYTICS_ID=${NETBIRD_GOOGLE_ANALYTICS_ID}
export NETBIRD_GOOGLE_TAG_MANAGER_ID=${NETBIRD_GOOGLE_TAG_MANAGER_ID}
export NETBIRD_TOKEN_SOURCE=${NETBIRD_TOKEN_SOURCE:-accessToken}
export NETBIRD_DRAG_QUERY_PARAMS=${NETBIRD_DRAG_QUERY_PARAMS:-false}
echo "NetBird latest version: ${NETBIRD_LATEST_VERSION}"
# replace ENVs in the config
ENV_STR="\$\$USE_AUTH0 \$\$AUTH_AUDIENCE \$\$AUTH_AUTHORITY \$\$AUTH_CLIENT_ID \$\$AUTH_CLIENT_SECRET \$\$AUTH_SUPPORTED_SCOPES \$\$NETBIRD_MGMT_API_ENDPOINT \$\$NETBIRD_MGMT_GRPC_API_ENDPOINT \$\$NETBIRD_HOTJAR_TRACK_ID \$\$NETBIRD_GOOGLE_ANALYTICS_ID \$\$AUTH_REDIRECT_URI \$\$AUTH_SILENT_REDIRECT_URI \$\$NETBIRD_TOKEN_SOURCE \$\$NETBIRD_DRAG_QUERY_PARAMS"
ENV_STR="\$\$USE_AUTH0 \$\$AUTH_AUDIENCE \$\$AUTH_AUTHORITY \$\$AUTH_CLIENT_ID \$\$AUTH_CLIENT_SECRET \$\$AUTH_SUPPORTED_SCOPES \$\$NETBIRD_MGMT_API_ENDPOINT \$\$NETBIRD_MGMT_GRPC_API_ENDPOINT \$\$NETBIRD_HOTJAR_TRACK_ID \$\$NETBIRD_GOOGLE_ANALYTICS_ID \$\$NETBIRD_GOOGLE_TAG_MANAGER_ID \$\$AUTH_REDIRECT_URI \$\$AUTH_SILENT_REDIRECT_URI \$\$NETBIRD_TOKEN_SOURCE \$\$NETBIRD_DRAG_QUERY_PARAMS"
OIDC_TRUSTED_DOMAINS="/usr/share/nginx/html/OidcTrustedDomains.js"
envsubst "$ENV_STR" < "$OIDC_TRUSTED_DOMAINS".tmpl > "$OIDC_TRUSTED_DOMAINS"
+912 -293
View File
File diff suppressed because it is too large. Load diff
+12 -5
View File
@@ -41,6 +41,8 @@
"@types/react-dom": "^18",
"@types/react-window": "^1.8.8",
"autoprefixer": "^10",
"chart.js": "^4.4.8",
"chroma-js": "^3.1.2",
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
"cmdk": "^0.2.0",
@@ -48,21 +50,21 @@
"date-fns": "^2.30.0",
"dayjs": "^1.11.10",
"eslint": "^8",
"eslint-config-next": "13.5.5",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-simple-import-sort": "^10.0.0",
"flowbite": "^1.8.1",
"flowbite-react": "^0.6.4",
"framer-motion": "^10.16.4",
"ip-cidr": "^3.1.0",
"js-cookie": "^3.0.5",
"lodash": "^4.17.21",
"lucide-react": "^0.460.0",
"next": "13.5.7",
"lucide-react": "^0.481.0",
"next": "^14.2.28",
"next-themes": "^0.2.1",
"punycode": "^2.3.1",
"react": "^18",
"react": "^18.3.1",
"react-day-picker": "^8.9.1",
"react-dom": "^18",
"react-dom": "^18.3.1",
"react-ga4": "^2.1.0",
"react-hot-toast": "^2.4.1",
"react-hotjar": "^6.2.0",
@@ -74,9 +76,14 @@
"swr": "^2.2.4",
"tailwind-merge": "^1.14.0",
"tailwindcss-animate": "^1.0.7",
"timescape": "^0.7.1",
"typescript": "^5"
},
"devDependencies": {
"@faker-js/faker": "^9.5.1",
"@types/chroma-js": "^3.1.1",
"@types/js-cookie": "^3.0.6",
"eslint-config-next": "^14.2.28",
"cypress": "^13.13.0",
"postcss": "^8",
"prettier": "3.0.3",
@@ -0,0 +1,10 @@
"use client";
import FullScreenLoading from "@components/ui/FullScreenLoading";
import { useRedirect } from "@hooks/useRedirect";
import React from "react";
export default function Redirect() {
useRedirect("/events/audit");
return <FullScreenLoading height={"auto"} />;
}
+7 -1
View File
@@ -11,6 +11,7 @@ import { ExternalLinkIcon } from "lucide-react";
import React, { lazy, Suspense } from "react";
import AccessControlIcon from "@/assets/icons/AccessControlIcon";
import GroupsProvider from "@/contexts/GroupsProvider";
import { usePermissions } from "@/contexts/PermissionsProvider";
import PoliciesProvider from "@/contexts/PoliciesProvider";
import { Policy } from "@/interfaces/Policy";
import PageContainer from "@/layouts/PageContainer";
@@ -19,6 +20,8 @@ const AccessControlTable = lazy(
() => import("@/modules/access-control/table/AccessControlTable"),
);
export default function AccessControlPage() {
const { permission } = usePermissions();
const { data: policies, isLoading } = useFetchApi<Policy[]>("/policies");
const { ref: headingRef, portalTarget } =
@@ -53,7 +56,10 @@ export default function AccessControlPage() {
</Paragraph>
</div>
<RestrictedAccess page={"Access Control"}>
<RestrictedAccess
page={"Access Control"}
hasAccess={permission.policies.read}
>
<PoliciesProvider>
<Suspense fallback={<SkeletonTable />}>
<AccessControlTable
+7 -1
View File
@@ -10,6 +10,7 @@ import useFetchApi from "@utils/api";
import { ExternalLinkIcon, ServerIcon } from "lucide-react";
import React, { lazy, Suspense } from "react";
import DNSIcon from "@/assets/icons/DNSIcon";
import { usePermissions } from "@/contexts/PermissionsProvider";
import { NameserverGroup } from "@/interfaces/Nameserver";
import PageContainer from "@/layouts/PageContainer";
@@ -18,6 +19,8 @@ const NameserverGroupTable = lazy(
);
export default function NameServers() {
const { permission } = usePermissions();
const { data: nameserverGroups, isLoading } =
useFetchApi<NameserverGroup[]>("/dns/nameservers");
@@ -57,7 +60,10 @@ export default function NameServers() {
</Paragraph>
</div>
<RestrictedAccess page={"Nameservers"}>
<RestrictedAccess
page={"Nameservers"}
hasAccess={permission.nameservers.read}
>
<Suspense fallback={<SkeletonTable />}>
<NameserverGroupTable
nameserverGroups={nameserverGroups}
+7 -2
View File
@@ -17,6 +17,7 @@ import React from "react";
import Skeleton from "react-loading-skeleton";
import { useSWRConfig } from "swr";
import DNSIcon from "@/assets/icons/DNSIcon";
import { usePermissions } from "@/contexts/PermissionsProvider";
import { useHasChanges } from "@/hooks/useHasChanges";
import { Group } from "@/interfaces/Group";
import { NameserverSettings } from "@/interfaces/NameserverSettings";
@@ -25,6 +26,8 @@ import useGroupHelper from "@/modules/groups/useGroupHelper";
import { useGroupIdsToGroups } from "@/modules/groups/useGroupIdsToGroups";
export default function NameServerSettings() {
const { permission } = usePermissions();
const { data: settings, isLoading } =
useFetchApi<NameserverSettings>("/dns/settings");
@@ -61,7 +64,7 @@ export default function NameServerSettings() {
</InlineLink>
in our documentation.
</Paragraph>
<RestrictedAccess page={"DNS Settings"}>
<RestrictedAccess page={"DNS Settings"} hasAccess={permission.dns.read}>
{!isLoading && initialDNSGroups !== undefined ? (
<SettingDisabledManagementGroups initialGroups={initialDNSGroups} />
) : (
@@ -86,6 +89,7 @@ const SettingDisabledManagementGroups = ({
}) => {
const settingRequest = useApiCall<NameserverSettings>("/dns/settings");
const { mutate } = useSWRConfig();
const { permission } = usePermissions();
const [selectedGroups, setSelectedGroups, { save: saveGroups }] =
useGroupHelper({
@@ -124,6 +128,7 @@ const SettingDisabledManagementGroups = ({
dataCy={"dns-groups-selector"}
onChange={setSelectedGroups}
values={selectedGroups}
disabled={!permission.dns.update}
/>
</div>
<div
@@ -135,7 +140,7 @@ const SettingDisabledManagementGroups = ({
variant={"primary"}
size={"sm"}
onClick={saveSettings}
disabled={!hasChanges}
disabled={!hasChanges || !permission.dns.update}
data-cy={"save-changes"}
>
Save Changes
@@ -0,0 +1,8 @@
import { globalMetaTitle } from "@utils/meta";
import type { Metadata } from "next";
import BlankLayout from "@/layouts/BlankLayout";
export const metadata: Metadata = {
title: `Audit Events - Activity - ${globalMetaTitle}`,
};
export default BlankLayout;
@@ -6,15 +6,19 @@ import Paragraph from "@components/Paragraph";
import { RestrictedAccess } from "@components/ui/RestrictedAccess";
import { usePortalElement } from "@hooks/usePortalElement";
import useFetchApi from "@utils/api";
import { ExternalLinkIcon } from "lucide-react";
import { ExternalLinkIcon, LogsIcon } from "lucide-react";
import React from "react";
import ActivityIcon from "@/assets/icons/ActivityIcon";
import { usePermissions } from "@/contexts/PermissionsProvider";
import { ActivityEvent } from "@/interfaces/ActivityEvent";
import PageContainer from "@/layouts/PageContainer";
import ActivityTable from "@/modules/activity/ActivityTable";
export default function Activity() {
const { data: events, isLoading } = useFetchApi<ActivityEvent[]>("/events");
const { permission } = usePermissions();
const { data: events, isLoading } =
useFetchApi<ActivityEvent[]>("/events/audit");
const { ref: headingRef, portalTarget } =
usePortalElement<HTMLHeadingElement>();
@@ -24,30 +28,31 @@ export default function Activity() {
<div className={"p-default py-6"}>
<Breadcrumbs>
<Breadcrumbs.Item
href={"/activity"}
label={"Activity"}
disabled={true}
icon={<ActivityIcon size={13} />}
/>
<Breadcrumbs.Item
href={"/events/audit"}
label={"Audit Events"}
icon={<LogsIcon size={18} />}
/>
</Breadcrumbs>
<h1 ref={headingRef}>Activity Events</h1>
<Paragraph>
Here you can see all the account and network activity events.
</Paragraph>
<h1 ref={headingRef}>Audit Events</h1>
<Paragraph>Here you can see all the audit activity events.</Paragraph>
<Paragraph>
Learn more about{" "}
<InlineLink
href={
"https://docs.netbird.io/how-to/monitor-system-and-network-activity"
}
href={"https://docs.netbird.io/how-to/audit-events-logging"}
target={"_blank"}
>
Activity Events
Audit Events
<ExternalLinkIcon size={12} />
</InlineLink>
in our documentation.
</Paragraph>
</div>
<RestrictedAccess page={"Activity"}>
<RestrictedAccess page={"Activity"} hasAccess={permission.events.read}>
<ActivityTable
events={events}
isLoading={isLoading}
+3 -1
View File
@@ -11,6 +11,7 @@ import { ExternalLinkIcon } from "lucide-react";
import React, { lazy, Suspense } from "react";
import NetworkRoutesIcon from "@/assets/icons/NetworkRoutesIcon";
import PeersProvider from "@/contexts/PeersProvider";
import { usePermissions } from "@/contexts/PermissionsProvider";
import RoutesProvider from "@/contexts/RoutesProvider";
import { Route } from "@/interfaces/Route";
import PageContainer from "@/layouts/PageContainer";
@@ -21,6 +22,7 @@ const NetworkRoutesTable = lazy(
);
export default function NetworkRoutes() {
const { permission } = usePermissions();
const { data: routes, isLoading } = useFetchApi<Route[]>("/routes");
const groupedRoutes = useGroupedRoutes({ routes });
@@ -59,7 +61,7 @@ export default function NetworkRoutes() {
</Paragraph>
</div>
<RestrictedAccess>
<RestrictedAccess hasAccess={permission.routes.read}>
<Suspense fallback={<SkeletonTable />}>
<NetworkRoutesTable
isLoading={isLoading}
+14 -11
View File
@@ -21,7 +21,7 @@ import { useSearchParams } from "next/navigation";
import React, { useMemo, useState } from "react";
import { useSWRConfig } from "swr";
import NetworkRoutesIcon from "@/assets/icons/NetworkRoutesIcon";
import { useLoggedInUser } from "@/contexts/UsersProvider";
import { usePermissions } from "@/contexts/PermissionsProvider";
import { Network } from "@/interfaces/Network";
import PageContainer from "@/layouts/PageContainer";
import { NetworkInformationSquare } from "@/modules/networks/misc/NetworkInformationSquare";
@@ -48,7 +48,8 @@ export default function NetworkDetailPage() {
}
function NetworkOverview({ network }: Readonly<{ network: Network }>) {
const { isUser } = useLoggedInUser();
const { permission } = usePermissions();
const [networkModal, setNetworkModal] = useState(false);
const { mutate } = useSWRConfig();
@@ -64,7 +65,7 @@ function NetworkOverview({ network }: Readonly<{ network: Network }>) {
<Breadcrumbs.Item
href={"/networks"}
label={"Networks"}
disabled={isUser}
disabled={!permission.networks.read}
icon={<NetworkRoutesIcon size={13} />}
/>
<Breadcrumbs.Item
@@ -87,14 +88,16 @@ function NetworkOverview({ network }: Readonly<{ network: Network }>) {
size={"lg"}
description={network.description}
/>
<button
className={
"flex items-center gap-2 dark:text-neutral-300 text-neutral-500 hover:text-neutral-100 transition-all hover:bg-nb-gray-800/60 py-2 px-3 rounded-md cursor-pointer"
}
onClick={() => setNetworkModal(true)}
>
<PencilLineIcon size={18} />
</button>
{permission.networks.update && (
<button
className={
"flex items-center gap-2 dark:text-neutral-300 text-neutral-500 hover:text-neutral-100 transition-all hover:bg-nb-gray-800/60 py-2 px-3 rounded-md cursor-pointer"
}
onClick={() => setNetworkModal(true)}
>
<PencilLineIcon size={18} />
</button>
)}
<NetworkModal
open={networkModal}
setOpen={setNetworkModal}
+5 -3
View File
@@ -10,12 +10,14 @@ import useFetchApi from "@utils/api";
import { ExternalLinkIcon } from "lucide-react";
import React, { Suspense } from "react";
import NetworkRoutesIcon from "@/assets/icons/NetworkRoutesIcon";
import { usePermissions } from "@/contexts/PermissionsProvider";
import { Network } from "@/interfaces/Network";
import PageContainer from "@/layouts/PageContainer";
import NetworksTable from "@/modules/networks/table/NetworksTable";
export default function Networks() {
const { data: networks, isLoading } = useFetchApi<Network[]>("/networks");
const { permission } = usePermissions();
const { ref: headingRef, portalTarget } =
usePortalElement<HTMLHeadingElement>();
@@ -31,8 +33,8 @@ export default function Networks() {
</Breadcrumbs>
<h1 ref={headingRef}>Networks</h1>
<Paragraph>
Networks allow you to access internal resources in LANs and VPCs without
installing NetBird on every machine.
Networks allow you to access internal resources in LANs and VPCs
without installing NetBird on every machine.
</Paragraph>
<Paragraph>
Learn more about
@@ -47,7 +49,7 @@ export default function Networks() {
</Paragraph>
</div>
<RestrictedAccess>
<RestrictedAccess hasAccess={permission.networks.read}>
<Suspense fallback={<SkeletonTable />}>
<NetworksTable
data={networks}
File diff suppressed because it is too large. Load diff
+5 -4
View File
@@ -9,18 +9,19 @@ import { ExternalLinkIcon } from "lucide-react";
import React, { lazy, Suspense } from "react";
import PeerIcon from "@/assets/icons/PeerIcon";
import PeersProvider, { usePeers } from "@/contexts/PeersProvider";
import { useLoggedInUser, useUsers } from "@/contexts/UsersProvider";
import { usePermissions } from "@/contexts/PermissionsProvider";
import { useUsers } from "@/contexts/UsersProvider";
import PageContainer from "@/layouts/PageContainer";
import { SetupModalContent } from "@/modules/setup-netbird-modal/SetupModal";
const PeersTable = lazy(() => import("@/modules/peers/PeersTable"));
export default function Peers() {
const { permission } = useLoggedInUser();
const { isRestricted } = usePermissions();
return (
<PageContainer>
{permission.dashboard_view === "blocked" ? (
{isRestricted ? (
<PeersBlockedView />
) : (
<PeersProvider>
@@ -104,7 +105,7 @@ function PeersBlockedView() {
<div className={"px-3 pt-1 pb-8 max-w-3xl w-full"}>
<div
className={
"rounded-md border border-nb-gray-900/70 grid w-full bg-nb-gray-930/40 stepper-bg-variant"
"rounded-md border border-nb-gray-900/70 grid w-full bg-nb-gray-930/40"
}
>
<SetupModalContent header={false} footer={false} />
+6 -1
View File
@@ -11,6 +11,7 @@ import { ExternalLinkIcon, ShieldCheck } from "lucide-react";
import React, { lazy, Suspense } from "react";
import AccessControlIcon from "@/assets/icons/AccessControlIcon";
import GroupsProvider from "@/contexts/GroupsProvider";
import { usePermissions } from "@/contexts/PermissionsProvider";
import PoliciesProvider from "@/contexts/PoliciesProvider";
import { PostureCheck } from "@/interfaces/PostureCheck";
import PageContainer from "@/layouts/PageContainer";
@@ -19,6 +20,7 @@ const PostureCheckTable = lazy(
() => import("@/modules/posture-checks/table/PostureCheckTable"),
);
export default function PostureChecksPage() {
const { permission } = usePermissions();
const { data: postureChecks, isLoading } =
useFetchApi<PostureCheck[]>("/posture-checks");
@@ -59,7 +61,10 @@ export default function PostureChecksPage() {
</Paragraph>
</div>
<RestrictedAccess page={"Posture Checks"}>
<RestrictedAccess
page={"Posture Checks"}
hasAccess={permission.policies.read}
>
<PoliciesProvider>
<Suspense fallback={<SkeletonTable />}>
<PostureCheckTable
+57 -24
View File
@@ -6,15 +6,18 @@ import {
AlertOctagonIcon,
FolderGit2Icon,
LockIcon,
MonitorSmartphoneIcon,
NetworkIcon,
ShieldIcon,
} from "lucide-react";
import { useSearchParams } from "next/navigation";
import React, { useEffect, useState } from "react";
import React, { useEffect, useMemo, useState } from "react";
import { usePermissions } from "@/contexts/PermissionsProvider";
import { useLoggedInUser } from "@/contexts/UsersProvider";
import PageContainer from "@/layouts/PageContainer";
import { useAccount } from "@/modules/account/useAccount";
import AuthenticationTab from "@/modules/settings/AuthenticationTab";
import ClientSettingsTab from "@/modules/settings/ClientSettingsTab";
import DangerZoneTab from "@/modules/settings/DangerZoneTab";
import GroupsTab from "@/modules/settings/GroupsTab";
import NetworkSettingsTab from "@/modules/settings/NetworkSettingsTab";
@@ -23,8 +26,15 @@ import PermissionsTab from "@/modules/settings/PermissionsTab";
export default function NetBirdSettings() {
const queryParams = useSearchParams();
const queryTab = queryParams.get("tab");
const [tab, setTab] = useState(queryTab || "authentication");
const { isOwner } = useLoggedInUser();
const { permission } = usePermissions();
const initialTab = useMemo(() => {
if (permission.settings.read) return "authentication";
return "authentication";
}, [permission]);
const [tab, setTab] = useState(queryTab ?? initialTab);
const account = useAccount();
useEffect(() => {
@@ -37,33 +47,43 @@ export default function NetBirdSettings() {
<PageContainer>
<VerticalTabs value={tab} onChange={setTab}>
<VerticalTabs.List>
<VerticalTabs.Trigger value="authentication">
<ShieldIcon size={14} />
Authentication
</VerticalTabs.Trigger>
<VerticalTabs.Trigger value="groups">
<FolderGit2Icon size={14} />
Groups
</VerticalTabs.Trigger>
<VerticalTabs.Trigger value="permissions">
<LockIcon size={14} />
Permissions
</VerticalTabs.Trigger>
<VerticalTabs.Trigger value="networks">
<NetworkIcon size={14} />
Networks
</VerticalTabs.Trigger>
<VerticalTabs.Trigger value="danger-zone" disabled={!isOwner}>
<AlertOctagonIcon size={14} />
Danger zone
</VerticalTabs.Trigger>
{permission.settings.read && (
<>
<VerticalTabs.Trigger value="authentication">
<ShieldIcon size={14} />
Authentication
</VerticalTabs.Trigger>
<VerticalTabs.Trigger value="groups">
<FolderGit2Icon size={14} />
Groups
</VerticalTabs.Trigger>
<VerticalTabs.Trigger value="permissions">
<LockIcon size={14} />
Permissions
</VerticalTabs.Trigger>
<VerticalTabs.Trigger value="networks">
<NetworkIcon size={14} />
Networks
</VerticalTabs.Trigger>
<VerticalTabs.Trigger value="clients">
<MonitorSmartphoneIcon size={14} />
Clients
</VerticalTabs.Trigger>
</>
)}
<DangerZoneTabTrigger />
</VerticalTabs.List>
<RestrictedAccess page={"Settings"}>
<RestrictedAccess
page={"Settings"}
hasAccess={permission.settings.read}
>
<div className={"border-l border-nb-gray-930 w-full"}>
{account && <AuthenticationTab account={account} />}
{account && <PermissionsTab account={account} />}
{account && <GroupsTab account={account} />}
{account && <NetworkSettingsTab account={account} />}
{account && <ClientSettingsTab account={account} />}
{account && <DangerZoneTab account={account} />}
</div>
</RestrictedAccess>
@@ -71,3 +91,16 @@ export default function NetBirdSettings() {
</PageContainer>
);
}
const DangerZoneTabTrigger = () => {
const { isOwner } = useLoggedInUser();
return (
isOwner && (
<VerticalTabs.Trigger value="danger-zone" disabled={!isOwner}>
<AlertOctagonIcon size={14} />
Danger zone
</VerticalTabs.Trigger>
)
);
};
+6 -1
View File
@@ -11,6 +11,7 @@ import { ExternalLinkIcon } from "lucide-react";
import React, { lazy, Suspense, useMemo } from "react";
import SetupKeysIcon from "@/assets/icons/SetupKeysIcon";
import { useGroups } from "@/contexts/GroupsProvider";
import { usePermissions } from "@/contexts/PermissionsProvider";
import { Group } from "@/interfaces/Group";
import { SetupKey } from "@/interfaces/SetupKey";
import PageContainer from "@/layouts/PageContainer";
@@ -21,6 +22,7 @@ const SetupKeysTable = lazy(
export default function SetupKeys() {
const { data: setupKeys, isLoading } = useFetchApi<SetupKey[]>("/setup-keys");
const { permission } = usePermissions();
const { groups } = useGroups();
const setupKeysWithGroups = useMemo(() => {
@@ -71,7 +73,10 @@ export default function SetupKeys() {
in our documentation.
</Paragraph>
</div>
<RestrictedAccess page={"Setup Keys"}>
<RestrictedAccess
page={"Setup Keys"}
hasAccess={permission.setup_keys.read}
>
<Suspense fallback={<SkeletonTable />}>
<SetupKeysTable
headingTarget={portalTarget}
Loaded 20 of 218 files, more files were not shown because too many files have changed in this diff. Show more