Skip to content

Commit

Permalink
Do not register tenancy app if disabled in yml (opensearch-project#2057)
Browse files Browse the repository at this point in the history
* Do not register tenancy app if disabled in yml

Signed-off-by: Derek Ho <[email protected]>

* Adds a test

Signed-off-by: Derek Ho <[email protected]>

* Revert type and export

Signed-off-by: Derek Ho <[email protected]>

* Use constants

Signed-off-by: Derek Ho <[email protected]>

* Remove extra license field

Signed-off-by: Derek Ho <[email protected]>

* Refactor with constants

Signed-off-by: Derek Ho <[email protected]>

---------

Signed-off-by: Derek Ho <[email protected]>
  • Loading branch information
derek-ho authored Jul 25, 2024
1 parent 2cc3f60 commit 0ed2cf2
Show file tree
Hide file tree
Showing 3 changed files with 223 additions and 24 deletions.
7 changes: 7 additions & 0 deletions common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@

export const PLUGIN_ID = 'opensearchDashboardsSecurity';
export const PLUGIN_NAME = 'security-dashboards-plugin';
export const PLUGIN_GET_STARTED_APP_ID = `${PLUGIN_NAME}_getstarted`;
export const PLUGIN_AUTH_APP_ID = `${PLUGIN_NAME}_auth`;
export const PLUGIN_ROLES_APP_ID = `${PLUGIN_NAME}_roles`;
export const PLUGIN_USERS_APP_ID = `${PLUGIN_NAME}_users`;
export const PLUGIN_PERMISSIONS_APP_ID = `${PLUGIN_NAME}_permissions`;
export const PLUGIN_TENANTS_APP_ID = `${PLUGIN_NAME}_tenants`;
export const PLUGIN_AUDITLOG_APP_ID = `${PLUGIN_NAME}_auditlog`;

export const APP_ID_LOGIN = 'login';
export const APP_ID_CUSTOMERROR = 'customerror';
Expand Down
62 changes: 38 additions & 24 deletions public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,19 @@ import {
PluginInitializerContext,
WorkspaceAvailability,
} from '../../../src/core/public';
import { APP_ID_LOGIN, CUSTOM_ERROR_PAGE_URI, LOGIN_PAGE_URI, PLUGIN_NAME } from '../common';
import {
APP_ID_LOGIN,
CUSTOM_ERROR_PAGE_URI,
LOGIN_PAGE_URI,
PLUGIN_AUDITLOG_APP_ID,
PLUGIN_AUTH_APP_ID,
PLUGIN_GET_STARTED_APP_ID,
PLUGIN_NAME,
PLUGIN_PERMISSIONS_APP_ID,
PLUGIN_ROLES_APP_ID,
PLUGIN_TENANTS_APP_ID,
PLUGIN_USERS_APP_ID,
} from '../common';
import { APP_ID_CUSTOMERROR } from '../common';
import { setupTopNavButton } from './apps/account/account-app';
import { fetchAccountInfoSafe } from './apps/account/utils';
Expand Down Expand Up @@ -175,7 +187,7 @@ export class SecurityPlugin

if (core.chrome.navGroup.getNavGroupEnabled()) {
core.application.register({
id: `security-dashboards-plugin_getstarted`,
id: PLUGIN_GET_STARTED_APP_ID,
title: 'Get Started',
order: 8040,
workspaceAvailability: WorkspaceAvailability.outsideWorkspace,
Expand All @@ -185,7 +197,7 @@ export class SecurityPlugin
},
});
core.application.register({
id: `security-dashboards-plugin_auth`,
id: PLUGIN_AUTH_APP_ID,
title: 'Authentication',
order: 8040,
workspaceAvailability: WorkspaceAvailability.outsideWorkspace,
Expand All @@ -195,7 +207,7 @@ export class SecurityPlugin
},
});
core.application.register({
id: `security-dashboards-plugin_roles`,
id: PLUGIN_ROLES_APP_ID,
title: 'Roles',
order: 8040,
workspaceAvailability: WorkspaceAvailability.outsideWorkspace,
Expand All @@ -205,7 +217,7 @@ export class SecurityPlugin
},
});
core.application.register({
id: `security-dashboards-plugin_users`,
id: PLUGIN_USERS_APP_ID,
title: 'Internal users',
order: 8040,
workspaceAvailability: WorkspaceAvailability.outsideWorkspace,
Expand All @@ -215,7 +227,7 @@ export class SecurityPlugin
},
});
core.application.register({
id: `security-dashboards-plugin_permissions`,
id: PLUGIN_PERMISSIONS_APP_ID,
title: 'Permissions',
order: 8040,
workspaceAvailability: WorkspaceAvailability.outsideWorkspace,
Expand All @@ -224,18 +236,20 @@ export class SecurityPlugin
return mountWrapper(params, '/permissions');
},
});
if (config.multitenancy.enabled) {
core.application.register({
id: PLUGIN_TENANTS_APP_ID,
title: 'Tenants',
order: 8040,
workspaceAvailability: WorkspaceAvailability.outsideWorkspace,
updater$: this.appStateUpdater,
mount: async (params: AppMountParameters) => {
return mountWrapper(params, '/tenants');
},
});
}
core.application.register({
id: `security-dashboards-plugin_tenants`,
title: 'Tenants',
order: 8040,
workspaceAvailability: WorkspaceAvailability.outsideWorkspace,
updater$: this.appStateUpdater,
mount: async (params: AppMountParameters) => {
return mountWrapper(params, '/tenants');
},
});
core.application.register({
id: `security-dashboards-plugin_auditlog`,
id: PLUGIN_AUDITLOG_APP_ID,
title: 'Audit logs',
order: 8040,
workspaceAvailability: WorkspaceAvailability.outsideWorkspace,
Expand All @@ -248,31 +262,31 @@ export class SecurityPlugin

core.chrome.navGroup.addNavLinksToGroup(DEFAULT_NAV_GROUPS.dataAdministration, [
{
id: `security-dashboards-plugin_getstarted`,
id: PLUGIN_GET_STARTED_APP_ID,
category: dataAccessUsersCategory,
},
{
id: `security-dashboards-plugin_auth`,
id: PLUGIN_AUTH_APP_ID,
category: dataAccessUsersCategory,
},
{
id: `security-dashboards-plugin_roles`,
id: PLUGIN_ROLES_APP_ID,
category: dataAccessUsersCategory,
},
{
id: `security-dashboards-plugin_users`,
id: PLUGIN_USERS_APP_ID,
category: dataAccessUsersCategory,
},
{
id: `security-dashboards-plugin_permissions`,
id: PLUGIN_PERMISSIONS_APP_ID,
category: dataAccessUsersCategory,
},
{
id: `security-dashboards-plugin_tenants`,
id: PLUGIN_TENANTS_APP_ID,
category: dataAccessUsersCategory,
},
{
id: `security-dashboards-plugin_auditlog`,
id: PLUGIN_AUDITLOG_APP_ID,
category: dataAccessUsersCategory,
},
]);
Expand Down
178 changes: 178 additions & 0 deletions public/test/plugin.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
/*
* 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 { coreMock } from '../../../../src/core/public/mocks';
import { SecurityPlugin } from '../plugin.ts';
import * as pluginModule from '../plugin'; // Import the entire module to mock specific functions
import {
PLUGIN_AUDITLOG_APP_ID,
PLUGIN_AUTH_APP_ID,
PLUGIN_GET_STARTED_APP_ID,
PLUGIN_PERMISSIONS_APP_ID,
PLUGIN_ROLES_APP_ID,
PLUGIN_TENANTS_APP_ID,
PLUGIN_USERS_APP_ID,
} from '../../common/index.ts';

// Mock the hasApiPermission function
jest.mock('../plugin', () => {
const originalModule = jest.requireActual('../plugin');
return {
...originalModule,
hasApiPermission: jest.fn(), // Mock the function here
};
});

describe('SecurityPlugin', () => {
let plugin;
let coreSetup;
let coreStart;
let initializerContext;
let deps;

beforeEach(() => {
coreSetup = coreMock.createSetup();
coreStart = coreMock.createStart();
initializerContext = {
config: {
get: jest.fn().mockReturnValue({
readonly_mode: { roles: [] },
multitenancy: { enabled: true, enable_aggregation_view: false },
clusterPermissions: { include: [] },
indexPermissions: { include: [] },
disabledTransportCategories: { exclude: [] },
disabledRestCategories: { exclude: [] },
ui: { autologout: false },
}),
},
};
deps = {
dataSource: { dataSourceEnabled: true },
savedObjectsManagement: { createSetup: jest.fn() },
};
});

it('does not call register function for certain applications when getNavGroupEnabled is off', async () => {
// Mock hasApiPermission to return false
pluginModule.hasApiPermission.mockResolvedValue(false); // Access the mock via the imported module

// Instantiate the plugin after mocking
plugin = new SecurityPlugin(initializerContext);

// Override getNavGroupEnabled to return false
coreSetup.chrome.navGroup = {
...coreSetup.chrome.navGroup,
getNavGroupEnabled: () => false,
};
// Mock the core.application.register function
const registerSpy = jest.spyOn(coreSetup.application, 'register');

// Execute the setup function
await plugin.setup(coreSetup, deps);

// Assert that the register function was not called for specific applications
const registeredApps = registerSpy.mock.calls.map((call) => call[0].id);
const expectedApps = [
PLUGIN_GET_STARTED_APP_ID,
PLUGIN_AUTH_APP_ID,
PLUGIN_ROLES_APP_ID,
PLUGIN_USERS_APP_ID,
PLUGIN_PERMISSIONS_APP_ID,
PLUGIN_TENANTS_APP_ID,
PLUGIN_AUDITLOG_APP_ID,
];

expectedApps.forEach((app) => {
expect(registeredApps).not.toContain(app);
});
});

it('calls register function for certain applications when getNavGroupEnabled is on', async () => {
// Mock hasApiPermission to return true
pluginModule.hasApiPermission.mockResolvedValue(true); // Access the mock via the imported module

// Instantiate the plugin after mocking
plugin = new SecurityPlugin(initializerContext);

// Override getNavGroupEnabled to return true
coreSetup.chrome.navGroup = {
...coreSetup.chrome.navGroup,
getNavGroupEnabled: () => true,
};
// Mock the core.application.register function
const registerSpy = jest.spyOn(coreSetup.application, 'register');

// Execute the setup function
await plugin.setup(coreSetup, deps);

// Assert that the register function was called for specific applications
const registeredApps = registerSpy.mock.calls.map((call) => call[0].id);
const expectedApps = [
PLUGIN_GET_STARTED_APP_ID,
PLUGIN_AUTH_APP_ID,
PLUGIN_ROLES_APP_ID,
PLUGIN_USERS_APP_ID,
PLUGIN_PERMISSIONS_APP_ID,
PLUGIN_TENANTS_APP_ID,
PLUGIN_AUDITLOG_APP_ID,
];

expectedApps.forEach((app) => {
expect(registeredApps).toContain(app);
});
});

it('does not call register function for tenant app when multitenancy is off', async () => {
// Mock hasApiPermission to return true
pluginModule.hasApiPermission.mockResolvedValue(true);

// InitializerContext with multitenancy disabled
initializerContext = {
config: {
get: jest.fn().mockReturnValue({
readonly_mode: { roles: [] },
multitenancy: { enabled: false, enable_aggregation_view: false },
clusterPermissions: { include: [] },
indexPermissions: { include: [] },
disabledTransportCategories: { exclude: [] },
disabledRestCategories: { exclude: [] },
ui: { autologout: false },
}),
},
};

// Instantiate the plugin after mocking
plugin = new SecurityPlugin(initializerContext);

// Override getNavGroupEnabled to return true
coreSetup.chrome.navGroup = {
...coreSetup.chrome.navGroup,
getNavGroupEnabled: () => true,
};
// Mock the core.application.register function
const registerSpy = jest.spyOn(coreSetup.application, 'register');

// Execute the setup function
await plugin.setup(coreSetup, deps);

// Assert that the register function was not called for tenancy app
const registeredApps = registerSpy.mock.calls.map((call) => call[0].id);

expect(registeredApps).not.toContain(PLUGIN_TENANTS_APP_ID);

// Assert that other apps are registered because the feature flag is on
expect(registeredApps).toContain(PLUGIN_GET_STARTED_APP_ID);
});
});

0 comments on commit 0ed2cf2

Please sign in to comment.