diff --git a/backend/src/db/migrations/20250311105617_add-share-to-anyone-setting-to-organizations.ts b/backend/src/db/migrations/20250311105617_add-share-to-anyone-setting-to-organizations.ts new file mode 100644 index 000000000..8f767c748 --- /dev/null +++ b/backend/src/db/migrations/20250311105617_add-share-to-anyone-setting-to-organizations.ts @@ -0,0 +1,32 @@ +import { Knex } from "knex"; + +import { TableName } from "../schemas"; + +export async function up(knex: Knex): Promise { + if (await knex.schema.hasTable(TableName.Organization)) { + const hasSecretShareToAnyoneCol = await knex.schema.hasColumn( + TableName.Organization, + "allowSecretSharingOutsideOrganization" + ); + + if (!hasSecretShareToAnyoneCol) { + await knex.schema.alterTable(TableName.Organization, (t) => { + t.boolean("allowSecretSharingOutsideOrganization").defaultTo(true); + }); + } + } +} + +export async function down(knex: Knex): Promise { + if (await knex.schema.hasTable(TableName.Organization)) { + const hasSecretShareToAnyoneCol = await knex.schema.hasColumn( + TableName.Organization, + "allowSecretSharingOutsideOrganization" + ); + if (hasSecretShareToAnyoneCol) { + await knex.schema.alterTable(TableName.Organization, (t) => { + t.dropColumn("allowSecretSharingOutsideOrganization"); + }); + } + } +} diff --git a/backend/src/db/schemas/organizations.ts b/backend/src/db/schemas/organizations.ts index 3f40447ad..6639528a0 100644 --- a/backend/src/db/schemas/organizations.ts +++ b/backend/src/db/schemas/organizations.ts @@ -22,7 +22,8 @@ export const OrganizationsSchema = z.object({ kmsEncryptedDataKey: zodBuffer.nullable().optional(), defaultMembershipRole: z.string().default("member"), enforceMfa: z.boolean().default(false), - selectedMfaMethod: z.string().nullable().optional() + selectedMfaMethod: z.string().nullable().optional(), + allowSecretSharingOutsideOrganization: z.boolean().default(true).nullable().optional() }); export type TOrganizations = z.infer; diff --git a/backend/src/ee/services/permission/org-permission.ts b/backend/src/ee/services/permission/org-permission.ts index dbabcc2c1..8a2646227 100644 --- a/backend/src/ee/services/permission/org-permission.ts +++ b/backend/src/ee/services/permission/org-permission.ts @@ -32,6 +32,10 @@ export enum OrgPermissionAdminConsoleAction { AccessAllProjects = "access-all-projects" } +export enum OrgPermissionSecretShareAction { + ManageSettings = "manage-settings" +} + export enum OrgPermissionGatewayActions { // is there a better word for this. This mean can an identity be a gateway CreateGateways = "create-gateways", @@ -59,7 +63,8 @@ export enum OrgPermissionSubjects { ProjectTemplates = "project-templates", AppConnections = "app-connections", Kmip = "kmip", - Gateway = "gateway" + Gateway = "gateway", + SecretShare = "secret-share" } export type AppConnectionSubjectFields = { @@ -91,7 +96,8 @@ export type OrgPermissionSet = ) ] | [OrgPermissionAdminConsoleAction, OrgPermissionSubjects.AdminConsole] - | [OrgPermissionKmipActions, OrgPermissionSubjects.Kmip]; + | [OrgPermissionKmipActions, OrgPermissionSubjects.Kmip] + | [OrgPermissionSecretShareAction, OrgPermissionSubjects.SecretShare]; const AppConnectionConditionSchema = z .object({ @@ -185,6 +191,12 @@ export const OrgPermissionSchema = z.discriminatedUnion("subject", [ "Describe what action an entity can take." ) }), + z.object({ + subject: z.literal(OrgPermissionSubjects.SecretShare).describe("The entity this permission pertains to."), + action: CASL_ACTION_SCHEMA_NATIVE_ENUM(OrgPermissionSecretShareAction).describe( + "Describe what action an entity can take." + ) + }), z.object({ subject: z.literal(OrgPermissionSubjects.Kmip).describe("The entity this permission pertains to."), action: CASL_ACTION_SCHEMA_NATIVE_ENUM(OrgPermissionKmipActions).describe( @@ -292,6 +304,8 @@ const buildAdminPermission = () => { // the proxy assignment is temporary in order to prevent "more privilege" error during role assignment to MI can(OrgPermissionKmipActions.Proxy, OrgPermissionSubjects.Kmip); + can(OrgPermissionSecretShareAction.ManageSettings, OrgPermissionSubjects.SecretShare); + return rules; }; diff --git a/backend/src/server/routes/v1/organization-router.ts b/backend/src/server/routes/v1/organization-router.ts index d117a4303..21723ef58 100644 --- a/backend/src/server/routes/v1/organization-router.ts +++ b/backend/src/server/routes/v1/organization-router.ts @@ -257,7 +257,8 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => { scimEnabled: z.boolean().optional(), defaultMembershipRoleSlug: slugSchema({ max: 64, field: "Default Membership Role" }).optional(), enforceMfa: z.boolean().optional(), - selectedMfaMethod: z.nativeEnum(MfaMethod).optional() + selectedMfaMethod: z.nativeEnum(MfaMethod).optional(), + allowSecretSharingOutsideOrganization: z.boolean().optional() }), response: { 200: z.object({ diff --git a/backend/src/services/org/org-service.ts b/backend/src/services/org/org-service.ts index b0e5c1f67..4ef52a551 100644 --- a/backend/src/services/org/org-service.ts +++ b/backend/src/services/org/org-service.ts @@ -19,7 +19,11 @@ import { import { TGroupDALFactory } from "@app/ee/services/group/group-dal"; import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; import { TOidcConfigDALFactory } from "@app/ee/services/oidc/oidc-config-dal"; -import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission"; +import { + OrgPermissionActions, + OrgPermissionSecretShareAction, + OrgPermissionSubjects +} from "@app/ee/services/permission/org-permission"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { TProjectUserAdditionalPrivilegeDALFactory } from "@app/ee/services/project-user-additional-privilege/project-user-additional-privilege-dal"; @@ -286,12 +290,27 @@ export const orgServiceFactory = ({ actorOrgId, actorAuthMethod, orgId, - data: { name, slug, authEnforced, scimEnabled, defaultMembershipRoleSlug, enforceMfa, selectedMfaMethod } + data: { + name, + slug, + authEnforced, + scimEnabled, + defaultMembershipRoleSlug, + enforceMfa, + selectedMfaMethod, + allowSecretSharingOutsideOrganization + } }: TUpdateOrgDTO) => { const appCfg = getConfig(); const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Settings); + if (allowSecretSharingOutsideOrganization !== undefined) { + ForbiddenError.from(permission).throwUnlessCan( + OrgPermissionSecretShareAction.ManageSettings, + OrgPermissionSubjects.SecretShare + ); + } const plan = await licenseService.getPlan(orgId); const currentOrg = await orgDAL.findOrgById(actorOrgId); @@ -358,7 +377,8 @@ export const orgServiceFactory = ({ scimEnabled, defaultMembershipRole, enforceMfa, - selectedMfaMethod + selectedMfaMethod, + allowSecretSharingOutsideOrganization }); if (!org) throw new NotFoundError({ message: `Organization with ID '${orgId}' not found` }); return org; diff --git a/backend/src/services/org/org-types.ts b/backend/src/services/org/org-types.ts index b9228377b..3c6faeae9 100644 --- a/backend/src/services/org/org-types.ts +++ b/backend/src/services/org/org-types.ts @@ -72,6 +72,7 @@ export type TUpdateOrgDTO = { defaultMembershipRoleSlug: string; enforceMfa: boolean; selectedMfaMethod: MfaMethod; + allowSecretSharingOutsideOrganization: boolean; }>; } & TOrgPermission; diff --git a/backend/src/services/secret-sharing/secret-sharing-service.ts b/backend/src/services/secret-sharing/secret-sharing-service.ts index 1ce7acf44..9649be722 100644 --- a/backend/src/services/secret-sharing/secret-sharing-service.ts +++ b/backend/src/services/secret-sharing/secret-sharing-service.ts @@ -82,6 +82,13 @@ export const secretSharingServiceFactory = ({ if (!permission) throw new ForbiddenRequestError({ name: "User is not a part of the specified organization" }); $validateSharedSecretExpiry(expiresAt); + const org = await orgDAL.findOrgById(orgId); + if (!org.allowSecretSharingOutsideOrganization && accessType === SecretSharingAccessType.Anyone) { + throw new BadRequestError({ + message: "Organization does not allow sharing secrets to members outside of this organization" + }); + } + if (secretValue.length > 10_000) { throw new BadRequestError({ message: "Shared secret value too long" }); } diff --git a/frontend/src/const/routes.ts b/frontend/src/const/routes.ts index c36264f5c..2be3f3dbc 100644 --- a/frontend/src/const/routes.ts +++ b/frontend/src/const/routes.ts @@ -23,7 +23,11 @@ export const ROUTE_PATHS = Object.freeze({ ), SecretSharing: setRoute( "/organization/secret-sharing", - "/_authenticate/_inject-org-details/_org-layout/organization/secret-sharing" + "/_authenticate/_inject-org-details/_org-layout/organization/secret-sharing/" + ), + SecretSharingSettings: setRoute( + "/organization/secret-sharing/settings", + "/_authenticate/_inject-org-details/_org-layout/organization/secret-sharing/settings" ), SettingsPage: setRoute( "/organization/settings", diff --git a/frontend/src/context/OrgPermissionContext/types.ts b/frontend/src/context/OrgPermissionContext/types.ts index 2a31ed6a8..9e60b5cb3 100644 --- a/frontend/src/context/OrgPermissionContext/types.ts +++ b/frontend/src/context/OrgPermissionContext/types.ts @@ -34,13 +34,18 @@ export enum OrgPermissionSubjects { ProjectTemplates = "project-templates", AppConnections = "app-connections", Kmip = "kmip", - Gateway = "gateway" + Gateway = "gateway", + SecretShare = "secret-share" } export enum OrgPermissionAdminConsoleAction { AccessAllProjects = "access-all-projects" } +export enum OrgPermissionSecretShareAction { + ManageSettings = "manage-settings" +} + export enum OrgPermissionAppConnectionActions { Read = "read", Create = "create", @@ -78,7 +83,8 @@ export type OrgPermissionSet = | [OrgPermissionActions, OrgPermissionSubjects.ProjectTemplates] | [OrgPermissionAppConnectionActions, OrgPermissionSubjects.AppConnections] | [OrgPermissionKmipActions, OrgPermissionSubjects.Kmip] - | [OrgGatewayPermissionActions, OrgPermissionSubjects.Gateway]; + | [OrgGatewayPermissionActions, OrgPermissionSubjects.Gateway] + | [OrgPermissionSecretShareAction, OrgPermissionSubjects.SecretShare]; // TODO(scott): add back once org UI refactored // | [ // OrgPermissionAppConnectionActions, diff --git a/frontend/src/hooks/api/organization/queries.tsx b/frontend/src/hooks/api/organization/queries.tsx index b5637f049..d3e925dca 100644 --- a/frontend/src/hooks/api/organization/queries.tsx +++ b/frontend/src/hooks/api/organization/queries.tsx @@ -109,7 +109,8 @@ export const useUpdateOrg = () => { orgId, defaultMembershipRoleSlug, enforceMfa, - selectedMfaMethod + selectedMfaMethod, + allowSecretSharingOutsideOrganization }) => { return apiRequest.patch(`/api/v1/organization/${orgId}`, { name, @@ -118,7 +119,8 @@ export const useUpdateOrg = () => { slug, defaultMembershipRoleSlug, enforceMfa, - selectedMfaMethod + selectedMfaMethod, + allowSecretSharingOutsideOrganization }); }, onSuccess: () => { diff --git a/frontend/src/hooks/api/organization/types.ts b/frontend/src/hooks/api/organization/types.ts index 629c4a330..2e082f408 100644 --- a/frontend/src/hooks/api/organization/types.ts +++ b/frontend/src/hooks/api/organization/types.ts @@ -15,6 +15,7 @@ export type Organization = { defaultMembershipRole: string; enforceMfa: boolean; selectedMfaMethod?: MfaMethod; + allowSecretSharingOutsideOrganization?: boolean; }; export type UpdateOrgDTO = { @@ -26,6 +27,7 @@ export type UpdateOrgDTO = { defaultMembershipRoleSlug?: string; enforceMfa?: boolean; selectedMfaMethod?: MfaMethod; + allowSecretSharingOutsideOrganization?: boolean; }; export type BillingDetails = { diff --git a/frontend/src/layouts/OrganizationLayout/OrganizationLayout.tsx b/frontend/src/layouts/OrganizationLayout/OrganizationLayout.tsx index 0e71c7bb6..7cfc21838 100644 --- a/frontend/src/layouts/OrganizationLayout/OrganizationLayout.tsx +++ b/frontend/src/layouts/OrganizationLayout/OrganizationLayout.tsx @@ -1,30 +1,33 @@ import { useTranslation } from "react-i18next"; import { faMobile } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { Link, linkOptions, Outlet, useLocation, useRouterState } from "@tanstack/react-router"; +import { linkOptions, Outlet, useLocation, useRouterState } from "@tanstack/react-router"; import { AnimatePresence, motion } from "framer-motion"; import { twMerge } from "tailwind-merge"; import { CreateOrgModal } from "@app/components/organization/CreateOrgModal"; import { Banner } from "@app/components/page-frames/Banner"; -import { - BreadcrumbContainer, - Menu, - MenuGroup, - MenuItem, - TBreadcrumbFormat -} from "@app/components/v2"; -import { useServerConfig } from "@app/context"; +import { BreadcrumbContainer, TBreadcrumbFormat } from "@app/components/v2"; +import { OrgPermissionSubjects, useOrgPermission, useServerConfig } from "@app/context"; +import { OrgPermissionSecretShareAction } from "@app/context/OrgPermissionContext/types"; import { usePopUp } from "@app/hooks"; import { InsecureConnectionBanner } from "./components/InsecureConnectionBanner"; import { MinimizedOrgSidebar } from "./components/MinimizedOrgSidebar"; import { SidebarHeader } from "./components/SidebarHeader"; +import { DefaultSideBar, SecretSharingSideBar } from "./ProductsSideBar"; export const OrganizationLayout = () => { const matches = useRouterState({ select: (s) => s.matches.at(-1)?.context }); const location = useLocation(); const { config } = useServerConfig(); + const { permission } = useOrgPermission(); + + const shouldShowProductsSidebar = permission.can( + OrgPermissionSecretShareAction.ManageSettings, + OrgPermissionSubjects.SecretShare + ); + const isOrganizationSpecificPage = location.pathname.startsWith("/organization"); const breadcrumbs = isOrganizationSpecificPage && matches && "breadcrumbs" in matches @@ -35,16 +38,23 @@ export const OrganizationLayout = () => { const { t } = useTranslation(); + const isSecretSharingPage = ( + [ + linkOptions({ to: "/organization/secret-sharing" }).to, + linkOptions({ to: "/organization/secret-sharing/settings" }).to + ] as string[] + ).includes(location.pathname); + const shouldShowOrgSidebar = location.pathname.startsWith("/organization") && + (!isSecretSharingPage || shouldShowProductsSidebar) && !( [ linkOptions({ to: "/organization/secret-manager/overview" }).to, linkOptions({ to: "/organization/cert-manager/overview" }).to, linkOptions({ to: "/organization/ssh/overview" }).to, linkOptions({ to: "/organization/kms/overview" }).to, - linkOptions({ to: "/organization/secret-scanning" }).to, - linkOptions({ to: "/organization/secret-sharing" }).to + linkOptions({ to: "/organization/secret-scanning" }).to ] as string[] ).includes(location.pathname); @@ -73,58 +83,7 @@ export const OrganizationLayout = () => {
- - - - {({ isActive }) => ( - - Audit Logs - - )} - - {(window.location.origin.includes("https://app.infisical.com") || - window.location.origin.includes("https://eu.infisical.com") || - window.location.origin.includes("https://gamma.infisical.com")) && ( - - {({ isActive }) => ( - - Usage & Billing - - )} - - )} - - - - {({ isActive }) => ( - - Access Control - - )} - - - {({ isActive }) => ( - - App Connections - - )} - - - {({ isActive }) => ( - - Gateways - - )} - - - {({ isActive }) => ( - - Organization Settings - - )} - - - + {isSecretSharingPage ? : } )} diff --git a/frontend/src/layouts/OrganizationLayout/ProductsSideBar/DefaultSideBar.tsx b/frontend/src/layouts/OrganizationLayout/ProductsSideBar/DefaultSideBar.tsx new file mode 100644 index 000000000..e78d86703 --- /dev/null +++ b/frontend/src/layouts/OrganizationLayout/ProductsSideBar/DefaultSideBar.tsx @@ -0,0 +1,58 @@ +import { Link } from "@tanstack/react-router"; + +import { Menu, MenuGroup, MenuItem } from "@app/components/v2"; + +export const DefaultSideBar = () => ( + + + + {({ isActive }) => ( + + Audit Logs + + )} + + {(window.location.origin.includes("https://app.infisical.com") || + window.location.origin.includes("https://eu.infisical.com") || + window.location.origin.includes("https://gamma.infisical.com")) && ( + + {({ isActive }) => ( + + Usage & Billing + + )} + + )} + + + + {({ isActive }) => ( + + Access Control + + )} + + + {({ isActive }) => ( + + App Connections + + )} + + + {({ isActive }) => ( + + Gateways + + )} + + + {({ isActive }) => ( + + Organization Settings + + )} + + + +); diff --git a/frontend/src/layouts/OrganizationLayout/ProductsSideBar/SecretSharingSideBar.tsx b/frontend/src/layouts/OrganizationLayout/ProductsSideBar/SecretSharingSideBar.tsx new file mode 100644 index 000000000..fbfc018c0 --- /dev/null +++ b/frontend/src/layouts/OrganizationLayout/ProductsSideBar/SecretSharingSideBar.tsx @@ -0,0 +1,34 @@ +import { Link, useMatchRoute } from "@tanstack/react-router"; + +import { Menu, MenuGroup, MenuItem } from "@app/components/v2"; + +export const SecretSharingSideBar = () => { + const matchRoute = useMatchRoute(); + const isOverviewActive = !!matchRoute({ + to: "/organization/secret-sharing", + fuzzy: false + }); + + return ( + + + + {() => ( + + Secret Sharing + + )} + + + + + {({ isActive }) => ( + + Settings + + )} + + + + ); +}; diff --git a/frontend/src/layouts/OrganizationLayout/ProductsSideBar/index.ts b/frontend/src/layouts/OrganizationLayout/ProductsSideBar/index.ts new file mode 100644 index 000000000..b2c79d873 --- /dev/null +++ b/frontend/src/layouts/OrganizationLayout/ProductsSideBar/index.ts @@ -0,0 +1,2 @@ +export * from "./DefaultSideBar"; +export * from "./SecretSharingSideBar"; diff --git a/frontend/src/pages/organization/RoleByIDPage/components/OrgRoleModifySection.utils.ts b/frontend/src/pages/organization/RoleByIDPage/components/OrgRoleModifySection.utils.ts index 42dd31417..512368fbf 100644 --- a/frontend/src/pages/organization/RoleByIDPage/components/OrgRoleModifySection.utils.ts +++ b/frontend/src/pages/organization/RoleByIDPage/components/OrgRoleModifySection.utils.ts @@ -5,7 +5,8 @@ import { OrgPermissionSubjects } from "@app/context"; import { OrgGatewayPermissionActions, OrgPermissionAppConnectionActions, - OrgPermissionKmipActions + OrgPermissionKmipActions, + OrgPermissionSecretShareAction } from "@app/context/OrgPermissionContext/types"; import { TPermission } from "@app/hooks/api/roles/types"; @@ -50,6 +51,12 @@ const adminConsolePermissionSchmea = z }) .optional(); +const secretSharingPermissionSchema = z + .object({ + [OrgPermissionSecretShareAction.ManageSettings]: z.boolean().optional() + }) + .optional(); + export const formSchema = z.object({ name: z.string().trim(), description: z.string().trim().optional(), @@ -83,7 +90,8 @@ export const formSchema = z.object({ [OrgPermissionSubjects.ProjectTemplates]: generalPermissionSchema, "app-connections": appConnectionsPermissionSchema, kmip: kmipPermissionSchema, - gateway: orgGatewayPermissionSchema + gateway: orgGatewayPermissionSchema, + "secret-share": secretSharingPermissionSchema }) .optional() }); diff --git a/frontend/src/pages/organization/RoleByIDPage/components/RolePermissionsSection/OrgPermissionSecretShareRow.tsx b/frontend/src/pages/organization/RoleByIDPage/components/RolePermissionsSection/OrgPermissionSecretShareRow.tsx new file mode 100644 index 000000000..4a35644c7 --- /dev/null +++ b/frontend/src/pages/organization/RoleByIDPage/components/RolePermissionsSection/OrgPermissionSecretShareRow.tsx @@ -0,0 +1,130 @@ +import { useEffect, useMemo } from "react"; +import { Control, Controller, UseFormSetValue, useWatch } from "react-hook-form"; +import { faChevronDown, faChevronRight } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; + +import { createNotification } from "@app/components/notifications"; +import { Checkbox, Select, SelectItem, Td, Tr } from "@app/components/v2"; +import { useToggle } from "@app/hooks"; + +import { TFormSchema } from "../OrgRoleModifySection.utils"; + +type Props = { + isEditable: boolean; + setValue: UseFormSetValue; + control: Control; +}; + +enum Permission { + NoAccess = "no-access", + Custom = "custom" +} + +const PERMISSION_ACTIONS = [{ action: "manage-settings", label: "Manage settings" }] as const; + +export const OrgPermissionSecretShareRow = ({ isEditable, control, setValue }: Props) => { + const [isRowExpanded, setIsRowExpanded] = useToggle(); + const [isCustom, setIsCustom] = useToggle(); + + const rule = useWatch({ + control, + name: "permissions.secret-share" + }); + + const selectedPermissionCategory = useMemo(() => { + if (rule?.["manage-settings"]) { + return Permission.Custom; + } + return Permission.NoAccess; + }, [rule, isCustom]); + + useEffect(() => { + if (selectedPermissionCategory === Permission.Custom) setIsCustom.on(); + else setIsCustom.off(); + }, [selectedPermissionCategory]); + + useEffect(() => { + const isRowCustom = selectedPermissionCategory === Permission.Custom; + if (isRowCustom) { + setIsRowExpanded.on(); + } + }, []); + + const handlePermissionChange = (val: Permission) => { + if (!val) return; + if (val === Permission.Custom) { + setIsRowExpanded.on(); + setIsCustom.on(); + return; + } + setIsCustom.off(); + + if (val === Permission.NoAccess) { + setValue("permissions.secret-share", { "manage-settings": false }, { shouldDirty: true }); + } + }; + + return ( + <> + setIsRowExpanded.toggle()} + > + + + + Secret Share + + + + + {isRowExpanded && ( + + +
+ {PERMISSION_ACTIONS.map(({ action, label }) => { + return ( + ( + { + if (!isEditable) { + createNotification({ + type: "error", + text: "Failed to update default role" + }); + return; + } + field.onChange(e); + }} + id={`permissions.secret-share.${action}`} + > + {label} + + )} + /> + ); + })} +
+ + + )} + + ); +}; diff --git a/frontend/src/pages/organization/RoleByIDPage/components/RolePermissionsSection/RolePermissionRow.tsx b/frontend/src/pages/organization/RoleByIDPage/components/RolePermissionsSection/RolePermissionRow.tsx index aed309d04..0bc765eb0 100644 --- a/frontend/src/pages/organization/RoleByIDPage/components/RolePermissionsSection/RolePermissionRow.tsx +++ b/frontend/src/pages/organization/RoleByIDPage/components/RolePermissionsSection/RolePermissionRow.tsx @@ -74,7 +74,7 @@ type Props = { title: string; formName: keyof Omit< Exclude, - "workspace" | "organization-admin-console" | "kmip" | "gateway" + "workspace" | "organization-admin-console" | "kmip" | "gateway" | "secret-share" >; setValue: UseFormSetValue; control: Control; diff --git a/frontend/src/pages/organization/RoleByIDPage/components/RolePermissionsSection/RolePermissionsSection.tsx b/frontend/src/pages/organization/RoleByIDPage/components/RolePermissionsSection/RolePermissionsSection.tsx index de9fbde80..7a2feba36 100644 --- a/frontend/src/pages/organization/RoleByIDPage/components/RolePermissionsSection/RolePermissionsSection.tsx +++ b/frontend/src/pages/organization/RoleByIDPage/components/RolePermissionsSection/RolePermissionsSection.tsx @@ -16,6 +16,7 @@ import { import { OrgPermissionAdminConsoleRow } from "./OrgPermissionAdminConsoleRow"; import { OrgGatewayPermissionRow } from "./OrgPermissionGatewayRow"; import { OrgPermissionKmipRow } from "./OrgPermissionKmipRow"; +import { OrgPermissionSecretShareRow } from "./OrgPermissionSecretShareRow"; import { OrgRoleWorkspaceRow } from "./OrgRoleWorkspaceRow"; import { RolePermissionRow } from "./RolePermissionRow"; @@ -177,6 +178,11 @@ export const RolePermissionsSection = ({ roleId }: Props) => { setValue={setValue} isEditable={isCustomRole} /> + { + const { currentOrg } = useOrganization(); return ( { diff --git a/frontend/src/pages/organization/SecretSharingPage/route.tsx b/frontend/src/pages/organization/SecretSharingPage/route.tsx index 827fc2623..2040fd1bb 100644 --- a/frontend/src/pages/organization/SecretSharingPage/route.tsx +++ b/frontend/src/pages/organization/SecretSharingPage/route.tsx @@ -11,7 +11,7 @@ const SecretSharingQueryParams = z.object({ }); export const Route = createFileRoute( - "/_authenticate/_inject-org-details/_org-layout/organization/secret-sharing" + "/_authenticate/_inject-org-details/_org-layout/organization/secret-sharing/" )({ component: SecretSharingPage, diff --git a/frontend/src/pages/organization/SecretSharingSettingsPage/SecretSharingSettingsPage.tsx b/frontend/src/pages/organization/SecretSharingSettingsPage/SecretSharingSettingsPage.tsx new file mode 100644 index 000000000..8ee37f631 --- /dev/null +++ b/frontend/src/pages/organization/SecretSharingSettingsPage/SecretSharingSettingsPage.tsx @@ -0,0 +1,35 @@ +import { Helmet } from "react-helmet"; +import { useTranslation } from "react-i18next"; + +import { PageHeader } from "@app/components/v2"; +import { + OrgPermissionSecretShareAction, + OrgPermissionSubjects +} from "@app/context/OrgPermissionContext/types"; +import { withPermission } from "@app/hoc"; + +import { SecretSharingSettingsTabGroup } from "./components"; + +export const SecretSharingSettingsPage = withPermission( + () => { + const { t } = useTranslation(); + + return ( + <> + + {t("common.head-title", { title: t("settings.org.title") })} + +
+
+ + +
+
+ + ); + }, + { + action: OrgPermissionSecretShareAction.ManageSettings, + subject: OrgPermissionSubjects.SecretShare + } +); diff --git a/frontend/src/pages/organization/SecretSharingSettingsPage/components/SecretSharingAllowShareToAnyone/SecretSharingAllowShareToAnyone.tsx b/frontend/src/pages/organization/SecretSharingSettingsPage/components/SecretSharingAllowShareToAnyone/SecretSharingAllowShareToAnyone.tsx new file mode 100644 index 000000000..1ea62c187 --- /dev/null +++ b/frontend/src/pages/organization/SecretSharingSettingsPage/components/SecretSharingAllowShareToAnyone/SecretSharingAllowShareToAnyone.tsx @@ -0,0 +1,58 @@ +import { createNotification } from "@app/components/notifications"; +import { OrgPermissionCan } from "@app/components/permissions"; +import { Switch } from "@app/components/v2"; +import { OrgPermissionActions, OrgPermissionSubjects, useOrganization } from "@app/context"; +import { useUpdateOrg } from "@app/hooks/api"; + +export const SecretSharingAllowShareToAnyone = () => { + const { currentOrg } = useOrganization(); + const { mutateAsync } = useUpdateOrg(); + + const handleSecretSharingToggle = async (value: boolean) => { + try { + if (!currentOrg?.id) return; + + await mutateAsync({ + orgId: currentOrg.id, + allowSecretSharingOutsideOrganization: value + }); + + createNotification({ + text: `Successfully ${value ? "enabled" : "disabled"} secret sharing to members outside of this organization`, + type: "success" + }); + } catch (err) { + console.error(err); + createNotification({ + text: (err as { response: { data: { message: string } } }).response.data.message, + type: "error" + }); + } + }; + + return ( +
+
+
+

+ Allow sharing secrets to members outside of this organization +

+ + {(isAllowed) => ( + handleSecretSharingToggle(value)} + isChecked={currentOrg?.allowSecretSharingOutsideOrganization ?? false} + isDisabled={!isAllowed} + /> + )} + +
+

+ If enabled, team members will be able to share secrets to members outside of this + organization +

+
+
+ ); +}; diff --git a/frontend/src/pages/organization/SecretSharingSettingsPage/components/SecretSharingAllowShareToAnyone/index.tsx b/frontend/src/pages/organization/SecretSharingSettingsPage/components/SecretSharingAllowShareToAnyone/index.tsx new file mode 100644 index 000000000..d02460498 --- /dev/null +++ b/frontend/src/pages/organization/SecretSharingSettingsPage/components/SecretSharingAllowShareToAnyone/index.tsx @@ -0,0 +1 @@ +export { SecretSharingAllowShareToAnyone } from "./SecretSharingAllowShareToAnyone"; diff --git a/frontend/src/pages/organization/SecretSharingSettingsPage/components/SecretSharingSettingsGeneralTab/SecretSharingSettingsGeneralTab.tsx b/frontend/src/pages/organization/SecretSharingSettingsPage/components/SecretSharingSettingsGeneralTab/SecretSharingSettingsGeneralTab.tsx new file mode 100644 index 000000000..ede3d9fc8 --- /dev/null +++ b/frontend/src/pages/organization/SecretSharingSettingsPage/components/SecretSharingSettingsGeneralTab/SecretSharingSettingsGeneralTab.tsx @@ -0,0 +1,9 @@ +import { SecretSharingAllowShareToAnyone } from "../SecretSharingAllowShareToAnyone"; + +export const SecretSharingSettingsGeneralTab = () => { + return ( +
+ +
+ ); +}; diff --git a/frontend/src/pages/organization/SecretSharingSettingsPage/components/SecretSharingSettingsGeneralTab/index.tsx b/frontend/src/pages/organization/SecretSharingSettingsPage/components/SecretSharingSettingsGeneralTab/index.tsx new file mode 100644 index 000000000..306109025 --- /dev/null +++ b/frontend/src/pages/organization/SecretSharingSettingsPage/components/SecretSharingSettingsGeneralTab/index.tsx @@ -0,0 +1 @@ +export { SecretSharingSettingsGeneralTab } from "./SecretSharingSettingsGeneralTab"; diff --git a/frontend/src/pages/organization/SecretSharingSettingsPage/components/SecretSharingSettingsTabGroup/SecretSharingSettingsTabGroup.tsx b/frontend/src/pages/organization/SecretSharingSettingsPage/components/SecretSharingSettingsTabGroup/SecretSharingSettingsTabGroup.tsx new file mode 100644 index 000000000..594df474f --- /dev/null +++ b/frontend/src/pages/organization/SecretSharingSettingsPage/components/SecretSharingSettingsTabGroup/SecretSharingSettingsTabGroup.tsx @@ -0,0 +1,39 @@ +import { useState } from "react"; +import { useSearch } from "@tanstack/react-router"; + +import { Tab, TabList, TabPanel, Tabs } from "@app/components/v2"; +import { ROUTE_PATHS } from "@app/const/routes"; + +import { SecretSharingSettingsGeneralTab } from "../SecretSharingSettingsGeneralTab"; + +export const SecretSharingSettingsTabGroup = () => { + const search = useSearch({ + from: ROUTE_PATHS.Organization.SecretSharingSettings.id + }); + const tabs = [ + { + name: "General", + key: "tab-secret-sharing-general", + component: SecretSharingSettingsGeneralTab + } + ]; + + const [selectedTab, setSelectedTab] = useState(search.selectedTab || tabs[0].key); + + return ( + + + {tabs.map((tab) => ( + + {tab.name} + + ))} + + {tabs.map(({ key, component: Component }) => ( + + + + ))} + + ); +}; diff --git a/frontend/src/pages/organization/SecretSharingSettingsPage/components/SecretSharingSettingsTabGroup/index.tsx b/frontend/src/pages/organization/SecretSharingSettingsPage/components/SecretSharingSettingsTabGroup/index.tsx new file mode 100644 index 000000000..2c60c7e20 --- /dev/null +++ b/frontend/src/pages/organization/SecretSharingSettingsPage/components/SecretSharingSettingsTabGroup/index.tsx @@ -0,0 +1 @@ +export { SecretSharingSettingsTabGroup } from "./SecretSharingSettingsTabGroup"; diff --git a/frontend/src/pages/organization/SecretSharingSettingsPage/components/index.tsx b/frontend/src/pages/organization/SecretSharingSettingsPage/components/index.tsx new file mode 100644 index 000000000..2c60c7e20 --- /dev/null +++ b/frontend/src/pages/organization/SecretSharingSettingsPage/components/index.tsx @@ -0,0 +1 @@ +export { SecretSharingSettingsTabGroup } from "./SecretSharingSettingsTabGroup"; diff --git a/frontend/src/pages/organization/SecretSharingSettingsPage/route.tsx b/frontend/src/pages/organization/SecretSharingSettingsPage/route.tsx new file mode 100644 index 000000000..b7a9e128d --- /dev/null +++ b/frontend/src/pages/organization/SecretSharingSettingsPage/route.tsx @@ -0,0 +1,37 @@ +import { faHome } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { createFileRoute, linkOptions, stripSearchParams } from "@tanstack/react-router"; +import { zodValidator } from "@tanstack/zod-adapter"; +import { z } from "zod"; + +import { SecretSharingSettingsPage } from "./SecretSharingSettingsPage"; + +const SettingsPageQueryParams = z.object({ + selectedTab: z.string().catch("") +}); + +export const Route = createFileRoute( + "/_authenticate/_inject-org-details/_org-layout/organization/secret-sharing/settings" +)({ + component: SecretSharingSettingsPage, + validateSearch: zodValidator(SettingsPageQueryParams), + search: { + middlewares: [stripSearchParams({ selectedTab: "" })] + }, + context: () => ({ + breadcrumbs: [ + { + label: "Home", + icon: () => , + link: linkOptions({ to: "/" }) + }, + { + label: "Secret Sharing", + link: linkOptions({ to: "/organization/secret-sharing" }) + }, + { + label: "Settings" + } + ] + }) +}); diff --git a/frontend/src/pages/public/ShareSecretPage/components/ShareSecretForm.tsx b/frontend/src/pages/public/ShareSecretPage/components/ShareSecretForm.tsx index 8e3d34b5a..a2228e46b 100644 --- a/frontend/src/pages/public/ShareSecretPage/components/ShareSecretForm.tsx +++ b/frontend/src/pages/public/ShareSecretPage/components/ShareSecretForm.tsx @@ -41,9 +41,14 @@ export type FormData = z.infer; type Props = { isPublic: boolean; // whether or not this is a public (non-authenticated) secret sharing form value?: string; + allowSecretSharingOutsideOrganization?: boolean; }; -export const ShareSecretForm = ({ isPublic, value }: Props) => { +export const ShareSecretForm = ({ + isPublic, + value, + allowSecretSharingOutsideOrganization = true +}: Props) => { const [secretLink, setSecretLink] = useState(""); const [, isCopyingSecret, setCopyTextSecret] = useTimedReset({ initialState: "Copy to clipboard" @@ -230,7 +235,9 @@ export const ShareSecretForm = ({ isPublic, value }: Props) => { onValueChange={(e) => onChange(e)} className="w-full" > - Anyone + {allowSecretSharingOutsideOrganization && ( + Anyone + )} People within your organization diff --git a/frontend/src/routeTree.gen.ts b/frontend/src/routeTree.gen.ts index e6bd0c8ee..ef31a5a33 100644 --- a/frontend/src/routeTree.gen.ts +++ b/frontend/src/routeTree.gen.ts @@ -42,7 +42,6 @@ import { Route as authProviderSuccessPageRouteImport } from './pages/auth/Provid import { Route as authProviderErrorPageRouteImport } from './pages/auth/ProviderErrorPage/route' import { Route as userPersonalSettingsPageRouteImport } from './pages/user/PersonalSettingsPage/route' import { Route as organizationSettingsPageRouteImport } from './pages/organization/SettingsPage/route' -import { Route as organizationSecretSharingPageRouteImport } from './pages/organization/SecretSharingPage/route' import { Route as organizationSecretScanningPageRouteImport } from './pages/organization/SecretScanningPage/route' import { Route as organizationBillingPageRouteImport } from './pages/organization/BillingPage/route' import { Route as organizationAuditLogsPageRouteImport } from './pages/organization/AuditLogsPage/route' @@ -54,6 +53,7 @@ import { Route as secretManagerLayoutImport } from './pages/secret-manager/layou import { Route as kmsLayoutImport } from './pages/kms/layout' import { Route as certManagerLayoutImport } from './pages/cert-manager/layout' import { Route as organizationSshOverviewPageRouteImport } from './pages/organization/SshOverviewPage/route' +import { Route as organizationSecretSharingSettingsPageRouteImport } from './pages/organization/SecretSharingSettingsPage/route' import { Route as organizationSecretManagerOverviewPageRouteImport } from './pages/organization/SecretManagerOverviewPage/route' import { Route as organizationRoleByIDPageRouteImport } from './pages/organization/RoleByIDPage/route' import { Route as organizationUserDetailsByIDPageRouteImport } from './pages/organization/UserDetailsByIDPage/route' @@ -61,6 +61,7 @@ import { Route as organizationKmsOverviewPageRouteImport } from './pages/organiz import { Route as organizationIdentityDetailsByIDPageRouteImport } from './pages/organization/IdentityDetailsByIDPage/route' import { Route as organizationGroupDetailsByIDPageRouteImport } from './pages/organization/GroupDetailsByIDPage/route' import { Route as organizationCertManagerOverviewPageRouteImport } from './pages/organization/CertManagerOverviewPage/route' +import { Route as organizationSecretSharingPageRouteImport } from './pages/organization/SecretSharingPage/route' import { Route as organizationGatewaysGatewayListPageRouteImport } from './pages/organization/Gateways/GatewayListPage/route' import { Route as organizationAppConnectionsAppConnectionsPageRouteImport } from './pages/organization/AppConnections/AppConnectionsPage/route' import { Route as projectAccessControlPageRouteSshImport } from './pages/project/AccessControlPage/route-ssh' @@ -211,6 +212,10 @@ const AuthenticateInjectOrgDetailsOrgLayoutSecretManagerProjectIdImport = createFileRoute( '/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId', )() +const AuthenticateInjectOrgDetailsOrgLayoutOrganizationSecretSharingImport = + createFileRoute( + '/_authenticate/_inject-org-details/_org-layout/organization/secret-sharing', + )() const AuthenticateInjectOrgDetailsOrgLayoutOrganizationGatewaysImport = createFileRoute( '/_authenticate/_inject-org-details/_org-layout/organization/gateways', @@ -467,6 +472,14 @@ const AuthenticateInjectOrgDetailsOrgLayoutSecretManagerProjectIdRoute = getParentRoute: () => organizationLayoutRoute, } as any) +const AuthenticateInjectOrgDetailsOrgLayoutOrganizationSecretSharingRoute = + AuthenticateInjectOrgDetailsOrgLayoutOrganizationSecretSharingImport.update({ + id: '/secret-sharing', + path: '/secret-sharing', + getParentRoute: () => + AuthenticateInjectOrgDetailsOrgLayoutOrganizationRoute, + } as any) + const AuthenticateInjectOrgDetailsOrgLayoutOrganizationGatewaysRoute = AuthenticateInjectOrgDetailsOrgLayoutOrganizationGatewaysImport.update({ id: '/gateways', @@ -505,14 +518,6 @@ const organizationSettingsPageRouteRoute = AuthenticateInjectOrgDetailsOrgLayoutOrganizationRoute, } as any) -const organizationSecretSharingPageRouteRoute = - organizationSecretSharingPageRouteImport.update({ - id: '/secret-sharing', - path: '/secret-sharing', - getParentRoute: () => - AuthenticateInjectOrgDetailsOrgLayoutOrganizationRoute, - } as any) - const organizationSecretScanningPageRouteRoute = organizationSecretScanningPageRouteImport.update({ id: '/secret-scanning', @@ -590,6 +595,14 @@ const organizationSshOverviewPageRouteRoute = AuthenticateInjectOrgDetailsOrgLayoutOrganizationRoute, } as any) +const organizationSecretSharingSettingsPageRouteRoute = + organizationSecretSharingSettingsPageRouteImport.update({ + id: '/settings', + path: '/settings', + getParentRoute: () => + AuthenticateInjectOrgDetailsOrgLayoutOrganizationSecretSharingRoute, + } as any) + const organizationSecretManagerOverviewPageRouteRoute = organizationSecretManagerOverviewPageRouteImport.update({ id: '/secret-manager/overview', @@ -646,6 +659,14 @@ const organizationCertManagerOverviewPageRouteRoute = AuthenticateInjectOrgDetailsOrgLayoutOrganizationRoute, } as any) +const organizationSecretSharingPageRouteRoute = + organizationSecretSharingPageRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => + AuthenticateInjectOrgDetailsOrgLayoutOrganizationSecretSharingRoute, + } as any) + const organizationGatewaysGatewayListPageRouteRoute = organizationGatewaysGatewayListPageRouteImport.update({ id: '/', @@ -1887,13 +1908,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof organizationSecretScanningPageRouteImport parentRoute: typeof AuthenticateInjectOrgDetailsOrgLayoutOrganizationImport } - '/_authenticate/_inject-org-details/_org-layout/organization/secret-sharing': { - id: '/_authenticate/_inject-org-details/_org-layout/organization/secret-sharing' - path: '/secret-sharing' - fullPath: '/organization/secret-sharing' - preLoaderRoute: typeof organizationSecretSharingPageRouteImport - parentRoute: typeof AuthenticateInjectOrgDetailsOrgLayoutOrganizationImport - } '/_authenticate/_inject-org-details/_org-layout/organization/settings': { id: '/_authenticate/_inject-org-details/_org-layout/organization/settings' path: '/settings' @@ -1929,6 +1943,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof AuthenticateInjectOrgDetailsOrgLayoutOrganizationGatewaysImport parentRoute: typeof AuthenticateInjectOrgDetailsOrgLayoutOrganizationImport } + '/_authenticate/_inject-org-details/_org-layout/organization/secret-sharing': { + id: '/_authenticate/_inject-org-details/_org-layout/organization/secret-sharing' + path: '/secret-sharing' + fullPath: '/organization/secret-sharing' + preLoaderRoute: typeof AuthenticateInjectOrgDetailsOrgLayoutOrganizationSecretSharingImport + parentRoute: typeof AuthenticateInjectOrgDetailsOrgLayoutOrganizationImport + } '/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId': { id: '/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId' path: '/secret-manager/$projectId' @@ -1957,6 +1978,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof organizationGatewaysGatewayListPageRouteImport parentRoute: typeof AuthenticateInjectOrgDetailsOrgLayoutOrganizationGatewaysImport } + '/_authenticate/_inject-org-details/_org-layout/organization/secret-sharing/': { + id: '/_authenticate/_inject-org-details/_org-layout/organization/secret-sharing/' + path: '/' + fullPath: '/organization/secret-sharing/' + preLoaderRoute: typeof organizationSecretSharingPageRouteImport + parentRoute: typeof AuthenticateInjectOrgDetailsOrgLayoutOrganizationSecretSharingImport + } '/_authenticate/_inject-org-details/_org-layout/organization/cert-manager/overview': { id: '/_authenticate/_inject-org-details/_org-layout/organization/cert-manager/overview' path: '/cert-manager/overview' @@ -2006,6 +2034,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof organizationSecretManagerOverviewPageRouteImport parentRoute: typeof AuthenticateInjectOrgDetailsOrgLayoutOrganizationImport } + '/_authenticate/_inject-org-details/_org-layout/organization/secret-sharing/settings': { + id: '/_authenticate/_inject-org-details/_org-layout/organization/secret-sharing/settings' + path: '/settings' + fullPath: '/organization/secret-sharing/settings' + preLoaderRoute: typeof organizationSecretSharingSettingsPageRouteImport + parentRoute: typeof AuthenticateInjectOrgDetailsOrgLayoutOrganizationSecretSharingImport + } '/_authenticate/_inject-org-details/_org-layout/organization/ssh/overview': { id: '/_authenticate/_inject-org-details/_org-layout/organization/ssh/overview' path: '/ssh/overview' @@ -2965,16 +3000,34 @@ const AuthenticateInjectOrgDetailsOrgLayoutOrganizationGatewaysRouteWithChildren AuthenticateInjectOrgDetailsOrgLayoutOrganizationGatewaysRouteChildren, ) +interface AuthenticateInjectOrgDetailsOrgLayoutOrganizationSecretSharingRouteChildren { + organizationSecretSharingPageRouteRoute: typeof organizationSecretSharingPageRouteRoute + organizationSecretSharingSettingsPageRouteRoute: typeof organizationSecretSharingSettingsPageRouteRoute +} + +const AuthenticateInjectOrgDetailsOrgLayoutOrganizationSecretSharingRouteChildren: AuthenticateInjectOrgDetailsOrgLayoutOrganizationSecretSharingRouteChildren = + { + organizationSecretSharingPageRouteRoute: + organizationSecretSharingPageRouteRoute, + organizationSecretSharingSettingsPageRouteRoute: + organizationSecretSharingSettingsPageRouteRoute, + } + +const AuthenticateInjectOrgDetailsOrgLayoutOrganizationSecretSharingRouteWithChildren = + AuthenticateInjectOrgDetailsOrgLayoutOrganizationSecretSharingRoute._addFileChildren( + AuthenticateInjectOrgDetailsOrgLayoutOrganizationSecretSharingRouteChildren, + ) + interface AuthenticateInjectOrgDetailsOrgLayoutOrganizationRouteChildren { organizationAccessManagementPageRouteRoute: typeof organizationAccessManagementPageRouteRoute organizationAdminPageRouteRoute: typeof organizationAdminPageRouteRoute organizationAuditLogsPageRouteRoute: typeof organizationAuditLogsPageRouteRoute organizationBillingPageRouteRoute: typeof organizationBillingPageRouteRoute organizationSecretScanningPageRouteRoute: typeof organizationSecretScanningPageRouteRoute - organizationSecretSharingPageRouteRoute: typeof organizationSecretSharingPageRouteRoute organizationSettingsPageRouteRoute: typeof organizationSettingsPageRouteRoute AuthenticateInjectOrgDetailsOrgLayoutOrganizationAppConnectionsRoute: typeof AuthenticateInjectOrgDetailsOrgLayoutOrganizationAppConnectionsRouteWithChildren AuthenticateInjectOrgDetailsOrgLayoutOrganizationGatewaysRoute: typeof AuthenticateInjectOrgDetailsOrgLayoutOrganizationGatewaysRouteWithChildren + AuthenticateInjectOrgDetailsOrgLayoutOrganizationSecretSharingRoute: typeof AuthenticateInjectOrgDetailsOrgLayoutOrganizationSecretSharingRouteWithChildren organizationCertManagerOverviewPageRouteRoute: typeof organizationCertManagerOverviewPageRouteRoute organizationGroupDetailsByIDPageRouteRoute: typeof organizationGroupDetailsByIDPageRouteRoute organizationIdentityDetailsByIDPageRouteRoute: typeof organizationIdentityDetailsByIDPageRouteRoute @@ -2994,13 +3047,13 @@ const AuthenticateInjectOrgDetailsOrgLayoutOrganizationRouteChildren: Authentica organizationBillingPageRouteRoute: organizationBillingPageRouteRoute, organizationSecretScanningPageRouteRoute: organizationSecretScanningPageRouteRoute, - organizationSecretSharingPageRouteRoute: - organizationSecretSharingPageRouteRoute, organizationSettingsPageRouteRoute: organizationSettingsPageRouteRoute, AuthenticateInjectOrgDetailsOrgLayoutOrganizationAppConnectionsRoute: AuthenticateInjectOrgDetailsOrgLayoutOrganizationAppConnectionsRouteWithChildren, AuthenticateInjectOrgDetailsOrgLayoutOrganizationGatewaysRoute: AuthenticateInjectOrgDetailsOrgLayoutOrganizationGatewaysRouteWithChildren, + AuthenticateInjectOrgDetailsOrgLayoutOrganizationSecretSharingRoute: + AuthenticateInjectOrgDetailsOrgLayoutOrganizationSecretSharingRouteWithChildren, organizationCertManagerOverviewPageRouteRoute: organizationCertManagerOverviewPageRouteRoute, organizationGroupDetailsByIDPageRouteRoute: @@ -3672,16 +3725,17 @@ export interface FileRoutesByFullPath { '/organization/audit-logs': typeof organizationAuditLogsPageRouteRoute '/organization/billing': typeof organizationBillingPageRouteRoute '/organization/secret-scanning': typeof organizationSecretScanningPageRouteRoute - '/organization/secret-sharing': typeof organizationSecretSharingPageRouteRoute '/organization/settings': typeof organizationSettingsPageRouteRoute '/cert-manager/$projectId': typeof certManagerLayoutRouteWithChildren '/kms/$projectId': typeof kmsLayoutRouteWithChildren '/organization/app-connections': typeof AuthenticateInjectOrgDetailsOrgLayoutOrganizationAppConnectionsRouteWithChildren '/organization/gateways': typeof AuthenticateInjectOrgDetailsOrgLayoutOrganizationGatewaysRouteWithChildren + '/organization/secret-sharing': typeof AuthenticateInjectOrgDetailsOrgLayoutOrganizationSecretSharingRouteWithChildren '/secret-manager/$projectId': typeof secretManagerLayoutRouteWithChildren '/ssh/$projectId': typeof sshLayoutRouteWithChildren '/organization/app-connections/': typeof organizationAppConnectionsAppConnectionsPageRouteRoute '/organization/gateways/': typeof organizationGatewaysGatewayListPageRouteRoute + '/organization/secret-sharing/': typeof organizationSecretSharingPageRouteRoute '/organization/cert-manager/overview': typeof organizationCertManagerOverviewPageRouteRoute '/organization/groups/$groupId': typeof organizationGroupDetailsByIDPageRouteRoute '/organization/identities/$identityId': typeof organizationIdentityDetailsByIDPageRouteRoute @@ -3689,6 +3743,7 @@ export interface FileRoutesByFullPath { '/organization/members/$membershipId': typeof organizationUserDetailsByIDPageRouteRoute '/organization/roles/$roleId': typeof organizationRoleByIDPageRouteRoute '/organization/secret-manager/overview': typeof organizationSecretManagerOverviewPageRouteRoute + '/organization/secret-sharing/settings': typeof organizationSecretSharingSettingsPageRouteRoute '/organization/ssh/overview': typeof organizationSshOverviewPageRouteRoute '/cert-manager/$projectId/overview': typeof certManagerCertificatesPageRouteRoute '/cert-manager/$projectId/settings': typeof certManagerSettingsPageRouteRoute @@ -3845,7 +3900,6 @@ export interface FileRoutesByTo { '/organization/audit-logs': typeof organizationAuditLogsPageRouteRoute '/organization/billing': typeof organizationBillingPageRouteRoute '/organization/secret-scanning': typeof organizationSecretScanningPageRouteRoute - '/organization/secret-sharing': typeof organizationSecretSharingPageRouteRoute '/organization/settings': typeof organizationSettingsPageRouteRoute '/cert-manager/$projectId': typeof certManagerLayoutRouteWithChildren '/kms/$projectId': typeof kmsLayoutRouteWithChildren @@ -3853,6 +3907,7 @@ export interface FileRoutesByTo { '/ssh/$projectId': typeof sshLayoutRouteWithChildren '/organization/app-connections': typeof organizationAppConnectionsAppConnectionsPageRouteRoute '/organization/gateways': typeof organizationGatewaysGatewayListPageRouteRoute + '/organization/secret-sharing': typeof organizationSecretSharingPageRouteRoute '/organization/cert-manager/overview': typeof organizationCertManagerOverviewPageRouteRoute '/organization/groups/$groupId': typeof organizationGroupDetailsByIDPageRouteRoute '/organization/identities/$identityId': typeof organizationIdentityDetailsByIDPageRouteRoute @@ -3860,6 +3915,7 @@ export interface FileRoutesByTo { '/organization/members/$membershipId': typeof organizationUserDetailsByIDPageRouteRoute '/organization/roles/$roleId': typeof organizationRoleByIDPageRouteRoute '/organization/secret-manager/overview': typeof organizationSecretManagerOverviewPageRouteRoute + '/organization/secret-sharing/settings': typeof organizationSecretSharingSettingsPageRouteRoute '/organization/ssh/overview': typeof organizationSshOverviewPageRouteRoute '/cert-manager/$projectId/overview': typeof certManagerCertificatesPageRouteRoute '/cert-manager/$projectId/settings': typeof certManagerSettingsPageRouteRoute @@ -4025,16 +4081,17 @@ export interface FileRoutesById { '/_authenticate/_inject-org-details/_org-layout/organization/audit-logs': typeof organizationAuditLogsPageRouteRoute '/_authenticate/_inject-org-details/_org-layout/organization/billing': typeof organizationBillingPageRouteRoute '/_authenticate/_inject-org-details/_org-layout/organization/secret-scanning': typeof organizationSecretScanningPageRouteRoute - '/_authenticate/_inject-org-details/_org-layout/organization/secret-sharing': typeof organizationSecretSharingPageRouteRoute '/_authenticate/_inject-org-details/_org-layout/organization/settings': typeof organizationSettingsPageRouteRoute '/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId': typeof AuthenticateInjectOrgDetailsOrgLayoutCertManagerProjectIdRouteWithChildren '/_authenticate/_inject-org-details/_org-layout/kms/$projectId': typeof AuthenticateInjectOrgDetailsOrgLayoutKmsProjectIdRouteWithChildren '/_authenticate/_inject-org-details/_org-layout/organization/app-connections': typeof AuthenticateInjectOrgDetailsOrgLayoutOrganizationAppConnectionsRouteWithChildren '/_authenticate/_inject-org-details/_org-layout/organization/gateways': typeof AuthenticateInjectOrgDetailsOrgLayoutOrganizationGatewaysRouteWithChildren + '/_authenticate/_inject-org-details/_org-layout/organization/secret-sharing': typeof AuthenticateInjectOrgDetailsOrgLayoutOrganizationSecretSharingRouteWithChildren '/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId': typeof AuthenticateInjectOrgDetailsOrgLayoutSecretManagerProjectIdRouteWithChildren '/_authenticate/_inject-org-details/_org-layout/ssh/$projectId': typeof AuthenticateInjectOrgDetailsOrgLayoutSshProjectIdRouteWithChildren '/_authenticate/_inject-org-details/_org-layout/organization/app-connections/': typeof organizationAppConnectionsAppConnectionsPageRouteRoute '/_authenticate/_inject-org-details/_org-layout/organization/gateways/': typeof organizationGatewaysGatewayListPageRouteRoute + '/_authenticate/_inject-org-details/_org-layout/organization/secret-sharing/': typeof organizationSecretSharingPageRouteRoute '/_authenticate/_inject-org-details/_org-layout/organization/cert-manager/overview': typeof organizationCertManagerOverviewPageRouteRoute '/_authenticate/_inject-org-details/_org-layout/organization/groups/$groupId': typeof organizationGroupDetailsByIDPageRouteRoute '/_authenticate/_inject-org-details/_org-layout/organization/identities/$identityId': typeof organizationIdentityDetailsByIDPageRouteRoute @@ -4042,6 +4099,7 @@ export interface FileRoutesById { '/_authenticate/_inject-org-details/_org-layout/organization/members/$membershipId': typeof organizationUserDetailsByIDPageRouteRoute '/_authenticate/_inject-org-details/_org-layout/organization/roles/$roleId': typeof organizationRoleByIDPageRouteRoute '/_authenticate/_inject-org-details/_org-layout/organization/secret-manager/overview': typeof organizationSecretManagerOverviewPageRouteRoute + '/_authenticate/_inject-org-details/_org-layout/organization/secret-sharing/settings': typeof organizationSecretSharingSettingsPageRouteRoute '/_authenticate/_inject-org-details/_org-layout/organization/ssh/overview': typeof organizationSshOverviewPageRouteRoute '/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout': typeof certManagerLayoutRouteWithChildren '/_authenticate/_inject-org-details/_org-layout/kms/$projectId/_kms-layout': typeof kmsLayoutRouteWithChildren @@ -4208,16 +4266,17 @@ export interface FileRouteTypes { | '/organization/audit-logs' | '/organization/billing' | '/organization/secret-scanning' - | '/organization/secret-sharing' | '/organization/settings' | '/cert-manager/$projectId' | '/kms/$projectId' | '/organization/app-connections' | '/organization/gateways' + | '/organization/secret-sharing' | '/secret-manager/$projectId' | '/ssh/$projectId' | '/organization/app-connections/' | '/organization/gateways/' + | '/organization/secret-sharing/' | '/organization/cert-manager/overview' | '/organization/groups/$groupId' | '/organization/identities/$identityId' @@ -4225,6 +4284,7 @@ export interface FileRouteTypes { | '/organization/members/$membershipId' | '/organization/roles/$roleId' | '/organization/secret-manager/overview' + | '/organization/secret-sharing/settings' | '/organization/ssh/overview' | '/cert-manager/$projectId/overview' | '/cert-manager/$projectId/settings' @@ -4380,7 +4440,6 @@ export interface FileRouteTypes { | '/organization/audit-logs' | '/organization/billing' | '/organization/secret-scanning' - | '/organization/secret-sharing' | '/organization/settings' | '/cert-manager/$projectId' | '/kms/$projectId' @@ -4388,6 +4447,7 @@ export interface FileRouteTypes { | '/ssh/$projectId' | '/organization/app-connections' | '/organization/gateways' + | '/organization/secret-sharing' | '/organization/cert-manager/overview' | '/organization/groups/$groupId' | '/organization/identities/$identityId' @@ -4395,6 +4455,7 @@ export interface FileRouteTypes { | '/organization/members/$membershipId' | '/organization/roles/$roleId' | '/organization/secret-manager/overview' + | '/organization/secret-sharing/settings' | '/organization/ssh/overview' | '/cert-manager/$projectId/overview' | '/cert-manager/$projectId/settings' @@ -4558,16 +4619,17 @@ export interface FileRouteTypes { | '/_authenticate/_inject-org-details/_org-layout/organization/audit-logs' | '/_authenticate/_inject-org-details/_org-layout/organization/billing' | '/_authenticate/_inject-org-details/_org-layout/organization/secret-scanning' - | '/_authenticate/_inject-org-details/_org-layout/organization/secret-sharing' | '/_authenticate/_inject-org-details/_org-layout/organization/settings' | '/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId' | '/_authenticate/_inject-org-details/_org-layout/kms/$projectId' | '/_authenticate/_inject-org-details/_org-layout/organization/app-connections' | '/_authenticate/_inject-org-details/_org-layout/organization/gateways' + | '/_authenticate/_inject-org-details/_org-layout/organization/secret-sharing' | '/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId' | '/_authenticate/_inject-org-details/_org-layout/ssh/$projectId' | '/_authenticate/_inject-org-details/_org-layout/organization/app-connections/' | '/_authenticate/_inject-org-details/_org-layout/organization/gateways/' + | '/_authenticate/_inject-org-details/_org-layout/organization/secret-sharing/' | '/_authenticate/_inject-org-details/_org-layout/organization/cert-manager/overview' | '/_authenticate/_inject-org-details/_org-layout/organization/groups/$groupId' | '/_authenticate/_inject-org-details/_org-layout/organization/identities/$identityId' @@ -4575,6 +4637,7 @@ export interface FileRouteTypes { | '/_authenticate/_inject-org-details/_org-layout/organization/members/$membershipId' | '/_authenticate/_inject-org-details/_org-layout/organization/roles/$roleId' | '/_authenticate/_inject-org-details/_org-layout/organization/secret-manager/overview' + | '/_authenticate/_inject-org-details/_org-layout/organization/secret-sharing/settings' | '/_authenticate/_inject-org-details/_org-layout/organization/ssh/overview' | '/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout' | '/_authenticate/_inject-org-details/_org-layout/kms/$projectId/_kms-layout' @@ -4936,10 +4999,10 @@ export const routeTree = rootRoute "/_authenticate/_inject-org-details/_org-layout/organization/audit-logs", "/_authenticate/_inject-org-details/_org-layout/organization/billing", "/_authenticate/_inject-org-details/_org-layout/organization/secret-scanning", - "/_authenticate/_inject-org-details/_org-layout/organization/secret-sharing", "/_authenticate/_inject-org-details/_org-layout/organization/settings", "/_authenticate/_inject-org-details/_org-layout/organization/app-connections", "/_authenticate/_inject-org-details/_org-layout/organization/gateways", + "/_authenticate/_inject-org-details/_org-layout/organization/secret-sharing", "/_authenticate/_inject-org-details/_org-layout/organization/cert-manager/overview", "/_authenticate/_inject-org-details/_org-layout/organization/groups/$groupId", "/_authenticate/_inject-org-details/_org-layout/organization/identities/$identityId", @@ -4981,10 +5044,6 @@ export const routeTree = rootRoute "filePath": "organization/SecretScanningPage/route.tsx", "parent": "/_authenticate/_inject-org-details/_org-layout/organization" }, - "/_authenticate/_inject-org-details/_org-layout/organization/secret-sharing": { - "filePath": "organization/SecretSharingPage/route.tsx", - "parent": "/_authenticate/_inject-org-details/_org-layout/organization" - }, "/_authenticate/_inject-org-details/_org-layout/organization/settings": { "filePath": "organization/SettingsPage/route.tsx", "parent": "/_authenticate/_inject-org-details/_org-layout/organization" @@ -5018,6 +5077,14 @@ export const routeTree = rootRoute "/_authenticate/_inject-org-details/_org-layout/organization/gateways/" ] }, + "/_authenticate/_inject-org-details/_org-layout/organization/secret-sharing": { + "filePath": "", + "parent": "/_authenticate/_inject-org-details/_org-layout/organization", + "children": [ + "/_authenticate/_inject-org-details/_org-layout/organization/secret-sharing/", + "/_authenticate/_inject-org-details/_org-layout/organization/secret-sharing/settings" + ] + }, "/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId": { "filePath": "", "parent": "/_authenticate/_inject-org-details/_org-layout", @@ -5040,6 +5107,10 @@ export const routeTree = rootRoute "filePath": "organization/Gateways/GatewayListPage/route.tsx", "parent": "/_authenticate/_inject-org-details/_org-layout/organization/gateways" }, + "/_authenticate/_inject-org-details/_org-layout/organization/secret-sharing/": { + "filePath": "organization/SecretSharingPage/route.tsx", + "parent": "/_authenticate/_inject-org-details/_org-layout/organization/secret-sharing" + }, "/_authenticate/_inject-org-details/_org-layout/organization/cert-manager/overview": { "filePath": "organization/CertManagerOverviewPage/route.tsx", "parent": "/_authenticate/_inject-org-details/_org-layout/organization" @@ -5068,6 +5139,10 @@ export const routeTree = rootRoute "filePath": "organization/SecretManagerOverviewPage/route.tsx", "parent": "/_authenticate/_inject-org-details/_org-layout/organization" }, + "/_authenticate/_inject-org-details/_org-layout/organization/secret-sharing/settings": { + "filePath": "organization/SecretSharingSettingsPage/route.tsx", + "parent": "/_authenticate/_inject-org-details/_org-layout/organization/secret-sharing" + }, "/_authenticate/_inject-org-details/_org-layout/organization/ssh/overview": { "filePath": "organization/SshOverviewPage/route.tsx", "parent": "/_authenticate/_inject-org-details/_org-layout/organization" diff --git a/frontend/src/routes.ts b/frontend/src/routes.ts index 983129e33..19cf8fc62 100644 --- a/frontend/src/routes.ts +++ b/frontend/src/routes.ts @@ -16,7 +16,10 @@ const organizationRoutes = route("/organization", [ route("/admin", "organization/AdminPage/route.tsx"), route("/audit-logs", "organization/AuditLogsPage/route.tsx"), route("/billing", "organization/BillingPage/route.tsx"), - route("/secret-sharing", "organization/SecretSharingPage/route.tsx"), + route("/secret-sharing", [ + index("organization/SecretSharingPage/route.tsx"), + route("/settings", "organization/SecretSharingSettingsPage/route.tsx") + ]), route("/settings", "organization/SettingsPage/route.tsx"), route("/secret-scanning", "organization/SecretScanningPage/route.tsx"), route("/groups/$groupId", "organization/GroupDetailsByIDPage/route.tsx"),