diff --git a/ui/litellm-dashboard/src/app/(dashboard)/settings/admin-settings/page.tsx b/ui/litellm-dashboard/src/app/(dashboard)/settings/admin-settings/page.tsx index be2551f670f..14f7ca1eeb5 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/settings/admin-settings/page.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/settings/admin-settings/page.tsx @@ -1,26 +1,11 @@ "use client"; -import AdminPanel from "@/components/admins"; -import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized"; -import { useState } from "react"; -import useTeams from "@/app/(dashboard)/hooks/useTeams"; +import AdminPanel from "@/components/Admins"; const AdminSettings = () => { - const { teams, setTeams } = useTeams(); - - const [searchParams, setSearchParams] = useState(() => - typeof window === "undefined" ? new URLSearchParams() : new URLSearchParams(window.location.search), - ); - const { accessToken, userId, premiumUser, showSSOBanner } = useAuthorized(); return ( ); }; diff --git a/ui/litellm-dashboard/src/app/page.tsx b/ui/litellm-dashboard/src/app/page.tsx index 8e887d80fb3..fe460abf6bb 100644 --- a/ui/litellm-dashboard/src/app/page.tsx +++ b/ui/litellm-dashboard/src/app/page.tsx @@ -4,7 +4,7 @@ import APIReferenceView from "@/app/(dashboard)/api-reference/APIReferenceView"; import SidebarProvider from "@/app/(dashboard)/components/SidebarProvider"; import OldModelDashboard from "@/app/(dashboard)/models-and-endpoints/ModelsAndEndpointsView"; import PlaygroundPage from "@/app/(dashboard)/playground/page"; -import AdminPanel from "@/components/admins"; +import AdminPanel from "@/components/Admins"; import AgentsPanel from "@/components/agents"; import BudgetPanel from "@/components/budgets/budget_panel"; import CacheDashboard from "@/components/cache_dashboard"; @@ -469,12 +469,6 @@ function CreateKeyPageContent() { /> ) : page == "admin-panel" ? ( ) : page == "api_ref" ? ( diff --git a/ui/litellm-dashboard/src/components/Admins.test.tsx b/ui/litellm-dashboard/src/components/Admins.test.tsx new file mode 100644 index 00000000000..77b403fab1d --- /dev/null +++ b/ui/litellm-dashboard/src/components/Admins.test.tsx @@ -0,0 +1,325 @@ +import { render, screen, waitFor } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { beforeEach, describe, expect, it, vi } from "vitest"; +import AdminPanel from "./Admins"; + +const mockGetSSOSettings = vi.fn(); +const mockGetAllowedIPs = vi.fn(); +const mockAddAllowedIP = vi.fn(); +const mockDeleteAllowedIP = vi.fn(); + +vi.mock("./networking", () => ({ + getSSOSettings: (...args: unknown[]) => mockGetSSOSettings(...args), + getAllowedIPs: (...args: unknown[]) => mockGetAllowedIPs(...args), + addAllowedIP: (...args: unknown[]) => mockAddAllowedIP(...args), + deleteAllowedIP: (...args: unknown[]) => mockDeleteAllowedIP(...args), +})); + +vi.mock("./constants", () => ({ + useBaseUrl: () => "http://localhost:4000", +})); + +vi.mock("./Settings/AdminSettings/SSOSettings/SSOSettings", () => ({ + default: () =>
SSO Settings
, +})); + +vi.mock("./Settings/AdminSettings/UISettings/UISettings", () => ({ + default: () =>
UI Settings
, +})); + +vi.mock("./SCIM", () => ({ + default: () =>
SCIM Config
, +})); + +vi.mock("./SSOModals", () => ({ + default: () =>
SSO Modals
, +})); + +vi.mock("./UIAccessControlForm", () => ({ + default: () =>
UI Access Control Form
, +})); + +const mockUseAuthorized = vi.fn(); +vi.mock("@/app/(dashboard)/hooks/useAuthorized", () => ({ + default: () => mockUseAuthorized(), +})); + +describe("AdminPanel", () => { + beforeEach(() => { + vi.clearAllMocks(); + mockUseAuthorized.mockReturnValue({ + premiumUser: false, + accessToken: "test-token", + userId: "user-1", + }); + mockGetSSOSettings.mockResolvedValue({ + values: {}, + }); + mockGetAllowedIPs.mockResolvedValue([]); + mockAddAllowedIP.mockResolvedValue({}); + mockDeleteAllowedIP.mockResolvedValue({}); + }); + + it("should render the admin panel", () => { + render(); + expect(screen.getByRole("heading", { name: /admin access/i })).toBeInTheDocument(); + expect(screen.getByText(/go to 'internal users' page to add other admins/i)).toBeInTheDocument(); + }); + + describe("Tabs", () => { + it("should render all tabs", () => { + render(); + expect(screen.getByRole("tab", { name: /sso settings/i })).toBeInTheDocument(); + expect(screen.getByRole("tab", { name: /security settings/i })).toBeInTheDocument(); + expect(screen.getByRole("tab", { name: /scim/i })).toBeInTheDocument(); + expect(screen.getByRole("tab", { name: /ui settings/i })).toBeInTheDocument(); + }); + + it("should display Security Settings content when Security Settings tab is clicked", async () => { + const user = userEvent.setup(); + render(); + const securityTab = screen.getByRole("tab", { name: /security settings/i }); + await user.click(securityTab); + expect(screen.getByRole("heading", { name: /security settings/i })).toBeInTheDocument(); + expect(screen.getByRole("button", { name: /add sso/i })).toBeInTheDocument(); + expect(screen.getByRole("button", { name: /allowed ips/i })).toBeInTheDocument(); + expect(screen.getByRole("button", { name: /ui access control/i })).toBeInTheDocument(); + }); + + it("should display SCIM content when SCIM tab is clicked", async () => { + const user = userEvent.setup(); + render(); + const scimTab = screen.getByRole("tab", { name: /scim/i }); + await user.click(scimTab); + expect(screen.getByText("SCIM Config")).toBeInTheDocument(); + }); + }); + + describe("SSO Configuration", () => { + it("should check SSO configuration on mount when accessToken is available", async () => { + render(); + await waitFor(() => { + expect(mockGetSSOSettings).toHaveBeenCalledWith("test-token"); + }); + }); + + it("should display 'Add SSO' button when SSO is not configured", async () => { + const user = userEvent.setup(); + mockGetSSOSettings.mockResolvedValue({ + values: {}, + }); + render(); + const securityTab = screen.getByRole("tab", { name: /security settings/i }); + await user.click(securityTab); + await waitFor(() => { + expect(screen.getByRole("button", { name: /add sso/i })).toBeInTheDocument(); + }); + }); + + it("should display 'Edit SSO Settings' button when SSO is configured", async () => { + const user = userEvent.setup(); + mockGetSSOSettings.mockResolvedValue({ + values: { + google_client_id: "test-id", + google_client_secret: "test-secret", + }, + }); + render(); + const securityTab = screen.getByRole("tab", { name: /security settings/i }); + await user.click(securityTab); + await waitFor(() => { + expect(screen.getByRole("button", { name: /edit sso settings/i })).toBeInTheDocument(); + }); + }); + + it("should detect Google SSO configuration", async () => { + mockGetSSOSettings.mockResolvedValue({ + values: { + google_client_id: "test-id", + google_client_secret: "test-secret", + }, + }); + render(); + await waitFor(() => { + expect(mockGetSSOSettings).toHaveBeenCalled(); + }); + }); + + it("should detect Microsoft SSO configuration", async () => { + mockGetSSOSettings.mockResolvedValue({ + values: { + microsoft_client_id: "test-id", + microsoft_client_secret: "test-secret", + }, + }); + render(); + await waitFor(() => { + expect(mockGetSSOSettings).toHaveBeenCalled(); + }); + }); + + it("should detect Generic SSO configuration", async () => { + mockGetSSOSettings.mockResolvedValue({ + values: { + generic_client_id: "test-id", + generic_client_secret: "test-secret", + }, + }); + render(); + await waitFor(() => { + expect(mockGetSSOSettings).toHaveBeenCalled(); + }); + }); + + it("should handle SSO configuration check error gracefully", async () => { + mockGetSSOSettings.mockRejectedValue(new Error("Network error")); + render(); + await waitFor(() => { + expect(mockGetSSOSettings).toHaveBeenCalled(); + }); + }); + }); + + describe("Allowed IPs", () => { + beforeEach(async () => { + const user = userEvent.setup(); + mockUseAuthorized.mockReturnValue({ + premiumUser: true, + accessToken: "test-token", + userId: "user-1", + }); + render(); + const securityTab = screen.getByRole("tab", { name: /security settings/i }); + await user.click(securityTab); + }); + + it("should open allowed IPs modal when premium user clicks Allowed IPs button", async () => { + const user = userEvent.setup(); + mockGetAllowedIPs.mockResolvedValue(["192.168.1.1", "10.0.0.1"]); + const allowedIPsButton = screen.getByRole("button", { name: /allowed ips/i }); + await user.click(allowedIPsButton); + await waitFor(() => { + expect(screen.getByRole("dialog", { name: /manage allowed ip addresses/i })).toBeInTheDocument(); + }); + }); + + it("should display 'All IP Addresses Allowed' when no IPs are configured", async () => { + const user = userEvent.setup(); + mockGetAllowedIPs.mockResolvedValue([]); + const allowedIPsButton = screen.getByRole("button", { name: /allowed ips/i }); + await user.click(allowedIPsButton); + await waitFor(() => { + expect(screen.getByText("All IP Addresses Allowed")).toBeInTheDocument(); + }); + }); + + it("should display list of allowed IPs", async () => { + const user = userEvent.setup(); + mockGetAllowedIPs.mockResolvedValue(["192.168.1.1", "10.0.0.1"]); + const allowedIPsButton = screen.getByRole("button", { name: /allowed ips/i }); + await user.click(allowedIPsButton); + await waitFor(() => { + expect(screen.getByText("192.168.1.1")).toBeInTheDocument(); + expect(screen.getByText("10.0.0.1")).toBeInTheDocument(); + }); + }); + + it("should show delete button for IP addresses except 'All IP Addresses Allowed'", async () => { + const user = userEvent.setup(); + mockGetAllowedIPs.mockResolvedValue(["192.168.1.1", "All IP Addresses Allowed"]); + const allowedIPsButton = screen.getByRole("button", { name: /allowed ips/i }); + await user.click(allowedIPsButton); + await waitFor(() => { + const deleteButtons = screen.queryAllByRole("button", { name: /delete/i }); + expect(deleteButtons.length).toBeGreaterThan(0); + }); + }); + + it("should not show delete button for 'All IP Addresses Allowed'", async () => { + const user = userEvent.setup(); + mockGetAllowedIPs.mockResolvedValue(["All IP Addresses Allowed"]); + const allowedIPsButton = screen.getByRole("button", { name: /allowed ips/i }); + await user.click(allowedIPsButton); + await waitFor(() => { + expect(screen.getByText("All IP Addresses Allowed")).toBeInTheDocument(); + }); + const deleteButtons = screen.queryAllByRole("button", { name: /delete/i }); + expect(deleteButtons.length).toBe(0); + }); + + it("should handle error when fetching allowed IPs fails", async () => { + const user = userEvent.setup(); + mockGetAllowedIPs.mockRejectedValue(new Error("Network error")); + const allowedIPsButton = screen.getByRole("button", { name: /allowed ips/i }); + await user.click(allowedIPsButton); + await waitFor(() => { + expect(mockGetAllowedIPs).toHaveBeenCalled(); + }); + }); + }); + + describe("UI Access Control", () => { + it("should show premium user message when non-premium user tries to access UI Access Control", async () => { + const user = userEvent.setup(); + mockUseAuthorized.mockReturnValue({ + premiumUser: false, + accessToken: "test-token", + userId: "user-1", + }); + render(); + const securityTab = screen.getByRole("tab", { name: /security settings/i }); + await user.click(securityTab); + const uiAccessControlButton = screen.getByRole("button", { name: /ui access control/i }); + await user.click(uiAccessControlButton); + await waitFor(() => { + expect(screen.queryByRole("dialog", { name: /ui access control settings/i })).not.toBeInTheDocument(); + }); + }); + + it("should open UI Access Control modal when premium user clicks button", async () => { + const user = userEvent.setup(); + mockUseAuthorized.mockReturnValue({ + premiumUser: true, + accessToken: "test-token", + userId: "user-1", + }); + render(); + const securityTab = screen.getByRole("tab", { name: /security settings/i }); + await user.click(securityTab); + const uiAccessControlButton = screen.getByRole("button", { name: /ui access control/i }); + await user.click(uiAccessControlButton); + await waitFor(() => { + expect(screen.getByRole("dialog", { name: /ui access control settings/i })).toBeInTheDocument(); + expect(screen.getByText("UI Access Control Form")).toBeInTheDocument(); + }); + }); + }); + + describe("Login without SSO", () => { + it("should display fallback login URL", async () => { + const user = userEvent.setup(); + render(); + const securityTab = screen.getByRole("tab", { name: /security settings/i }); + await user.click(securityTab); + const link = screen.getByRole("link", { name: /http:\/\/localhost:4000\/fallback\/login/i }); + expect(link).toBeInTheDocument(); + expect(link).toHaveAttribute("href", "http://localhost:4000/fallback/login"); + expect(link).toHaveAttribute("target", "_blank"); + }); + }); + + describe("SSO Configuration Deprecation Warning", () => { + it("should display deprecation warning in Security Settings tab", async () => { + const user = userEvent.setup(); + render(); + const securityTab = screen.getByRole("tab", { name: /security settings/i }); + await user.click(securityTab); + await waitFor(() => { + expect(screen.getByText(/sso configuration deprecated/i)).toBeInTheDocument(); + expect( + screen.getByText(/editing sso settings on this page is deprecated and will be removed/i), + ).toBeInTheDocument(); + }); + }); + }); +}); diff --git a/ui/litellm-dashboard/src/components/admins.tsx b/ui/litellm-dashboard/src/components/admins.tsx index 1022a9d49b7..d04c0d562f1 100644 --- a/ui/litellm-dashboard/src/components/admins.tsx +++ b/ui/litellm-dashboard/src/components/admins.tsx @@ -2,80 +2,43 @@ * Allow proxy admin to add other people to view global spend * Use this to avoid sharing master key with others */ -import React, { useState, useEffect } from "react"; -import { Alert, Typography } from "antd"; -import { useRouter } from "next/navigation"; -import { Button as Button2, Modal, Form, Input } from "antd"; -import { Select, SelectItem } from "@tremor/react"; -import { Team } from "./key_team_helpers/key_list"; +import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized"; import { + Button, + Callout, + Card, Table, TableBody, TableCell, TableHead, TableHeaderCell, TableRow, - Card, - Button, - Callout, - TabGroup, - TabList, - Tab, - TabPanel, - TabPanels, } from "@tremor/react"; -import { InvitationLink } from "./onboarding_link"; -import SSOModals from "./SSOModals"; -import SCIMConfig from "./SCIM"; -import UIAccessControlForm from "./UIAccessControlForm"; -import NotificationsManager from "./molecules/notifications_manager"; - -interface AdminPanelProps { - searchParams: any; - accessToken: string | null; - userID: string | null; - setTeams: React.Dispatch>; - showSSOBanner: boolean; - premiumUser: boolean; - proxySettings?: any; - userRole?: string | null; -} +import { Alert, Button as Button2, Form, Input, Modal, Tabs, Typography } from "antd"; +import React, { useEffect, useState } from "react"; import { useBaseUrl } from "./constants"; - +import NotificationsManager from "./molecules/notifications_manager"; import { - userUpdateUserCall, - Member, - userGetAllUsersCall, - User, - invitationCreateCall, - getPossibleUserRoles, addAllowedIP, - getAllowedIPs, deleteAllowedIP, + getAllowedIPs, getSSOSettings, } from "./networking"; -import UISettings from "./Settings/AdminSettings/UISettings/UISettings"; +import SCIMConfig from "./SCIM"; import SSOSettings from "./Settings/AdminSettings/SSOSettings/SSOSettings"; +import UISettings from "./Settings/AdminSettings/UISettings/UISettings"; +import SSOModals from "./SSOModals"; +import UIAccessControlForm from "./UIAccessControlForm"; -const AdminPanel: React.FC = ({ - searchParams, - accessToken, - userID, - showSSOBanner, - premiumUser, - proxySettings, - userRole, -}) => { +const { Title, Paragraph, Text } = Typography; + +interface AdminPanelProps { + proxySettings?: any; +} + +const AdminPanel: React.FC = ({ proxySettings }) => { + const { premiumUser, accessToken, userId: userID } = useAuthorized(); const [form] = Form.useForm(); - const [memberForm] = Form.useForm(); - const { Title, Paragraph } = Typography; - const [value, setValue] = useState(""); - const [admins, setAdmins] = useState(null); - const [invitationLinkData, setInvitationLinkData] = useState(null); - const [isInvitationLinkModalVisible, setIsInvitationLinkModalVisible] = useState(false); - const [isAddMemberModalVisible, setIsAddMemberModalVisible] = useState(false); - const [isAddAdminModalVisible, setIsAddAdminModalVisible] = useState(false); - const [isUpdateMemberModalVisible, setIsUpdateModalModalVisible] = useState(false); const [isAddSSOModalVisible, setIsAddSSOModalVisible] = useState(false); const [isInstructionsModalVisible, setIsInstructionsModalVisible] = useState(false); const [isAllowedIPModalVisible, setIsAllowedIPModalVisible] = useState(false); @@ -85,14 +48,6 @@ const AdminPanel: React.FC = ({ const [allowedIPs, setAllowedIPs] = useState([]); const [ipToDelete, setIPToDelete] = useState(null); const [ssoConfigured, setSsoConfigured] = useState(false); - const router = useRouter(); - - const [possibleUIRoles, setPossibleUIRoles] = useState>>(null); - - const isLocal = process.env.NODE_ENV === "development"; - if (isLocal != true) { - console.log = function () { }; - } const baseUrl = useBaseUrl(); const all_ip_address_allowed = "All IP Addresses Allowed"; @@ -100,14 +55,11 @@ const AdminPanel: React.FC = ({ let nonSssoUrl = baseUrl; nonSssoUrl += "/fallback/login"; - // Extract the SSO configuration check logic into a separate function for reuse const checkSSOConfiguration = async () => { if (accessToken) { try { const ssoData = await getSSOSettings(accessToken); - console.log("SSO data:", ssoData); - // Check if any SSO provider is configured if (ssoData && ssoData.values) { const hasGoogleSSO = ssoData.values.google_client_id && ssoData.values.google_client_secret; const hasMicrosoftSSO = ssoData.values.microsoft_client_id && ssoData.values.microsoft_client_secret; @@ -192,7 +144,6 @@ const AdminPanel: React.FC = ({ const handleAddSSOOk = () => { setIsAddSSOModalVisible(false); form.resetFields(); - // Refresh SSO configuration status if (accessToken && premiumUser) { checkSSOConfiguration(); } @@ -210,7 +161,6 @@ const AdminPanel: React.FC = ({ const handleInstructionsOk = () => { setIsInstructionsModalVisible(false); - // Refresh SSO configuration status after instructions are closed if (accessToken && premiumUser) { checkSSOConfiguration(); } @@ -218,270 +168,15 @@ const AdminPanel: React.FC = ({ const handleInstructionsCancel = () => { setIsInstructionsModalVisible(false); - // Refresh SSO configuration status after instructions are closed if (accessToken && premiumUser) { checkSSOConfiguration(); } }; - const roles = ["proxy_admin", "proxy_admin_viewer"]; - - // useEffect(() => { - // if (router) { - // const { protocol, host } = window.location; - // const baseUrl = `${protocol}//${host}`; - // setBaseUrl(baseUrl); - // } - // }, [router]); - - useEffect(() => { - // Fetch model info and set the default selected model - const fetchProxyAdminInfo = async () => { - if (accessToken != null) { - const combinedList: any[] = []; - const response = await userGetAllUsersCall(accessToken, "proxy_admin_viewer"); - console.log("proxy admin viewer response: ", response); - const proxyViewers: User[] = response["users"]; - console.log(`proxy viewers response: ${proxyViewers}`); - proxyViewers.forEach((viewer: User) => { - combinedList.push({ - user_role: viewer.user_role, - user_id: viewer.user_id, - user_email: viewer.user_email, - }); - }); - - console.log(`proxy viewers: ${proxyViewers}`); - - const response2 = await userGetAllUsersCall(accessToken, "proxy_admin"); - - const proxyAdmins: User[] = response2["users"]; - - proxyAdmins.forEach((admins: User) => { - combinedList.push({ - user_role: admins.user_role, - user_id: admins.user_id, - user_email: admins.user_email, - }); - }); - - console.log(`proxy admins: ${proxyAdmins}`); - console.log(`combinedList: ${combinedList}`); - setAdmins(combinedList); - - const availableUserRoles = await getPossibleUserRoles(accessToken); - setPossibleUIRoles(availableUserRoles); - } - }; - - fetchProxyAdminInfo(); - }, [accessToken]); - - // Add new useEffect to check SSO configuration useEffect(() => { checkSSOConfiguration(); }, [accessToken, premiumUser]); - const handleMemberUpdateOk = () => { - setIsUpdateModalModalVisible(false); - memberForm.resetFields(); - form.resetFields(); - }; - - const handleMemberOk = () => { - setIsAddMemberModalVisible(false); - memberForm.resetFields(); - form.resetFields(); - }; - - const handleAdminOk = () => { - setIsAddAdminModalVisible(false); - memberForm.resetFields(); - form.resetFields(); - }; - - const handleMemberCancel = () => { - setIsAddMemberModalVisible(false); - memberForm.resetFields(); - form.resetFields(); - }; - - const handleAdminCancel = () => { - setIsAddAdminModalVisible(false); - setIsInvitationLinkModalVisible(false); - memberForm.resetFields(); - form.resetFields(); - }; - - const handleMemberUpdateCancel = () => { - setIsUpdateModalModalVisible(false); - memberForm.resetFields(); - form.resetFields(); - }; - // Define the type for the handleMemberCreate function - type HandleMemberCreate = (formValues: Record) => Promise; - - const addMemberForm = (handleMemberCreate: HandleMemberCreate) => { - return ( -
- <> - - - - -
- Add member -
-
- ); - }; - - const modifyMemberForm = (handleMemberUpdate: HandleMemberCreate, currentRole: string, userID: string) => { - return ( -
- <> - - - - - -
- Update role -
-
- ); - }; - - const handleMemberUpdate = async (formValues: Record) => { - try { - if (accessToken != null && admins != null) { - NotificationsManager.info("Making API Call"); - const response: any = await userUpdateUserCall(accessToken, formValues, null); - console.log(`response for team create call: ${response}`); - // Checking if the team exists in the list and updating or adding accordingly - const foundIndex = admins.findIndex((user) => { - console.log(`user.user_id=${user.user_id}; response.user_id=${response.user_id}`); - return user.user_id === response.user_id; - }); - console.log(`foundIndex: ${foundIndex}`); - if (foundIndex == -1) { - console.log(`updates admin with new user`); - admins.push(response); - // If new user is found, update it - setAdmins(admins); // Set the new state - } - NotificationsManager.success("Refresh tab to see updated user role"); - setIsUpdateModalModalVisible(false); - } - } catch (error) { - console.error("Error creating the key:", error); - } - }; - - const handleMemberCreate = async (formValues: Record) => { - try { - if (accessToken != null && admins != null) { - NotificationsManager.info("Making API Call"); - const response: any = await userUpdateUserCall(accessToken, formValues, "proxy_admin_viewer"); - console.log(`response for team create call: ${response}`); - // Checking if the team exists in the list and updating or adding accordingly - - // Give admin an invite link for inviting user to proxy - const user_id = response.data?.user_id || response.user_id; - invitationCreateCall(accessToken, user_id).then((data) => { - setInvitationLinkData(data); - setIsInvitationLinkModalVisible(true); - }); - - const foundIndex = admins.findIndex((user) => { - console.log(`user.user_id=${user.user_id}; response.user_id=${response.user_id}`); - return user.user_id === response.user_id; - }); - console.log(`foundIndex: ${foundIndex}`); - if (foundIndex == -1) { - console.log(`updates admin with new user`); - admins.push(response); - // If new user is found, update it - setAdmins(admins); // Set the new state - } - form.resetFields(); - setIsAddMemberModalVisible(false); - } - } catch (error) { - console.error("Error creating the key:", error); - } - }; - const handleAdminCreate = async (formValues: Record) => { - try { - if (accessToken != null && admins != null) { - NotificationsManager.info("Making API Call"); - const user_role: Member = { - role: "user", - user_email: formValues.user_email, - user_id: formValues.user_id, - }; - const response: any = await userUpdateUserCall(accessToken, formValues, "proxy_admin"); - - // Give admin an invite link for inviting user to proxy - const user_id = response.data?.user_id || response.user_id; - invitationCreateCall(accessToken, user_id).then((data) => { - setInvitationLinkData(data); - setIsInvitationLinkModalVisible(true); - }); - console.log(`response for team create call: ${response}`); - // Checking if the team exists in the list and updating or adding accordingly - const foundIndex = admins.findIndex((user) => { - console.log(`user.user_id=${user.user_id}; response.user_id=${user_id}`); - return user.user_id === response.user_id; - }); - console.log(`foundIndex: ${foundIndex}`); - if (foundIndex == -1) { - console.log(`updates admin with new user`); - admins.push(response); - // If new user is found, update it - setAdmins(admins); // Set the new state - } - form.resetFields(); - setIsAddAdminModalVisible(false); - } - } catch (error) { - console.error("Error creating the key:", error); - } - }; - const handleUIAccessControlOk = () => { setIsUIAccessControlModalVisible(false); }; @@ -490,182 +185,187 @@ const AdminPanel: React.FC = ({ setIsUIAccessControlModalVisible(false); }; - console.log(`admins: ${admins?.length}`); + const tabItems = [ + { + key: "sso-settings", + label: "SSO Settings", + children: , + }, + { + key: "security-settings", + label: "Security Settings", + children: ( + <> + + ✨ Security Settings + +
+
+ +
+
+ +
+
+ +
+
+
+ +
+ + setIsAllowedIPModalVisible(false)} + footer={[ + , + , + ]} + > + + + + IP Address + Action + + + + {allowedIPs.map((ip, index) => ( + + {ip} + + {ip !== all_ip_address_allowed && ( + + )} + + + ))} + +
+
+ + setIsAddIPModalVisible(false)} + footer={null} + > +
+ + + + + Add IP Address + +
+
+ + setIsDeleteIPModalVisible(false)} + onOk={confirmDeleteIP} + footer={[ + , + , + ]} + > + Are you sure you want to delete the IP address: {ipToDelete}? + + + {/* UI Access Control Modal */} + + { + handleUIAccessControlOk(); + NotificationsManager.success("UI Access Control settings updated successfully"); + }} + /> + +
+ + If you need to login without sso, you can access{" "} + + {nonSssoUrl}{" "} + + + + ), + }, + { + key: "scim", + label: "SCIM", + children: , + }, + { + key: "ui-settings", + label: "UI Settings", + children: , + }, + ]; + return (
Admin Access Go to 'Internal Users' page to add other admins. - - - SSO Settings - Security Settings - SCIM - UI Settings - - - - - - - - ✨ Security Settings - -
-
- -
-
- -
-
- -
-
-
- -
- - setIsAllowedIPModalVisible(false)} - footer={[ - , - , - ]} - > - - - - IP Address - Action - - - - {allowedIPs.map((ip, index) => ( - - {ip} - - {ip !== all_ip_address_allowed && ( - - )} - - - ))} - -
-
- - setIsAddIPModalVisible(false)} - footer={null} - > -
- - - - - Add IP Address - -
-
- - setIsDeleteIPModalVisible(false)} - onOk={confirmDeleteIP} - footer={[ - , - , - ]} - > -

Are you sure you want to delete the IP address: {ipToDelete}?

-
- - {/* UI Access Control Modal */} - - { - handleUIAccessControlOk(); - NotificationsManager.success("UI Access Control settings updated successfully"); - }} - /> - -
- - If you need to login without sso, you can access{" "} - - {nonSssoUrl}{" "} - - -
- - - - - - -
-
+
); };