diff --git a/docker/default.conf b/docker/default.conf index 5c3fd49..1aad77a 100644 --- a/docker/default.conf +++ b/docker/default.conf @@ -1,4 +1,3 @@ -# simple server configuration to replace nginx's default server { listen 80 default_server; listen [::]:80 default_server; @@ -7,10 +6,14 @@ server { location / { try_files $uri $uri.html $uri/ =404; + add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0"; + expires off; } error_page 404 /404.html; location = /404.html { internal; + add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0"; + expires off; } } \ No newline at end of file diff --git a/src/app/(dashboard)/activity/page.tsx b/src/app/(dashboard)/activity/page.tsx index 5cdc913..f1b399a 100644 --- a/src/app/(dashboard)/activity/page.tsx +++ b/src/app/(dashboard)/activity/page.tsx @@ -5,14 +5,12 @@ import InlineLink from "@components/InlineLink"; import Paragraph from "@components/Paragraph"; import { RestrictedAccess } from "@components/ui/RestrictedAccess"; import useFetchApi from "@utils/api"; -import { isLocalDev, isNetBirdHosted } from "@utils/netbird"; import { ExternalLinkIcon } from "lucide-react"; import React from "react"; import ActivityIcon from "@/assets/icons/ActivityIcon"; import { ActivityEvent } from "@/interfaces/ActivityEvent"; import PageContainer from "@/layouts/PageContainer"; import ActivityTable from "@/modules/activity/ActivityTable"; -import { EventStreamingCard } from "@/modules/integrations/event-streaming/EventStreamingCard"; export default function Activity() { const { data: events, isLoading } = useFetchApi("/events"); @@ -50,7 +48,6 @@ export default function Activity() { - {(isLocalDev() || isNetBirdHosted()) && } diff --git a/src/app/(dashboard)/integrations/layout.tsx b/src/app/(dashboard)/integrations/layout.tsx deleted file mode 100644 index a41667c..0000000 --- a/src/app/(dashboard)/integrations/layout.tsx +++ /dev/null @@ -1,8 +0,0 @@ -import { globalMetaTitle } from "@utils/meta"; -import type { Metadata } from "next"; -import BlankLayout from "@/layouts/BlankLayout"; - -export const metadata: Metadata = { - title: `Integrations - ${globalMetaTitle}`, -}; -export default BlankLayout; diff --git a/src/app/(dashboard)/integrations/page.tsx b/src/app/(dashboard)/integrations/page.tsx deleted file mode 100644 index 374e55f..0000000 --- a/src/app/(dashboard)/integrations/page.tsx +++ /dev/null @@ -1,39 +0,0 @@ -"use client"; - -import { RestrictedAccess } from "@components/ui/RestrictedAccess"; -import { VerticalTabs } from "@components/VerticalTabs"; -import { FileText, FingerprintIcon } from "lucide-react"; -import { useSearchParams } from "next/navigation"; -import React, { useState } from "react"; -import PageContainer from "@/layouts/PageContainer"; -import EventStreamingTab from "@/modules/integrations/event-streaming/EventStreamingTab"; -import IdentityProviderTab from "@/modules/integrations/idp-sync/IdentityProviderTab"; - -export default function Integrations() { - const searchParams = useSearchParams(); - const currentTab = searchParams.get("tab"); - const [tab, setTab] = useState(currentTab || "event-streaming"); - - return ( - - - - - - Event Streaming - - - - Identity Provider - - - -
- - -
-
-
-
- ); -} diff --git a/src/app/(dashboard)/settings/page.tsx b/src/app/(dashboard)/settings/page.tsx index a44333d..fd5f427 100644 --- a/src/app/(dashboard)/settings/page.tsx +++ b/src/app/(dashboard)/settings/page.tsx @@ -8,7 +8,8 @@ import { LockIcon, ShieldIcon, } from "lucide-react"; -import React, { useState } from "react"; +import { useSearchParams } from "next/navigation"; +import React, { useEffect, useState } from "react"; import { useLoggedInUser } from "@/contexts/UsersProvider"; import PageContainer from "@/layouts/PageContainer"; import { useAccount } from "@/modules/account/useAccount"; @@ -18,10 +19,18 @@ import GroupsTab from "@/modules/settings/GroupsTab"; import PermissionsTab from "@/modules/settings/PermissionsTab"; export default function NetBirdSettings() { - const [tab, setTab] = useState("authentication"); + const queryParams = useSearchParams(); + const queryTab = queryParams.get("tab"); + const [tab, setTab] = useState(queryTab || "authentication"); const { isOwner } = useLoggedInUser(); const account = useAccount(); + useEffect(() => { + if (queryTab) { + setTab(queryTab); + } + }, [queryTab]); + return ( diff --git a/src/assets/icons/IconProperties.tsx b/src/assets/icons/IconProperties.tsx index 0946f20..703ff49 100644 --- a/src/assets/icons/IconProperties.tsx +++ b/src/assets/icons/IconProperties.tsx @@ -5,7 +5,7 @@ export type IconProps = { }; export const defaultIconProps: IconProps = { - size: 16, + size: 15, className: "dark:fill-nb-gray-400 fill-gray-500 peer-data-[active=true]/icon:dark:fill-white peer-data-[active=true]/icon:fill-gray-900 shrink-0", autoHeight: false, diff --git a/src/components/SidebarItem.tsx b/src/components/SidebarItem.tsx index 86222b2..1836b61 100644 --- a/src/components/SidebarItem.tsx +++ b/src/components/SidebarItem.tsx @@ -60,7 +60,7 @@ export default function SidebarItem({
  • - - ) : ( - children - )} - - ); -} diff --git a/src/modules/integrations/IntegrationModalHeader.tsx b/src/modules/integrations/IntegrationModalHeader.tsx deleted file mode 100644 index 03affd8..0000000 --- a/src/modules/integrations/IntegrationModalHeader.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import Paragraph from "@components/Paragraph"; -import { cn } from "@utils/helpers"; -import { ArrowRightLeft } from "lucide-react"; -import { StaticImport } from "next/dist/shared/lib/get-img-props"; -import Image from "next/image"; -import * as React from "react"; -import netBirdLogo from "@/assets/netbird.svg"; - -type Props = { - image: StaticImport | string; - title: string; - description: string; -}; -export const IntegrationModalHeader = ({ - image, - title, - description, -}: Props) => { - return ( - <> -
    -
    - {"NetBird"} -
    -
    - -
    -
    - {""} -
    -
    -
    -

    {title}

    - - {description} - -
    - - ); -}; diff --git a/src/modules/integrations/event-streaming/EventStreamingCard.tsx b/src/modules/integrations/event-streaming/EventStreamingCard.tsx deleted file mode 100644 index 0c190b1..0000000 --- a/src/modules/integrations/event-streaming/EventStreamingCard.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import { IconCircleFilled } from "@tabler/icons-react"; -import useFetchApi from "@utils/api"; -import { cn } from "@utils/helpers"; -import { FileText } from "lucide-react"; -import Image from "next/image"; -import { useRouter } from "next/navigation"; -import * as React from "react"; -import datadogLogo from "@/assets/integrations/datadog.png"; -import { EventStream } from "@/interfaces/EventStream"; - -export const EventStreamingCard = () => { - const { data: eventStreamIntegrations } = useFetchApi( - "/integrations/event-streaming", - ); - const dataDogSettings = eventStreamIntegrations?.find( - (integration) => integration.platform === "datadog", - ); - - const enabled = dataDogSettings ? dataDogSettings.enabled : false; - const router = useRouter(); - - return ( -
    -
    router.push("/integrations")} - className={cn( - "border cursor-pointer border-nb-gray-900/50 bg-nb-gray-900/30 hover:bg-nb-gray-900/50 py-3 pl-3 pr-5 rounded-lg transition-all min-w-[310px] max-w-[400px]", - )} - > -
    -
    - {dataDogSettings?.enabled && ( - {"Datadog"} - )} - - {!dataDogSettings && } -
    -
    -
    -
    - Event Streaming -
    -
    - - {enabled ? "Enabled" : "Disabled"} -
    -
    - -

    - Stream your activity events to third-party services. -

    -
    -
    -
    -
    - ); -}; diff --git a/src/modules/integrations/event-streaming/EventStreamingTab.tsx b/src/modules/integrations/event-streaming/EventStreamingTab.tsx deleted file mode 100644 index 98febf0..0000000 --- a/src/modules/integrations/event-streaming/EventStreamingTab.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import Breadcrumbs from "@components/Breadcrumbs"; -import InlineLink from "@components/InlineLink"; -import Paragraph from "@components/Paragraph"; -import * as Tabs from "@radix-ui/react-tabs"; -import { ExternalLinkIcon, FileText } from "lucide-react"; -import React from "react"; -import IntegrationIcon from "@/assets/icons/IntegrationIcon"; -import Datadog from "@/modules/integrations/event-streaming/datadog/Datadog"; - -export default function EventStreamingTab() { - return ( - -
    - - } - /> - } - active - /> - -

    Event Streaming

    - - Event Streaming allows you to stream NetBirds activity events to - different third-party services. - - - Learn more about{" "} - - Event Streaming - - - in our documentation. - -
    - -
    -
    -
    - ); -} diff --git a/src/modules/integrations/event-streaming/datadog/Datadog.tsx b/src/modules/integrations/event-streaming/datadog/Datadog.tsx deleted file mode 100644 index a049fab..0000000 --- a/src/modules/integrations/event-streaming/datadog/Datadog.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import { notify } from "@components/Notification"; -import { SkeletonIntegration } from "@components/skeletons/SkeletonIntegration"; -import useFetchApi, { useApiCall } from "@utils/api"; -import * as React from "react"; -import { useState } from "react"; -import { useSWRConfig } from "swr"; -import integrationImage from "@/assets/integrations/datadog.png"; -import { useDialog } from "@/contexts/DialogProvider"; -import { EventStream } from "@/interfaces/EventStream"; -import DatadogSetup from "@/modules/integrations/event-streaming/datadog/DatadogSetup"; -import { IntegrationCard } from "@/modules/integrations/IntegrationCard"; - -export default function Datadog() { - const { mutate } = useSWRConfig(); - const { data: eventStreamIntegrations, isLoading } = useFetchApi< - EventStream[] - >("/integrations/event-streaming"); - - const dataDogSettings = eventStreamIntegrations?.find( - (integration) => integration.platform === "datadog", - ); - - const integrationRequest = useApiCall( - "/integrations/event-streaming", - ); - - const [setupModal, setSetupModal] = useState(false); - const { confirm } = useDialog(); - - const toggleSwitch = async () => { - if (!dataDogSettings) return setSetupModal(true); - - const choice = await confirm({ - title: `Disconnect Datadog?`, - description: - "Disconnecting deletes the current configuration. You will need to start the setup process again.", - confirmText: "Disconnect", - cancelText: "Cancel", - type: "warning", - }); - if (!choice) return; - - notify({ - title: "Datadog Integration", - description: `Datadog was successfully disconnected`, - promise: integrationRequest.del({}, "/" + dataDogSettings.id).then(() => { - mutate("/integrations/event-streaming"); - }), - loadingMessage: "Disconnecting integration...", - }); - }; - - return isLoading ? ( - <> - - - ) : ( - <> - setSetupModal(true)} - > - - - ); -} diff --git a/src/modules/integrations/event-streaming/datadog/DatadogRegions.ts b/src/modules/integrations/event-streaming/datadog/DatadogRegions.ts deleted file mode 100644 index 8816bc2..0000000 --- a/src/modules/integrations/event-streaming/datadog/DatadogRegions.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { CountryEURounded } from "@/assets/countries/CountryEURounded"; -import { CountryJPRounded } from "@/assets/countries/CountryJPRounded"; -import { CountryUSRounded } from "@/assets/countries/CountryUSRounded"; - -export const DatadogRegions = [ - { - name: "Europe (EU)", - site_url: "https://app.datadoghq.eu", - send_logs_url: "https://http-intake.logs.datadoghq.eu/api/v2/logs", - icon: CountryEURounded, - }, - { - name: "United States (US1)", - site_url: "https://app.datadoghq.com", - send_logs_url: "https://http-intake.logs.datadoghq.com/api/v2/logs", - icon: CountryUSRounded, - }, - { - name: "United States (US3)", - site_url: "https://us3.datadoghq.com", - send_logs_url: "https://http-intake.logs.us3.datadoghq.com/api/v2/logs", - icon: CountryUSRounded, - }, - { - name: "United States (US5)", - site_url: "https://us5.datadoghq.com", - send_logs_url: "https://http-intake.logs.us5.datadoghq.com/api/v2/logs", - icon: CountryUSRounded, - }, - { - name: "United States (US1-FED)", - site_url: "https://app.ddog-gov.com", - send_logs_url: "https://http-intake.logs.ddog-gov.com/api/v2/logs", - icon: CountryUSRounded, - }, - { - name: "Japan (AP1)", - site_url: "https://ap1.datadoghq.com", - send_logs_url: "https://http-intake.logs.ap1.datadoghq.com/api/v2/logs", - icon: CountryJPRounded, - }, -] as const; - -export const DatadogApiKeysPage = "/organization-settings/api-keys"; diff --git a/src/modules/integrations/event-streaming/datadog/DatadogSetup.tsx b/src/modules/integrations/event-streaming/datadog/DatadogSetup.tsx deleted file mode 100644 index 5172ef6..0000000 --- a/src/modules/integrations/event-streaming/datadog/DatadogSetup.tsx +++ /dev/null @@ -1,277 +0,0 @@ -import Button from "@components/Button"; -import InlineLink from "@components/InlineLink"; -import { Input } from "@components/Input"; -import { Modal, ModalContent, ModalFooter } from "@components/modal/Modal"; -import { notify } from "@components/Notification"; -import { - SelectDropdown, - SelectOption, -} from "@components/select/SelectDropdown"; -import Steps from "@components/Steps"; -import { GradientFadedBackground } from "@components/ui/GradientFadedBackground"; -import { IconArrowLeft, IconArrowRight } from "@tabler/icons-react"; -import { useApiCall } from "@utils/api"; -import { cn } from "@utils/helpers"; -import { - ExternalLinkIcon, - Globe, - GlobeIcon, - KeyRound, - Repeat, -} from "lucide-react"; -import Link from "next/link"; -import React, { useState } from "react"; -import { useSWRConfig } from "swr"; -import datadogLogo from "@/assets/integrations/datadog.png"; -import { EventStream } from "@/interfaces/EventStream"; -import { - DatadogApiKeysPage, - DatadogRegions, -} from "@/modules/integrations/event-streaming/datadog/DatadogRegions"; -import { IntegrationModalHeader } from "@/modules/integrations/IntegrationModalHeader"; - -type Props = { - open: boolean; - onOpenChange: (open: boolean) => void; - onSuccess?: () => void; -}; - -export default function DatadogSetup({ open, onOpenChange, onSuccess }: Props) { - return ( - <> - - { - onOpenChange(false); - onSuccess && onSuccess(); - }} - /> - - - ); -} - -type ModalProps = { - onSuccess: () => void; -}; - -export function SetupContent({ onSuccess }: ModalProps) { - const { mutate } = useSWRConfig(); - - const integrationRequest = useApiCall( - "/integrations/event-streaming", - ); - - const datadogRegions = DatadogRegions.map((region) => { - return { - label: region.name, - value: region.send_logs_url, - icon: region.icon, - } as SelectOption; - }); - - const [selectedRegion, setSelectedRegion] = useState(datadogRegions[0].value); - - const changeRegion = (region: string) => { - setSelectedRegion(region); - setApiUrl(region); - }; - - const [apiKey, setApiKey] = useState(""); - const [apiUrl, setApiUrl] = useState(datadogRegions[0].value); - const [step, setStep] = useState(1); - - const apiKeyEntered = apiKey.length > 0 && apiKey != ""; - const apiUrlEntered = apiUrl.length > 0 && apiUrl != ""; - const apiKeyAndUrlEntered = apiKeyEntered && apiUrlEntered; - - const apiPageUrl = - DatadogRegions.find((region) => region.send_logs_url == apiUrl)?.site_url + - DatadogApiKeysPage; - - const connect = async () => { - notify({ - title: "Datadog Integration", - description: `Datadog was successfully connected to NetBird.`, - promise: integrationRequest - .post({ - platform: "datadog", - config: { - api_key: apiKey, - api_url: apiUrl, - }, - enabled: true, - }) - .then(() => { - mutate("/integrations/event-streaming"); - onSuccess(); - }), - loadingMessage: "Setting up integration...", - }); - }; - - return ( - - - - - - {step == 1 && ( -
    -

    - - Select your Datadog region -

    -

    - To identify which region you are on please check out the{" "} - - Datadog Documentation. - -

    - -
    - - -
    - } - placeholder={"https://http-intake.logs.datadoghq.eu/api/v2/logs"} - value={apiUrl} - onChange={(e) => setApiUrl(e.target.value)} - /> -
    - -
    - - )} - - {step == 2 && ( -
    -

    - - Get your Datadog API Key -

    - - -

    Navigate to Datadogs API Keys page

    -
    - - - -
    -
    - -

    - Click{" "} -

    - + New Key -
    {" "} - at the top -

    -
    - -

    - Give it a descriptive name like{" "} -

    - NetBird Activity Events -
    - and click{" "} -
    - Create Key -
    -

    -
    - -

    Enter your API-Key

    -
    -
    -
    - - -
    - } - placeholder={"1c17401cf170f7ac33dd9dcdf8040eb2"} - value={apiKey} - onChange={(e) => setApiKey(e.target.value)} - /> -
    - - )} - - - {step == 1 && ( - - )} - {step == 2 && ( - <> - - - - )} - -
    - ); -} diff --git a/src/modules/integrations/idp-sync/GroupPrefixInput.tsx b/src/modules/integrations/idp-sync/GroupPrefixInput.tsx deleted file mode 100644 index 4001e85..0000000 --- a/src/modules/integrations/idp-sync/GroupPrefixInput.tsx +++ /dev/null @@ -1,109 +0,0 @@ -import Button from "@components/Button"; -import { Input } from "@components/Input"; -import { useDebounce } from "@hooks/useDebounce"; -import { Folder, MinusCircleIcon, PlusIcon } from "lucide-react"; -import * as React from "react"; -import { useEffect, useState } from "react"; - -type GroupPrefixInputProps = { - value: string[]; - onChange: (values: string[]) => void; - addText?: string; - icon?: React.ReactNode; - text?: string; - placeholder?: string; -}; - -export function GroupPrefixInput({ - value, - onChange, - addText = "Add group filter", - icon = , - text = "Group starts with...", - placeholder = "e.g., NetBird_", -}: GroupPrefixInputProps) { - const [groupPrefixes, setGroupPrefixes] = useState(value); - const prefixes = useDebounce(groupPrefixes, 100); - - useEffect(() => { - onChange(prefixes); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [prefixes]); - - const onChangeHandler = ( - e: React.ChangeEvent, - index: number, - ) => { - const newPrefixes = [...groupPrefixes]; - newPrefixes[index] = e.target.value; - setGroupPrefixes(newPrefixes); - }; - - const onRemoveGroupPrefix = (index: number) => { - setGroupPrefixes((p) => { - const newPrefixes = [...p]; - newPrefixes.splice(index, 1); - return newPrefixes; - }); - }; - - const onAddGroupPrefix = () => { - setGroupPrefixes((p) => { - const newPrefixes = [...p]; - newPrefixes.push(""); - return newPrefixes; - }); - }; - - return ( -
    - {groupPrefixes.length > 0 && ( -
    -
    - {groupPrefixes.map((g, i) => { - return ( -
    -
    - - {icon} - {text} -
    - } - placeholder={placeholder} - maxWidthClass={"w-full"} - value={g} - className={" !text-[13px]"} - onKeyDown={(event) => { - if (event.code === "Space") event.preventDefault(); - }} - onChange={(e) => onChangeHandler(e, i)} - /> -
    - -
    - ); - })} -
    -
    - )} - - - - ); -} diff --git a/src/modules/integrations/idp-sync/IdentityProviderTab.tsx b/src/modules/integrations/idp-sync/IdentityProviderTab.tsx deleted file mode 100644 index da78763..0000000 --- a/src/modules/integrations/idp-sync/IdentityProviderTab.tsx +++ /dev/null @@ -1,92 +0,0 @@ -import Breadcrumbs from "@components/Breadcrumbs"; -import InlineLink from "@components/InlineLink"; -import { Label } from "@components/Label"; -import Paragraph from "@components/Paragraph"; -import { SkeletonIntegration } from "@components/skeletons/SkeletonIntegration"; -import * as Tabs from "@radix-ui/react-tabs"; -import { ExternalLinkIcon, FingerprintIcon } from "lucide-react"; -import React from "react"; -import IntegrationIcon from "@/assets/icons/IntegrationIcon"; -import { useAccount } from "@/modules/account/useAccount"; -import { AzureAD } from "@/modules/integrations/idp-sync/azure-ad/AzureAD"; -import { GoogleWorkspace } from "@/modules/integrations/idp-sync/google-workspace/GoogleWorkspace"; -import { Okta } from "@/modules/integrations/idp-sync/okta-scim/Okta"; -import { useIntegrations } from "@/modules/integrations/idp-sync/useIntegrations"; - -export default function IdentityProviderTab() { - const account = useAccount(); - - useIntegrations(); - - return ( - -
    - - } - /> - } - active - /> - -

    Identity Provider

    - - Configure your preferred Identity Provider (IdP) to synchronize your - users and groups to NetBird. - - - Learn more about{" "} - - Identity Provider - - - in our documentation. - -
    - {!account ? ( - <> - - - - - ) : ( - <> - - - - - )} -
    -
    -
    - -

    - Please contact us at{" "} - - {" "} - support@netbird.io - {" "} -

    -
    -
    -
    -
    - ); -} diff --git a/src/modules/integrations/idp-sync/azure-ad/AzureAD.tsx b/src/modules/integrations/idp-sync/azure-ad/AzureAD.tsx deleted file mode 100644 index a85d981..0000000 --- a/src/modules/integrations/idp-sync/azure-ad/AzureAD.tsx +++ /dev/null @@ -1,164 +0,0 @@ -import Button from "@components/Button"; -import FullTooltip from "@components/FullTooltip"; -import { notify } from "@components/Notification"; -import { SkeletonIntegration } from "@components/skeletons/SkeletonIntegration"; -import useFetchApi, { useApiCall } from "@utils/api"; -import dayjs from "dayjs"; -import { isEmpty } from "lodash"; -import { RefreshCw, Settings } from "lucide-react"; -import * as React from "react"; -import { useEffect, useMemo, useState } from "react"; -import { useSWRConfig } from "swr"; -import integrationImage from "@/assets/integrations/entra-id.png"; -import { - AzureADIntegration, - IdentityProviderLog, -} from "@/interfaces/IdentityProvider"; -import AzureADConfiguration from "@/modules/integrations/idp-sync/azure-ad/AzureADConfiguration"; -import AzureADSetup from "@/modules/integrations/idp-sync/azure-ad/AzureADSetup"; -import { useIntegrations } from "@/modules/integrations/idp-sync/useIntegrations"; -import { IntegrationCard } from "@/modules/integrations/IntegrationCard"; - -export const AzureAD = () => { - const { mutate } = useSWRConfig(); - const [setupModal, setSetupModal] = useState(false); - - const { - azure: integration, - isAnyIntegrationEnabled, - isAzureLoading, - } = useIntegrations(); - const azureRequest = useApiCall( - "/integrations/azure-idp", - ); - - const [enabled, setEnabled] = useState( - integration ? integration.enabled : false, - ); - - useEffect(() => { - setEnabled(integration?.enabled ?? false); - }, [integration]); - - const toggleSwitch = async (state: boolean) => { - if (!integration) return setSetupModal(true); - - notify({ - title: "Entra ID (Azure AD) Integration", - description: `Entra ID (Azure AD) was successfully ${ - state ? "enabled" : "disabled" - }`, - promise: azureRequest - .put( - { - enabled: state, - }, - "/" + integration.id, - ) - .then(() => { - mutate("/integrations/azure-idp"); - setEnabled(state); - }), - loadingMessage: "Updating integration...", - }); - }; - - return isAzureLoading ? ( - - ) : ( - <> - setSetupModal(true)} - > - {integration && } - - setEnabled(true)} - /> - - ); -}; - -type ConfigurationProps = { - config: AzureADIntegration; -}; -const ConfigurationButton = ({ config }: ConfigurationProps) => { - const { data: logs } = useFetchApi( - `/integrations/azure-idp/${config.id}/logs`, - ); - const { mutate } = useSWRConfig(); - const syncRequest = useApiCall<{ response: boolean }>( - `/integrations/azure-idp/${config.id}/sync`, - ); - - const [configModal, setConfigModal] = useState(false); - - const forceSync = async () => { - notify({ - title: "Entra ID (Azure AD) Integration", - description: `Entra ID (Azure AD) was successfully synced`, - loadingMessage: "Syncing integration...", - promise: syncRequest.post({}).then(() => { - mutate(`/integrations/azure-idp/${config.id}/logs`); - }), - }); - }; - - const lastSync = useMemo(() => { - if (isEmpty(logs)) return "Not synchronized"; - return "Synced " + dayjs().to(logs?.[0]?.timestamp); - }, [logs]); - - return ( - <> -
    - - Force synchronization of users and groups -
    - } - disabled={!config.enabled} - className={"w-full"} - interactive={false} - > - - - - - - - - ); -}; diff --git a/src/modules/integrations/idp-sync/azure-ad/AzureADConfiguration.tsx b/src/modules/integrations/idp-sync/azure-ad/AzureADConfiguration.tsx deleted file mode 100644 index 14c49eb..0000000 --- a/src/modules/integrations/idp-sync/azure-ad/AzureADConfiguration.tsx +++ /dev/null @@ -1,372 +0,0 @@ -import Button from "@components/Button"; -import HelpText from "@components/HelpText"; -import { Input } from "@components/Input"; -import { Label } from "@components/Label"; -import { - Modal, - ModalClose, - ModalContent, - ModalFooter, -} from "@components/modal/Modal"; -import { notify } from "@components/Notification"; -import { Tabs, TabsContent, TabsList, TabsTrigger } from "@components/Tabs"; -import { GradientFadedBackground } from "@components/ui/GradientFadedBackground"; -import { useHasChanges } from "@hooks/useHasChanges"; -import { useApiCall } from "@utils/api"; -import { cn } from "@utils/helpers"; -import { - AlertOctagon, - Box, - Cog, - Folder, - FolderGit2, - KeyRound, - RefreshCw, - UserCircle, -} from "lucide-react"; -import React, { useState } from "react"; -import { useSWRConfig } from "swr"; -import integrationImage from "@/assets/integrations/entra-id.png"; -import { useDialog } from "@/contexts/DialogProvider"; -import { AzureADIntegration } from "@/interfaces/IdentityProvider"; -import { GroupPrefixInput } from "@/modules/integrations/idp-sync/GroupPrefixInput"; -import { useIntegrations } from "@/modules/integrations/idp-sync/useIntegrations"; -import { IntegrationModalHeader } from "@/modules/integrations/IntegrationModalHeader"; - -type Props = { - open: boolean; - onOpenChange: (open: boolean) => void; - onSuccess?: () => void; -}; - -export default function AzureADConfiguration({ - open, - onOpenChange, - onSuccess, -}: Props) { - const { azure } = useIntegrations(); - - return ( - <> - - {azure && ( - { - onOpenChange(false); - onSuccess && onSuccess(); - }} - config={azure} - /> - )} - - - ); -} - -type ModalProps = { - onSuccess: () => void; - config: AzureADIntegration; -}; - -export function ConfigurationContent({ onSuccess, config }: ModalProps) { - const { mutate } = useSWRConfig(); - const { confirm } = useDialog(); - - const [tab, setTab] = useState("settings"); - - const azureRequest = useApiCall( - "/integrations/azure-idp", - ); - - const clientSecretPlaceholder = "******************************"; - const [clientSecret, setClientSecret] = useState(clientSecretPlaceholder); - - const [clientId, setClientId] = useState(config.clientId); - const [tenantId, setTenantId] = useState(config.tenantId); - const [interval, setInterval] = useState(config.syncInterval.toString()); - - const [groupPrefixes, setGroupPrefixes] = useState( - config.group_prefixes || [], - ); - const [userGroupPrefixes, setUserGroupPrefixes] = useState( - config.user_group_prefixes || [], - ); - - const deleteIntegration = async () => { - const choice = await confirm({ - title: `Delete integration?`, - description: "Are you sure you want to delete this integration?", - confirmText: "Delete", - cancelText: "Cancel", - type: "danger", - }); - - if (!choice) return; - - notify({ - title: "Entra ID (Azure AD) Integration", - description: `Entra ID (Azure AD) was successfully deleted`, - promise: azureRequest.del({}, `/${config.id}`).then(() => { - mutate("/integrations/azure-idp"); - onSuccess(); - }), - loadingMessage: "Deleting integration...", - }); - }; - - const updateIntegration = async () => { - notify({ - title: "Entra ID (Azure AD) Integration", - description: `Entra ID (Azure AD) was successfully updated`, - promise: azureRequest - .put( - { - client_id: clientId, - tenant_id: tenantId, - client_secret: - clientSecretPlaceholder == clientSecret - ? undefined - : btoa(clientSecret), - sync_interval: interval ? parseInt(interval) : 300, - group_prefixes: groupPrefixes || [], - user_group_prefixes: userGroupPrefixes || [], - }, - `/${config.id}`, - ) - .then(() => { - mutate("/integrations/azure-idp"); - onSuccess(); - }), - loadingMessage: "Updating integration...", - }); - }; - - const { hasChanges } = useHasChanges([ - clientId, - tenantId, - clientSecret, - interval, - groupPrefixes, - userGroupPrefixes, - ]); - - return ( - - - - - - setTab(v)} - className={"mt-6"} - > - - - - Settings - - - - Group Sync - - - - User Sync - - - - Danger Zone - - - -
    - - - Application (client) ID -
    - } - placeholder={"62d3a656-c87d-4f30-a242-5b6347e29e9f"} - value={clientId} - onChange={(e) => setClientId(e.target.value)} - /> - - - Directory (tenant) ID - - } - placeholder={"5d60468a-65b7-45eb-a61a-53ecfbcd1ea3"} - value={tenantId} - onChange={(e) => setTenantId(e.target.value)} - /> - - - - Client Secret - - } - placeholder={"YdV7Q~JJ62Xl.LvYoBanxZR2sJA2va_3UbqvncY8"} - value={clientSecret} - onChange={(e) => setClientSecret(e.target.value)} - /> - -
    -
    - - - The interval in seconds when the synchronization should - happen. - -
    - setInterval(e.target.value)} - customPrefix={ - - } - customSuffix={"Seconds"} - /> -
    - -
    - - -
    - - - By default,{" "} - All Groups{" "} - will be synchronized from your IdP to NetBird.
    - If you want to synchronize only groups that start with a specific - prefix, you can add them below. -
    -
    - -
    - - -
    - - - By default,{" "} - All Users{" "} - will be synchronized from your IdP to NetBird.
    - If you want to synchronize only users that belong to a specific - group, you can add them below. -
    -
    - -
    - - -
    - - - Deleting this integration will remove the ability to sync users - and groups from your IdP to NetBird. If you delete the integration - you will need to reconfigure it again to enable the - synchronization. - -
    - -
    -
    -
    - - - - - - - - -
    - ); -} diff --git a/src/modules/integrations/idp-sync/azure-ad/AzureADSetup.tsx b/src/modules/integrations/idp-sync/azure-ad/AzureADSetup.tsx deleted file mode 100644 index c525f1a..0000000 --- a/src/modules/integrations/idp-sync/azure-ad/AzureADSetup.tsx +++ /dev/null @@ -1,498 +0,0 @@ -import Button from "@components/Button"; -import HelpText from "@components/HelpText"; -import InlineLink from "@components/InlineLink"; -import { Input } from "@components/Input"; -import { Modal, ModalContent, ModalFooter } from "@components/modal/Modal"; -import { notify } from "@components/Notification"; -import Steps from "@components/Steps"; -import { GradientFadedBackground } from "@components/ui/GradientFadedBackground"; -import { Lightbox } from "@components/ui/Lightbox"; -import { Mark } from "@components/ui/Mark"; -import { MinimalList } from "@components/ui/MinimalList"; -import { IconArrowLeft, IconArrowRight } from "@tabler/icons-react"; -import { useApiCall } from "@utils/api"; -import { cn } from "@utils/helpers"; -import { isEmpty } from "lodash"; -import { - Box, - Clock4, - Folder, - FolderGit2, - KeyRound, - PlusCircle, - Repeat, - Settings2, - Shield, - UserCircle, -} from "lucide-react"; -import React, { useState } from "react"; -import { useSWRConfig } from "swr"; -import integrationImage from "@/assets/integrations/entra-id.png"; -import { AzureADIntegration } from "@/interfaces/IdentityProvider"; -import azureGrantAdmin from "@/modules/integrations/idp-sync/azure-ad/images/azure-grant-admin-conset.png"; -import { IntegrationModalHeader } from "@/modules/integrations/IntegrationModalHeader"; -import { GroupPrefixInput } from "../GroupPrefixInput"; - -type Props = { - open: boolean; - onOpenChange: (open: boolean) => void; - onSuccess?: () => void; -}; - -export default function AzureADSetup({ open, onOpenChange, onSuccess }: Props) { - return ( - <> - - { - onOpenChange(false); - onSuccess && onSuccess(); - }} - /> - - - ); -} - -type ModalProps = { - onSuccess: () => void; -}; - -export function SetupContent({ onSuccess }: ModalProps) { - const { mutate } = useSWRConfig(); - const azureRequest = useApiCall( - "/integrations/azure-idp", - ); - - const [step, setStep] = useState(0); - const maxSteps = 6; - - const [clientSecret, setClientSecret] = useState(""); - const [clientId, setClientId] = useState(""); - const [tenantId, setTenantId] = useState(""); - - const clientSecretEntered = !isEmpty(clientSecret); - const clientIdEntered = !isEmpty(clientId); - const tenantIdEntered = !isEmpty(tenantId); - - const allEntered = clientIdEntered && tenantIdEntered && clientSecretEntered; - - const isDisabled = - (step == 8 && !clientSecretEntered) || (step == 9 && !allEntered); - - const [groupPrefixes, setGroupPrefixes] = useState([]); - const [userGroupPrefixes, setUserGroupPrefixes] = useState([]); - - const connect = async () => { - notify({ - title: "Entra ID Integration", - description: `Entra ID was successfully connected to NetBird.`, - promise: azureRequest - .post({ - client_secret: btoa(clientSecret), // Encode client secret to base64 - client_id: clientId, - tenant_id: tenantId, - group_prefixes: groupPrefixes || [], - user_group_prefixes: userGroupPrefixes || [], - }) - .then(() => { - mutate("/integrations/azure-idp"); - onSuccess(); - }), - loadingMessage: "Setting up integration...", - }); - }; - - return ( - step > 0 && e.preventDefault()} - onInteractOutside={(e) => step > 0 && e.preventDefault()} - onPointerDownOutside={(e) => step > 0 && e.preventDefault()} - > - - - {step > 0 && ( -
    - {Array.from({ length: maxSteps }).map((_, index) => ( -
    = index + 1 && "bg-netbird", - )} - /> - ))} -
    - )} - - - - {step == 0 && ( -
    -
    - - Required Permissions -
    -

    - Ensure that you have an an{" "} - - Azure AD user account - {" "} - with the following{" "} - - permissions - - .{" "} - { - "If you don't have the required permissions, ask your Azure AD administrator to grant them to you." - } -

    -
    -
    - - Create Azure AD applications -
    -
    - - Manage Azure AD applications -
    -
    -
    - )} - - {step == 1 && ( -
    -

    - - Create and configure Azure AD application -

    - - -

    - Navigate to{" "} - - Azure Active Directory - -

    -
    - -

    - Click App Registrations in the left menu then click - on the + New registration button to create a new - application. -

    -
    - -

    - Fill in the form with the following values and click{" "} - Register -

    -
    -
    - - -
    - )} - - {step == 2 && ( -
    -

    - - Add API permissions -

    - - -

    - Click API permissions on the left side menu -

    -
    - -

    - Click Add a permission then{" "} - Microsoft Graph and then on the{" "} - Application permissions tab. -

    -
    - -

    - In Select permissions select{" "} - User.Read.All and Group.Read.All and - click Add permissions -

    -
    - -

    - Click Grant admin conset for Default Directory and - click Yes -

    - -
    -
    -
    - )} - - {step == 3 && ( -
    -

    - - Generate client secret -

    - - -

    - Navigate to Certificates & secrets on left side - menu -

    -
    - -

    - Click on + New client secret -

    -
    - -

    - Add NetBird as the description and click{" "} - Add -

    -
    - -

    - Copy the Value and paste it here -

    -
    -
    -
    - - -
    - } - placeholder={"YdV7Q~JJ62Xl.LvYoBanxZR2sJA2va_3UbqvncY8"} - value={clientSecret} - onChange={(e) => setClientSecret(e.target.value)} - /> -
    -
    - )} - - {step == 4 && ( -
    -

    - - Enter Application ID and Directory ID -

    - - -

    - Navigate to{" "} - - Owner applications - -

    -
    - -

    - Select NetBird application in overview page and - enter your Application (client) ID and{" "} - Directory (tenant) ID -

    -
    -
    -
    - - - Application (client) ID -
    - } - placeholder={"62d3a656-c87d-4f30-a242-5b6347e29e9f"} - value={clientId} - onChange={(e) => setClientId(e.target.value)} - /> - - - Directory (tenant) ID -
    - } - placeholder={"5d60468a-65b7-45eb-a61a-53ecfbcd1ea3"} - value={tenantId} - onChange={(e) => setTenantId(e.target.value)} - /> - - - )} - - {step == 5 && ( -
    -

    - - Groups to be synchronized -

    - -
    -
    - - By default,{" "} - All Groups{" "} - will be synchronized from your IdP to NetBird.
    - If you want to synchronize only groups that start with a - specific prefix, you can add them below. -
    -
    - -
    -
    - )} - - {step == 6 && ( -
    -

    - - Users to be synchronized -

    - -
    -
    - - By default,{" "} - All Users{" "} - will be synchronized from your IdP to NetBird.
    - If you want to synchronize only users that belong to a specific - group, you can add them below. -
    -
    - - -
    -
    - )} - - - {step > 0 && ( - - )} - {step >= 0 && step < maxSteps && ( - - )} - {step == maxSteps && ( - - )} - - {step == 0 && ( -
    - -
    - Estimated setup time: - 10-20 Minutes -
    -
    - )} -
    - ); -} diff --git a/src/modules/integrations/idp-sync/azure-ad/images/azure-add-application-uri.png b/src/modules/integrations/idp-sync/azure-ad/images/azure-add-application-uri.png deleted file mode 100644 index 154f1f7..0000000 Binary files a/src/modules/integrations/idp-sync/azure-ad/images/azure-add-application-uri.png and /dev/null differ diff --git a/src/modules/integrations/idp-sync/azure-ad/images/azure-authorize-application.png b/src/modules/integrations/idp-sync/azure-ad/images/azure-authorize-application.png deleted file mode 100644 index 9ec8713..0000000 Binary files a/src/modules/integrations/idp-sync/azure-ad/images/azure-authorize-application.png and /dev/null differ diff --git a/src/modules/integrations/idp-sync/azure-ad/images/azure-grant-admin-conset.png b/src/modules/integrations/idp-sync/azure-ad/images/azure-grant-admin-conset.png deleted file mode 100644 index 081b2ff..0000000 Binary files a/src/modules/integrations/idp-sync/azure-ad/images/azure-grant-admin-conset.png and /dev/null differ diff --git a/src/modules/integrations/idp-sync/azure-ad/images/azure-new-application.png b/src/modules/integrations/idp-sync/azure-ad/images/azure-new-application.png deleted file mode 100644 index 49b6b2b..0000000 Binary files a/src/modules/integrations/idp-sync/azure-ad/images/azure-new-application.png and /dev/null differ diff --git a/src/modules/integrations/idp-sync/azure-ad/images/azure-spa-uri-setup.png b/src/modules/integrations/idp-sync/azure-ad/images/azure-spa-uri-setup.png deleted file mode 100644 index dfd6a94..0000000 Binary files a/src/modules/integrations/idp-sync/azure-ad/images/azure-spa-uri-setup.png and /dev/null differ diff --git a/src/modules/integrations/idp-sync/google-workspace/GoogleWorkspace.tsx b/src/modules/integrations/idp-sync/google-workspace/GoogleWorkspace.tsx deleted file mode 100644 index f8cb7ad..0000000 --- a/src/modules/integrations/idp-sync/google-workspace/GoogleWorkspace.tsx +++ /dev/null @@ -1,168 +0,0 @@ -import Button from "@components/Button"; -import FullTooltip from "@components/FullTooltip"; -import { notify } from "@components/Notification"; -import { SkeletonIntegration } from "@components/skeletons/SkeletonIntegration"; -import useFetchApi, { useApiCall } from "@utils/api"; -import dayjs from "dayjs"; -import { isEmpty } from "lodash"; -import { RefreshCw, Settings } from "lucide-react"; -import * as React from "react"; -import { useEffect, useMemo, useState } from "react"; -import { useSWRConfig } from "swr"; -import integrationImage from "@/assets/integrations/google-workspace.png"; -import { - AzureADIntegration, - GoogleWorkspaceIntegration, - IdentityProviderLog, -} from "@/interfaces/IdentityProvider"; -import GoogleWorkspaceConfiguration from "@/modules/integrations/idp-sync/google-workspace/GoogleWorkspaceConfiguration"; -import GoogleWorkspaceSetup from "@/modules/integrations/idp-sync/google-workspace/GoogleWorkspaceSetup"; -import { useIntegrations } from "@/modules/integrations/idp-sync/useIntegrations"; -import { IntegrationCard } from "@/modules/integrations/IntegrationCard"; - -export const GoogleWorkspace = () => { - const { mutate } = useSWRConfig(); - const [setupModal, setSetupModal] = useState(false); - - const { - google: integration, - isAnyIntegrationEnabled, - isGoogleLoading, - } = useIntegrations(); - const googleRequest = useApiCall( - "/integrations/google-idp", - ); - - const [enabled, setEnabled] = useState( - integration ? integration.enabled : false, - ); - - useEffect(() => { - setEnabled(integration?.enabled ?? false); - }, [integration]); - - const toggleSwitch = async (state: boolean) => { - if (!integration) return setSetupModal(true); - - notify({ - title: "Google Workspace Integration", - description: `Google Workspace was successfully ${ - state ? "enabled" : "disabled" - }`, - promise: googleRequest - .put( - { - enabled: state, - }, - "/" + integration.id, - ) - .then(() => { - mutate("/integrations/google-idp"); - setEnabled(state); - }), - loadingMessage: "Updating integration...", - }); - }; - - return isGoogleLoading ? ( - - ) : ( - <> - setSetupModal(true)} - > - {integration && } - - setEnabled(true)} - /> - - ); -}; - -type ConfigurationProps = { - config: GoogleWorkspaceIntegration; -}; -const ConfigurationButton = ({ config }: ConfigurationProps) => { - const { data: logs } = useFetchApi( - `/integrations/google-idp/${config.id}/logs`, - ); - const { mutate } = useSWRConfig(); - const syncRequest = useApiCall<{ response: boolean }>( - `/integrations/google-idp/${config.id}/sync`, - ); - - const [configModal, setConfigModal] = useState(false); - - const forceSync = async () => { - notify({ - title: "Google Workspace Integration", - description: `Google Workspace was successfully synced`, - loadingMessage: "Syncing integration...", - promise: syncRequest.post({}).then(() => { - mutate(`/integrations/google-idp/${config.id}/logs`); - }), - }); - }; - - const lastSync = useMemo(() => { - if (isEmpty(logs)) return "Not synchronized"; - return "Synced " + dayjs().to(logs?.[0]?.timestamp); - }, [logs]); - - return ( - <> -
    - - Force synchronization of users and groups -
    - } - disabled={!config.enabled} - className={"w-full"} - interactive={false} - > - - - - - - - - ); -}; diff --git a/src/modules/integrations/idp-sync/google-workspace/GoogleWorkspaceConfiguration.tsx b/src/modules/integrations/idp-sync/google-workspace/GoogleWorkspaceConfiguration.tsx deleted file mode 100644 index baf946e..0000000 --- a/src/modules/integrations/idp-sync/google-workspace/GoogleWorkspaceConfiguration.tsx +++ /dev/null @@ -1,361 +0,0 @@ -import Button from "@components/Button"; -import HelpText from "@components/HelpText"; -import { Input } from "@components/Input"; -import { JSONFileUpload } from "@components/JSONFileUpload"; -import { Label } from "@components/Label"; -import { - Modal, - ModalClose, - ModalContent, - ModalFooter, -} from "@components/modal/Modal"; -import { notify } from "@components/Notification"; -import { Tabs, TabsContent, TabsList, TabsTrigger } from "@components/Tabs"; -import { GradientFadedBackground } from "@components/ui/GradientFadedBackground"; -import { useHasChanges } from "@hooks/useHasChanges"; -import { useApiCall } from "@utils/api"; -import { cn } from "@utils/helpers"; -import { - AlertOctagon, - Box, - Cog, - FolderGit2, - KeyRound, - RefreshCw, - UserCircle, -} from "lucide-react"; -import React, { useState } from "react"; -import { useSWRConfig } from "swr"; -import integrationImage from "@/assets/integrations/google-workspace.png"; -import { useDialog } from "@/contexts/DialogProvider"; -import { GoogleWorkspaceIntegration } from "@/interfaces/IdentityProvider"; -import { GroupPrefixInput } from "@/modules/integrations/idp-sync/GroupPrefixInput"; -import { useIntegrations } from "@/modules/integrations/idp-sync/useIntegrations"; -import { IntegrationModalHeader } from "@/modules/integrations/IntegrationModalHeader"; - -type Props = { - open: boolean; - onOpenChange: (open: boolean) => void; - onSuccess?: () => void; -}; - -export default function GoogleWorkspaceConfiguration({ - open, - onOpenChange, - onSuccess, -}: Props) { - const { google } = useIntegrations(); - - return ( - <> - - {google && ( - { - onOpenChange(false); - onSuccess && onSuccess(); - }} - config={google} - /> - )} - - - ); -} - -type ModalProps = { - onSuccess: () => void; - config: GoogleWorkspaceIntegration; -}; - -export function ConfigurationContent({ onSuccess, config }: ModalProps) { - const { mutate } = useSWRConfig(); - const { confirm } = useDialog(); - - const [tab, setTab] = useState("settings"); - - const googleRequest = useApiCall( - "/integrations/google-idp", - ); - - const accountKeyPlaceholder = "******************************"; - const [serviceAccountKey, setServiceAccountKey] = useState( - accountKeyPlaceholder, - ); - - const [customerID, setCustomerID] = useState(config.customerId); - const [interval, setInterval] = useState(config.syncInterval.toString()); - - const [groupPrefixes, setGroupPrefixes] = useState( - config.group_prefixes || [], - ); - const [userGroupPrefixes, setUserGroupPrefixes] = useState( - config.user_group_prefixes || [], - ); - - const deleteIntegration = async () => { - const choice = await confirm({ - title: `Delete integration?`, - description: "Are you sure you want to delete this integration?", - confirmText: "Delete", - cancelText: "Cancel", - type: "danger", - }); - - if (!choice) return; - - notify({ - title: "Google Workspace Integration", - description: `Google Workspace was successfully deleted`, - promise: googleRequest.del({}, `/${config.id}`).then(() => { - mutate("/integrations/google-idp"); - onSuccess(); - }), - loadingMessage: "Deleting integration...", - }); - }; - - const updateIntegration = async () => { - notify({ - title: "Google Workspace Integration", - description: `Google Workspace was successfully updated`, - promise: googleRequest - .put( - { - customerId: customerID, - service_account_key: - accountKeyPlaceholder == serviceAccountKey - ? undefined - : serviceAccountKey, - sync_interval: interval ? parseInt(interval) : 300, - group_prefixes: groupPrefixes || [], - user_group_prefixes: userGroupPrefixes || [], - }, - `/${config.id}`, - ) - .then(() => { - mutate("/integrations/google-idp"); - onSuccess(); - }), - loadingMessage: "Updating integration...", - }); - }; - - const { hasChanges } = useHasChanges([ - customerID, - serviceAccountKey, - interval, - groupPrefixes, - userGroupPrefixes, - ]); - - return ( - - - - - - setTab(v)} - className={"mt-6"} - > - - - - Settings - - - - Group Sync - - - - User Sync - - - - Danger Zone - - - -
    - - - Customer ID -
    - } - placeholder={"62d3a656-c87d-4f30-a242-5b6347e29e9f"} - value={customerID} - onChange={(e) => setCustomerID(e.target.value)} - /> - - - - Service Account Key - - } - placeholder={"YdV7Q~JJ62Xl.LvYoBanxZR2sJA2va_3UbqvncY8"} - value={serviceAccountKey} - readOnly={true} - /> - - setServiceAccountKey(btoa(val))} - /> - -
    -
    - - - The interval in seconds when the synchronization should - happen. - -
    - setInterval(e.target.value)} - customPrefix={ - - } - customSuffix={"Seconds"} - /> -
    - -
    - - -
    - - - By default,{" "} - All Groups{" "} - will be synchronized from your IdP to NetBird.
    - If you want to synchronize only groups that start with a specific - prefix, you can add them below. -
    -
    - -
    - - -
    - - - By default,{" "} - All Users{" "} - will be synchronized from your IdP to NetBird.
    - If you want to synchronize only users that belong to a specific - group, you can add them below. -
    -
    - -
    - - -
    - - - Deleting this integration will remove the ability to sync users - and groups from your IdP to NetBird. If you delete the integration - you will need to reconfigure it again to enable the - synchronization. - -
    - -
    -
    -
    - - - - - - - - -
    - ); -} diff --git a/src/modules/integrations/idp-sync/google-workspace/GoogleWorkspaceSetup.tsx b/src/modules/integrations/idp-sync/google-workspace/GoogleWorkspaceSetup.tsx deleted file mode 100644 index bf4f761..0000000 --- a/src/modules/integrations/idp-sync/google-workspace/GoogleWorkspaceSetup.tsx +++ /dev/null @@ -1,632 +0,0 @@ -import Button from "@components/Button"; -import HelpText from "@components/HelpText"; -import InlineLink from "@components/InlineLink"; -import { Input } from "@components/Input"; -import { JSONFileUpload } from "@components/JSONFileUpload"; -import { Modal, ModalContent, ModalFooter } from "@components/modal/Modal"; -import { notify } from "@components/Notification"; -import Steps from "@components/Steps"; -import { GradientFadedBackground } from "@components/ui/GradientFadedBackground"; -import { Lightbox } from "@components/ui/Lightbox"; -import { Mark } from "@components/ui/Mark"; -import { MinimalList } from "@components/ui/MinimalList"; -import { IconArrowLeft, IconArrowRight } from "@tabler/icons-react"; -import { useApiCall } from "@utils/api"; -import { cn } from "@utils/helpers"; -import { isEmpty } from "lodash"; -import { - Box, - Clock4, - FolderCog2, - FolderGit2, - KeyRound, - Mail, - MailPlus, - PlusCircle, - Repeat, - Settings2, - Shield, - UserCircle, -} from "lucide-react"; -import React, { useState } from "react"; -import { useSWRConfig } from "swr"; -import integrationImage from "@/assets/integrations/google-workspace.png"; -import { GoogleWorkspaceIntegration } from "@/interfaces/IdentityProvider"; -import googleAssignServiceAccount from "@/modules/integrations/idp-sync/google-workspace/images/google-assign-service-account.png"; -import googleEditServiceAccount from "@/modules/integrations/idp-sync/google-workspace/images/google-edit-service-account.png"; -import googlePrivilegesReview from "@/modules/integrations/idp-sync/google-workspace/images/google-privileges-review.png"; -import { IntegrationModalHeader } from "@/modules/integrations/IntegrationModalHeader"; -import { GroupPrefixInput } from "../GroupPrefixInput"; - -type Props = { - open: boolean; - onOpenChange: (open: boolean) => void; - onSuccess?: () => void; -}; - -export default function GoogleWorkspaceSetup({ - open, - onOpenChange, - onSuccess, -}: Props) { - return ( - <> - - { - onOpenChange(false); - onSuccess && onSuccess(); - }} - /> - - - ); -} - -type ModalProps = { - onSuccess: () => void; -}; - -export function SetupContent({ onSuccess }: ModalProps) { - const { mutate } = useSWRConfig(); - const googleRequest = useApiCall( - "/integrations/google-idp", - ); - - const [step, setStep] = useState(0); - const maxSteps = 9; - - const [serviceAccountKey, setServiceAccountKey] = useState(""); - const [customerID, setCustomerID] = useState(""); - const [serviceAccountMail, setServiceAccountMail] = useState(""); - - const clientSecretEntered = !isEmpty(serviceAccountKey); - const customerIDEntered = !isEmpty(customerID); - const serviceAccountMailEntered = !isEmpty(serviceAccountMail); - - const allEntered = - clientSecretEntered && customerIDEntered && serviceAccountMailEntered; - - const isDisabled = - (step == 2 && !serviceAccountMailEntered) || - (step == 3 && !clientSecretEntered) || - (step == 7 && !customerIDEntered); - - const [groupPrefixes, setGroupPrefixes] = useState([]); - const [userGroupPrefixes, setUserGroupPrefixes] = useState([]); - - const connect = async () => { - notify({ - title: "Google Workspace Integration", - description: `Google Workspace was successfully connected to NetBird.`, - promise: googleRequest - .post({ - service_account_key: btoa(serviceAccountKey), // Encode client secret to base64 - customer_id: customerID, - group_prefixes: groupPrefixes || [], - user_group_prefixes: userGroupPrefixes || [], - }) - .then(() => { - mutate("/integrations/google-idp"); - onSuccess(); - }), - loadingMessage: "Setting up integration...", - }); - }; - - return ( - step > 0 && e.preventDefault()} - onInteractOutside={(e) => step > 0 && e.preventDefault()} - onPointerDownOutside={(e) => step > 0 && e.preventDefault()} - > - - - {step > 0 && ( -
    - {Array.from({ length: maxSteps }).map((_, index) => ( -
    = index + 1 && "bg-netbird", - )} - /> - ))} -
    - )} - - - - {step == 0 && ( -
    -
    - - Required Permissions -
    -

    - Ensure that you have an an{" "} - - Google Workspace user account - {" "} - with the following{" "} - - permissions - - .{" "} - { - "If you don't have the required permissions, ask your workspace administrator to grant them to you." - } -

    -
    -
    - - Create Google Workspace applications -
    -
    - - Manage Google Workspace applications -
    -
    -
    - )} - - {step == 1 && ( -
    -

    - - Create a service account -

    - - -

    - Navigate to{" "} - - API Credentials - -

    -
    - -

    - Click CREATE CREDENTIALS at the top and select{" "} - Service account -

    -
    - -

    - Fill in the form with the following values and click{" "} - DONE -

    -
    -
    - - -
    - )} - - {step == 2 && ( -
    -

    - - Get your service account email -

    - - -

    - Navigate to{" "} - - Service Accounts - -

    -
    - -

    - Click NetBird to edit the service account. Copy the - service account email address. -

    - -
    - -

    - Enter your service account email address -

    -
    -
    -
    - - -
    - } - placeholder={"netbird@loadtests-347817.iam.gserviceaccount.com"} - value={serviceAccountMail} - onChange={(e) => setServiceAccountMail(e.target.value)} - /> -
    -
    - )} - - {step == 3 && ( -
    -

    - - Create service account key -

    - - -

    - On the same page, now click the Keys tab, open the{" "} - Add key dropdown and select{" "} - Create new key -

    -
    - -

    - Select JSON as the key type and click{" "} - Create -

    -
    - -

    - Most browsers immediately download the new key and save it in a - download folder on your computer. Read how to manage and secure - your service keys{" "} - - here - - . -

    -
    -
    -
    - - {serviceAccountKey && ( -
    - - -
    - } - placeholder={"YdV7Q~JJ62Xl.LvYoBanxZR2sJA2va_3UbqvncY8"} - value={btoa(serviceAccountKey)} - readOnly={true} - /> -
    - )} -
    - - )} - - {step == 4 && ( -
    -

    - - Create admin role -

    - - -

    - Navigate to{" "} - - Admin Console - -

    -
    - -

    - Select Account on the left menu and then click{" "} - Admin Roles -

    -
    - -

    - Click Create new role and fill in the form with the - following values -

    -
    -
    - -
    - )} - - {step == 5 && ( -
    -

    - - Add role privileges -

    - - -

    - Scroll down to Admin API privileges and add the - following privileges to the role -

    - -
    - -

    - Verify preview of assigned Admin API privileges to ensure that - everything is properly configured, and then click{" "} - CREATE ROLE -

    - -
    -
    -
    - )} - - {step == 6 && ( -
    -

    - - Assign service account -

    - - -

    - Click Assign service accounts -

    -
    - -

    - Enter your E-Mail and then click ADD -

    - -
    - -

    - Click ASSIGN ROLE -

    - -
    -
    -
    - )} - - {step == 7 && ( -
    -

    - - Enter Customer ID -

    - - -

    - Navigate to{" "} - - Account Settings - -

    -
    - -

    - Take note of the Customer ID and enter it below -

    -
    -
    -
    - - - Customer ID -
    - } - placeholder={"C03f4c3po"} - value={customerID} - onChange={(e) => setCustomerID(e.target.value)} - /> -
    - - )} - - {step == 8 && ( -
    -

    - - Groups to be synchronized -

    - -
    -
    - - By default,{" "} - All Groups{" "} - will be synchronized from your IdP to NetBird.
    - If you want to synchronize only groups that start with a - specific prefix, you can add them below. -
    -
    - -
    -
    - )} - - {step == 9 && ( -
    -

    - - Users to be synchronized -

    - -
    -
    - - By default,{" "} - All Users{" "} - will be synchronized from your IdP to NetBird.
    - If you want to synchronize only users that belong to a specific - group, you can add them below. -
    -
    - - -
    -
    - )} - - - {step > 0 && ( - - )} - {step >= 0 && step < maxSteps && ( - - )} - {step == maxSteps && ( - - )} - - {step == 0 && ( -
    - -
    - Estimated setup time: - 10-20 Minutes -
    -
    - )} -
    - ); -} diff --git a/src/modules/integrations/idp-sync/google-workspace/images/google-assign-service-account.png b/src/modules/integrations/idp-sync/google-workspace/images/google-assign-service-account.png deleted file mode 100644 index 0fc7450..0000000 Binary files a/src/modules/integrations/idp-sync/google-workspace/images/google-assign-service-account.png and /dev/null differ diff --git a/src/modules/integrations/idp-sync/google-workspace/images/google-edit-service-account.png b/src/modules/integrations/idp-sync/google-workspace/images/google-edit-service-account.png deleted file mode 100644 index 50a6bdc..0000000 Binary files a/src/modules/integrations/idp-sync/google-workspace/images/google-edit-service-account.png and /dev/null differ diff --git a/src/modules/integrations/idp-sync/google-workspace/images/google-new-admin-role.png b/src/modules/integrations/idp-sync/google-workspace/images/google-new-admin-role.png deleted file mode 100644 index b87dfd1..0000000 Binary files a/src/modules/integrations/idp-sync/google-workspace/images/google-new-admin-role.png and /dev/null differ diff --git a/src/modules/integrations/idp-sync/google-workspace/images/google-privileges-review.png b/src/modules/integrations/idp-sync/google-workspace/images/google-privileges-review.png deleted file mode 100644 index f66a127..0000000 Binary files a/src/modules/integrations/idp-sync/google-workspace/images/google-privileges-review.png and /dev/null differ diff --git a/src/modules/integrations/idp-sync/google-workspace/images/google-service-account-create.png b/src/modules/integrations/idp-sync/google-workspace/images/google-service-account-create.png deleted file mode 100644 index 21e0ddb..0000000 Binary files a/src/modules/integrations/idp-sync/google-workspace/images/google-service-account-create.png and /dev/null differ diff --git a/src/modules/integrations/idp-sync/google-workspace/images/google-service-account-privileges.png b/src/modules/integrations/idp-sync/google-workspace/images/google-service-account-privileges.png deleted file mode 100644 index fa8bf3e..0000000 Binary files a/src/modules/integrations/idp-sync/google-workspace/images/google-service-account-privileges.png and /dev/null differ diff --git a/src/modules/integrations/idp-sync/okta-scim/Okta.tsx b/src/modules/integrations/idp-sync/okta-scim/Okta.tsx deleted file mode 100644 index aa6807d..0000000 --- a/src/modules/integrations/idp-sync/okta-scim/Okta.tsx +++ /dev/null @@ -1,131 +0,0 @@ -import Button from "@components/Button"; -import { notify } from "@components/Notification"; -import { SkeletonIntegration } from "@components/skeletons/SkeletonIntegration"; -import useFetchApi, { useApiCall } from "@utils/api"; -import dayjs from "dayjs"; -import { RefreshCw, Settings } from "lucide-react"; -import * as React from "react"; -import { useEffect, useState } from "react"; -import { useSWRConfig } from "swr"; -import integrationImage from "@/assets/integrations/okta.png"; -import { - IdentityProviderLog, - OktaIntegration, -} from "@/interfaces/IdentityProvider"; -import OktaConfiguration from "@/modules/integrations/idp-sync/okta-scim/OktaConfiguration"; -import OktaSetup from "@/modules/integrations/idp-sync/okta-scim/OktaSetup"; -import { useIntegrations } from "@/modules/integrations/idp-sync/useIntegrations"; -import { IntegrationCard } from "@/modules/integrations/IntegrationCard"; - -export const Okta = () => { - const { mutate } = useSWRConfig(); - const [setupModal, setSetupModal] = useState(false); - - const { - okta: integration, - isAnyIntegrationEnabled, - isOktaLoading, - } = useIntegrations(); - const oktaRequest = useApiCall( - "/integrations/okta-scim-idp", - ); - - const [enabled, setEnabled] = useState( - integration ? integration.enabled : false, - ); - - useEffect(() => { - setEnabled(integration?.enabled ?? false); - }, [integration]); - - const toggleSwitch = async (state: boolean) => { - if (!integration) return setSetupModal(true); - - notify({ - title: "Okta Integration", - description: `Okta was successfully ${state ? "enabled" : "disabled"}`, - promise: oktaRequest - .put( - { - enabled: state, - }, - "/" + integration.id, - ) - .then(() => { - mutate("/integrations/okta-scim-idp"); - setEnabled(state); - }), - loadingMessage: "Updating integration...", - }); - }; - - return isOktaLoading ? ( - - ) : ( - <> - setSetupModal(true)} - > - {integration && } - - { - setEnabled(true); - mutate("/integrations/okta-scim-idp"); - }} - /> - - ); -}; - -type ConfigurationProps = { - config: OktaIntegration; -}; -const ConfigurationButton = ({ config }: ConfigurationProps) => { - const { data: logs } = useFetchApi( - `/integrations/okta-scim-idp/${config.id}/logs`, - ); - - const [configModal, setConfigModal] = useState(false); - - return ( - <> -
    - - - -
    - - - ); -}; diff --git a/src/modules/integrations/idp-sync/okta-scim/OktaConfiguration.tsx b/src/modules/integrations/idp-sync/okta-scim/OktaConfiguration.tsx deleted file mode 100644 index ef096ab..0000000 --- a/src/modules/integrations/idp-sync/okta-scim/OktaConfiguration.tsx +++ /dev/null @@ -1,333 +0,0 @@ -import Button from "@components/Button"; -import Card from "@components/Card"; -import HelpText from "@components/HelpText"; -import { Label } from "@components/Label"; -import { - Modal, - ModalClose, - ModalContent, - ModalFooter, -} from "@components/modal/Modal"; -import { notify } from "@components/Notification"; -import { Tabs, TabsContent, TabsList, TabsTrigger } from "@components/Tabs"; -import { GradientFadedBackground } from "@components/ui/GradientFadedBackground"; -import { useHasChanges } from "@hooks/useHasChanges"; -import { useApiCall } from "@utils/api"; -import { cn } from "@utils/helpers"; -import { - AlertOctagon, - Cog, - FolderGit2, - KeyRound, - RefreshCcw, - UserCircle, -} from "lucide-react"; -import React, { useState } from "react"; -import { useSWRConfig } from "swr"; -import integrationImage from "@/assets/integrations/entra-id.png"; -import { useDialog } from "@/contexts/DialogProvider"; -import { OktaIntegration } from "@/interfaces/IdentityProvider"; -import { GroupPrefixInput } from "@/modules/integrations/idp-sync/GroupPrefixInput"; -import { useIntegrations } from "@/modules/integrations/idp-sync/useIntegrations"; -import { IntegrationModalHeader } from "@/modules/integrations/IntegrationModalHeader"; - -type Props = { - open: boolean; - onOpenChange: (open: boolean) => void; - onSuccess?: () => void; -}; - -export default function OktaConfiguration({ - open, - onOpenChange, - onSuccess, -}: Props) { - const { okta } = useIntegrations(); - - return ( - <> - - {okta && ( - { - onOpenChange(false); - onSuccess && onSuccess(); - }} - config={okta} - /> - )} - - - ); -} - -type ModalProps = { - onSuccess: () => void; - config: OktaIntegration; -}; - -export function ConfigurationContent({ onSuccess, config }: ModalProps) { - const { mutate } = useSWRConfig(); - const { confirm } = useDialog(); - - const [tab, setTab] = useState("settings"); - - const oktaRequest = useApiCall( - "/integrations/okta-scim-idp", - ); - - const clientSecretPlaceholder = "******************************"; - const [authToken, setAuthToken] = useState( - config.auth_token || clientSecretPlaceholder, - ); - - const [groupPrefixes, setGroupPrefixes] = useState( - config.group_prefixes || [], - ); - const [userGroupPrefixes, setUserGroupPrefixes] = useState( - config.user_group_prefixes || [], - ); - - const { hasChanges, updateRef } = useHasChanges([ - authToken, - groupPrefixes, - userGroupPrefixes, - ]); - - const regenerateAuthToken = async () => { - const choice = await confirm({ - title: `Regenerate Auth Token?`, - description: - "Are you sure you want to regenerate the auth token? You will need to update the token in your Okta configuration.", - confirmText: "Regenerate", - cancelText: "Cancel", - type: "default", - }); - - if (!choice) return; - - notify({ - title: "Okta Integration", - description: `Auth token for Okta was successfully regenerated`, - promise: oktaRequest.post({}, `/${config.id}/token`).then((r) => { - mutate("/integrations/okta-scim-idp"); - setAuthToken(r.auth_token); - updateRef([r.auth_token, groupPrefixes, userGroupPrefixes]); - }), - loadingMessage: "Updating your auth token...", - }); - }; - - const deleteIntegration = async () => { - const choice = await confirm({ - title: `Delete integration?`, - description: "Are you sure you want to delete this integration?", - confirmText: "Delete", - cancelText: "Cancel", - type: "danger", - }); - - if (!choice) return; - - notify({ - title: "Okta Integration", - description: `Okta was successfully deleted`, - promise: oktaRequest.del({}, `/${config.id}`).then(() => { - mutate("/integrations/okta-scim-idp"); - onSuccess(); - }), - loadingMessage: "Deleting integration...", - }); - }; - - const updateIntegration = async () => { - notify({ - title: "Okta Integration", - description: `Okta was successfully updated`, - promise: oktaRequest - .put( - { - group_prefixes: groupPrefixes || [], - user_group_prefixes: userGroupPrefixes || [], - }, - `/${config.id}`, - ) - .then(() => { - mutate("/integrations/okta-scim-idp"); - onSuccess(); - }), - loadingMessage: "Updating integration...", - }); - }; - - return ( - - - - - - setTab(v)} - className={"mt-6"} - > - - - - Settings - - - - Group Sync - - - - User Sync - - - - Danger Zone - - - -
    - - - - - Auth Token - - } - value={authToken} - /> - - - -
    -
    - - -
    - - - By default,{" "} - All Groups{" "} - will be synchronized from your IdP to NetBird.
    - If you want to synchronize only groups that start with a specific - prefix, you can add them below. -
    -
    - -
    - - -
    - - - By default,{" "} - All Users{" "} - will be synchronized from your IdP to NetBird.
    - If you want to synchronize only users that belong to a specific - group, you can add them below. -
    -
    - -
    - - -
    - - - Deleting this integration will remove the ability to sync users - and groups from your IdP to NetBird. If you delete the integration - you will need to reconfigure it again to enable the - synchronization. - -
    - -
    -
    -
    - - - - - - - - -
    - ); -} diff --git a/src/modules/integrations/idp-sync/okta-scim/OktaSetup.tsx b/src/modules/integrations/idp-sync/okta-scim/OktaSetup.tsx deleted file mode 100644 index 3d8ad25..0000000 --- a/src/modules/integrations/idp-sync/okta-scim/OktaSetup.tsx +++ /dev/null @@ -1,491 +0,0 @@ -import Button from "@components/Button"; -import { Modal, ModalContent, ModalFooter } from "@components/modal/Modal"; -import Steps from "@components/Steps"; -import { GradientFadedBackground } from "@components/ui/GradientFadedBackground"; -import { Lightbox } from "@components/ui/Lightbox"; -import { Mark } from "@components/ui/Mark"; -import { MinimalList } from "@components/ui/MinimalList"; -import { - IconArrowLeft, - IconArrowRight, - IconCirclePlus, -} from "@tabler/icons-react"; -import { useApiCall } from "@utils/api"; -import { cn } from "@utils/helpers"; -import { isEmpty } from "lodash"; -import { - Box, - Clock4, - FolderGit2, - PlusCircle, - Settings2, - Share2, - Shield, - UserCircle, -} from "lucide-react"; -import React, { useEffect, useState } from "react"; -import integrationImage from "@/assets/integrations/okta.png"; -import { OktaIntegration } from "@/interfaces/IdentityProvider"; -import oktaGroupsAssignments from "@/modules/integrations/idp-sync/okta-scim/images/okta-groups-assignments.png"; -import oktaSAMLConfig from "@/modules/integrations/idp-sync/okta-scim/images/okta-saml-configuration.png"; -import oktaSCIMProvisioning from "@/modules/integrations/idp-sync/okta-scim/images/okta-scim-provisioning-enabled.png"; -import oktaSCIMToApp from "@/modules/integrations/idp-sync/okta-scim/images/okta-scim-to-app-sync-enabled.png"; -import oktaSyncGroups from "@/modules/integrations/idp-sync/okta-scim/images/okta-sync-groups.png"; -import { IntegrationModalHeader } from "@/modules/integrations/IntegrationModalHeader"; - -type Props = { - open: boolean; - onOpenChange: (open: boolean) => void; - onSuccess?: () => void; -}; - -export default function OktaSetup({ open, onOpenChange, onSuccess }: Props) { - const [authToken, setAuthToken] = useState(""); - - return ( - <> - - {open && ( - { - onOpenChange(false); - onSuccess && onSuccess(); - }} - /> - )} - - - ); -} - -type ModalProps = { - onSuccess: () => void; - authToken: string; - setAuthToken: (token: string) => void; -}; - -export function SetupContent({ - onSuccess, - authToken, - setAuthToken, -}: ModalProps) { - const integrationsRequest = useApiCall( - "/integrations/okta-scim-idp", - ); - const authTokenRequest = useApiCall( - "/integrations/okta-scim-idp", - ).post; - - const [step, setStep] = useState(0); - const maxSteps = 7; - - // const [groupPrefixes, setGroupPrefixes] = useState([]); - // const [userGroupPrefixes, setUserGroupPrefixes] = useState([]); - - useEffect(() => { - const getAuthToken = async () => { - const integration = await integrationsRequest.get(); - if (!isEmpty(integration)) { - const integrationId = integration[0].id; - if (authToken != "") return authToken; - const okta = await authTokenRequest({}, `${integrationId}/token`); - if (!okta) return ""; - return okta.auth_token; - } else { - const okta = await authTokenRequest({}); - if (!okta) return ""; - return okta.auth_token; - } - }; - - getAuthToken().then((t) => setAuthToken(t)); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - return ( - step > 0 && e.preventDefault()} - onInteractOutside={(e) => step > 0 && e.preventDefault()} - onPointerDownOutside={(e) => step > 0 && e.preventDefault()} - > - - - {step > 0 && ( -
    - {Array.from({ length: maxSteps }).map((_, index) => ( -
    = index + 1 && "bg-netbird", - )} - /> - ))} -
    - )} - - - - {step == 0 && ( -
    -
    - - Required Permissions -
    -

    - Ensure that you have an an{" "} - - Okta user account - {" "} - with the following{" "} - - permissions - - .{" "} - { - "If you don't have the required permissions, ask your Okta administrator to grant them to you." - } -

    -
    -
    - - Add Okta applications -
    -
    - - Configure Okta applications -
    -
    -
    - )} - - {step == 1 && ( -
    -

    - - Create and configure Okta application -

    - - -

    Navigate to your Okta Admin Dashboard

    -
    - -

    - Click Applications in the left menu then click on - Applications -

    -
    - -

    - Click Create App Integration, select{" "} - SAML 2.0 and click Next -

    -
    -
    -
    - )} - - {step == 2 && ( -
    -

    - - Create SAML Integration -

    - - -

    - Enter NetBird SCIM as the{" "} - App name and click Next -

    -
    - -

    - Enter http://localhost as the Single sign-on - URL and Audience URI (SP Entity ID) and click Next -

    - -
    - -

    - Select App type as - This is an internal app that we have created - and click Finish -

    -
    -
    -
    - )} - - {step == 3 && ( -
    -

    - - Enable and configure SCIM provisioning -

    - - -

    Navigate to your Okta Admin Dashboard

    -
    - -

    - Click Applications in the left menu then click on - Applications -

    -
    - -

    - Select the NetBird SCIM application we created - earlier -

    -
    - -

    - Click General tab and in App Settings{" "} - click Edit to update the settings -

    -
    - -

    - Tick Enable SCIM provisioning and click{" "} - Save -

    - -
    -
    -
    - )} - - {step == 4 && ( -
    -

    - - Enable and configure SCIM provisioning -

    - - -

    - Click Provisioning tab and under{" "} - SCIM connection click - Edit -

    -
    - -

    - Fill in the form with the following details -

    - - Push New Users
    - Push Profile Updates
    - Push Groups -
    - ), - noCopy: true, - }, - { - label: "Authentication Mode", - value: "HTTP Header", - noCopy: true, - }, - { - label: "Authorization (Bearer)", - value: authToken, - }, - ]} - /> - - -

    - Click on Test Connector Configuration to verify if - the SCIM configuration is working. After the test is completed, - make sure Create Users,{" "} - Update User Attributes, and{" "} - Push Groups were successful. -

    -
    - -

    - Click Save -

    -
    - -
    - )} - - {step == 5 && ( -
    -

    - - Configure SCIM provisioning to NetBird -

    - - -

    - Go to the Provisioning tab, and select the{" "} - To App settings and click Edit -

    -
    - -

    - Enable Create Users,{" "} - Update User Attributes, and{" "} - Deactivate Users and click Save -

    - -
    -
    -
    - )} - - {step == 6 && ( -
    -

    - - Assign members to NetBird -

    - - -

    - Go to the Assignments tab, select the{" "} - Assign and click Assign to Groups -

    - -
    - -

    - Select the groups you want to provision, and then select{" "} - Assign and click Save and Go Back -

    -
    - -

    - Select Done after you have finished assigning - groups. At this point, all members of the groups assigned to the - application will be synced to NetBird. -

    -
    -
    -
    - )} - - {step == 7 && ( -
    -

    - - Assign groups to NetBird -

    - - -

    - Go to the Push Groups tab, select{" "} - Push Groups and click{" "} - Find groups by name -

    - -
    - -

    - Search groups to push and then click Save. The - selected groups will then be synced to NetBird. -

    -
    -
    -
    - )} - - - {step > 0 && ( - - )} - {step >= 0 && step < maxSteps && ( - - )} - {step == maxSteps && ( - - )} - - {step == 0 && ( -
    - -
    - Estimated setup time: - 10-20 Minutes -
    -
    - )} -
    - ); -} diff --git a/src/modules/integrations/idp-sync/okta-scim/images/okta-groups-assignments.png b/src/modules/integrations/idp-sync/okta-scim/images/okta-groups-assignments.png deleted file mode 100644 index adf364e..0000000 Binary files a/src/modules/integrations/idp-sync/okta-scim/images/okta-groups-assignments.png and /dev/null differ diff --git a/src/modules/integrations/idp-sync/okta-scim/images/okta-saml-configuration.png b/src/modules/integrations/idp-sync/okta-scim/images/okta-saml-configuration.png deleted file mode 100644 index 00f3c95..0000000 Binary files a/src/modules/integrations/idp-sync/okta-scim/images/okta-saml-configuration.png and /dev/null differ diff --git a/src/modules/integrations/idp-sync/okta-scim/images/okta-scim-provisioning-enabled.png b/src/modules/integrations/idp-sync/okta-scim/images/okta-scim-provisioning-enabled.png deleted file mode 100644 index 682e2fa..0000000 Binary files a/src/modules/integrations/idp-sync/okta-scim/images/okta-scim-provisioning-enabled.png and /dev/null differ diff --git a/src/modules/integrations/idp-sync/okta-scim/images/okta-scim-to-app-sync-enabled.png b/src/modules/integrations/idp-sync/okta-scim/images/okta-scim-to-app-sync-enabled.png deleted file mode 100644 index 3118610..0000000 Binary files a/src/modules/integrations/idp-sync/okta-scim/images/okta-scim-to-app-sync-enabled.png and /dev/null differ diff --git a/src/modules/integrations/idp-sync/okta-scim/images/okta-sync-groups.png b/src/modules/integrations/idp-sync/okta-scim/images/okta-sync-groups.png deleted file mode 100644 index 0fb7585..0000000 Binary files a/src/modules/integrations/idp-sync/okta-scim/images/okta-sync-groups.png and /dev/null differ diff --git a/src/modules/integrations/idp-sync/useIntegrations.tsx b/src/modules/integrations/idp-sync/useIntegrations.tsx deleted file mode 100644 index a89dd4c..0000000 --- a/src/modules/integrations/idp-sync/useIntegrations.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import useFetchApi from "@utils/api"; -import { - AzureADIntegration, - GoogleWorkspaceIntegration, - OktaIntegration, -} from "@/interfaces/IdentityProvider"; - -export const useIntegrations = () => { - const { data: azureIntegrations, isLoading: isAzureLoading } = useFetchApi< - AzureADIntegration[] - >("/integrations/azure-idp"); - const { data: googleIntegrations, isLoading: isGoogleLoading } = useFetchApi< - GoogleWorkspaceIntegration[] - >("/integrations/google-idp"); - const { data: oktaIntegration, isLoading: isOktaLoading } = useFetchApi< - OktaIntegration[] - >("/integrations/okta-scim-idp"); - - const azure = azureIntegrations?.[0]; - const google = googleIntegrations?.[0]; - const okta = oktaIntegration?.[0]; - - const isAnyIntegrationEnabled = - azure?.enabled || google?.enabled || okta?.enabled; - - return { - azure, - google, - okta, - isAnyIntegrationEnabled, - isAzureLoading, - isGoogleLoading, - isOktaLoading, - }; -}; diff --git a/src/modules/settings/AuthenticationTab.tsx b/src/modules/settings/AuthenticationTab.tsx index a24b194..11f0235 100644 --- a/src/modules/settings/AuthenticationTab.tsx +++ b/src/modules/settings/AuthenticationTab.tsx @@ -16,8 +16,7 @@ import { import * as Tabs from "@radix-ui/react-tabs"; import { useApiCall } from "@utils/api"; import { cn, isInt } from "@utils/helpers"; -import { isLocalDev, isNetBirdHosted } from "@utils/netbird"; -import { CalendarClock, ShieldIcon, TimerReset, VoteIcon } from "lucide-react"; +import { CalendarClock, ShieldIcon, TimerReset } from "lucide-react"; import React, { useMemo, useState } from "react"; import { useSWRConfig } from "swr"; import SettingsIcon from "@/assets/icons/SettingsIcon"; @@ -143,22 +142,6 @@ export default function AuthenticationTab({ account }: Props) {
    - {(isLocalDev() || isNetBirdHosted()) && ( -
    - - - Peer approval - - } - helpText={"Require peers to be approved by an administrator."} - /> -
    - )} -