diff --git a/.github/workflows/cypress-test-tenancy-disabled.yml b/.github/workflows/cypress-test-tenancy-disabled.yml index 024dd9cbc..d67e478a4 100644 --- a/.github/workflows/cypress-test-tenancy-disabled.yml +++ b/.github/workflows/cypress-test-tenancy-disabled.yml @@ -83,4 +83,4 @@ jobs: git clone https://github.com/opensearch-project/opensearch-dashboards-functional-test.git cd opensearch-dashboards-functional-test npm install cypress --save-dev - yarn cypress:run-with-security --browser chrome --spec "cypress/integration/plugins/security-dashboards-plugin/inaccessible_tenancy_features.js" + yarn cypress:run-with-security --browser chrome --spec "cypress/integration/plugins/security/inaccessible_tenancy_features.js" diff --git a/public/apps/configuration/app-router.tsx b/public/apps/configuration/app-router.tsx index 8fba2f24f..782d833a7 100644 --- a/public/apps/configuration/app-router.tsx +++ b/public/apps/configuration/app-router.tsx @@ -70,10 +70,6 @@ const ROUTE_MAP: { [key: string]: RouteItem } = { name: 'Tenants', href: buildUrl(ResourceType.tenants), }, - [ResourceType.tenantsManageTab]: { - name: 'TenantsManageTab', - href: buildUrl(ResourceType.tenantsManageTab), - }, [ResourceType.tenantsConfigureTab]: { name: '', href: buildUrl(ResourceType.tenantsConfigureTab), @@ -88,22 +84,27 @@ const ROUTE_MAP: { [key: string]: RouteItem } = { }, }; -const ROUTE_LIST = [ - ROUTE_MAP.getStarted, - ROUTE_MAP[ResourceType.auth], - ROUTE_MAP[ResourceType.roles], - ROUTE_MAP[ResourceType.users], - ROUTE_MAP[ResourceType.serviceAccounts], - ROUTE_MAP[ResourceType.permissions], - ROUTE_MAP[ResourceType.tenants], - ROUTE_MAP[ResourceType.auditLogging], - ROUTE_MAP[ResourceType.tenantsConfigureTab], -]; +const getRouteList = (multitenancyEnabled: boolean) => { + return [ + ROUTE_MAP.getStarted, + ROUTE_MAP[ResourceType.auth], + ROUTE_MAP[ResourceType.roles], + ROUTE_MAP[ResourceType.users], + ROUTE_MAP[ResourceType.serviceAccounts], + ROUTE_MAP[ResourceType.permissions], + ...(multitenancyEnabled ? [ROUTE_MAP[ResourceType.tenants]] : []), + ROUTE_MAP[ResourceType.auditLogging], + ]; +}; -const allNavPanelUrls = ROUTE_LIST.map((route) => route.href).concat([ - buildUrl(ResourceType.auditLogging) + SUB_URL_FOR_GENERAL_SETTINGS_EDIT, - buildUrl(ResourceType.auditLogging) + SUB_URL_FOR_COMPLIANCE_SETTINGS_EDIT, -]); +export const allNavPanelUrls = (multitenancyEnabled: boolean) => + getRouteList(multitenancyEnabled) + .map((route) => route.href) + .concat([ + buildUrl(ResourceType.auditLogging) + SUB_URL_FOR_GENERAL_SETTINGS_EDIT, + buildUrl(ResourceType.auditLogging) + SUB_URL_FOR_COMPLIANCE_SETTINGS_EDIT, + ...(multitenancyEnabled ? [buildUrl(ResourceType.tenantsConfigureTab)] : []), + ]); export function getBreadcrumbs( resourceType?: ResourceType, @@ -155,21 +156,46 @@ export const LocalCluster = { label: 'Local cluster', id: '' }; export const DataSourceContext = createContext(null); export function AppRouter(props: AppDependencies) { + const multitenancyEnabled = props.config.multitenancy.enabled; const dataSourceEnabled = !!props.depsStart.dataSource?.dataSourceEnabled; const setGlobalBreadcrumbs = flow(getBreadcrumbs, props.coreStart.chrome.setBreadcrumbs); const dataSourceFromUrl = dataSourceEnabled ? getDataSourceFromUrl() : LocalCluster; const [dataSource, setDataSource] = useState(dataSourceFromUrl); + function getTenancyRoutes() { + if (multitenancyEnabled) { + return ( + <> + { + setGlobalBreadcrumbs(ResourceType.tenants); + return ; + }} + /> + { + setGlobalBreadcrumbs(ResourceType.tenants); + return ; + }} + /> + + ); + } + return null; + } + return ( - {allNavPanelUrls.map((route) => ( + {allNavPanelUrls(multitenancyEnabled).map((route) => ( // Create different routes to update the 'selected' nav item . - + ))} @@ -267,20 +293,6 @@ export function AppRouter(props: AppDependencies) { return ; }} /> - { - setGlobalBreadcrumbs(ResourceType.tenants); - return ; - }} - /> - { - setGlobalBreadcrumbs(ResourceType.tenants); - return ; - }} - /> { @@ -288,6 +300,7 @@ export function AppRouter(props: AppDependencies) { return ; }} /> + {getTenancyRoutes()} diff --git a/public/apps/configuration/panels/get-started.tsx b/public/apps/configuration/panels/get-started.tsx index ced4503c2..dceea1691 100644 --- a/public/apps/configuration/panels/get-started.tsx +++ b/public/apps/configuration/panels/get-started.tsx @@ -280,39 +280,41 @@ export function GetStarted(props: AppDependencies) { - - -

Optional: Multi-tenancy

-
- -

- By default tenancy is activated in Dashboards. Tenants in OpenSearch Dashboards are - spaces for saving index patterns, visualizations, dashboards, and other OpenSearch - Dashboards objects. -

- - - { - window.location.href = buildHashUrl(ResourceType.tenants); - }} - > - Manage Multi-tenancy - - - - { - window.location.href = buildHashUrl(ResourceType.tenantsConfigureTab); - }} - > - Configure Multi-tenancy - - - -
-
+ {props.config.multitenancy.enabled ? ( + + +

Optional: Multi-tenancy

+
+ +

+ By default tenancy is activated in Dashboards. Tenants in OpenSearch Dashboards are + spaces for saving index patterns, visualizations, dashboards, and other OpenSearch + Dashboards objects. +

+ + + { + window.location.href = buildHashUrl(ResourceType.tenants); + }} + > + Manage Multi-tenancy + + + + { + window.location.href = buildHashUrl(ResourceType.tenantsConfigureTab); + }} + > + Configure Multi-tenancy + + + +
+
+ ) : null} diff --git a/public/apps/configuration/panels/tenant-list/tenant-instruction-view.tsx b/public/apps/configuration/panels/tenant-list/tenant-instruction-view.tsx deleted file mode 100644 index d5e5ffe83..000000000 --- a/public/apps/configuration/panels/tenant-list/tenant-instruction-view.tsx +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -import React from 'react'; -import { EuiSpacer, EuiText, EuiTitle } from '@elastic/eui'; - -export function TenantInstructionView() { - return ( - <> - -

Tenants

-
- - - - -

You have not enabled multi tenancy

-
- - - Contact your administrator to enable multi tenancy. - - - ); -} diff --git a/public/apps/configuration/panels/tenant-list/tenant-list.tsx b/public/apps/configuration/panels/tenant-list/tenant-list.tsx index 0b97c6ae1..bc6aa71b8 100644 --- a/public/apps/configuration/panels/tenant-list/tenant-list.tsx +++ b/public/apps/configuration/panels/tenant-list/tenant-list.tsx @@ -30,7 +30,6 @@ import { AppDependencies } from '../../../types'; import { ExternalLink } from '../../utils/display-utils'; import { DocLinks } from '../../constants'; import { getDashboardsInfo } from '../../../../utils/dashboards-info-utils'; -import { TenantInstructionView } from './tenant-instruction-view'; import { LocalCluster } from '../../app-router'; import { SecurityPluginTopNavMenu } from '../../top-nav-menu'; @@ -59,8 +58,8 @@ export function TenantList(props: TenantListProps) { <>

- Tenancy is currently disabled and users don't have access to this feature. To create, - edit tenants you must enabled tenanc throught he configure tenancy page. + Tenancy is currently disabled and users don't have access to this feature. Enable + tenancy through the configure tenancy page.

; - } - return ( <> - -

- Tenants -

-
- - -

- You have not enabled multi tenancy -

-
- - Contact your administrator to enable multi tenancy. - - -`; diff --git a/public/apps/configuration/panels/tenant-list/test/tenant-instruction-view.test.tsx b/public/apps/configuration/panels/tenant-list/test/tenant-instruction-view.test.tsx deleted file mode 100644 index 929ab0629..000000000 --- a/public/apps/configuration/panels/tenant-list/test/tenant-instruction-view.test.tsx +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -import { shallow } from 'enzyme'; -import React from 'react'; -import { TenantInstructionView } from '../tenant-instruction-view'; - -describe('Tenant instruction view', () => { - it('renders', () => { - const component = shallow(); - expect(component).toMatchSnapshot(); - }); -}); diff --git a/public/apps/configuration/panels/tenant-list/test/tenant-list.test.tsx b/public/apps/configuration/panels/tenant-list/test/tenant-list.test.tsx index a00a417d8..e6f5bfbf3 100644 --- a/public/apps/configuration/panels/tenant-list/test/tenant-list.test.tsx +++ b/public/apps/configuration/panels/tenant-list/test/tenant-list.test.tsx @@ -20,7 +20,6 @@ import { EuiInMemoryTable } from '@elastic/eui'; import { useDeleteConfirmState } from '../../../utils/delete-confirm-modal-utils'; import { Tenant } from '../../../types'; import { TenantEditModal } from '../edit-modal'; -import { TenantInstructionView } from '../tenant-instruction-view'; import { getDashboardsInfo } from '../../../../../utils/dashboards-info-utils'; jest.mock('../../../utils/tenant-utils'); @@ -107,33 +106,6 @@ describe('Tenant list', () => { expect(component.find(EuiInMemoryTable).prop('items').length).toBe(0); }); - it('renders when multitenancy is disabled in the opensearch_dashboards.yml', () => { - (getDashboardsInfo as jest.Mock).mockImplementation(() => { - return { - multitenancy_enabled: false, - private_tenant_enabled: true, - default_tenant: '', - }; - }); - const config1 = { - multitenancy: { - enabled: false, - tenants: { - enable_private: true, - }, - }, - }; - const component = shallow( - - ); - expect(component.find(TenantInstructionView).length).toBe(0); - }); - it('fetch data error', (done) => { jest.spyOn(React, 'useEffect').mockImplementationOnce((f) => f()); // Hide the error message diff --git a/public/apps/configuration/panels/test/__snapshots__/get-started.test.tsx.snap b/public/apps/configuration/panels/test/__snapshots__/get-started.test.tsx.snap index bbecedbc5..9c4bdb223 100644 --- a/public/apps/configuration/panels/test/__snapshots__/get-started.test.tsx.snap +++ b/public/apps/configuration/panels/test/__snapshots__/get-started.test.tsx.snap @@ -33,6 +33,9 @@ exports[`Get started (landing page) renders when backend configuration is disabl { ui: { backend_configurable: true, }, + multitenancy: { + enabled: true, + }, }; afterEach(() => { React.useContext.mockRestore(); @@ -69,6 +72,9 @@ describe('Get started (landing page)', () => { ui: { backend_configurable: false, }, + multitenancy: { + enabled: true, + }, }; const component = shallow( @@ -107,10 +103,6 @@ exports[`SecurityPluginTopNavMenu renders DataSourceMenu when dataSource is enab "href": "/auditLogging", "name": "Audit logs", }, - Object { - "href": "/tenantsConfigureTab", - "name": "", - }, ] } /> @@ -157,10 +149,6 @@ exports[`SecurityPluginTopNavMenu renders DataSourceMenu when dataSource is enab "href": "/auditLogging", "name": "Audit logs", }, - Object { - "href": "/tenantsConfigureTab", - "name": "", - }, ] } /> @@ -207,10 +195,6 @@ exports[`SecurityPluginTopNavMenu renders DataSourceMenu when dataSource is enab "href": "/auditLogging", "name": "Audit logs", }, - Object { - "href": "/tenantsConfigureTab", - "name": "", - }, ] } /> @@ -257,10 +241,6 @@ exports[`SecurityPluginTopNavMenu renders DataSourceMenu when dataSource is enab "href": "/auditLogging", "name": "Audit logs", }, - Object { - "href": "/tenantsConfigureTab", - "name": "", - }, ] } /> @@ -307,10 +287,6 @@ exports[`SecurityPluginTopNavMenu renders DataSourceMenu when dataSource is enab "href": "/auditLogging", "name": "Audit logs", }, - Object { - "href": "/tenantsConfigureTab", - "name": "", - }, ] } /> @@ -357,10 +333,6 @@ exports[`SecurityPluginTopNavMenu renders DataSourceMenu when dataSource is enab "href": "/auditLogging", "name": "Audit logs", }, - Object { - "href": "/tenantsConfigureTab", - "name": "", - }, ] } /> @@ -407,10 +379,6 @@ exports[`SecurityPluginTopNavMenu renders DataSourceMenu when dataSource is enab "href": "/auditLogging", "name": "Audit logs", }, - Object { - "href": "/tenantsConfigureTab", - "name": "", - }, ] } /> @@ -418,8 +386,8 @@ exports[`SecurityPluginTopNavMenu renders DataSourceMenu when dataSource is enab
@@ -468,8 +432,8 @@ exports[`SecurityPluginTopNavMenu renders DataSourceMenu when dataSource is enab @@ -518,8 +478,8 @@ exports[`SecurityPluginTopNavMenu renders DataSourceMenu when dataSource is enab @@ -617,15 +573,15 @@ exports[`SecurityPluginTopNavMenu renders DataSourceMenu when dataSource is enab render={[Function]} /> ({ @@ -33,23 +33,28 @@ describe('SecurityPluginTopNavMenu', () => { }, }; - const dataSourceMenuMock = jest.fn(() =>
Mock DataSourceMenu
); + const securityPluginConfigMock = { + multitenancy: { + enabled: true, + }, + ui: {}, + }; - const dataSourceManagementMock = { - ui: { - DataSourceMenu: dataSourceMenuMock, + const securityPluginConfigMockMultitenancyDisabled = { + multitenancy: { + enabled: false, }, + ui: {}, }; it('renders DataSourceMenu when dataSource is enabled', () => { - const securityPluginStartDepsMock = { - dataSource: { - dataSourceEnabled: true, - }, - }; - const wrapper = shallow( - + ); expect(wrapper).toMatchSnapshot(); @@ -67,6 +72,7 @@ describe('SecurityPluginTopNavMenu', () => { coreStart={coreStartMock} depsStart={securityPluginStartDepsMock} params={{ appBasePath: '' }} + config={securityPluginConfigMock} /> ); @@ -85,9 +91,15 @@ describe('SecurityPluginTopNavMenu', () => { coreStart={coreStartMock} depsStart={securityPluginStartDepsMock} params={{ appBasePath: '' }} + config={securityPluginConfigMock} /> ); expect(getDataSourceFromUrl).toHaveBeenCalled(); }); + + it('checks paths returned with multitenancy off vs on', () => { + expect(allNavPanelUrls(true)).toContain('/tenants'); + expect(allNavPanelUrls(false)).not.toContain('/tenants'); + }); }); diff --git a/public/apps/configuration/test/top-nav-menu.test.tsx b/public/apps/configuration/test/top-nav-menu.test.tsx index 867474aa8..62d495877 100644 --- a/public/apps/configuration/test/top-nav-menu.test.tsx +++ b/public/apps/configuration/test/top-nav-menu.test.tsx @@ -39,11 +39,17 @@ describe('SecurityPluginTopNavMenu', () => { dataSourceEnabled: true, }, }; + const securityPluginConfigMock = { + multitenancy: { + enabled: false, + }, + }; const wrapper = render(