From 918faa5620551e637d00373e453ab97bf9c3a4fa Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Fri, 17 May 2024 13:28:22 -0400 Subject: [PATCH 01/43] Introduce opensearch_security.configuration.admin_pages_enabled setting to be able to disable security admin pages Signed-off-by: Craig Perkins --- public/plugin.ts | 85 ++-- public/types.ts | 4 + ...pensearch_security_configuration_plugin.ts | 53 --- server/backend/opensearch_security_plugin.ts | 55 ++- server/index.ts | 2 + server/plugin.ts | 15 +- server/routes/index.ts | 364 +++++++++--------- 7 files changed, 305 insertions(+), 273 deletions(-) diff --git a/public/plugin.ts b/public/plugin.ts index ca9798a94..a3dee68b7 100644 --- a/public/plugin.ts +++ b/public/plugin.ts @@ -63,6 +63,7 @@ async function hasApiPermission(core: CoreSetup): Promise { } const DEFAULT_READONLY_ROLES = ['kibana_read_only']; +const DEFAULT_ADMIN_PAGES_ENABLED = true; const APP_ID_HOME = 'home'; const APP_ID_DASHBOARDS = 'dashboards'; // OpenSearchDashboards app is for legacy url migration @@ -86,8 +87,6 @@ export class SecurityPlugin core: CoreSetup, deps: SecurityPluginSetupDependencies ): Promise { - const apiPermission = await hasApiPermission(core); - const config = this.initializerContext.config.get(); const accountInfo = (await fetchAccountInfoSafe(core.http))?.data; @@ -96,43 +95,53 @@ export class SecurityPlugin (config.readonly_mode?.roles || DEFAULT_READONLY_ROLES).includes(role) ); - if (apiPermission) { - core.application.register({ - id: PLUGIN_NAME, - title: 'Security', - order: 9050, - mount: async (params: AppMountParameters) => { - const { renderApp } = await import('./apps/configuration/configuration-app'); - const [coreStart, depsStart] = await core.getStartServices(); - - // merge OpenSearchDashboards yml configuration - includeClusterPermissions(config.clusterPermissions.include); - includeIndexPermissions(config.indexPermissions.include); - - excludeFromDisabledTransportCategories(config.disabledTransportCategories.exclude); - excludeFromDisabledRestCategories(config.disabledRestCategories.exclude); - - return renderApp( - coreStart, - depsStart as SecurityPluginStartDependencies, - params, - config, - deps.dataSourceManagement - ); - }, - category: DEFAULT_APP_CATEGORIES.management, - }); - - if (deps.managementOverview) { - deps.managementOverview.register({ + const adminPagesEnabled = + config.configuration?.admin_pages_enabled !== undefined + ? config.configuration?.admin_pages_enabled + : DEFAULT_ADMIN_PAGES_ENABLED; + + let apiPermission: boolean | undefined; + if (adminPagesEnabled) { + apiPermission = await hasApiPermission(core); + console.log('apiPermission: ' + apiPermission); + if (apiPermission) { + core.application.register({ id: PLUGIN_NAME, title: 'Security', order: 9050, - description: i18n.translate('security.securityDescription', { - defaultMessage: - 'Configure how users access data in OpenSearch with authentication, access control and audit logging.', - }), + mount: async (params: AppMountParameters) => { + const { renderApp } = await import('./apps/configuration/configuration-app'); + const [coreStart, depsStart] = await core.getStartServices(); + + // merge OpenSearchDashboards yml configuration + includeClusterPermissions(config.clusterPermissions.include); + includeIndexPermissions(config.indexPermissions.include); + + excludeFromDisabledTransportCategories(config.disabledTransportCategories.exclude); + excludeFromDisabledRestCategories(config.disabledRestCategories.exclude); + + return renderApp( + coreStart, + depsStart as SecurityPluginStartDependencies, + params, + config, + deps.dataSourceManagement + ); + }, + category: DEFAULT_APP_CATEGORIES.management, }); + + if (deps.managementOverview) { + deps.managementOverview.register({ + id: PLUGIN_NAME, + title: 'Security', + order: 9050, + description: i18n.translate('security.securityDescription', { + defaultMessage: + 'Configure how users access data in OpenSearch with authentication, access control and audit logging.', + }), + }); + } } } @@ -163,7 +172,11 @@ export class SecurityPlugin core.application.registerAppUpdater( new BehaviorSubject((app) => { - if (!apiPermission && isReadonly && !APP_LIST_FOR_READONLY_ROLE.includes(app.id)) { + let shouldDisableForReadOnly = isReadonly && !APP_LIST_FOR_READONLY_ROLE.includes(app.id); + if (apiPermission !== undefined) { + shouldDisableForReadOnly = !apiPermission && shouldDisableForReadOnly; + } + if (shouldDisableForReadOnly) { return { status: AppStatus.inaccessible, }; diff --git a/public/types.ts b/public/types.ts index 96e587354..93af2885b 100644 --- a/public/types.ts +++ b/public/types.ts @@ -112,6 +112,10 @@ export interface ClientConfigType { anonymous_auth_enabled: boolean; logout_url: string; }; + configuration: { + enabled: boolean; + admin_pages_enabled: boolean; + }; clusterPermissions: { include: string[]; }; diff --git a/server/backend/opensearch_security_configuration_plugin.ts b/server/backend/opensearch_security_configuration_plugin.ts index 3b87863b8..09735c63a 100644 --- a/server/backend/opensearch_security_configuration_plugin.ts +++ b/server/backend/opensearch_security_configuration_plugin.ts @@ -21,12 +21,6 @@ export default function (Client: any, config: any, components: any) { Client.prototype.opensearch_security = components.clientAction.namespaceFactory(); } - Client.prototype.opensearch_security.prototype.restapiinfo = ca({ - url: { - fmt: '/_plugins/_security/api/permissionsinfo', - }, - }); - /** * list all field mappings for all indices. */ @@ -36,29 +30,6 @@ export default function (Client: any, config: any, components: any) { }, }); - /** - * Returns a Security resource configuration. - * - * Sample response: - * - * { - * "user": { - * "hash": "#123123" - * } - * } - */ - Client.prototype.opensearch_security.prototype.listResource = ca({ - url: { - fmt: '/_plugins/_security/api/<%=resourceName%>', - req: { - resourceName: { - type: 'string', - required: true, - }, - }, - }, - }); - Client.prototype.opensearch_security.prototype.listInternalAccounts = ca({ url: { fmt: '/_plugins/_security/api/internalusers?filterBy=internal', @@ -101,30 +72,6 @@ export default function (Client: any, config: any, components: any) { }, }); - /** - * Updates a resource. - * Resource identification is expected to computed from headers. Eg: auth headers. - * - * Sample response: - * { - * "status": "OK", - * "message": "Username updated." - * } - */ - Client.prototype.opensearch_security.prototype.saveResourceWithoutId = ca({ - method: 'PUT', - needBody: true, - url: { - fmt: '/_plugins/_security/api/<%=resourceName%>', - req: { - resourceName: { - type: 'string', - required: true, - }, - }, - }, - }); - /** * Returns a Security resource instance. * diff --git a/server/backend/opensearch_security_plugin.ts b/server/backend/opensearch_security_plugin.ts index 33b72cc0d..875fde75f 100644 --- a/server/backend/opensearch_security_plugin.ts +++ b/server/backend/opensearch_security_plugin.ts @@ -21,6 +21,59 @@ export default function (Client: any, config: any, components: any) { Client.prototype.opensearch_security = components.clientAction.namespaceFactory(); } + Client.prototype.opensearch_security.prototype.restapiinfo = ca({ + url: { + fmt: '/_plugins/_security/api/permissionsinfo', + }, + }); + + /** + * Updates a resource. + * Resource identification is expected to computed from headers. Eg: auth headers. + * + * Sample response: + * { + * "status": "OK", + * "message": "Username updated." + * } + */ + Client.prototype.opensearch_security.prototype.saveResourceWithoutId = ca({ + method: 'PUT', + needBody: true, + url: { + fmt: '/_plugins/_security/api/<%=resourceName%>', + req: { + resourceName: { + type: 'string', + required: true, + }, + }, + }, + }); + + /** + * Returns a Security resource configuration. + * + * Sample response: + * + * { + * "user": { + * "hash": "#123123" + * } + * } + */ + Client.prototype.opensearch_security.prototype.listResource = ca({ + url: { + fmt: '/_plugins/_security/api/<%=resourceName%>', + req: { + resourceName: { + type: 'string', + required: true, + }, + }, + }, + }); + /** * Gets auth info. */ @@ -48,7 +101,7 @@ export default function (Client: any, config: any, components: any) { * "opensearch_dashboards_server_user": "kibanaserver" * } */ - Client.prototype.opensearch_security.prototype.multitenancyinfo = ca({ + Client.prototype.opensearch_security.prototype.xx = ca({ url: { fmt: '/_plugins/_security/dashboardsinfo', }, diff --git a/server/index.ts b/server/index.ts index 68a20f533..ab5231033 100644 --- a/server/index.ts +++ b/server/index.ts @@ -158,6 +158,7 @@ export const configSchema = schema.object({ }), configuration: schema.object({ enabled: schema.boolean({ defaultValue: true }), + admin_pages_enabled: schema.boolean({ defaultValue: true }), }), accountinfo: schema.object({ enabled: schema.boolean({ defaultValue: false }), @@ -299,6 +300,7 @@ export const config: PluginConfigDescriptor = { ui: true, multitenancy: true, readonly_mode: true, + configuration: true, clusterPermissions: true, indexPermissions: true, disabledTransportCategories: true, diff --git a/server/plugin.ts b/server/plugin.ts index a6d87bea1..1012528b8 100644 --- a/server/plugin.ts +++ b/server/plugin.ts @@ -27,7 +27,7 @@ import { } from '../../../src/core/server'; import { SecurityPluginSetup, SecurityPluginStart } from './types'; -import { defineRoutes } from './routes'; +import { defineRoutes, defineSecurityConfigurationRoutes } from './routes'; import { SecurityPluginConfigType } from '.'; import opensearchSecurityConfigurationPlugin from './backend/opensearch_security_configuration_plugin'; import opensearchSecurityPlugin from './backend/opensearch_security_plugin'; @@ -97,10 +97,18 @@ export class SecurityPlugin implements Plugin> => { + try { + validateRequestBody(request.params.resourceName, request.body); + } catch (error) { + return response.badRequest({ body: error }); + } try { const esResp = await wrapRouteWithDataSource( dataSourceEnabled, context, request, - 'opensearch_security.deleteResource', + 'opensearch_security.saveResourceWithoutId', { resourceName: request.params.resourceName, - id: request.params.id, + body: request.body, } ); return response.ok({ @@ -438,23 +448,123 @@ export function defineRoutes(router: IRouter, dataSourceEnabled: boolean) { ); /** - * Update object with out Id. Resource identification is expected to computed from headers. Eg: auth headers + * Gets authentication info of the user. * - * Request sample: - * /configuration/account + * The response looks like: * { - * "password": "new-password", - * "current_password": "old-password" + * "user": "User [name=admin, roles=[], requestedTenant=__user__]", + * "user_name": "admin", + * "user_requested_tenant": "__user__", + * "remote_address": "127.0.0.1:35044", + * "backend_roles": [], + * "custom_attribute_names": [], + * "roles": ["all_access", "security_manager"], + * "tenants": { + * "another_tenant": true, + * "admin": true, + * "global_tenant": true, + * "aaaaa": true, + * "test tenant": true + * }, + * "principal": null, + * "peer_certificates": "0", + * "sso_logout_url": null * } */ - router.post( + router.get( { - path: `${API_PREFIX}/${CONFIGURATION_API_PREFIX}/{resourceName}`, + path: `${API_PREFIX}/auth/authinfo`, + validate: false, + }, + async ( + context, + request, + response + ): Promise> => { + const client = context.security_plugin.esClient.asScoped(request); + let esResp; + try { + esResp = await client.callAsCurrentUser('opensearch_security.authinfo'); + + return response.ok({ + body: esResp, + }); + } catch (error) { + return errorResponse(response, error); + } + } + ); + + router.get( + { + path: `${API_PREFIX}/auth/dashboardsinfo`, + validate: false, + }, + async ( + context, + request, + response + ): Promise> => { + const client = context.security_plugin.esClient.asScoped(request); + let esResp; + try { + esResp = await client.callAsCurrentUser('opensearch_security.dashboardsinfo'); + + return response.ok({ + body: esResp, + }); + } catch (error) { + return errorResponse(response, error); + } + } + ); + + /** + * Gets permission info of current user. + * + * Sample response: + * { + * "user": "User [name=admin, roles=[], requestedTenant=__user__]", + * "user_name": "admin", + * "has_api_access": true, + * "disabled_endpoints": {} + * } + */ + router.get( + { + path: `${API_PREFIX}/restapiinfo`, + validate: false, + }, + async (context, request, response) => { + const client = context.security_plugin.esClient.asScoped(request); + try { + const esResponse = await client.callAsCurrentUser('opensearch_security.restapiinfo'); + return response.ok({ + body: esResponse, + }); + } catch (error) { + return response.badRequest({ + body: error, + }); + } + } + ); +} + +export function defineSecurityConfigurationRoutes(router: IRouter, dataSourceEnabled: boolean) { + /** + * Deletes an entity by id. + */ + router.delete( + { + path: `${API_PREFIX}/${CONFIGURATION_API_PREFIX}/{resourceName}/{id}`, validate: { params: schema.object({ resourceName: schema.string(), + id: schema.string({ + minLength: 1, + }), }), - body: schema.any(), query: schema.object({ dataSourceId: schema.maybe(schema.string()), }), @@ -465,20 +575,15 @@ export function defineRoutes(router: IRouter, dataSourceEnabled: boolean) { request, response ): Promise> => { - try { - validateRequestBody(request.params.resourceName, request.body); - } catch (error) { - return response.badRequest({ body: error }); - } try { const esResp = await wrapRouteWithDataSource( dataSourceEnabled, context, request, - 'opensearch_security.saveResourceWithoutId', + 'opensearch_security.deleteResource', { resourceName: request.params.resourceName, - body: request.body, + id: request.params.id, } ); return response.ok({ @@ -544,78 +649,6 @@ export function defineRoutes(router: IRouter, dataSourceEnabled: boolean) { } ); - /** - * Gets authentication info of the user. - * - * The response looks like: - * { - * "user": "User [name=admin, roles=[], requestedTenant=__user__]", - * "user_name": "admin", - * "user_requested_tenant": "__user__", - * "remote_address": "127.0.0.1:35044", - * "backend_roles": [], - * "custom_attribute_names": [], - * "roles": ["all_access", "security_manager"], - * "tenants": { - * "another_tenant": true, - * "admin": true, - * "global_tenant": true, - * "aaaaa": true, - * "test tenant": true - * }, - * "principal": null, - * "peer_certificates": "0", - * "sso_logout_url": null - * } - */ - router.get( - { - path: `${API_PREFIX}/auth/authinfo`, - validate: false, - }, - async ( - context, - request, - response - ): Promise> => { - const client = context.security_plugin.esClient.asScoped(request); - let esResp; - try { - esResp = await client.callAsCurrentUser('opensearch_security.authinfo'); - - return response.ok({ - body: esResp, - }); - } catch (error) { - return errorResponse(response, error); - } - } - ); - - router.get( - { - path: `${API_PREFIX}/auth/dashboardsinfo`, - validate: false, - }, - async ( - context, - request, - response - ): Promise> => { - const client = context.security_plugin.esClient.asScoped(request); - let esResp; - try { - esResp = await client.callAsCurrentUser('opensearch_security.dashboardsinfo'); - - return response.ok({ - body: esResp, - }); - } catch (error) { - return errorResponse(response, error); - } - } - ); - /** * Gets audit log configuration。 * @@ -825,37 +858,6 @@ export function defineRoutes(router: IRouter, dataSourceEnabled: boolean) { } ); - /** - * Gets permission info of current user. - * - * Sample response: - * { - * "user": "User [name=admin, roles=[], requestedTenant=__user__]", - * "user_name": "admin", - * "has_api_access": true, - * "disabled_endpoints": {} - * } - */ - router.get( - { - path: `${API_PREFIX}/restapiinfo`, - validate: false, - }, - async (context, request, response) => { - const client = context.security_plugin.esClient.asScoped(request); - try { - const esResponse = await client.callAsCurrentUser('opensearch_security.restapiinfo'); - return response.ok({ - body: esResponse, - }); - } catch (error) { - return response.badRequest({ - body: error, - }); - } - } - ); - /** * Validates DLS (document level security) query. * From 27779f9aae393726e804b43a280b08bf3b154680 Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Fri, 17 May 2024 14:27:57 -0400 Subject: [PATCH 02/43] Fix typo Signed-off-by: Craig Perkins --- server/backend/opensearch_security_plugin.ts | 2 +- server/plugin.ts | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/server/backend/opensearch_security_plugin.ts b/server/backend/opensearch_security_plugin.ts index 875fde75f..ea2c4c5a4 100644 --- a/server/backend/opensearch_security_plugin.ts +++ b/server/backend/opensearch_security_plugin.ts @@ -101,7 +101,7 @@ export default function (Client: any, config: any, components: any) { * "opensearch_dashboards_server_user": "kibanaserver" * } */ - Client.prototype.opensearch_security.prototype.xx = ca({ + Client.prototype.opensearch_security.prototype.multitenancyinfo = ca({ url: { fmt: '/_plugins/_security/dashboardsinfo', }, diff --git a/server/plugin.ts b/server/plugin.ts index 1012528b8..86fa5fc20 100644 --- a/server/plugin.ts +++ b/server/plugin.ts @@ -98,9 +98,6 @@ export class SecurityPlugin implements Plugin Date: Fri, 17 May 2024 16:49:53 -0400 Subject: [PATCH 03/43] Define common routes Signed-off-by: Craig Perkins --- server/plugin.ts | 3 ++- server/routes/index.ts | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/server/plugin.ts b/server/plugin.ts index 86fa5fc20..adb9d2888 100644 --- a/server/plugin.ts +++ b/server/plugin.ts @@ -27,7 +27,7 @@ import { } from '../../../src/core/server'; import { SecurityPluginSetup, SecurityPluginStart } from './types'; -import { defineRoutes, defineSecurityConfigurationRoutes } from './routes'; +import { defineRoutes, defineCommonRoutes, defineSecurityConfigurationRoutes } from './routes'; import { SecurityPluginConfigType } from '.'; import opensearchSecurityConfigurationPlugin from './backend/opensearch_security_configuration_plugin'; import opensearchSecurityPlugin from './backend/opensearch_security_plugin'; @@ -148,6 +148,7 @@ export class SecurityPlugin implements Plugin Date: Fri, 17 May 2024 21:12:56 -0400 Subject: [PATCH 04/43] WIP on admin screen only Signed-off-by: Craig Perkins --- common/index.ts | 4 ++-- opensearch_dashboards.json | 4 ++-- public/plugin.ts | 6 ++++++ public/types.ts | 1 + server/index.ts | 1 + 5 files changed, 12 insertions(+), 4 deletions(-) diff --git a/common/index.ts b/common/index.ts index 84445486d..6336b645d 100644 --- a/common/index.ts +++ b/common/index.ts @@ -13,8 +13,8 @@ * permissions and limitations under the License. */ -export const PLUGIN_ID = 'opensearchDashboardsSecurity'; -export const PLUGIN_NAME = 'security-dashboards-plugin'; +export const PLUGIN_ID = 'opensearchDashboardsSecurityAdmin'; +export const PLUGIN_NAME = 'security-admin-dashboards-plugin'; export const APP_ID_LOGIN = 'login'; export const APP_ID_CUSTOMERROR = 'customerror'; diff --git a/opensearch_dashboards.json b/opensearch_dashboards.json index 3a2642593..abbe908ec 100644 --- a/opensearch_dashboards.json +++ b/opensearch_dashboards.json @@ -1,9 +1,9 @@ { - "id": "securityDashboards", + "id": "securityAdminDashboards", "version": "3.0.0.0", "opensearchDashboardsVersion": "3.0.0", "configPath": [ - "opensearch_security" + "opensearch_security_admin" ], "requiredPlugins": [ "navigation", diff --git a/public/plugin.ts b/public/plugin.ts index a3dee68b7..17461c5b1 100644 --- a/public/plugin.ts +++ b/public/plugin.ts @@ -64,6 +64,7 @@ async function hasApiPermission(core: CoreSetup): Promise { const DEFAULT_READONLY_ROLES = ['kibana_read_only']; const DEFAULT_ADMIN_PAGES_ENABLED = true; +const DEFAULT_MANAGE_SESSION_ENABLED = true; const APP_ID_HOME = 'home'; const APP_ID_DASHBOARDS = 'dashboards'; // OpenSearchDashboards app is for legacy url migration @@ -89,6 +90,11 @@ export class SecurityPlugin ): Promise { const config = this.initializerContext.config.get(); + const manageSessionEnabled = + config.configuration?.session_management_enabled !== undefined + ? config.configuration?.session_management_enabled + : DEFAULT_MANAGE_SESSION_ENABLED; + const accountInfo = (await fetchAccountInfoSafe(core.http))?.data; const multitenancyEnabled = (await getDashboardsInfoSafe(core.http))?.multitenancy_enabled; const isReadonly = accountInfo?.roles.some((role) => diff --git a/public/types.ts b/public/types.ts index 93af2885b..30a1a77cc 100644 --- a/public/types.ts +++ b/public/types.ts @@ -115,6 +115,7 @@ export interface ClientConfigType { configuration: { enabled: boolean; admin_pages_enabled: boolean; + session_management_enabled: boolean; }; clusterPermissions: { include: string[]; diff --git a/server/index.ts b/server/index.ts index ab5231033..c472ea70a 100644 --- a/server/index.ts +++ b/server/index.ts @@ -159,6 +159,7 @@ export const configSchema = schema.object({ configuration: schema.object({ enabled: schema.boolean({ defaultValue: true }), admin_pages_enabled: schema.boolean({ defaultValue: true }), + session_management_enabled: schema.boolean({ defaultValue: true }), }), accountinfo: schema.object({ enabled: schema.boolean({ defaultValue: false }), From d66fd636239863bdb382562a91e6e4517fa1f517 Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Fri, 17 May 2024 21:14:04 -0400 Subject: [PATCH 05/43] Only register custom error page if pages are enabled Signed-off-by: Craig Perkins --- public/plugin.ts | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/public/plugin.ts b/public/plugin.ts index a3dee68b7..28fbe1ee6 100644 --- a/public/plugin.ts +++ b/public/plugin.ts @@ -143,6 +143,18 @@ export class SecurityPlugin }); } } + + core.application.register({ + id: APP_ID_CUSTOMERROR, + title: 'Security', + chromeless: true, + appRoute: CUSTOM_ERROR_PAGE_URI, + mount: async (params: AppMountParameters) => { + const { renderPage } = await import('./apps/customerror/custom-error'); + const [coreStart] = await core.getStartServices(); + return renderPage(coreStart, params, config); + }, + }); } core.application.register({ @@ -158,18 +170,6 @@ export class SecurityPlugin }, }); - core.application.register({ - id: APP_ID_CUSTOMERROR, - title: 'Security', - chromeless: true, - appRoute: CUSTOM_ERROR_PAGE_URI, - mount: async (params: AppMountParameters) => { - const { renderPage } = await import('./apps/customerror/custom-error'); - const [coreStart] = await core.getStartServices(); - return renderPage(coreStart, params, config); - }, - }); - core.application.registerAppUpdater( new BehaviorSubject((app) => { let shouldDisableForReadOnly = isReadonly && !APP_LIST_FOR_READONLY_ROLE.includes(app.id); From 4858db38764954fb7c308235cb98522641cd1ffc Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Fri, 17 May 2024 21:18:13 -0400 Subject: [PATCH 06/43] Organize sections in public/plugin.ts Signed-off-by: Craig Perkins --- public/plugin.ts | 94 ++++++++++++++++++++++++------------------------ 1 file changed, 48 insertions(+), 46 deletions(-) diff --git a/public/plugin.ts b/public/plugin.ts index f8b46ed40..b5993d5b7 100644 --- a/public/plugin.ts +++ b/public/plugin.ts @@ -95,12 +95,6 @@ export class SecurityPlugin ? config.configuration?.session_management_enabled : DEFAULT_MANAGE_SESSION_ENABLED; - const accountInfo = (await fetchAccountInfoSafe(core.http))?.data; - const multitenancyEnabled = (await getDashboardsInfoSafe(core.http))?.multitenancy_enabled; - const isReadonly = accountInfo?.roles.some((role) => - (config.readonly_mode?.roles || DEFAULT_READONLY_ROLES).includes(role) - ); - const adminPagesEnabled = config.configuration?.admin_pages_enabled !== undefined ? config.configuration?.admin_pages_enabled @@ -163,47 +157,55 @@ export class SecurityPlugin }); } - core.application.register({ - id: APP_ID_LOGIN, - title: 'Security', - chromeless: true, - appRoute: LOGIN_PAGE_URI, - mount: async (params: AppMountParameters) => { - const { renderApp } = await import('./apps/login/login-app'); - // @ts-ignore depsStart not used. - const [coreStart, depsStart] = await core.getStartServices(); - return renderApp(coreStart, params, config); - }, - }); - - core.application.registerAppUpdater( - new BehaviorSubject((app) => { - let shouldDisableForReadOnly = isReadonly && !APP_LIST_FOR_READONLY_ROLE.includes(app.id); - if (apiPermission !== undefined) { - shouldDisableForReadOnly = !apiPermission && shouldDisableForReadOnly; - } - if (shouldDisableForReadOnly) { - return { - status: AppStatus.inaccessible, - }; - } - }) - ); - - if ( - multitenancyEnabled && - config.multitenancy.enabled && - config.multitenancy.enable_aggregation_view - ) { - deps.savedObjectsManagement.columns.register( - (tenantColumn as unknown) as SavedObjectsManagementColumn + if (manageSessionEnabled) { + const accountInfo = (await fetchAccountInfoSafe(core.http))?.data; + const multitenancyEnabled = (await getDashboardsInfoSafe(core.http))?.multitenancy_enabled; + const isReadonly = accountInfo?.roles.some((role) => + (config.readonly_mode?.roles || DEFAULT_READONLY_ROLES).includes(role) ); - if (!!accountInfo) { - const namespacesToRegister = getNamespacesToRegister(accountInfo); - deps.savedObjectsManagement.namespaces.registerAlias('Tenant'); - namespacesToRegister.forEach((ns) => { - deps.savedObjectsManagement.namespaces.register(ns as SavedObjectsManagementNamespace); - }); + + core.application.register({ + id: APP_ID_LOGIN, + title: 'Security', + chromeless: true, + appRoute: LOGIN_PAGE_URI, + mount: async (params: AppMountParameters) => { + const { renderApp } = await import('./apps/login/login-app'); + // @ts-ignore depsStart not used. + const [coreStart, depsStart] = await core.getStartServices(); + return renderApp(coreStart, params, config); + }, + }); + + core.application.registerAppUpdater( + new BehaviorSubject((app) => { + let shouldDisableForReadOnly = isReadonly && !APP_LIST_FOR_READONLY_ROLE.includes(app.id); + if (apiPermission !== undefined) { + shouldDisableForReadOnly = !apiPermission && shouldDisableForReadOnly; + } + if (shouldDisableForReadOnly) { + return { + status: AppStatus.inaccessible, + }; + } + }) + ); + + if ( + multitenancyEnabled && + config.multitenancy.enabled && + config.multitenancy.enable_aggregation_view + ) { + deps.savedObjectsManagement.columns.register( + (tenantColumn as unknown) as SavedObjectsManagementColumn + ); + if (!!accountInfo) { + const namespacesToRegister = getNamespacesToRegister(accountInfo); + deps.savedObjectsManagement.namespaces.registerAlias('Tenant'); + namespacesToRegister.forEach((ns) => { + deps.savedObjectsManagement.namespaces.register(ns as SavedObjectsManagementNamespace); + }); + } } } From 9ffa45b5459cb786c05f6402c92435a29420d32b Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Fri, 17 May 2024 21:19:39 -0400 Subject: [PATCH 07/43] Remove comment Signed-off-by: Craig Perkins --- public/plugin.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/public/plugin.ts b/public/plugin.ts index 28fbe1ee6..efe8a5142 100644 --- a/public/plugin.ts +++ b/public/plugin.ts @@ -89,12 +89,6 @@ export class SecurityPlugin ): Promise { const config = this.initializerContext.config.get(); - const accountInfo = (await fetchAccountInfoSafe(core.http))?.data; - const multitenancyEnabled = (await getDashboardsInfoSafe(core.http))?.multitenancy_enabled; - const isReadonly = accountInfo?.roles.some((role) => - (config.readonly_mode?.roles || DEFAULT_READONLY_ROLES).includes(role) - ); - const adminPagesEnabled = config.configuration?.admin_pages_enabled !== undefined ? config.configuration?.admin_pages_enabled @@ -103,7 +97,6 @@ export class SecurityPlugin let apiPermission: boolean | undefined; if (adminPagesEnabled) { apiPermission = await hasApiPermission(core); - console.log('apiPermission: ' + apiPermission); if (apiPermission) { core.application.register({ id: PLUGIN_NAME, @@ -157,6 +150,12 @@ export class SecurityPlugin }); } + const accountInfo = (await fetchAccountInfoSafe(core.http))?.data; + const multitenancyEnabled = (await getDashboardsInfoSafe(core.http))?.multitenancy_enabled; + const isReadonly = accountInfo?.roles.some((role) => + (config.readonly_mode?.roles || DEFAULT_READONLY_ROLES).includes(role) + ); + core.application.register({ id: APP_ID_LOGIN, title: 'Security', From 90f4fc6b75bc114314ea0975b3030f6d3ea8783a Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Fri, 17 May 2024 21:32:50 -0400 Subject: [PATCH 08/43] Update server/plugin.ts Signed-off-by: Craig Perkins --- server/plugin.ts | 129 ++++++++++++++++++++++++++--------------------- 1 file changed, 72 insertions(+), 57 deletions(-) diff --git a/server/plugin.ts b/server/plugin.ts index adb9d2888..a541ed7a4 100644 --- a/server/plugin.ts +++ b/server/plugin.ts @@ -97,86 +97,101 @@ export class SecurityPlugin implements Plugin = await core.http.createCookieSessionStorageFactory< - SecuritySessionCookie - >(getSecurityCookieOptions(config)); - // put logger into route handler context, so that we don't need to pass througth parameters - core.http.registerRouteHandlerContext('security_plugin', (context, request) => { + core.http.registerRouteHandlerContext(handlerContextName, (context, request) => { return { logger: this.logger, esClient, }; }); - // setup auth - const auth: IAuthenticationType = await getAuthenticationHandler( - config.auth.type, - router, - config, - core, - esClient, - securitySessionStorageFactory, - this.logger - ); - core.http.registerAuth(auth.authHandler); - - /* Here we check if multitenancy is enabled to ensure if it is, we insert the tenant info (security_tenant) into the resolved, short URL so the page can correctly load with the right tenant information [Fix for issue 1203](https://github.com/opensearch-project/security-dashboards-plugin/issues/1203 */ - if (config.multitenancy?.enabled) { - core.http.registerOnPreResponse((request, preResponse, toolkit) => { - addTenantParameterToResolvedShortLink(request); - return toolkit.next(); - }); + if (config.configuration.session_management_enabled) { + const securitySessionStorageFactory: SessionStorageFactory = await core.http.createCookieSessionStorageFactory< + SecuritySessionCookie + >(getSecurityCookieOptions(config)); + + // setup auth + const auth: IAuthenticationType = await getAuthenticationHandler( + config.auth.type, + router, + config, + core, + esClient, + securitySessionStorageFactory, + this.logger + ); + core.http.registerAuth(auth.authHandler); + + /* Here we check if multitenancy is enabled to ensure if it is, we insert the tenant info (security_tenant) into the resolved, short URL so the page can correctly load with the right tenant information [Fix for issue 1203](https://github.com/opensearch-project/security-dashboards-plugin/issues/1203 */ + if (config.multitenancy?.enabled) { + core.http.registerOnPreResponse((request, preResponse, toolkit) => { + addTenantParameterToResolvedShortLink(request); + return toolkit.next(); + }); + } + + // set up multi-tenant routes + if (config.multitenancy?.enabled) { + setupMultitenantRoutes(router, securitySessionStorageFactory, this.securityClient); + } + + if (config.multitenancy.enabled && config.multitenancy.enable_aggregation_view) { + core.savedObjects.addClientWrapper( + 2, + 'security-saved-object-client-wrapper', + this.savedObjectClientWrapper.wrapperFactory + ); + } + + const service = new ReadonlyService( + this.logger, + this.securityClient, + auth, + securitySessionStorageFactory, + config + ); + + core.security.registerReadonlyService(service); } // Register server side APIs defineCommonRoutes(router, dataSourceEnabled); - defineRoutes(router, dataSourceEnabled); + if (config.configuration.session_management_enabled) { + defineRoutes(router, dataSourceEnabled); + defineAuthTypeRoutes(router, config); + } if (config.configuration.admin_pages_enabled) { defineSecurityConfigurationRoutes(router, dataSourceEnabled); } - defineAuthTypeRoutes(router, config); - - // set up multi-tenant routes - if (config.multitenancy?.enabled) { - setupMultitenantRoutes(router, securitySessionStorageFactory, this.securityClient); - } - - if (config.multitenancy.enabled && config.multitenancy.enable_aggregation_view) { - core.savedObjects.addClientWrapper( - 2, - 'security-saved-object-client-wrapper', - this.savedObjectClientWrapper.wrapperFactory - ); - } - - const service = new ReadonlyService( - this.logger, - this.securityClient, - auth, - securitySessionStorageFactory, - config - ); - - core.security.registerReadonlyService(service); return { config$, From dbb53188b638bb3f962cdb7664a979e3b8e36d18 Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Fri, 17 May 2024 21:33:56 -0400 Subject: [PATCH 09/43] Update datasource Signed-off-by: Craig Perkins --- server/plugin.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/server/plugin.ts b/server/plugin.ts index adb9d2888..564bf5d1d 100644 --- a/server/plugin.ts +++ b/server/plugin.ts @@ -109,7 +109,9 @@ export class SecurityPlugin implements Plugin Date: Fri, 17 May 2024 22:01:47 -0400 Subject: [PATCH 10/43] Remove defineCommonRoutes Signed-off-by: Craig Perkins --- server/plugin.ts | 3 +-- server/routes/index.ts | 5 +---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/server/plugin.ts b/server/plugin.ts index a541ed7a4..d9a80660e 100644 --- a/server/plugin.ts +++ b/server/plugin.ts @@ -27,7 +27,7 @@ import { } from '../../../src/core/server'; import { SecurityPluginSetup, SecurityPluginStart } from './types'; -import { defineRoutes, defineCommonRoutes, defineSecurityConfigurationRoutes } from './routes'; +import { defineRoutes, defineSecurityConfigurationRoutes } from './routes'; import { SecurityPluginConfigType } from '.'; import opensearchSecurityConfigurationPlugin from './backend/opensearch_security_configuration_plugin'; import opensearchSecurityPlugin from './backend/opensearch_security_plugin'; @@ -184,7 +184,6 @@ export class SecurityPlugin implements Plugin Date: Fri, 17 May 2024 22:05:25 -0400 Subject: [PATCH 11/43] Fix top nav bar Signed-off-by: Craig Perkins --- public/plugin.ts | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/public/plugin.ts b/public/plugin.ts index cc08d37e9..110cf7523 100644 --- a/public/plugin.ts +++ b/public/plugin.ts @@ -215,17 +215,24 @@ export class SecurityPlugin public start(core: CoreStart, deps: SecurityPluginStartDependencies): SecurityPluginStart { const config = this.initializerContext.config.get(); - setupTopNavButton(core, config); + const manageSessionEnabled = + config.configuration?.session_management_enabled !== undefined + ? config.configuration?.session_management_enabled + : DEFAULT_MANAGE_SESSION_ENABLED; - if (config.ui.autologout) { - // logout the user when getting 401 unauthorized, e.g. when session timed out. - core.http.intercept({ - responseError: interceptError(config.auth.logout_url, window), - }); - } + if (manageSessionEnabled) { + setupTopNavButton(core, config); - if (config.multitenancy.enabled) { - addTenantToShareURL(core); + if (config.ui.autologout) { + // logout the user when getting 401 unauthorized, e.g. when session timed out. + core.http.intercept({ + responseError: interceptError(config.auth.logout_url, window), + }); + } + + if (config.multitenancy.enabled) { + addTenantToShareURL(core); + } } return {}; } From 6ac1fea961513f37b20fc67f889813d5d83c61f3 Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Mon, 20 May 2024 17:00:51 -0400 Subject: [PATCH 12/43] Create common plugin Signed-off-by: Craig Perkins --- .../opensearch_security_common_plugin.ts | 79 +++++++++++++++++++ server/backend/opensearch_security_plugin.ts | 56 ------------- server/plugin.ts | 3 +- 3 files changed, 81 insertions(+), 57 deletions(-) create mode 100644 server/backend/opensearch_security_common_plugin.ts diff --git a/server/backend/opensearch_security_common_plugin.ts b/server/backend/opensearch_security_common_plugin.ts new file mode 100644 index 000000000..1020fcb78 --- /dev/null +++ b/server/backend/opensearch_security_common_plugin.ts @@ -0,0 +1,79 @@ +/* + * 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. + */ + +// eslint-disable-next-line import/no-default-export +export default function (Client: any, config: any, components: any) { + const ca = components.clientAction.factory; + + if (!Client.prototype.opensearch_security) { + Client.prototype.opensearch_security = components.clientAction.namespaceFactory(); + } + + /** + * Updates a resource. + * Resource identification is expected to computed from headers. Eg: auth headers. + * + * Sample response: + * { + * "status": "OK", + * "message": "Username updated." + * } + */ + Client.prototype.opensearch_security.prototype.saveResourceWithoutId = ca({ + method: 'PUT', + needBody: true, + url: { + fmt: '/_plugins/_security/api/<%=resourceName%>', + req: { + resourceName: { + type: 'string', + required: true, + }, + }, + }, + }); + + /** + * Returns a Security resource configuration. + * + * Sample response: + * + * { + * "user": { + * "hash": "#123123" + * } + * } + */ + Client.prototype.opensearch_security.prototype.listResource = ca({ + url: { + fmt: '/_plugins/_security/api/<%=resourceName%>', + req: { + resourceName: { + type: 'string', + required: true, + }, + }, + }, + }); + + /** + * Gets auth info. + */ + Client.prototype.opensearch_security.prototype.authinfo = ca({ + url: { + fmt: '/_plugins/_security/authinfo', + }, + }); +} diff --git a/server/backend/opensearch_security_plugin.ts b/server/backend/opensearch_security_plugin.ts index ea2c4c5a4..d26af53c9 100644 --- a/server/backend/opensearch_security_plugin.ts +++ b/server/backend/opensearch_security_plugin.ts @@ -27,62 +27,6 @@ export default function (Client: any, config: any, components: any) { }, }); - /** - * Updates a resource. - * Resource identification is expected to computed from headers. Eg: auth headers. - * - * Sample response: - * { - * "status": "OK", - * "message": "Username updated." - * } - */ - Client.prototype.opensearch_security.prototype.saveResourceWithoutId = ca({ - method: 'PUT', - needBody: true, - url: { - fmt: '/_plugins/_security/api/<%=resourceName%>', - req: { - resourceName: { - type: 'string', - required: true, - }, - }, - }, - }); - - /** - * Returns a Security resource configuration. - * - * Sample response: - * - * { - * "user": { - * "hash": "#123123" - * } - * } - */ - Client.prototype.opensearch_security.prototype.listResource = ca({ - url: { - fmt: '/_plugins/_security/api/<%=resourceName%>', - req: { - resourceName: { - type: 'string', - required: true, - }, - }, - }, - }); - - /** - * Gets auth info. - */ - Client.prototype.opensearch_security.prototype.authinfo = ca({ - url: { - fmt: '/_plugins/_security/authinfo', - }, - }); - Client.prototype.opensearch_security.prototype.dashboardsinfo = ca({ url: { fmt: '/_plugins/_security/dashboardsinfo', diff --git a/server/plugin.ts b/server/plugin.ts index d9a80660e..654663565 100644 --- a/server/plugin.ts +++ b/server/plugin.ts @@ -31,6 +31,7 @@ import { defineRoutes, defineSecurityConfigurationRoutes } from './routes'; import { SecurityPluginConfigType } from '.'; import opensearchSecurityConfigurationPlugin from './backend/opensearch_security_configuration_plugin'; import opensearchSecurityPlugin from './backend/opensearch_security_plugin'; +import opensearchSecurityCommonPlugin from './backend/opensearch_security_common_plugin'; import { SecuritySessionCookie, getSecurityCookieOptions } from './session/security_cookie'; import { SecurityClient } from './backend/opensearch_security_client'; import { @@ -97,7 +98,7 @@ export class SecurityPlugin implements Plugin Date: Mon, 20 May 2024 17:28:32 -0400 Subject: [PATCH 13/43] Pass appropriate handler context Signed-off-by: Craig Perkins --- .../opensearch_security_common_plugin.ts | 79 ------------------- server/backend/opensearch_security_plugin.ts | 56 +++++++++++++ server/plugin.ts | 38 ++++----- server/routes/index.ts | 20 ++++- 4 files changed, 91 insertions(+), 102 deletions(-) delete mode 100644 server/backend/opensearch_security_common_plugin.ts diff --git a/server/backend/opensearch_security_common_plugin.ts b/server/backend/opensearch_security_common_plugin.ts deleted file mode 100644 index 1020fcb78..000000000 --- a/server/backend/opensearch_security_common_plugin.ts +++ /dev/null @@ -1,79 +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. - */ - -// eslint-disable-next-line import/no-default-export -export default function (Client: any, config: any, components: any) { - const ca = components.clientAction.factory; - - if (!Client.prototype.opensearch_security) { - Client.prototype.opensearch_security = components.clientAction.namespaceFactory(); - } - - /** - * Updates a resource. - * Resource identification is expected to computed from headers. Eg: auth headers. - * - * Sample response: - * { - * "status": "OK", - * "message": "Username updated." - * } - */ - Client.prototype.opensearch_security.prototype.saveResourceWithoutId = ca({ - method: 'PUT', - needBody: true, - url: { - fmt: '/_plugins/_security/api/<%=resourceName%>', - req: { - resourceName: { - type: 'string', - required: true, - }, - }, - }, - }); - - /** - * Returns a Security resource configuration. - * - * Sample response: - * - * { - * "user": { - * "hash": "#123123" - * } - * } - */ - Client.prototype.opensearch_security.prototype.listResource = ca({ - url: { - fmt: '/_plugins/_security/api/<%=resourceName%>', - req: { - resourceName: { - type: 'string', - required: true, - }, - }, - }, - }); - - /** - * Gets auth info. - */ - Client.prototype.opensearch_security.prototype.authinfo = ca({ - url: { - fmt: '/_plugins/_security/authinfo', - }, - }); -} diff --git a/server/backend/opensearch_security_plugin.ts b/server/backend/opensearch_security_plugin.ts index d26af53c9..ea2c4c5a4 100644 --- a/server/backend/opensearch_security_plugin.ts +++ b/server/backend/opensearch_security_plugin.ts @@ -27,6 +27,62 @@ export default function (Client: any, config: any, components: any) { }, }); + /** + * Updates a resource. + * Resource identification is expected to computed from headers. Eg: auth headers. + * + * Sample response: + * { + * "status": "OK", + * "message": "Username updated." + * } + */ + Client.prototype.opensearch_security.prototype.saveResourceWithoutId = ca({ + method: 'PUT', + needBody: true, + url: { + fmt: '/_plugins/_security/api/<%=resourceName%>', + req: { + resourceName: { + type: 'string', + required: true, + }, + }, + }, + }); + + /** + * Returns a Security resource configuration. + * + * Sample response: + * + * { + * "user": { + * "hash": "#123123" + * } + * } + */ + Client.prototype.opensearch_security.prototype.listResource = ca({ + url: { + fmt: '/_plugins/_security/api/<%=resourceName%>', + req: { + resourceName: { + type: 'string', + required: true, + }, + }, + }, + }); + + /** + * Gets auth info. + */ + Client.prototype.opensearch_security.prototype.authinfo = ca({ + url: { + fmt: '/_plugins/_security/authinfo', + }, + }); + Client.prototype.opensearch_security.prototype.dashboardsinfo = ca({ url: { fmt: '/_plugins/_security/dashboardsinfo', diff --git a/server/plugin.ts b/server/plugin.ts index 654663565..db209b41a 100644 --- a/server/plugin.ts +++ b/server/plugin.ts @@ -31,7 +31,6 @@ import { defineRoutes, defineSecurityConfigurationRoutes } from './routes'; import { SecurityPluginConfigType } from '.'; import opensearchSecurityConfigurationPlugin from './backend/opensearch_security_configuration_plugin'; import opensearchSecurityPlugin from './backend/opensearch_security_plugin'; -import opensearchSecurityCommonPlugin from './backend/opensearch_security_common_plugin'; import { SecuritySessionCookie, getSecurityCookieOptions } from './session/security_cookie'; import { SecurityClient } from './backend/opensearch_security_client'; import { @@ -60,7 +59,7 @@ export interface SecurityPluginSetupDependencies { declare module 'opensearch-dashboards/server' { interface RequestHandlerContext { - security_plugin: SecurityPluginRequestContext; + security_admin_plugin: SecurityPluginRequestContext; } } @@ -98,7 +97,7 @@ export class SecurityPlugin implements Plugin { - return { - logger: this.logger, - esClient, - }; - }); + core.http.registerRouteHandlerContext( + !config.configuration.session_management_enabled + ? 'security_admin_plugin' + : 'security_plugin', + (context, request) => { + return { + logger: this.logger, + esClient, + }; + } + ); if (config.configuration.session_management_enabled) { const securitySessionStorageFactory: SessionStorageFactory = await core.http.createCookieSessionStorageFactory< diff --git a/server/routes/index.ts b/server/routes/index.ts index c129eb2dd..c7ef31e91 100644 --- a/server/routes/index.ts +++ b/server/routes/index.ts @@ -875,7 +875,10 @@ export function defineSecurityConfigurationRoutes(router: IRouter, dataSourceEna }, }, async (context, request, response) => { - const client = context.security_plugin.esClient.asScoped(request); + const handlerContext = context.security_plugin + ? context.security_plugin + : context.security_admin_plugin; + const client = handlerContext.esClient.asScoped(request); try { const esResponse = await client.callAsCurrentUser('opensearch_security.validateDls', { body: request.body, @@ -905,7 +908,10 @@ export function defineSecurityConfigurationRoutes(router: IRouter, dataSourceEna }, }, async (context, request, response) => { - const client = context.security_plugin.esClient.asScoped(request); + const handlerContext = context.security_plugin + ? context.security_plugin + : context.security_admin_plugin; + const client = handlerContext.esClient.asScoped(request); try { const esResponse = await client.callAsCurrentUser('opensearch_security.getIndexMappings', { index: request.body.index.join(','), @@ -934,7 +940,10 @@ export function defineSecurityConfigurationRoutes(router: IRouter, dataSourceEna validate: false, }, async (context, request, response) => { - const client = context.security_plugin.esClient.asScoped(request); + const handlerContext = context.security_plugin + ? context.security_plugin + : context.security_admin_plugin; + const client = handlerContext.esClient.asScoped(request); try { const esResponse = await client.callAsCurrentUser('opensearch_security.indices'); return response.ok({ @@ -960,7 +969,10 @@ const wrapRouteWithDataSource = async ( body?: Record ) => { if (!dataSourceEnabled || !request.query?.dataSourceId) { - const client = context.security_plugin.esClient.asScoped(request); + const handlerContext = context.security_plugin + ? context.security_plugin + : context.security_admin_plugin; + const client = handlerContext.esClient.asScoped(request); return await client.callAsCurrentUser(endpoint, body); } else { const client = context.dataSource.opensearch.legacy.getClient(request.query?.dataSourceId); From 1a2c0dd0bea478dd4cb762a78a3225ee692bd148 Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Mon, 20 May 2024 17:32:22 -0400 Subject: [PATCH 14/43] Move list Signed-off-by: Craig Perkins --- server/routes/index.ts | 388 ++++++++++++++++++++--------------------- 1 file changed, 194 insertions(+), 194 deletions(-) diff --git a/server/routes/index.ts b/server/routes/index.ts index c7ef31e91..193ec3cb1 100644 --- a/server/routes/index.ts +++ b/server/routes/index.ts @@ -91,200 +91,6 @@ function validateEntityId(resourceName: string) { // TODO: consider to extract entity CRUD operations and put it into a client class export function defineRoutes(router: IRouter, dataSourceEnabled: boolean) { - /** - * Lists resources by resource name. - * - * The response format is: - * { - * "total": , - * "data": { - * "entity_id_1": { }, - * "entity_id_2": { }, - * ... - * } - * } - * - * e.g. when listing internal users, response may look like: - * { - * "total": 2, - * "data": { - * "api_test_user2": { - * "hash": "", - * "reserved": false, - * "hidden": false, - * "backend_roles": [], - * "attributes": {}, - * "description": "", - * "static": false - * }, - * "api_test_user1": { - * "hash": "", - * "reserved": false, - * "hidden": false, - * "backend_roles": [], - * "attributes": {}, - * "static": false - * } - * } - * - * when listing action groups, response will look like: - * { - * "total": 2, - * "data": { - * "read": { - * "reserved": true, - * "hidden": false, - * "allowed_actions": ["indices:data/read*", "indices:admin/mappings/fields/get*"], - * "type": "index", - * "description": "Allow all read operations", - * "static": false - * }, - * "cluster_all": { - * "reserved": true, - * "hidden": false, - * "allowed_actions": ["cluster:*"], - * "type": "cluster", - * "description": "Allow everything on cluster level", - * "static": false - * } - * } - * - * role: - * { - * "total": 2, - * "data": { - * "opensearch_dashboards_user": { - * "reserved": true, - * "hidden": false, - * "description": "Provide the minimum permissions for a opensearch_dashboards user", - * "cluster_permissions": ["cluster_composite_ops"], - * "index_permissions": [{ - * "index_patterns": [".opensearch_dashboards", ".opensearch_dashboards-6", ".opensearch_dashboards_*"], - * "fls": [], - * "masked_fields": [], - * "allowed_actions": ["read", "delete", "manage", "index"] - * }, { - * "index_patterns": [".tasks", ".management-beats"], - * "fls": [], - * "masked_fields": [], - * "allowed_actions": ["indices_all"] - * }], - * "tenant_permissions": [], - * "static": false - * }, - * "all_access": { - * "reserved": true, - * "hidden": false, - * "description": "Allow full access to all indices and all cluster APIs", - * "cluster_permissions": ["*"], - * "index_permissions": [{ - * "index_patterns": ["*"], - * "fls": [], - * "masked_fields": [], - * "allowed_actions": ["*"] - * }], - * "tenant_permissions": [{ - * "tenant_patterns": ["*"], - * "allowed_actions": ["opensearch_dashboards_all_write"] - * }], - * "static": false - * } - * } - * } - * - * rolesmapping: - * { - * "total": 2, - * "data": { - * "security_manager": { - * "reserved": false, - * "hidden": false, - * "backend_roles": [], - * "hosts": [], - * "users": ["zengyan", "admin"], - * "and_backend_roles": [] - * }, - * "all_access": { - * "reserved": false, - * "hidden": false, - * "backend_roles": [], - * "hosts": [], - * "users": ["zengyan", "admin", "indextest"], - * "and_backend_roles": [] - * } - * } - * } - * - * tenants: - * { - * "total": 2, - * "data": { - * "global_tenant": { - * "reserved": true, - * "hidden": false, - * "description": "Global tenant", - * "static": false - * }, - * "test tenant": { - * "reserved": false, - * "hidden": false, - * "description": "tenant description", - * "static": false - * } - * } - * } - */ - router.get( - { - path: `${API_PREFIX}/${CONFIGURATION_API_PREFIX}/{resourceName}`, - validate: { - params: schema.object({ - resourceName: schema.string(), - }), - query: schema.object({ - dataSourceId: schema.maybe(schema.string()), - }), - }, - }, - async ( - context, - request, - response - ): Promise> => { - const client = context.security_plugin.esClient.asScoped(request); - let esResp; - try { - if (request.params.resourceName === ResourceType.serviceAccounts.toLowerCase()) { - esResp = await client.callAsCurrentUser('opensearch_security.listServiceAccounts'); - } else if (request.params.resourceName === 'internalaccounts') { - esResp = await wrapRouteWithDataSource( - dataSourceEnabled, - context, - request, - 'opensearch_security.listInternalAccounts' - ); - } else { - esResp = await wrapRouteWithDataSource( - dataSourceEnabled, - context, - request, - 'opensearch_security.listResource', - { resourceName: request.params.resourceName } - ); - } - return response.ok({ - body: { - total: Object.keys(esResp).length, - data: esResp, - }, - }); - } catch (error) { - console.log(JSON.stringify(error)); - return errorResponse(response, error); - } - } - ); - /** * Gets entity by id. * @@ -552,6 +358,200 @@ export function defineRoutes(router: IRouter, dataSourceEnabled: boolean) { } export function defineSecurityConfigurationRoutes(router: IRouter, dataSourceEnabled: boolean) { + /** + * Lists resources by resource name. + * + * The response format is: + * { + * "total": , + * "data": { + * "entity_id_1": { }, + * "entity_id_2": { }, + * ... + * } + * } + * + * e.g. when listing internal users, response may look like: + * { + * "total": 2, + * "data": { + * "api_test_user2": { + * "hash": "", + * "reserved": false, + * "hidden": false, + * "backend_roles": [], + * "attributes": {}, + * "description": "", + * "static": false + * }, + * "api_test_user1": { + * "hash": "", + * "reserved": false, + * "hidden": false, + * "backend_roles": [], + * "attributes": {}, + * "static": false + * } + * } + * + * when listing action groups, response will look like: + * { + * "total": 2, + * "data": { + * "read": { + * "reserved": true, + * "hidden": false, + * "allowed_actions": ["indices:data/read*", "indices:admin/mappings/fields/get*"], + * "type": "index", + * "description": "Allow all read operations", + * "static": false + * }, + * "cluster_all": { + * "reserved": true, + * "hidden": false, + * "allowed_actions": ["cluster:*"], + * "type": "cluster", + * "description": "Allow everything on cluster level", + * "static": false + * } + * } + * + * role: + * { + * "total": 2, + * "data": { + * "opensearch_dashboards_user": { + * "reserved": true, + * "hidden": false, + * "description": "Provide the minimum permissions for a opensearch_dashboards user", + * "cluster_permissions": ["cluster_composite_ops"], + * "index_permissions": [{ + * "index_patterns": [".opensearch_dashboards", ".opensearch_dashboards-6", ".opensearch_dashboards_*"], + * "fls": [], + * "masked_fields": [], + * "allowed_actions": ["read", "delete", "manage", "index"] + * }, { + * "index_patterns": [".tasks", ".management-beats"], + * "fls": [], + * "masked_fields": [], + * "allowed_actions": ["indices_all"] + * }], + * "tenant_permissions": [], + * "static": false + * }, + * "all_access": { + * "reserved": true, + * "hidden": false, + * "description": "Allow full access to all indices and all cluster APIs", + * "cluster_permissions": ["*"], + * "index_permissions": [{ + * "index_patterns": ["*"], + * "fls": [], + * "masked_fields": [], + * "allowed_actions": ["*"] + * }], + * "tenant_permissions": [{ + * "tenant_patterns": ["*"], + * "allowed_actions": ["opensearch_dashboards_all_write"] + * }], + * "static": false + * } + * } + * } + * + * rolesmapping: + * { + * "total": 2, + * "data": { + * "security_manager": { + * "reserved": false, + * "hidden": false, + * "backend_roles": [], + * "hosts": [], + * "users": ["zengyan", "admin"], + * "and_backend_roles": [] + * }, + * "all_access": { + * "reserved": false, + * "hidden": false, + * "backend_roles": [], + * "hosts": [], + * "users": ["zengyan", "admin", "indextest"], + * "and_backend_roles": [] + * } + * } + * } + * + * tenants: + * { + * "total": 2, + * "data": { + * "global_tenant": { + * "reserved": true, + * "hidden": false, + * "description": "Global tenant", + * "static": false + * }, + * "test tenant": { + * "reserved": false, + * "hidden": false, + * "description": "tenant description", + * "static": false + * } + * } + * } + */ + router.get( + { + path: `${API_PREFIX}/${CONFIGURATION_API_PREFIX}/{resourceName}`, + validate: { + params: schema.object({ + resourceName: schema.string(), + }), + query: schema.object({ + dataSourceId: schema.maybe(schema.string()), + }), + }, + }, + async ( + context, + request, + response + ): Promise> => { + const client = context.security_plugin.esClient.asScoped(request); + let esResp; + try { + if (request.params.resourceName === ResourceType.serviceAccounts.toLowerCase()) { + esResp = await client.callAsCurrentUser('opensearch_security.listServiceAccounts'); + } else if (request.params.resourceName === 'internalaccounts') { + esResp = await wrapRouteWithDataSource( + dataSourceEnabled, + context, + request, + 'opensearch_security.listInternalAccounts' + ); + } else { + esResp = await wrapRouteWithDataSource( + dataSourceEnabled, + context, + request, + 'opensearch_security.listResource', + { resourceName: request.params.resourceName } + ); + } + return response.ok({ + body: { + total: Object.keys(esResp).length, + data: esResp, + }, + }); + } catch (error) { + console.log(JSON.stringify(error)); + return errorResponse(response, error); + } + } + ); + /** * Deletes an entity by id. */ From 1f54c72a832c243a5a5fe2b1d3e655b867c4dd9a Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Mon, 20 May 2024 17:34:53 -0400 Subject: [PATCH 15/43] defineCommonRoutes Signed-off-by: Craig Perkins --- server/plugin.ts | 3 +- server/routes/index.ts | 390 +++++++++++++++++++++-------------------- 2 files changed, 198 insertions(+), 195 deletions(-) diff --git a/server/plugin.ts b/server/plugin.ts index db209b41a..56a8665e7 100644 --- a/server/plugin.ts +++ b/server/plugin.ts @@ -27,7 +27,7 @@ import { } from '../../../src/core/server'; import { SecurityPluginSetup, SecurityPluginStart } from './types'; -import { defineRoutes, defineSecurityConfigurationRoutes } from './routes'; +import { defineRoutes, defineCommonRoutes, defineSecurityConfigurationRoutes } from './routes'; import { SecurityPluginConfigType } from '.'; import opensearchSecurityConfigurationPlugin from './backend/opensearch_security_configuration_plugin'; import opensearchSecurityPlugin from './backend/opensearch_security_plugin'; @@ -184,6 +184,7 @@ export class SecurityPlugin implements Plugin, + * "data": { + * "entity_id_1": { }, + * "entity_id_2": { }, + * ... + * } + * } + * + * e.g. when listing internal users, response may look like: + * { + * "total": 2, + * "data": { + * "api_test_user2": { + * "hash": "", + * "reserved": false, + * "hidden": false, + * "backend_roles": [], + * "attributes": {}, + * "description": "", + * "static": false + * }, + * "api_test_user1": { + * "hash": "", + * "reserved": false, + * "hidden": false, + * "backend_roles": [], + * "attributes": {}, + * "static": false + * } + * } + * + * when listing action groups, response will look like: + * { + * "total": 2, + * "data": { + * "read": { + * "reserved": true, + * "hidden": false, + * "allowed_actions": ["indices:data/read*", "indices:admin/mappings/fields/get*"], + * "type": "index", + * "description": "Allow all read operations", + * "static": false + * }, + * "cluster_all": { + * "reserved": true, + * "hidden": false, + * "allowed_actions": ["cluster:*"], + * "type": "cluster", + * "description": "Allow everything on cluster level", + * "static": false + * } + * } + * + * role: + * { + * "total": 2, + * "data": { + * "opensearch_dashboards_user": { + * "reserved": true, + * "hidden": false, + * "description": "Provide the minimum permissions for a opensearch_dashboards user", + * "cluster_permissions": ["cluster_composite_ops"], + * "index_permissions": [{ + * "index_patterns": [".opensearch_dashboards", ".opensearch_dashboards-6", ".opensearch_dashboards_*"], + * "fls": [], + * "masked_fields": [], + * "allowed_actions": ["read", "delete", "manage", "index"] + * }, { + * "index_patterns": [".tasks", ".management-beats"], + * "fls": [], + * "masked_fields": [], + * "allowed_actions": ["indices_all"] + * }], + * "tenant_permissions": [], + * "static": false + * }, + * "all_access": { + * "reserved": true, + * "hidden": false, + * "description": "Allow full access to all indices and all cluster APIs", + * "cluster_permissions": ["*"], + * "index_permissions": [{ + * "index_patterns": ["*"], + * "fls": [], + * "masked_fields": [], + * "allowed_actions": ["*"] + * }], + * "tenant_permissions": [{ + * "tenant_patterns": ["*"], + * "allowed_actions": ["opensearch_dashboards_all_write"] + * }], + * "static": false + * } + * } + * } + * + * rolesmapping: + * { + * "total": 2, + * "data": { + * "security_manager": { + * "reserved": false, + * "hidden": false, + * "backend_roles": [], + * "hosts": [], + * "users": ["zengyan", "admin"], + * "and_backend_roles": [] + * }, + * "all_access": { + * "reserved": false, + * "hidden": false, + * "backend_roles": [], + * "hosts": [], + * "users": ["zengyan", "admin", "indextest"], + * "and_backend_roles": [] + * } + * } + * } + * + * tenants: + * { + * "total": 2, + * "data": { + * "global_tenant": { + * "reserved": true, + * "hidden": false, + * "description": "Global tenant", + * "static": false + * }, + * "test tenant": { + * "reserved": false, + * "hidden": false, + * "description": "tenant description", + * "static": false + * } + * } + * } + */ + router.get( + { + path: `${API_PREFIX}/${CONFIGURATION_API_PREFIX}/{resourceName}`, + validate: { + params: schema.object({ + resourceName: schema.string(), + }), + query: schema.object({ + dataSourceId: schema.maybe(schema.string()), + }), + }, + }, + async ( + context, + request, + response + ): Promise> => { + const client = context.security_plugin.esClient.asScoped(request); + let esResp; + try { + if (request.params.resourceName === ResourceType.serviceAccounts.toLowerCase()) { + esResp = await client.callAsCurrentUser('opensearch_security.listServiceAccounts'); + } else if (request.params.resourceName === 'internalaccounts') { + esResp = await wrapRouteWithDataSource( + dataSourceEnabled, + context, + request, + 'opensearch_security.listInternalAccounts' + ); + } else { + esResp = await wrapRouteWithDataSource( + dataSourceEnabled, + context, + request, + 'opensearch_security.listResource', + { resourceName: request.params.resourceName } + ); + } + return response.ok({ + body: { + total: Object.keys(esResp).length, + data: esResp, + }, + }); + } catch (error) { + console.log(JSON.stringify(error)); + return errorResponse(response, error); + } + } + ); +} + // TODO: consider to extract entity CRUD operations and put it into a client class export function defineRoutes(router: IRouter, dataSourceEnabled: boolean) { /** @@ -358,200 +554,6 @@ export function defineRoutes(router: IRouter, dataSourceEnabled: boolean) { } export function defineSecurityConfigurationRoutes(router: IRouter, dataSourceEnabled: boolean) { - /** - * Lists resources by resource name. - * - * The response format is: - * { - * "total": , - * "data": { - * "entity_id_1": { }, - * "entity_id_2": { }, - * ... - * } - * } - * - * e.g. when listing internal users, response may look like: - * { - * "total": 2, - * "data": { - * "api_test_user2": { - * "hash": "", - * "reserved": false, - * "hidden": false, - * "backend_roles": [], - * "attributes": {}, - * "description": "", - * "static": false - * }, - * "api_test_user1": { - * "hash": "", - * "reserved": false, - * "hidden": false, - * "backend_roles": [], - * "attributes": {}, - * "static": false - * } - * } - * - * when listing action groups, response will look like: - * { - * "total": 2, - * "data": { - * "read": { - * "reserved": true, - * "hidden": false, - * "allowed_actions": ["indices:data/read*", "indices:admin/mappings/fields/get*"], - * "type": "index", - * "description": "Allow all read operations", - * "static": false - * }, - * "cluster_all": { - * "reserved": true, - * "hidden": false, - * "allowed_actions": ["cluster:*"], - * "type": "cluster", - * "description": "Allow everything on cluster level", - * "static": false - * } - * } - * - * role: - * { - * "total": 2, - * "data": { - * "opensearch_dashboards_user": { - * "reserved": true, - * "hidden": false, - * "description": "Provide the minimum permissions for a opensearch_dashboards user", - * "cluster_permissions": ["cluster_composite_ops"], - * "index_permissions": [{ - * "index_patterns": [".opensearch_dashboards", ".opensearch_dashboards-6", ".opensearch_dashboards_*"], - * "fls": [], - * "masked_fields": [], - * "allowed_actions": ["read", "delete", "manage", "index"] - * }, { - * "index_patterns": [".tasks", ".management-beats"], - * "fls": [], - * "masked_fields": [], - * "allowed_actions": ["indices_all"] - * }], - * "tenant_permissions": [], - * "static": false - * }, - * "all_access": { - * "reserved": true, - * "hidden": false, - * "description": "Allow full access to all indices and all cluster APIs", - * "cluster_permissions": ["*"], - * "index_permissions": [{ - * "index_patterns": ["*"], - * "fls": [], - * "masked_fields": [], - * "allowed_actions": ["*"] - * }], - * "tenant_permissions": [{ - * "tenant_patterns": ["*"], - * "allowed_actions": ["opensearch_dashboards_all_write"] - * }], - * "static": false - * } - * } - * } - * - * rolesmapping: - * { - * "total": 2, - * "data": { - * "security_manager": { - * "reserved": false, - * "hidden": false, - * "backend_roles": [], - * "hosts": [], - * "users": ["zengyan", "admin"], - * "and_backend_roles": [] - * }, - * "all_access": { - * "reserved": false, - * "hidden": false, - * "backend_roles": [], - * "hosts": [], - * "users": ["zengyan", "admin", "indextest"], - * "and_backend_roles": [] - * } - * } - * } - * - * tenants: - * { - * "total": 2, - * "data": { - * "global_tenant": { - * "reserved": true, - * "hidden": false, - * "description": "Global tenant", - * "static": false - * }, - * "test tenant": { - * "reserved": false, - * "hidden": false, - * "description": "tenant description", - * "static": false - * } - * } - * } - */ - router.get( - { - path: `${API_PREFIX}/${CONFIGURATION_API_PREFIX}/{resourceName}`, - validate: { - params: schema.object({ - resourceName: schema.string(), - }), - query: schema.object({ - dataSourceId: schema.maybe(schema.string()), - }), - }, - }, - async ( - context, - request, - response - ): Promise> => { - const client = context.security_plugin.esClient.asScoped(request); - let esResp; - try { - if (request.params.resourceName === ResourceType.serviceAccounts.toLowerCase()) { - esResp = await client.callAsCurrentUser('opensearch_security.listServiceAccounts'); - } else if (request.params.resourceName === 'internalaccounts') { - esResp = await wrapRouteWithDataSource( - dataSourceEnabled, - context, - request, - 'opensearch_security.listInternalAccounts' - ); - } else { - esResp = await wrapRouteWithDataSource( - dataSourceEnabled, - context, - request, - 'opensearch_security.listResource', - { resourceName: request.params.resourceName } - ); - } - return response.ok({ - body: { - total: Object.keys(esResp).length, - data: esResp, - }, - }); - } catch (error) { - console.log(JSON.stringify(error)); - return errorResponse(response, error); - } - } - ); - /** * Deletes an entity by id. */ From 6426257e13d6014dc84f28ee05593d407386cd3a Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Tue, 21 May 2024 11:50:00 -0400 Subject: [PATCH 16/43] Surround with try catch Signed-off-by: Craig Perkins --- server/routes/index.ts | 98 ++++++++++++++++++++++-------------------- 1 file changed, 51 insertions(+), 47 deletions(-) diff --git a/server/routes/index.ts b/server/routes/index.ts index f1345762f..5f5671827 100644 --- a/server/routes/index.ts +++ b/server/routes/index.ts @@ -233,56 +233,60 @@ export function defineCommonRoutes(router: IRouter, dataSourceEnabled: boolean) * } * } */ - router.get( - { - path: `${API_PREFIX}/${CONFIGURATION_API_PREFIX}/{resourceName}`, - validate: { - params: schema.object({ - resourceName: schema.string(), - }), - query: schema.object({ - dataSourceId: schema.maybe(schema.string()), - }), + try { + router.get( + { + path: `${API_PREFIX}/${CONFIGURATION_API_PREFIX}/{resourceName}`, + validate: { + params: schema.object({ + resourceName: schema.string(), + }), + query: schema.object({ + dataSourceId: schema.maybe(schema.string()), + }), + }, }, - }, - async ( - context, - request, - response - ): Promise> => { - const client = context.security_plugin.esClient.asScoped(request); - let esResp; - try { - if (request.params.resourceName === ResourceType.serviceAccounts.toLowerCase()) { - esResp = await client.callAsCurrentUser('opensearch_security.listServiceAccounts'); - } else if (request.params.resourceName === 'internalaccounts') { - esResp = await wrapRouteWithDataSource( - dataSourceEnabled, - context, - request, - 'opensearch_security.listInternalAccounts' - ); - } else { - esResp = await wrapRouteWithDataSource( - dataSourceEnabled, - context, - request, - 'opensearch_security.listResource', - { resourceName: request.params.resourceName } - ); + async ( + context, + request, + response + ): Promise> => { + const client = context.security_plugin.esClient.asScoped(request); + let esResp; + try { + if (request.params.resourceName === ResourceType.serviceAccounts.toLowerCase()) { + esResp = await client.callAsCurrentUser('opensearch_security.listServiceAccounts'); + } else if (request.params.resourceName === 'internalaccounts') { + esResp = await wrapRouteWithDataSource( + dataSourceEnabled, + context, + request, + 'opensearch_security.listInternalAccounts' + ); + } else { + esResp = await wrapRouteWithDataSource( + dataSourceEnabled, + context, + request, + 'opensearch_security.listResource', + { resourceName: request.params.resourceName } + ); + } + return response.ok({ + body: { + total: Object.keys(esResp).length, + data: esResp, + }, + }); + } catch (error) { + console.log(JSON.stringify(error)); + return errorResponse(response, error); } - return response.ok({ - body: { - total: Object.keys(esResp).length, - data: esResp, - }, - }); - } catch (error) { - console.log(JSON.stringify(error)); - return errorResponse(response, error); } - } - ); + ); + } catch (e) { + console.log('Caught exception while registering route: ' + e); + } } // TODO: consider to extract entity CRUD operations and put it into a client class From 1df7862c000d081e0f3c1eb9ce2ffebd380f03f2 Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Tue, 21 May 2024 12:38:12 -0400 Subject: [PATCH 17/43] Only register if not already registered Signed-off-by: Craig Perkins --- server/routes/index.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/server/routes/index.ts b/server/routes/index.ts index 5f5671827..4f60736c7 100644 --- a/server/routes/index.ts +++ b/server/routes/index.ts @@ -233,7 +233,13 @@ export function defineCommonRoutes(router: IRouter, dataSourceEnabled: boolean) * } * } */ - try { + const existingRoute = router + .getRoutes() + .find( + (r) => + r.method === 'get' && r.path === `${API_PREFIX}/${CONFIGURATION_API_PREFIX}/{resourceName}` + ); + if (!existingRoute) { router.get( { path: `${API_PREFIX}/${CONFIGURATION_API_PREFIX}/{resourceName}`, @@ -284,8 +290,6 @@ export function defineCommonRoutes(router: IRouter, dataSourceEnabled: boolean) } } ); - } catch (e) { - console.log('Caught exception while registering route: ' + e); } } From 80c1c3846743eba50617e4eed5a0a778c77aba8c Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Tue, 21 May 2024 16:43:17 -0400 Subject: [PATCH 18/43] Revert route handler context change Signed-off-by: Craig Perkins --- server/plugin.ts | 18 +++++++----------- server/routes/index.ts | 29 ++++++++--------------------- 2 files changed, 15 insertions(+), 32 deletions(-) diff --git a/server/plugin.ts b/server/plugin.ts index 56a8665e7..e0c74dd86 100644 --- a/server/plugin.ts +++ b/server/plugin.ts @@ -15,6 +15,7 @@ import { first } from 'rxjs/operators'; import { Observable } from 'rxjs'; +import { inspect } from 'util'; import { PluginInitializerContext, CoreSetup, @@ -123,17 +124,12 @@ export class SecurityPlugin implements Plugin { - return { - logger: this.logger, - esClient, - }; - } - ); + core.http.registerRouteHandlerContext('security_plugin', (context, request) => { + return { + logger: this.logger, + esClient, + }; + }); if (config.configuration.session_management_enabled) { const securitySessionStorageFactory: SessionStorageFactory = await core.http.createCookieSessionStorageFactory< diff --git a/server/routes/index.ts b/server/routes/index.ts index 4f60736c7..ddd61e172 100644 --- a/server/routes/index.ts +++ b/server/routes/index.ts @@ -233,12 +233,11 @@ export function defineCommonRoutes(router: IRouter, dataSourceEnabled: boolean) * } * } */ - const existingRoute = router - .getRoutes() - .find( - (r) => - r.method === 'get' && r.path === `${API_PREFIX}/${CONFIGURATION_API_PREFIX}/{resourceName}` + const existingRoute = router.getRoutes().find((r) => { + return ( + r.method === 'get' && r.path === `${API_PREFIX}/${CONFIGURATION_API_PREFIX}/{resourceName}` ); + }); if (!existingRoute) { router.get( { @@ -885,10 +884,7 @@ export function defineSecurityConfigurationRoutes(router: IRouter, dataSourceEna }, }, async (context, request, response) => { - const handlerContext = context.security_plugin - ? context.security_plugin - : context.security_admin_plugin; - const client = handlerContext.esClient.asScoped(request); + const client = context.security_plugin.esClient.asScoped(request); try { const esResponse = await client.callAsCurrentUser('opensearch_security.validateDls', { body: request.body, @@ -918,10 +914,7 @@ export function defineSecurityConfigurationRoutes(router: IRouter, dataSourceEna }, }, async (context, request, response) => { - const handlerContext = context.security_plugin - ? context.security_plugin - : context.security_admin_plugin; - const client = handlerContext.esClient.asScoped(request); + const client = context.security_plugin.esClient.asScoped(request); try { const esResponse = await client.callAsCurrentUser('opensearch_security.getIndexMappings', { index: request.body.index.join(','), @@ -950,10 +943,7 @@ export function defineSecurityConfigurationRoutes(router: IRouter, dataSourceEna validate: false, }, async (context, request, response) => { - const handlerContext = context.security_plugin - ? context.security_plugin - : context.security_admin_plugin; - const client = handlerContext.esClient.asScoped(request); + const client = context.security_plugin.esClient.asScoped(request); try { const esResponse = await client.callAsCurrentUser('opensearch_security.indices'); return response.ok({ @@ -979,10 +969,7 @@ const wrapRouteWithDataSource = async ( body?: Record ) => { if (!dataSourceEnabled || !request.query?.dataSourceId) { - const handlerContext = context.security_plugin - ? context.security_plugin - : context.security_admin_plugin; - const client = handlerContext.esClient.asScoped(request); + const client = context.security_plugin.esClient.asScoped(request); return await client.callAsCurrentUser(endpoint, body); } else { const client = context.dataSource.opensearch.legacy.getClient(request.query?.dataSourceId); From f777798ed8bed836c8334126c611841e72a99cff Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Tue, 21 May 2024 21:55:16 -0400 Subject: [PATCH 19/43] Add defineRoutes backs Signed-off-by: Craig Perkins --- server/plugin.ts | 13 +- server/routes/index.ts | 467 ++++++++++++++++++++--------------------- 2 files changed, 231 insertions(+), 249 deletions(-) diff --git a/server/plugin.ts b/server/plugin.ts index e0c74dd86..652050937 100644 --- a/server/plugin.ts +++ b/server/plugin.ts @@ -15,7 +15,6 @@ import { first } from 'rxjs/operators'; import { Observable } from 'rxjs'; -import { inspect } from 'util'; import { PluginInitializerContext, CoreSetup, @@ -28,7 +27,7 @@ import { } from '../../../src/core/server'; import { SecurityPluginSetup, SecurityPluginStart } from './types'; -import { defineRoutes, defineCommonRoutes, defineSecurityConfigurationRoutes } from './routes'; +import { defineRoutes } from './routes'; import { SecurityPluginConfigType } from '.'; import opensearchSecurityConfigurationPlugin from './backend/opensearch_security_configuration_plugin'; import opensearchSecurityPlugin from './backend/opensearch_security_plugin'; @@ -180,15 +179,9 @@ export class SecurityPlugin implements Plugin { - return ( - r.method === 'get' && r.path === `${API_PREFIX}/${CONFIGURATION_API_PREFIX}/{resourceName}` - ); - }); - if (!existingRoute) { - router.get( - { - path: `${API_PREFIX}/${CONFIGURATION_API_PREFIX}/{resourceName}`, - validate: { - params: schema.object({ - resourceName: schema.string(), - }), - query: schema.object({ - dataSourceId: schema.maybe(schema.string()), - }), - }, + router.get( + { + path: `${API_PREFIX}/${CONFIGURATION_API_PREFIX}/{resourceName}`, + validate: { + params: schema.object({ + resourceName: schema.string(), + }), + query: schema.object({ + dataSourceId: schema.maybe(schema.string()), + }), }, - async ( - context, - request, - response - ): Promise> => { - const client = context.security_plugin.esClient.asScoped(request); - let esResp; - try { - if (request.params.resourceName === ResourceType.serviceAccounts.toLowerCase()) { - esResp = await client.callAsCurrentUser('opensearch_security.listServiceAccounts'); - } else if (request.params.resourceName === 'internalaccounts') { - esResp = await wrapRouteWithDataSource( - dataSourceEnabled, - context, - request, - 'opensearch_security.listInternalAccounts' - ); - } else { - esResp = await wrapRouteWithDataSource( - dataSourceEnabled, - context, - request, - 'opensearch_security.listResource', - { resourceName: request.params.resourceName } - ); - } - return response.ok({ - body: { - total: Object.keys(esResp).length, - data: esResp, - }, - }); - } catch (error) { - console.log(JSON.stringify(error)); - return errorResponse(response, error); + }, + async ( + context, + request, + response + ): Promise> => { + const client = context.security_plugin.esClient.asScoped(request); + let esResp; + try { + if (request.params.resourceName === ResourceType.serviceAccounts.toLowerCase()) { + esResp = await client.callAsCurrentUser('opensearch_security.listServiceAccounts'); + } else if (request.params.resourceName === 'internalaccounts') { + esResp = await wrapRouteWithDataSource( + dataSourceEnabled, + context, + request, + 'opensearch_security.listInternalAccounts' + ); + } else { + esResp = await wrapRouteWithDataSource( + dataSourceEnabled, + context, + request, + 'opensearch_security.listResource', + { resourceName: request.params.resourceName } + ); } + return response.ok({ + body: { + total: Object.keys(esResp).length, + data: esResp, + }, + }); + } catch (error) { + console.log(JSON.stringify(error)); + return errorResponse(response, error); } - ); - } -} + } + ); -// TODO: consider to extract entity CRUD operations and put it into a client class -export function defineRoutes(router: IRouter, dataSourceEnabled: boolean) { /** * Gets entity by id. * @@ -402,23 +393,18 @@ export function defineRoutes(router: IRouter, dataSourceEnabled: boolean) { ); /** - * Update object with out Id. Resource identification is expected to computed from headers. Eg: auth headers - * - * Request sample: - * /configuration/account - * { - * "password": "new-password", - * "current_password": "old-password" - * } + * Deletes an entity by id. */ - router.post( + router.delete( { - path: `${API_PREFIX}/${CONFIGURATION_API_PREFIX}/{resourceName}`, + path: `${API_PREFIX}/${CONFIGURATION_API_PREFIX}/{resourceName}/{id}`, validate: { params: schema.object({ resourceName: schema.string(), + id: schema.string({ + minLength: 1, + }), }), - body: schema.any(), query: schema.object({ dataSourceId: schema.maybe(schema.string()), }), @@ -429,20 +415,15 @@ export function defineRoutes(router: IRouter, dataSourceEnabled: boolean) { request, response ): Promise> => { - try { - validateRequestBody(request.params.resourceName, request.body); - } catch (error) { - return response.badRequest({ body: error }); - } try { const esResp = await wrapRouteWithDataSource( dataSourceEnabled, context, request, - 'opensearch_security.saveResourceWithoutId', + 'opensearch_security.deleteResource', { resourceName: request.params.resourceName, - body: request.body, + id: request.params.id, } ); return response.ok({ @@ -457,123 +438,23 @@ export function defineRoutes(router: IRouter, dataSourceEnabled: boolean) { ); /** - * Gets authentication info of the user. - * - * The response looks like: - * { - * "user": "User [name=admin, roles=[], requestedTenant=__user__]", - * "user_name": "admin", - * "user_requested_tenant": "__user__", - * "remote_address": "127.0.0.1:35044", - * "backend_roles": [], - * "custom_attribute_names": [], - * "roles": ["all_access", "security_manager"], - * "tenants": { - * "another_tenant": true, - * "admin": true, - * "global_tenant": true, - * "aaaaa": true, - * "test tenant": true - * }, - * "principal": null, - * "peer_certificates": "0", - * "sso_logout_url": null - * } - */ - router.get( - { - path: `${API_PREFIX}/auth/authinfo`, - validate: false, - }, - async ( - context, - request, - response - ): Promise> => { - const client = context.security_plugin.esClient.asScoped(request); - let esResp; - try { - esResp = await client.callAsCurrentUser('opensearch_security.authinfo'); - - return response.ok({ - body: esResp, - }); - } catch (error) { - return errorResponse(response, error); - } - } - ); - - router.get( - { - path: `${API_PREFIX}/auth/dashboardsinfo`, - validate: false, - }, - async ( - context, - request, - response - ): Promise> => { - const client = context.security_plugin.esClient.asScoped(request); - let esResp; - try { - esResp = await client.callAsCurrentUser('opensearch_security.dashboardsinfo'); - - return response.ok({ - body: esResp, - }); - } catch (error) { - return errorResponse(response, error); - } - } - ); - - /** - * Gets permission info of current user. + * Update object with out Id. Resource identification is expected to computed from headers. Eg: auth headers * - * Sample response: + * Request sample: + * /configuration/account * { - * "user": "User [name=admin, roles=[], requestedTenant=__user__]", - * "user_name": "admin", - * "has_api_access": true, - * "disabled_endpoints": {} + * "password": "new-password", + * "current_password": "old-password" * } */ - router.get( - { - path: `${API_PREFIX}/restapiinfo`, - validate: false, - }, - async (context, request, response) => { - const client = context.security_plugin.esClient.asScoped(request); - try { - const esResponse = await client.callAsCurrentUser('opensearch_security.restapiinfo'); - return response.ok({ - body: esResponse, - }); - } catch (error) { - return response.badRequest({ - body: error, - }); - } - } - ); -} - -export function defineSecurityConfigurationRoutes(router: IRouter, dataSourceEnabled: boolean) { - /** - * Deletes an entity by id. - */ - router.delete( + router.post( { - path: `${API_PREFIX}/${CONFIGURATION_API_PREFIX}/{resourceName}/{id}`, + path: `${API_PREFIX}/${CONFIGURATION_API_PREFIX}/{resourceName}`, validate: { params: schema.object({ resourceName: schema.string(), - id: schema.string({ - minLength: 1, - }), }), + body: schema.any(), query: schema.object({ dataSourceId: schema.maybe(schema.string()), }), @@ -584,15 +465,20 @@ export function defineSecurityConfigurationRoutes(router: IRouter, dataSourceEna request, response ): Promise> => { + try { + validateRequestBody(request.params.resourceName, request.body); + } catch (error) { + return response.badRequest({ body: error }); + } try { const esResp = await wrapRouteWithDataSource( dataSourceEnabled, context, request, - 'opensearch_security.deleteResource', + 'opensearch_security.saveResourceWithoutId', { resourceName: request.params.resourceName, - id: request.params.id, + body: request.body, } ); return response.ok({ @@ -658,6 +544,78 @@ export function defineSecurityConfigurationRoutes(router: IRouter, dataSourceEna } ); + /** + * Gets authentication info of the user. + * + * The response looks like: + * { + * "user": "User [name=admin, roles=[], requestedTenant=__user__]", + * "user_name": "admin", + * "user_requested_tenant": "__user__", + * "remote_address": "127.0.0.1:35044", + * "backend_roles": [], + * "custom_attribute_names": [], + * "roles": ["all_access", "security_manager"], + * "tenants": { + * "another_tenant": true, + * "admin": true, + * "global_tenant": true, + * "aaaaa": true, + * "test tenant": true + * }, + * "principal": null, + * "peer_certificates": "0", + * "sso_logout_url": null + * } + */ + router.get( + { + path: `${API_PREFIX}/auth/authinfo`, + validate: false, + }, + async ( + context, + request, + response + ): Promise> => { + const client = context.security_plugin.esClient.asScoped(request); + let esResp; + try { + esResp = await client.callAsCurrentUser('opensearch_security.authinfo'); + + return response.ok({ + body: esResp, + }); + } catch (error) { + return errorResponse(response, error); + } + } + ); + + router.get( + { + path: `${API_PREFIX}/auth/dashboardsinfo`, + validate: false, + }, + async ( + context, + request, + response + ): Promise> => { + const client = context.security_plugin.esClient.asScoped(request); + let esResp; + try { + esResp = await client.callAsCurrentUser('opensearch_security.dashboardsinfo'); + + return response.ok({ + body: esResp, + }); + } catch (error) { + return errorResponse(response, error); + } + } + ); + /** * Gets audit log configuration。 * @@ -867,6 +825,37 @@ export function defineSecurityConfigurationRoutes(router: IRouter, dataSourceEna } ); + /** + * Gets permission info of current user. + * + * Sample response: + * { + * "user": "User [name=admin, roles=[], requestedTenant=__user__]", + * "user_name": "admin", + * "has_api_access": true, + * "disabled_endpoints": {} + * } + */ + router.get( + { + path: `${API_PREFIX}/restapiinfo`, + validate: false, + }, + async (context, request, response) => { + const client = context.security_plugin.esClient.asScoped(request); + try { + const esResponse = await client.callAsCurrentUser('opensearch_security.restapiinfo'); + return response.ok({ + body: esResponse, + }); + } catch (error) { + return response.badRequest({ + body: error, + }); + } + } + ); + /** * Validates DLS (document level security) query. * From fd776f3b0b1251dd5bdf8687f41374393ef59bdc Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Wed, 22 May 2024 09:00:34 -0400 Subject: [PATCH 20/43] Move up logic for defining routes Signed-off-by: Craig Perkins --- server/plugin.ts | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/server/plugin.ts b/server/plugin.ts index 652050937..6b4acb416 100644 --- a/server/plugin.ts +++ b/server/plugin.ts @@ -59,7 +59,7 @@ export interface SecurityPluginSetupDependencies { declare module 'opensearch-dashboards/server' { interface RequestHandlerContext { - security_admin_plugin: SecurityPluginRequestContext; + security_plugin: SecurityPluginRequestContext; } } @@ -97,14 +97,14 @@ export class SecurityPlugin implements Plugin Date: Wed, 22 May 2024 11:21:26 -0400 Subject: [PATCH 21/43] Add all routes Signed-off-by: Craig Perkins --- server/plugin.ts | 17 +- server/routes/index.ts | 367 ++++++++++++++++++++--------------------- 2 files changed, 184 insertions(+), 200 deletions(-) diff --git a/server/plugin.ts b/server/plugin.ts index 564bf5d1d..a6d87bea1 100644 --- a/server/plugin.ts +++ b/server/plugin.ts @@ -27,7 +27,7 @@ import { } from '../../../src/core/server'; import { SecurityPluginSetup, SecurityPluginStart } from './types'; -import { defineRoutes, defineCommonRoutes, defineSecurityConfigurationRoutes } from './routes'; +import { defineRoutes } from './routes'; import { SecurityPluginConfigType } from '.'; import opensearchSecurityConfigurationPlugin from './backend/opensearch_security_configuration_plugin'; import opensearchSecurityPlugin from './backend/opensearch_security_plugin'; @@ -97,21 +97,14 @@ export class SecurityPlugin implements Plugin> => { - try { - validateRequestBody(request.params.resourceName, request.body); - } catch (error) { - return response.badRequest({ body: error }); - } try { const esResp = await wrapRouteWithDataSource( dataSourceEnabled, context, request, - 'opensearch_security.saveResourceWithoutId', + 'opensearch_security.deleteResource', { resourceName: request.params.resourceName, - body: request.body, + id: request.params.id, } ); return response.ok({ @@ -447,127 +436,25 @@ export function defineCommonRoutes(router: IRouter, dataSourceEnabled: boolean) } } ); -} -export function defineRoutes(router: IRouter, dataSourceEnabled: boolean) { /** - * Gets authentication info of the user. - * - * The response looks like: - * { - * "user": "User [name=admin, roles=[], requestedTenant=__user__]", - * "user_name": "admin", - * "user_requested_tenant": "__user__", - * "remote_address": "127.0.0.1:35044", - * "backend_roles": [], - * "custom_attribute_names": [], - * "roles": ["all_access", "security_manager"], - * "tenants": { - * "another_tenant": true, - * "admin": true, - * "global_tenant": true, - * "aaaaa": true, - * "test tenant": true - * }, - * "principal": null, - * "peer_certificates": "0", - * "sso_logout_url": null - * } - */ - router.get( - { - path: `${API_PREFIX}/auth/authinfo`, - validate: false, - }, - async ( - context, - request, - response - ): Promise> => { - const client = context.security_plugin.esClient.asScoped(request); - let esResp; - try { - esResp = await client.callAsCurrentUser('opensearch_security.authinfo'); - - return response.ok({ - body: esResp, - }); - } catch (error) { - return errorResponse(response, error); - } - } - ); - - router.get( - { - path: `${API_PREFIX}/auth/dashboardsinfo`, - validate: false, - }, - async ( - context, - request, - response - ): Promise> => { - const client = context.security_plugin.esClient.asScoped(request); - let esResp; - try { - esResp = await client.callAsCurrentUser('opensearch_security.dashboardsinfo'); - - return response.ok({ - body: esResp, - }); - } catch (error) { - return errorResponse(response, error); - } - } - ); - - /** - * Gets permission info of current user. + * Update object with out Id. Resource identification is expected to computed from headers. Eg: auth headers * - * Sample response: + * Request sample: + * /configuration/account * { - * "user": "User [name=admin, roles=[], requestedTenant=__user__]", - * "user_name": "admin", - * "has_api_access": true, - * "disabled_endpoints": {} + * "password": "new-password", + * "current_password": "old-password" * } */ - router.get( - { - path: `${API_PREFIX}/restapiinfo`, - validate: false, - }, - async (context, request, response) => { - const client = context.security_plugin.esClient.asScoped(request); - try { - const esResponse = await client.callAsCurrentUser('opensearch_security.restapiinfo'); - return response.ok({ - body: esResponse, - }); - } catch (error) { - return response.badRequest({ - body: error, - }); - } - } - ); -} - -export function defineSecurityConfigurationRoutes(router: IRouter, dataSourceEnabled: boolean) { - /** - * Deletes an entity by id. - */ - router.delete( + router.post( { - path: `${API_PREFIX}/${CONFIGURATION_API_PREFIX}/{resourceName}/{id}`, + path: `${API_PREFIX}/${CONFIGURATION_API_PREFIX}/{resourceName}`, validate: { params: schema.object({ resourceName: schema.string(), - id: schema.string({ - minLength: 1, - }), }), + body: schema.any(), query: schema.object({ dataSourceId: schema.maybe(schema.string()), }), @@ -578,15 +465,20 @@ export function defineSecurityConfigurationRoutes(router: IRouter, dataSourceEna request, response ): Promise> => { + try { + validateRequestBody(request.params.resourceName, request.body); + } catch (error) { + return response.badRequest({ body: error }); + } try { const esResp = await wrapRouteWithDataSource( dataSourceEnabled, context, request, - 'opensearch_security.deleteResource', + 'opensearch_security.saveResourceWithoutId', { resourceName: request.params.resourceName, - id: request.params.id, + body: request.body, } ); return response.ok({ @@ -652,6 +544,78 @@ export function defineSecurityConfigurationRoutes(router: IRouter, dataSourceEna } ); + /** + * Gets authentication info of the user. + * + * The response looks like: + * { + * "user": "User [name=admin, roles=[], requestedTenant=__user__]", + * "user_name": "admin", + * "user_requested_tenant": "__user__", + * "remote_address": "127.0.0.1:35044", + * "backend_roles": [], + * "custom_attribute_names": [], + * "roles": ["all_access", "security_manager"], + * "tenants": { + * "another_tenant": true, + * "admin": true, + * "global_tenant": true, + * "aaaaa": true, + * "test tenant": true + * }, + * "principal": null, + * "peer_certificates": "0", + * "sso_logout_url": null + * } + */ + router.get( + { + path: `${API_PREFIX}/auth/authinfo`, + validate: false, + }, + async ( + context, + request, + response + ): Promise> => { + const client = context.security_plugin.esClient.asScoped(request); + let esResp; + try { + esResp = await client.callAsCurrentUser('opensearch_security.authinfo'); + + return response.ok({ + body: esResp, + }); + } catch (error) { + return errorResponse(response, error); + } + } + ); + + router.get( + { + path: `${API_PREFIX}/auth/dashboardsinfo`, + validate: false, + }, + async ( + context, + request, + response + ): Promise> => { + const client = context.security_plugin.esClient.asScoped(request); + let esResp; + try { + esResp = await client.callAsCurrentUser('opensearch_security.dashboardsinfo'); + + return response.ok({ + body: esResp, + }); + } catch (error) { + return errorResponse(response, error); + } + } + ); + /** * Gets audit log configuration。 * @@ -861,6 +825,37 @@ export function defineSecurityConfigurationRoutes(router: IRouter, dataSourceEna } ); + /** + * Gets permission info of current user. + * + * Sample response: + * { + * "user": "User [name=admin, roles=[], requestedTenant=__user__]", + * "user_name": "admin", + * "has_api_access": true, + * "disabled_endpoints": {} + * } + */ + router.get( + { + path: `${API_PREFIX}/restapiinfo`, + validate: false, + }, + async (context, request, response) => { + const client = context.security_plugin.esClient.asScoped(request); + try { + const esResponse = await client.callAsCurrentUser('opensearch_security.restapiinfo'); + return response.ok({ + body: esResponse, + }); + } catch (error) { + return response.badRequest({ + body: error, + }); + } + } + ); + /** * Validates DLS (document level security) query. * From 72b6990ec2dbd148e56735e6c8e35203d8661b74 Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Wed, 22 May 2024 11:23:34 -0400 Subject: [PATCH 22/43] Revert changes Signed-off-by: Craig Perkins --- ...pensearch_security_configuration_plugin.ts | 55 ++++++++++++++++++- server/backend/opensearch_security_plugin.ts | 55 +------------------ 2 files changed, 55 insertions(+), 55 deletions(-) diff --git a/server/backend/opensearch_security_configuration_plugin.ts b/server/backend/opensearch_security_configuration_plugin.ts index 09735c63a..ad91e34fd 100644 --- a/server/backend/opensearch_security_configuration_plugin.ts +++ b/server/backend/opensearch_security_configuration_plugin.ts @@ -21,6 +21,12 @@ export default function (Client: any, config: any, components: any) { Client.prototype.opensearch_security = components.clientAction.namespaceFactory(); } + Client.prototype.opensearch_security.prototype.restapiinfo = ca({ + url: { + fmt: '/_plugins/_security/api/permissionsinfo', + }, + }); + /** * list all field mappings for all indices. */ @@ -30,6 +36,29 @@ export default function (Client: any, config: any, components: any) { }, }); + /** + * Returns a Security resource configuration. + * + * Sample response: + * + * { + * "user": { + * "hash": "#123123" + * } + * } + */ + Client.prototype.opensearch_security.prototype.listResource = ca({ + url: { + fmt: '/_plugins/_security/api/<%=resourceName%>', + req: { + resourceName: { + type: 'string', + required: true, + }, + }, + }, + }); + Client.prototype.opensearch_security.prototype.listInternalAccounts = ca({ url: { fmt: '/_plugins/_security/api/internalusers?filterBy=internal', @@ -72,6 +101,30 @@ export default function (Client: any, config: any, components: any) { }, }); + /** + * Updates a resource. + * Resource identification is expected to computed from headers. Eg: auth headers. + * + * Sample response: + * { + * "status": "OK", + * "message": "Username updated." + * } + */ + Client.prototype.opensearch_security.prototype.saveResourceWithoutId = ca({ + method: 'PUT', + needBody: true, + url: { + fmt: '/_plugins/_security/api/<%=resourceName%>', + req: { + resourceName: { + type: 'string', + required: true, + }, + }, + }, + }); + /** * Returns a Security resource instance. * @@ -178,4 +231,4 @@ export default function (Client: any, config: any, components: any) { fmt: '/_plugins/_security/api/audit/config', }, }); -} +} \ No newline at end of file diff --git a/server/backend/opensearch_security_plugin.ts b/server/backend/opensearch_security_plugin.ts index ea2c4c5a4..77b5a5b1f 100644 --- a/server/backend/opensearch_security_plugin.ts +++ b/server/backend/opensearch_security_plugin.ts @@ -21,59 +21,6 @@ export default function (Client: any, config: any, components: any) { Client.prototype.opensearch_security = components.clientAction.namespaceFactory(); } - Client.prototype.opensearch_security.prototype.restapiinfo = ca({ - url: { - fmt: '/_plugins/_security/api/permissionsinfo', - }, - }); - - /** - * Updates a resource. - * Resource identification is expected to computed from headers. Eg: auth headers. - * - * Sample response: - * { - * "status": "OK", - * "message": "Username updated." - * } - */ - Client.prototype.opensearch_security.prototype.saveResourceWithoutId = ca({ - method: 'PUT', - needBody: true, - url: { - fmt: '/_plugins/_security/api/<%=resourceName%>', - req: { - resourceName: { - type: 'string', - required: true, - }, - }, - }, - }); - - /** - * Returns a Security resource configuration. - * - * Sample response: - * - * { - * "user": { - * "hash": "#123123" - * } - * } - */ - Client.prototype.opensearch_security.prototype.listResource = ca({ - url: { - fmt: '/_plugins/_security/api/<%=resourceName%>', - req: { - resourceName: { - type: 'string', - required: true, - }, - }, - }, - }); - /** * Gets auth info. */ @@ -137,4 +84,4 @@ export default function (Client: any, config: any, components: any) { fmt: '/_plugins/_security/api/tenancy/config', }, }); -} +} \ No newline at end of file From 002b3d310917f5f2ab6261aba803024887491100 Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Wed, 22 May 2024 11:23:45 -0400 Subject: [PATCH 23/43] Revert changes Signed-off-by: Craig Perkins --- server/backend/opensearch_security_configuration_plugin.ts | 2 +- server/backend/opensearch_security_plugin.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/server/backend/opensearch_security_configuration_plugin.ts b/server/backend/opensearch_security_configuration_plugin.ts index ad91e34fd..3b87863b8 100644 --- a/server/backend/opensearch_security_configuration_plugin.ts +++ b/server/backend/opensearch_security_configuration_plugin.ts @@ -231,4 +231,4 @@ export default function (Client: any, config: any, components: any) { fmt: '/_plugins/_security/api/audit/config', }, }); -} \ No newline at end of file +} diff --git a/server/backend/opensearch_security_plugin.ts b/server/backend/opensearch_security_plugin.ts index 77b5a5b1f..33b72cc0d 100644 --- a/server/backend/opensearch_security_plugin.ts +++ b/server/backend/opensearch_security_plugin.ts @@ -84,4 +84,4 @@ export default function (Client: any, config: any, components: any) { fmt: '/_plugins/_security/api/tenancy/config', }, }); -} \ No newline at end of file +} From f2c7455e2164c3648ff692446c419896c3c36f6f Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Wed, 22 May 2024 15:02:05 -0400 Subject: [PATCH 24/43] Run with multiple security-dashboards-plugin Signed-off-by: Craig Perkins --- .github/actions/run-cypress-tests/action.yml | 2 +- .../setup-opensearch-dashboards/action.yml | 201 ++++++++++++++++++ opensearch_dashboards.json | 4 +- 3 files changed, 204 insertions(+), 3 deletions(-) create mode 100644 .github/actions/setup-opensearch-dashboards/action.yml diff --git a/.github/actions/run-cypress-tests/action.yml b/.github/actions/run-cypress-tests/action.yml index d77adf5a2..c027983e8 100644 --- a/.github/actions/run-cypress-tests/action.yml +++ b/.github/actions/run-cypress-tests/action.yml @@ -51,7 +51,7 @@ runs: # OSD bootstrap - name: Run Dashboard with Security Dashboards Plugin - uses: derek-ho/setup-opensearch-dashboards@v1 + uses: ./.github/actions/run-cypress-tests with: plugin_name: security-dashboards-plugin opensearch_dashboards_yml: ${{ inputs.dashboards_config_file }} diff --git a/.github/actions/setup-opensearch-dashboards/action.yml b/.github/actions/setup-opensearch-dashboards/action.yml new file mode 100644 index 000000000..e9096d487 --- /dev/null +++ b/.github/actions/setup-opensearch-dashboards/action.yml @@ -0,0 +1,201 @@ +name: 'Starts Dashboards with Plugin' +description: 'Installs OpenSearch Dashboard with a plugin from github, then checks out the correct dashboards version for the plugin, configures npm/yarn, bootstraps Dashboards, and starts the instance' + +inputs: + plugin_name: + description: 'The name of the plugin to use, such as security-dashboards-plugin' + required: true + + built_plugin_name: + description: 'The name of the plugin after building, if doing zip install' + required: false + + built_plugin_suffix: + description: 'The suffix of the plugin after building, if doing zip instll. Leave empty to indicate default OSD build behavior' + required: false + + install_zip: + description: 'Whether the setup should be done using the install plugin flow. Leave empty to indicate dev setup. Only applicable for linux for now' + required: false + + plugin_repo: + description: 'The repo of the plugin to use. Leave empty to indicate the repo which is running this action' + required: false + + opensearch_dashboards_yml: + description: 'The file to replace opensearch_dashboards.yml. Leave empty to use the default' + required: false + + plugin_branch: + description: 'The branch of the repo of the plugin to use. Leave empty to indicate the branch which is running this action' + required: false + +outputs: + dashboards-directory: + description: "The directory where the dashboards has been configured" + value: ${{ steps.determine-dashboards-directory.outputs.dashboards-directory }} + + dashboards-binary-directory: + description: "The directory of the dashboards binary that was configured" + value: ${{ steps.determine-dashboards-directory-zip-install.outputs.dashboards-directory }} + + plugin-directory: + description: "The directory where the plugin has been configured" + value: ${{ steps.determine-plugin-directory.outputs.plugin-directory }} + + +runs: + using: "composite" + steps: + - id: determine-dashboards-directory + if: ${{ inputs.install_zip != 'true' }} + run: echo "dashboards-directory=OpenSearch-Dashboards" >> $GITHUB_OUTPUT + shell: bash + + - id: determine-plugin-directory + run: echo "::set-output name=plugin-directory::./OpenSearch-Dashboards/plugins/${{ inputs.plugin_name }}" + shell: bash + + - uses: actions/checkout@v2 + with: + path: OpenSearch-Dashboards + repository: cwperks/OpenSearch-Dashboards + ref: 'support-repeated-plugin' + fetch-depth: 0 + filter: | + cypress + test + + - uses: actions/checkout@v2 + with: + path: ${{ steps.determine-plugin-directory.outputs.plugin-directory }} + + - uses: actions/checkout@v2 + with: + path: ./OpenSearch-Dashboards/plugins/security-admin-dashboards-plugin + + - id: osd-version + run: | + echo "::set-output name=osd-version::$(jq -r '.opensearchDashboards.version | split(".") | .[:2] | join(".")' package.json)" + echo "::set-output name=osd-x-version::$(jq -r '.opensearchDashboards.version | split(".") | .[0]' package.json).x" + echo "::set-output name=plugin-version::$(jq -r '.opensearchDashboardsVersion' opensearch_dashboards.json)" + working-directory: ${{ steps.determine-plugin-directory.outputs.plugin-directory }} + shell: bash + + # - id: branch-switch-if-possible + # continue-on-error: true # Defaults onto main if the branch switch doesn't work + # if: ${{ steps.osd-version.outputs.osd-version }} + # run: git checkout ${{ steps.osd-version.outputs.osd-version }} || git checkout ${{ steps.osd-version.outputs.osd-x-version }} + # working-directory: ./OpenSearch-Dashboards + # shell: bash + + - id: tool-versions + run: | + echo "node_version=$(cat .node-version)" >> $GITHUB_OUTPUT + echo "yarn_version=$(jq -r '.engines.yarn' package.json)" >> $GITHUB_OUTPUT + working-directory: OpenSearch-Dashboards + shell: bash + + - uses: actions/setup-node@v1 + with: + node-version: ${{ steps.tool-versions.outputs.node_version }} + registry-url: 'https://registry.npmjs.org' + + - name: Setup Opensearch Dashboards + run: | + npm uninstall -g yarn + echo "Installing yarn ${{ steps.tool-versions.outputs.yarn_version }}" + npm i -g yarn@${{ steps.tool-versions.outputs.yarn_version }} + yarn cache clean + yarn add sha.js + working-directory: OpenSearch-Dashboards + shell: bash + + - name: Bootstrap the OpenSearch Dashboard + uses: nick-fields/retry@v2 + with: + timeout_minutes: 20 + max_attempts: 2 + command: yarn --cwd OpenSearch-Dashboards osd bootstrap --oss --single-version=loose # loose is passed in to ignore version conflicts on cypress version used in OSD and this repo + + - name: Download OpenSearch for Linux + uses: peternied/download-file@v2 + if: ${{ inputs.install_zip == 'true' && runner.os == 'Linux' }} + with: + url: https://ci.opensearch.org/ci/dbc/distribution-build-opensearch-dashboards/${{ steps.osd-version.outputs.plugin-version }}/latest/linux/x64/tar/builds/opensearch-dashboards/dist/opensearch-dashboards-min-${{ steps.osd-version.outputs.plugin-version }}-linux-x64.tar.gz + + # Extract downloaded zip + - name: Extract downloaded tar + if: ${{ inputs.install_zip == 'true' && runner.os == 'Linux' }} + run: | + tar -xzf opensearch-*.tar.gz + rm -f opensearch-*.tar.gz + shell: bash + + - id: determine-dashboards-directory-zip-install + if: ${{ inputs.install_zip == 'true' }} + run: echo "dashboards-directory=opensearch-dashboards-${{ steps.osd-version.outputs.plugin-version }}-linux-x64" >> $GITHUB_OUTPUT + shell: bash + + - name: Build the plugin + if: ${{ inputs.install_zip == 'true' }} + run: | + yarn build + working-directory: ${{ steps.determine-plugin-directory.outputs.plugin-directory }} + shell: bash + + - name: Install the plugin zip + if: ${{ inputs.install_zip == 'true' && inputs.built_plugin_suffix == '' }} + run: | + ${{ steps.determine-dashboards-directory-zip-install.outputs.dashboards-directory }}/bin/opensearch-dashboards-plugin install file:$(pwd)/OpenSearch-Dashboards/plugins/${{ inputs.plugin_name }}/build/${{ inputs.built_plugin_name }}-${{env.PLUGIN_VERSION}}.zip + shell: bash + + - name: Install the plugin zip + if: ${{ inputs.install_zip == 'true' && inputs.built_plugin_suffix != '' }} + run: | + ${{ steps.determine-dashboards-directory-zip-install.outputs.dashboards-directory }}/bin/opensearch-dashboards-plugin install file:$(pwd)/OpenSearch-Dashboards/plugins/${{ inputs.plugin_name }}/build/${{ inputs.built_plugin_name }}-${{inputs.built_plugin_suffix}}.zip + shell: bash + + - name: Replace opensearch_dashboards.yml file if applicable binary + if: ${{ inputs.opensearch_dashboards_yml != '' && inputs.install_zip == 'true' }} + run: | + mv ${{ inputs.opensearch_dashboards_yml }} ${{ steps.determine-dashboards-directory-zip-install.outputs.dashboards-directory }}/config/opensearch_dashboards.yml + shell: bash + + - name: Replace opensearch_dashboards.yml file if applicable dev + if: ${{ inputs.opensearch_dashboards_yml != '' && inputs.install_zip != 'true' }} + run: | + mv ${{ inputs.opensearch_dashboards_yml }} ./OpenSearch-Dashboards/config/opensearch_dashboards.yml + shell: bash + + - name: Add to opensearch_dashboards.yml + run: | + echo 'opensearch_security.configuration.admin_pages_enabled: false' >> ./OpenSearch-Dashboards/config/opensearch_dashboards.yml + echo 'opensearch_security_admin.configuration.session_management_enabled: false' >> ./OpenSearch-Dashboards/config/opensearch_dashboards.yml + shell: bash + + - name: Create opensearch_dashboards.json for security admin dashboards plugin + if: ${{ runner.os == 'Linux' }} + run: | + rm ./OpenSearch-Dashboards/plugins/security-admin-dashboards-plugin/opensearch_dashboards.json + cat << 'EOT' > ./OpenSearch-Dashboards/plugins/security-admin-dashboards-plugin/opensearch_dashboards.json + { + "id": "securityAdminDashboards", + "version": "3.0.0.0", + "opensearchDashboardsVersion": "3.0.0", + "configPath": [ + "opensearch_security_admin" + ], + "requiredPlugins": [ + "navigation", + "savedObjectsManagement" + ], + "optionalPlugins": [ + "managementOverview", + "dataSource", + "dataSourceManagement" + ], + "server": true, + "ui": true + } + EOT \ No newline at end of file diff --git a/opensearch_dashboards.json b/opensearch_dashboards.json index abbe908ec..3a2642593 100644 --- a/opensearch_dashboards.json +++ b/opensearch_dashboards.json @@ -1,9 +1,9 @@ { - "id": "securityAdminDashboards", + "id": "securityDashboards", "version": "3.0.0.0", "opensearchDashboardsVersion": "3.0.0", "configPath": [ - "opensearch_security_admin" + "opensearch_security" ], "requiredPlugins": [ "navigation", From 7b334f7d8b9047a36daca88851cca089af82d7a6 Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Wed, 22 May 2024 15:37:39 -0400 Subject: [PATCH 25/43] Use local action Signed-off-by: Craig Perkins --- .github/actions/run-cypress-tests/action.yml | 2 +- .github/workflows/cypress-test-tenancy-disabled.yml | 2 +- .github/workflows/cypress-test.yml | 2 +- .github/workflows/integration-test.yml | 2 +- .github/workflows/unit-test.yml | 2 +- .github/workflows/verify-binary-installation.yml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/actions/run-cypress-tests/action.yml b/.github/actions/run-cypress-tests/action.yml index c027983e8..6a85d3ff4 100644 --- a/.github/actions/run-cypress-tests/action.yml +++ b/.github/actions/run-cypress-tests/action.yml @@ -51,7 +51,7 @@ runs: # OSD bootstrap - name: Run Dashboard with Security Dashboards Plugin - uses: ./.github/actions/run-cypress-tests + uses: ./.github/actions/setup-opensearch-dashboards with: plugin_name: security-dashboards-plugin opensearch_dashboards_yml: ${{ inputs.dashboards_config_file }} diff --git a/.github/workflows/cypress-test-tenancy-disabled.yml b/.github/workflows/cypress-test-tenancy-disabled.yml index 024dd9cbc..7badd731f 100644 --- a/.github/workflows/cypress-test-tenancy-disabled.yml +++ b/.github/workflows/cypress-test-tenancy-disabled.yml @@ -70,7 +70,7 @@ jobs: EOT - name: Run Dashboard with Security Dashboards Plugin - uses: derek-ho/setup-opensearch-dashboards@v1 + uses: ./.github/actions/setup-opensearch-dashboards with: plugin_name: security-dashboards-plugin opensearch_dashboards_yml: tenancy-disabled-opensearch-dashboards-config.yml diff --git a/.github/workflows/cypress-test.yml b/.github/workflows/cypress-test.yml index c9a85ac6f..5de9d3ea3 100644 --- a/.github/workflows/cypress-test.yml +++ b/.github/workflows/cypress-test.yml @@ -72,7 +72,7 @@ jobs: EOT - name: Run Dashboard with Security Dashboards Plugin - uses: derek-ho/setup-opensearch-dashboards@v1 + uses: ./.github/actions/setup-opensearch-dashboards with: plugin_name: security-dashboards-plugin opensearch_dashboards_yml: cypress-opensearch-dashboards-config.yml diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml index 2126ff1d0..2ff050149 100644 --- a/.github/workflows/integration-test.yml +++ b/.github/workflows/integration-test.yml @@ -81,7 +81,7 @@ jobs: sudo rm -rf "$AGENT_TOOLSDIRECTORY" - id: install-dashboards - uses: derek-ho/setup-opensearch-dashboards@v1 + uses: ./.github/actions/setup-opensearch-dashboards with: plugin_name: security-dashboards-plugin diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index 7fd07c6cb..f1ac8ec70 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -20,7 +20,7 @@ jobs: uses: actions/checkout@v2 - id: install-dashboards - uses: derek-ho/setup-opensearch-dashboards@v1 + uses: ./.github/actions/setup-opensearch-dashboards with: plugin_name: security-dashboards-plugin diff --git a/.github/workflows/verify-binary-installation.yml b/.github/workflows/verify-binary-installation.yml index c19bc3c81..779bc148d 100644 --- a/.github/workflows/verify-binary-installation.yml +++ b/.github/workflows/verify-binary-installation.yml @@ -67,7 +67,7 @@ jobs: - name: Run Dashboard with Security Dashboards Plugin id: setup-dashboards - uses: derek-ho/setup-opensearch-dashboards@v1 + uses: ./.github/actions/setup-opensearch-dashboards with: plugin_name: security-dashboards-plugin built_plugin_name: security-dashboards From d39b94756a24751f1c0d2fa17ee22dceed67962e Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Wed, 22 May 2024 16:14:43 -0400 Subject: [PATCH 26/43] Comment out section Signed-off-by: Craig Perkins --- .../setup-opensearch-dashboards/action.yml | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/.github/actions/setup-opensearch-dashboards/action.yml b/.github/actions/setup-opensearch-dashboards/action.yml index e9096d487..391ff4d2e 100644 --- a/.github/actions/setup-opensearch-dashboards/action.yml +++ b/.github/actions/setup-opensearch-dashboards/action.yml @@ -174,28 +174,28 @@ runs: echo 'opensearch_security_admin.configuration.session_management_enabled: false' >> ./OpenSearch-Dashboards/config/opensearch_dashboards.yml shell: bash - - name: Create opensearch_dashboards.json for security admin dashboards plugin - if: ${{ runner.os == 'Linux' }} - run: | - rm ./OpenSearch-Dashboards/plugins/security-admin-dashboards-plugin/opensearch_dashboards.json - cat << 'EOT' > ./OpenSearch-Dashboards/plugins/security-admin-dashboards-plugin/opensearch_dashboards.json - { - "id": "securityAdminDashboards", - "version": "3.0.0.0", - "opensearchDashboardsVersion": "3.0.0", - "configPath": [ - "opensearch_security_admin" - ], - "requiredPlugins": [ - "navigation", - "savedObjectsManagement" - ], - "optionalPlugins": [ - "managementOverview", - "dataSource", - "dataSourceManagement" - ], - "server": true, - "ui": true - } - EOT \ No newline at end of file + # - name: Create opensearch_dashboards.json for security admin dashboards plugin + # if: ${{ runner.os == 'Linux' }} + # run: | + # rm ./OpenSearch-Dashboards/plugins/security-admin-dashboards-plugin/opensearch_dashboards.json + # cat << 'EOT' > ./OpenSearch-Dashboards/plugins/security-admin-dashboards-plugin/opensearch_dashboards.json + # { + # "id": "securityAdminDashboards", + # "version": "3.0.0.0", + # "opensearchDashboardsVersion": "3.0.0", + # "configPath": [ + # "opensearch_security_admin" + # ], + # "requiredPlugins": [ + # "navigation", + # "savedObjectsManagement" + # ], + # "optionalPlugins": [ + # "managementOverview", + # "dataSource", + # "dataSourceManagement" + # ], + # "server": true, + # "ui": true + # } + # EOT \ No newline at end of file From e7a780102922db298001de1a07639c043c7f6ff7 Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Wed, 22 May 2024 16:16:56 -0400 Subject: [PATCH 27/43] Fix indentation Signed-off-by: Craig Perkins --- .../setup-opensearch-dashboards/action.yml | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/.github/actions/setup-opensearch-dashboards/action.yml b/.github/actions/setup-opensearch-dashboards/action.yml index 391ff4d2e..c223d6c6d 100644 --- a/.github/actions/setup-opensearch-dashboards/action.yml +++ b/.github/actions/setup-opensearch-dashboards/action.yml @@ -174,28 +174,28 @@ runs: echo 'opensearch_security_admin.configuration.session_management_enabled: false' >> ./OpenSearch-Dashboards/config/opensearch_dashboards.yml shell: bash - # - name: Create opensearch_dashboards.json for security admin dashboards plugin - # if: ${{ runner.os == 'Linux' }} - # run: | - # rm ./OpenSearch-Dashboards/plugins/security-admin-dashboards-plugin/opensearch_dashboards.json - # cat << 'EOT' > ./OpenSearch-Dashboards/plugins/security-admin-dashboards-plugin/opensearch_dashboards.json - # { - # "id": "securityAdminDashboards", - # "version": "3.0.0.0", - # "opensearchDashboardsVersion": "3.0.0", - # "configPath": [ - # "opensearch_security_admin" - # ], - # "requiredPlugins": [ - # "navigation", - # "savedObjectsManagement" - # ], - # "optionalPlugins": [ - # "managementOverview", - # "dataSource", - # "dataSourceManagement" - # ], - # "server": true, - # "ui": true - # } - # EOT \ No newline at end of file + - name: Create opensearch_dashboards.json for security admin dashboards plugin + if: ${{ runner.os == 'Linux' }} + run: | + rm ./OpenSearch-Dashboards/plugins/security-admin-dashboards-plugin/opensearch_dashboards.json + cat << 'EOT' > ./OpenSearch-Dashboards/plugins/security-admin-dashboards-plugin/opensearch_dashboards.json + { + "id": "securityAdminDashboards", + "version": "3.0.0.0", + "opensearchDashboardsVersion": "3.0.0", + "configPath": [ + "opensearch_security_admin" + ], + "requiredPlugins": [ + "navigation", + "savedObjectsManagement" + ], + "optionalPlugins": [ + "managementOverview", + "dataSource", + "dataSourceManagement" + ], + "server": true, + "ui": true + } + EOT \ No newline at end of file From 7144beb8bac635d6d0889ed87aba30a9ab1d7f62 Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Wed, 22 May 2024 16:27:42 -0400 Subject: [PATCH 28/43] Add shell Signed-off-by: Craig Perkins --- .github/actions/setup-opensearch-dashboards/action.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/actions/setup-opensearch-dashboards/action.yml b/.github/actions/setup-opensearch-dashboards/action.yml index c223d6c6d..5623a7467 100644 --- a/.github/actions/setup-opensearch-dashboards/action.yml +++ b/.github/actions/setup-opensearch-dashboards/action.yml @@ -176,6 +176,7 @@ runs: - name: Create opensearch_dashboards.json for security admin dashboards plugin if: ${{ runner.os == 'Linux' }} + shell: bash run: | rm ./OpenSearch-Dashboards/plugins/security-admin-dashboards-plugin/opensearch_dashboards.json cat << 'EOT' > ./OpenSearch-Dashboards/plugins/security-admin-dashboards-plugin/opensearch_dashboards.json From e92eb03636c0f41c32de1540430e37a3c6df0b61 Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Wed, 22 May 2024 16:47:52 -0400 Subject: [PATCH 29/43] rewrite package.json Signed-off-by: Craig Perkins --- .../setup-opensearch-dashboards/action.yml | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/.github/actions/setup-opensearch-dashboards/action.yml b/.github/actions/setup-opensearch-dashboards/action.yml index 5623a7467..9d28272e2 100644 --- a/.github/actions/setup-opensearch-dashboards/action.yml +++ b/.github/actions/setup-opensearch-dashboards/action.yml @@ -199,4 +199,66 @@ runs: "server": true, "ui": true } + EOT + + - name: Create package.json for security admin dashboards plugin + if: ${{ runner.os == 'Linux' }} + shell: bash + run: | + rm ./OpenSearch-Dashboards/plugins/security-admin-dashboards-plugin/package.json + cat << 'EOT' > ./OpenSearch-Dashboards/plugins/security-admin-dashboards-plugin/package.json + { + "name": "opensearch-security-admin-dashboards", + "version": "3.0.0.0", + "main": "target/plugins/opensearch_security_dashboards", + "opensearchDashboards": { + "version": "3.0.0", + "templateVersion": "3.0.0" + }, + "license": "Apache-2.0", + "homepage": "https://github.com/opensearch-project/security-dashboards-plugin", + "scripts": { + "cypress:open": "cypress open", + "cypress:run": "cypress run", + "plugin-helpers": "node ../../scripts/plugin_helpers", + "osd": "node ../../scripts/osd", + "opensearch": "node ../../scripts/opensearch", + "build": "yarn plugin-helpers build && node build_tools/rename_zip.js", + "start": "node ../../scripts/opensearch-dashboards --dev", + "lint:es": "node ../../scripts/eslint", + "lint:style": "node ../../scripts/stylelint", + "lint": "yarn run lint:es && yarn run lint:style", + "runIdp": "node ./test/jest_integration/runIdpServer.js", + "test:jest_server": "ADMIN_PASSWORD=$ADMIN_PASSWORD node ./test/run_jest_tests.js --config ./test/jest.config.server.js", + "test:jest_ui": "node ./test/run_jest_tests.js --config ./test/jest.config.ui.js", + "prepare": "husky install" + }, + "devDependencies": { + "@elastic/eslint-import-resolver-kibana": "link:../../packages/osd-eslint-import-resolver-opensearch-dashboards", + "@testing-library/react-hooks": "^7.0.2", + "@types/hapi__wreck": "^15.0.1", + "cypress": "^13.6.0", + "cypress-mochawesome-reporter": "^3.3.0", + "eslint-plugin-cypress": "^2.8.1", + "eslint-plugin-unused-imports": "3.1.0", + "gulp-rename": "2.0.0", + "husky": "^8.0.0", + "jose": "^5.2.4", + "minimist": "^1.2.8", + "saml-idp": "^1.2.1", + "selfsigned": "^2.0.1", + "typescript": "4.0.2" + }, + "dependencies": { + "@hapi/cryptiles": "5.0.0", + "@hapi/wreck": "^17.1.0", + "html-entities": "1.3.1", + "zxcvbn": "^4.4.2" + }, + "resolutions": { + "selenium-webdriver": "4.10.0", + "glob-parent": "^5.1.2", + "debug": "^4.3.4" + } + } EOT \ No newline at end of file From 7498fadc1da6a85fb7912597b013d904dc432eb0 Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Wed, 22 May 2024 16:56:12 -0400 Subject: [PATCH 30/43] Change package.json before bootstrap Signed-off-by: Craig Perkins --- .../setup-opensearch-dashboards/action.yml | 124 +++++++++--------- 1 file changed, 62 insertions(+), 62 deletions(-) diff --git a/.github/actions/setup-opensearch-dashboards/action.yml b/.github/actions/setup-opensearch-dashboards/action.yml index 9d28272e2..7489a2c81 100644 --- a/.github/actions/setup-opensearch-dashboards/action.yml +++ b/.github/actions/setup-opensearch-dashboards/action.yml @@ -111,6 +111,68 @@ runs: working-directory: OpenSearch-Dashboards shell: bash + - name: Create package.json for security admin dashboards plugin + if: ${{ runner.os == 'Linux' }} + shell: bash + run: | + rm ./OpenSearch-Dashboards/plugins/security-admin-dashboards-plugin/package.json + cat << 'EOT' > ./OpenSearch-Dashboards/plugins/security-admin-dashboards-plugin/package.json + { + "name": "opensearch-security-admin-dashboards", + "version": "3.0.0.0", + "main": "target/plugins/opensearch_security_dashboards", + "opensearchDashboards": { + "version": "3.0.0", + "templateVersion": "3.0.0" + }, + "license": "Apache-2.0", + "homepage": "https://github.com/opensearch-project/security-dashboards-plugin", + "scripts": { + "cypress:open": "cypress open", + "cypress:run": "cypress run", + "plugin-helpers": "node ../../scripts/plugin_helpers", + "osd": "node ../../scripts/osd", + "opensearch": "node ../../scripts/opensearch", + "build": "yarn plugin-helpers build && node build_tools/rename_zip.js", + "start": "node ../../scripts/opensearch-dashboards --dev", + "lint:es": "node ../../scripts/eslint", + "lint:style": "node ../../scripts/stylelint", + "lint": "yarn run lint:es && yarn run lint:style", + "runIdp": "node ./test/jest_integration/runIdpServer.js", + "test:jest_server": "ADMIN_PASSWORD=$ADMIN_PASSWORD node ./test/run_jest_tests.js --config ./test/jest.config.server.js", + "test:jest_ui": "node ./test/run_jest_tests.js --config ./test/jest.config.ui.js", + "prepare": "husky install" + }, + "devDependencies": { + "@elastic/eslint-import-resolver-kibana": "link:../../packages/osd-eslint-import-resolver-opensearch-dashboards", + "@testing-library/react-hooks": "^7.0.2", + "@types/hapi__wreck": "^15.0.1", + "cypress": "^13.6.0", + "cypress-mochawesome-reporter": "^3.3.0", + "eslint-plugin-cypress": "^2.8.1", + "eslint-plugin-unused-imports": "3.1.0", + "gulp-rename": "2.0.0", + "husky": "^8.0.0", + "jose": "^5.2.4", + "minimist": "^1.2.8", + "saml-idp": "^1.2.1", + "selfsigned": "^2.0.1", + "typescript": "4.0.2" + }, + "dependencies": { + "@hapi/cryptiles": "5.0.0", + "@hapi/wreck": "^17.1.0", + "html-entities": "1.3.1", + "zxcvbn": "^4.4.2" + }, + "resolutions": { + "selenium-webdriver": "4.10.0", + "glob-parent": "^5.1.2", + "debug": "^4.3.4" + } + } + EOT + - name: Bootstrap the OpenSearch Dashboard uses: nick-fields/retry@v2 with: @@ -199,66 +261,4 @@ runs: "server": true, "ui": true } - EOT - - - name: Create package.json for security admin dashboards plugin - if: ${{ runner.os == 'Linux' }} - shell: bash - run: | - rm ./OpenSearch-Dashboards/plugins/security-admin-dashboards-plugin/package.json - cat << 'EOT' > ./OpenSearch-Dashboards/plugins/security-admin-dashboards-plugin/package.json - { - "name": "opensearch-security-admin-dashboards", - "version": "3.0.0.0", - "main": "target/plugins/opensearch_security_dashboards", - "opensearchDashboards": { - "version": "3.0.0", - "templateVersion": "3.0.0" - }, - "license": "Apache-2.0", - "homepage": "https://github.com/opensearch-project/security-dashboards-plugin", - "scripts": { - "cypress:open": "cypress open", - "cypress:run": "cypress run", - "plugin-helpers": "node ../../scripts/plugin_helpers", - "osd": "node ../../scripts/osd", - "opensearch": "node ../../scripts/opensearch", - "build": "yarn plugin-helpers build && node build_tools/rename_zip.js", - "start": "node ../../scripts/opensearch-dashboards --dev", - "lint:es": "node ../../scripts/eslint", - "lint:style": "node ../../scripts/stylelint", - "lint": "yarn run lint:es && yarn run lint:style", - "runIdp": "node ./test/jest_integration/runIdpServer.js", - "test:jest_server": "ADMIN_PASSWORD=$ADMIN_PASSWORD node ./test/run_jest_tests.js --config ./test/jest.config.server.js", - "test:jest_ui": "node ./test/run_jest_tests.js --config ./test/jest.config.ui.js", - "prepare": "husky install" - }, - "devDependencies": { - "@elastic/eslint-import-resolver-kibana": "link:../../packages/osd-eslint-import-resolver-opensearch-dashboards", - "@testing-library/react-hooks": "^7.0.2", - "@types/hapi__wreck": "^15.0.1", - "cypress": "^13.6.0", - "cypress-mochawesome-reporter": "^3.3.0", - "eslint-plugin-cypress": "^2.8.1", - "eslint-plugin-unused-imports": "3.1.0", - "gulp-rename": "2.0.0", - "husky": "^8.0.0", - "jose": "^5.2.4", - "minimist": "^1.2.8", - "saml-idp": "^1.2.1", - "selfsigned": "^2.0.1", - "typescript": "4.0.2" - }, - "dependencies": { - "@hapi/cryptiles": "5.0.0", - "@hapi/wreck": "^17.1.0", - "html-entities": "1.3.1", - "zxcvbn": "^4.4.2" - }, - "resolutions": { - "selenium-webdriver": "4.10.0", - "glob-parent": "^5.1.2", - "debug": "^4.3.4" - } - } EOT \ No newline at end of file From 0a0c3434a3d3d3a6d13f70d50d355edecd1ead2d Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Wed, 22 May 2024 21:28:45 -0400 Subject: [PATCH 31/43] minor change Signed-off-by: Craig Perkins --- .github/actions/setup-opensearch-dashboards/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/setup-opensearch-dashboards/action.yml b/.github/actions/setup-opensearch-dashboards/action.yml index 7489a2c81..b3d64e992 100644 --- a/.github/actions/setup-opensearch-dashboards/action.yml +++ b/.github/actions/setup-opensearch-dashboards/action.yml @@ -230,7 +230,7 @@ runs: mv ${{ inputs.opensearch_dashboards_yml }} ./OpenSearch-Dashboards/config/opensearch_dashboards.yml shell: bash - - name: Add to opensearch_dashboards.yml + - name: Add to opensearch_dashboards.yml config run: | echo 'opensearch_security.configuration.admin_pages_enabled: false' >> ./OpenSearch-Dashboards/config/opensearch_dashboards.yml echo 'opensearch_security_admin.configuration.session_management_enabled: false' >> ./OpenSearch-Dashboards/config/opensearch_dashboards.yml From 1400f567256c2897690fa756fd396d1a8e8c359d Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Wed, 22 May 2024 22:19:54 -0400 Subject: [PATCH 32/43] Add debug step Signed-off-by: Craig Perkins --- .github/actions/setup-opensearch-dashboards/action.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/actions/setup-opensearch-dashboards/action.yml b/.github/actions/setup-opensearch-dashboards/action.yml index b3d64e992..56c99c1d4 100644 --- a/.github/actions/setup-opensearch-dashboards/action.yml +++ b/.github/actions/setup-opensearch-dashboards/action.yml @@ -236,6 +236,11 @@ runs: echo 'opensearch_security_admin.configuration.session_management_enabled: false' >> ./OpenSearch-Dashboards/config/opensearch_dashboards.yml shell: bash + - name: Print contents + run: | + cat ./OpenSearch-Dashboards/config/opensearch_dashboards.yml + shell: bash + - name: Create opensearch_dashboards.json for security admin dashboards plugin if: ${{ runner.os == 'Linux' }} shell: bash From 439f303ce34d054ec6ee36ca3ee5641f7f99c03a Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Wed, 22 May 2024 22:51:51 -0400 Subject: [PATCH 33/43] Move opensearch_dashboards.json logic Signed-off-by: Craig Perkins --- .../setup-opensearch-dashboards/action.yml | 56 +++++++++---------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/.github/actions/setup-opensearch-dashboards/action.yml b/.github/actions/setup-opensearch-dashboards/action.yml index 56c99c1d4..cfd4eb15b 100644 --- a/.github/actions/setup-opensearch-dashboards/action.yml +++ b/.github/actions/setup-opensearch-dashboards/action.yml @@ -111,6 +111,33 @@ runs: working-directory: OpenSearch-Dashboards shell: bash + - name: Create opensearch_dashboards.json for security admin dashboards plugin + if: ${{ runner.os == 'Linux' }} + shell: bash + run: | + rm ./OpenSearch-Dashboards/plugins/security-admin-dashboards-plugin/opensearch_dashboards.json + cat << 'EOT' > ./OpenSearch-Dashboards/plugins/security-admin-dashboards-plugin/opensearch_dashboards.json + { + "id": "securityAdminDashboards", + "version": "3.0.0.0", + "opensearchDashboardsVersion": "3.0.0", + "configPath": [ + "opensearch_security_admin" + ], + "requiredPlugins": [ + "navigation", + "savedObjectsManagement" + ], + "optionalPlugins": [ + "managementOverview", + "dataSource", + "dataSourceManagement" + ], + "server": true, + "ui": true + } + EOT + - name: Create package.json for security admin dashboards plugin if: ${{ runner.os == 'Linux' }} shell: bash @@ -239,31 +266,4 @@ runs: - name: Print contents run: | cat ./OpenSearch-Dashboards/config/opensearch_dashboards.yml - shell: bash - - - name: Create opensearch_dashboards.json for security admin dashboards plugin - if: ${{ runner.os == 'Linux' }} - shell: bash - run: | - rm ./OpenSearch-Dashboards/plugins/security-admin-dashboards-plugin/opensearch_dashboards.json - cat << 'EOT' > ./OpenSearch-Dashboards/plugins/security-admin-dashboards-plugin/opensearch_dashboards.json - { - "id": "securityAdminDashboards", - "version": "3.0.0.0", - "opensearchDashboardsVersion": "3.0.0", - "configPath": [ - "opensearch_security_admin" - ], - "requiredPlugins": [ - "navigation", - "savedObjectsManagement" - ], - "optionalPlugins": [ - "managementOverview", - "dataSource", - "dataSourceManagement" - ], - "server": true, - "ui": true - } - EOT \ No newline at end of file + shell: bash \ No newline at end of file From 9d4639e6d2ccff5f2945ff9e6aa2ef753fe211ed Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Thu, 23 May 2024 10:37:43 -0400 Subject: [PATCH 34/43] Update URL Signed-off-by: Craig Perkins --- .../multi_datasources_disabled.spec.js | 2 +- .../multi_datasources_enabled.spec.js | 20 +++++++++++-------- test/cypress/e2e/oidc/oidc_auth_test.spec.js | 2 +- test/cypress/e2e/saml/saml_auth_test.spec.js | 2 +- 4 files changed, 15 insertions(+), 11 deletions(-) diff --git a/test/cypress/e2e/multi-datasources/multi_datasources_disabled.spec.js b/test/cypress/e2e/multi-datasources/multi_datasources_disabled.spec.js index b26079fe7..694a4721f 100644 --- a/test/cypress/e2e/multi-datasources/multi_datasources_disabled.spec.js +++ b/test/cypress/e2e/multi-datasources/multi_datasources_disabled.spec.js @@ -27,7 +27,7 @@ describe('Multi-datasources disabled', () => { it('Checks Get Started Tab', () => { // Remote cluster purge cache - cy.visit(`http://localhost:5601/app/security-dashboards-plugin#/getstarted`); + cy.visit(`http://localhost:5601/app/security-admin-dashboards-plugin#/getstarted`); cy.contains('h1', 'Get started'); cy.get('[data-test-subj="dataSourceSelectableButton"]').should('not.exist'); diff --git a/test/cypress/e2e/multi-datasources/multi_datasources_enabled.spec.js b/test/cypress/e2e/multi-datasources/multi_datasources_enabled.spec.js index 2a1a0c4f7..841aef666 100644 --- a/test/cypress/e2e/multi-datasources/multi_datasources_enabled.spec.js +++ b/test/cypress/e2e/multi-datasources/multi_datasources_enabled.spec.js @@ -93,7 +93,7 @@ describe('Multi-datasources enabled', () => { it('Checks Get Started Tab', () => { // Remote cluster purge cache cy.visit( - `http://localhost:5601/app/security-dashboards-plugin${externalDataSourceUrl}#/getstarted` + `http://localhost:5601/app/security-admin-dashboards-plugin${externalDataSourceUrl}#/getstarted` ); cy.contains('h1', 'Get started'); @@ -106,7 +106,9 @@ describe('Multi-datasources enabled', () => { }); it('Checks Auth Tab', () => { - cy.visit(`http://localhost:5601/app/security-dashboards-plugin${externalDataSourceUrl}#/auth`); + cy.visit( + `http://localhost:5601/app/security-admin-dashboards-plugin${externalDataSourceUrl}#/auth` + ); cy.get('.panel-header-count').first().invoke('text').should('contain', '(1)'); }); @@ -125,7 +127,7 @@ describe('Multi-datasources enabled', () => { }, }).then(() => { cy.visit( - `http://localhost:5601/app/security-dashboards-plugin${externalDataSourceUrl}#/users` + `http://localhost:5601/app/security-admin-dashboards-plugin${externalDataSourceUrl}#/users` ); cy.get('[data-test-subj="tableHeaderCell_username_0"]').click(); @@ -145,7 +147,7 @@ describe('Multi-datasources enabled', () => { }, }).then(() => { cy.visit( - `http://localhost:5601/app/security-dashboards-plugin${externalDataSourceUrl}#/permissions` + `http://localhost:5601/app/security-admin-dashboards-plugin${externalDataSourceUrl}#/permissions` ); // Permission exists on the remote data source @@ -156,7 +158,9 @@ describe('Multi-datasources enabled', () => { it('Checks Tenancy Tab', () => { // Datasource is locked to local cluster for tenancy tab - cy.visit(`http://localhost:5601/app/security-dashboards-plugin${localDataSourceUrl}#/tenants`); + cy.visit( + `http://localhost:5601/app/security-admin-dashboards-plugin${localDataSourceUrl}#/tenants` + ); cy.contains('h1', 'Dashboards multi-tenancy'); cy.get('[data-test-subj="dataSourceViewButton"]').should('contain', 'Local cluster'); @@ -165,7 +169,7 @@ describe('Multi-datasources enabled', () => { it('Checks Service Accounts Tab', () => { // Datasource is locked to local cluster for service accounts tab cy.visit( - `http://localhost:5601/app/security-dashboards-plugin${localDataSourceUrl}#/serviceAccounts` + `http://localhost:5601/app/security-admin-dashboards-plugin${localDataSourceUrl}#/serviceAccounts` ); cy.get('[data-test-subj="dataSourceViewButton"]').should('contain', 'Local cluster'); @@ -207,7 +211,7 @@ describe('Multi-datasources enabled', () => { }, }).then(() => { cy.visit( - `http://localhost:5601/app/security-dashboards-plugin${externalDataSourceUrl}#/auditLogging` + `http://localhost:5601/app/security-admin-dashboards-plugin${externalDataSourceUrl}#/auditLogging` ); cy.get('[class="euiSwitch__label"]').should('contain', 'Disabled'); }); @@ -227,7 +231,7 @@ describe('Multi-datasources enabled', () => { }, }).then(() => { cy.visit( - `http://localhost:5601/app/security-dashboards-plugin${externalDataSourceUrl}#/roles` + `http://localhost:5601/app/security-admin-dashboards-plugin${externalDataSourceUrl}#/roles` ); cy.get('[data-test-subj="dataSourceSelectableButton"]').should('contain', '9202'); diff --git a/test/cypress/e2e/oidc/oidc_auth_test.spec.js b/test/cypress/e2e/oidc/oidc_auth_test.spec.js index 2228d2fbf..893c743e0 100644 --- a/test/cypress/e2e/oidc/oidc_auth_test.spec.js +++ b/test/cypress/e2e/oidc/oidc_auth_test.spec.js @@ -69,7 +69,7 @@ describe('Log in via OIDC', () => { }); it('Login to Dashboard with Hash', () => { - const urlWithHash = `http://localhost:5601${basePath}/app/security-dashboards-plugin#/getstarted`; + const urlWithHash = `http://localhost:5601${basePath}/app/security-admin-dashboards-plugin#/getstarted`; cy.visit(urlWithHash, { failOnStatusCode: false, diff --git a/test/cypress/e2e/saml/saml_auth_test.spec.js b/test/cypress/e2e/saml/saml_auth_test.spec.js index 34f58da2b..923771a2d 100644 --- a/test/cypress/e2e/saml/saml_auth_test.spec.js +++ b/test/cypress/e2e/saml/saml_auth_test.spec.js @@ -82,7 +82,7 @@ describe('Log in via SAML', () => { localStorage.setItem('opendistro::security::tenant::saved', '"__user__"'); localStorage.setItem('home:newThemeModal:show', 'false'); - const urlWithHash = `http://localhost:5601${basePath}/app/security-dashboards-plugin#/getstarted`; + const urlWithHash = `http://localhost:5601${basePath}/app/security-admin-dashboards-plugin#/getstarted`; cy.visit(urlWithHash, { failOnStatusCode: false, From e3cb5d4fd79445b07921b2b6fe73b9b95413f8ce Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Thu, 23 May 2024 11:56:31 -0400 Subject: [PATCH 35/43] Only run linux Signed-off-by: Craig Perkins --- .github/workflows/integration-test.yml | 2 +- .github/workflows/unit-test.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml index 2ff050149..f264f5c4f 100644 --- a/.github/workflows/integration-test.yml +++ b/.github/workflows/integration-test.yml @@ -14,7 +14,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ ubuntu-latest , windows-latest ] + os: [ ubuntu-latest ] runs-on: ${{ matrix.os }} steps: diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index f1ac8ec70..2a8537acb 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -8,7 +8,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ ubuntu-latest , windows-latest, macos-latest ] + os: [ ubuntu-latest ] runs-on: ${{ matrix.os }} steps: From 018cc7fcd6f8118e5889e6efd2786e72b50c57ff Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Thu, 23 May 2024 15:11:34 -0400 Subject: [PATCH 36/43] Test change Signed-off-by: Craig Perkins --- .../setup-opensearch-dashboards/action.yml | 4 ++-- common/index.ts | 4 ++-- .../multi_datasources_disabled.spec.js | 2 +- .../multi_datasources_enabled.spec.js | 20 ++++++++----------- test/cypress/e2e/oidc/oidc_auth_test.spec.js | 2 +- test/cypress/e2e/saml/saml_auth_test.spec.js | 2 +- 6 files changed, 15 insertions(+), 19 deletions(-) diff --git a/.github/actions/setup-opensearch-dashboards/action.yml b/.github/actions/setup-opensearch-dashboards/action.yml index cfd4eb15b..1eab4a9cb 100644 --- a/.github/actions/setup-opensearch-dashboards/action.yml +++ b/.github/actions/setup-opensearch-dashboards/action.yml @@ -115,8 +115,8 @@ runs: if: ${{ runner.os == 'Linux' }} shell: bash run: | - rm ./OpenSearch-Dashboards/plugins/security-admin-dashboards-plugin/opensearch_dashboards.json - cat << 'EOT' > ./OpenSearch-Dashboards/plugins/security-admin-dashboards-plugin/opensearch_dashboards.json + rm ./OpenSearch-Dashboards/plugins/security-dashboards-plugin/opensearch_dashboards.json + cat << 'EOT' > ./OpenSearch-Dashboards/plugins/security-dashboards-plugin/opensearch_dashboards.json { "id": "securityAdminDashboards", "version": "3.0.0.0", diff --git a/common/index.ts b/common/index.ts index 6336b645d..84445486d 100644 --- a/common/index.ts +++ b/common/index.ts @@ -13,8 +13,8 @@ * permissions and limitations under the License. */ -export const PLUGIN_ID = 'opensearchDashboardsSecurityAdmin'; -export const PLUGIN_NAME = 'security-admin-dashboards-plugin'; +export const PLUGIN_ID = 'opensearchDashboardsSecurity'; +export const PLUGIN_NAME = 'security-dashboards-plugin'; export const APP_ID_LOGIN = 'login'; export const APP_ID_CUSTOMERROR = 'customerror'; diff --git a/test/cypress/e2e/multi-datasources/multi_datasources_disabled.spec.js b/test/cypress/e2e/multi-datasources/multi_datasources_disabled.spec.js index 694a4721f..b26079fe7 100644 --- a/test/cypress/e2e/multi-datasources/multi_datasources_disabled.spec.js +++ b/test/cypress/e2e/multi-datasources/multi_datasources_disabled.spec.js @@ -27,7 +27,7 @@ describe('Multi-datasources disabled', () => { it('Checks Get Started Tab', () => { // Remote cluster purge cache - cy.visit(`http://localhost:5601/app/security-admin-dashboards-plugin#/getstarted`); + cy.visit(`http://localhost:5601/app/security-dashboards-plugin#/getstarted`); cy.contains('h1', 'Get started'); cy.get('[data-test-subj="dataSourceSelectableButton"]').should('not.exist'); diff --git a/test/cypress/e2e/multi-datasources/multi_datasources_enabled.spec.js b/test/cypress/e2e/multi-datasources/multi_datasources_enabled.spec.js index 841aef666..2a1a0c4f7 100644 --- a/test/cypress/e2e/multi-datasources/multi_datasources_enabled.spec.js +++ b/test/cypress/e2e/multi-datasources/multi_datasources_enabled.spec.js @@ -93,7 +93,7 @@ describe('Multi-datasources enabled', () => { it('Checks Get Started Tab', () => { // Remote cluster purge cache cy.visit( - `http://localhost:5601/app/security-admin-dashboards-plugin${externalDataSourceUrl}#/getstarted` + `http://localhost:5601/app/security-dashboards-plugin${externalDataSourceUrl}#/getstarted` ); cy.contains('h1', 'Get started'); @@ -106,9 +106,7 @@ describe('Multi-datasources enabled', () => { }); it('Checks Auth Tab', () => { - cy.visit( - `http://localhost:5601/app/security-admin-dashboards-plugin${externalDataSourceUrl}#/auth` - ); + cy.visit(`http://localhost:5601/app/security-dashboards-plugin${externalDataSourceUrl}#/auth`); cy.get('.panel-header-count').first().invoke('text').should('contain', '(1)'); }); @@ -127,7 +125,7 @@ describe('Multi-datasources enabled', () => { }, }).then(() => { cy.visit( - `http://localhost:5601/app/security-admin-dashboards-plugin${externalDataSourceUrl}#/users` + `http://localhost:5601/app/security-dashboards-plugin${externalDataSourceUrl}#/users` ); cy.get('[data-test-subj="tableHeaderCell_username_0"]').click(); @@ -147,7 +145,7 @@ describe('Multi-datasources enabled', () => { }, }).then(() => { cy.visit( - `http://localhost:5601/app/security-admin-dashboards-plugin${externalDataSourceUrl}#/permissions` + `http://localhost:5601/app/security-dashboards-plugin${externalDataSourceUrl}#/permissions` ); // Permission exists on the remote data source @@ -158,9 +156,7 @@ describe('Multi-datasources enabled', () => { it('Checks Tenancy Tab', () => { // Datasource is locked to local cluster for tenancy tab - cy.visit( - `http://localhost:5601/app/security-admin-dashboards-plugin${localDataSourceUrl}#/tenants` - ); + cy.visit(`http://localhost:5601/app/security-dashboards-plugin${localDataSourceUrl}#/tenants`); cy.contains('h1', 'Dashboards multi-tenancy'); cy.get('[data-test-subj="dataSourceViewButton"]').should('contain', 'Local cluster'); @@ -169,7 +165,7 @@ describe('Multi-datasources enabled', () => { it('Checks Service Accounts Tab', () => { // Datasource is locked to local cluster for service accounts tab cy.visit( - `http://localhost:5601/app/security-admin-dashboards-plugin${localDataSourceUrl}#/serviceAccounts` + `http://localhost:5601/app/security-dashboards-plugin${localDataSourceUrl}#/serviceAccounts` ); cy.get('[data-test-subj="dataSourceViewButton"]').should('contain', 'Local cluster'); @@ -211,7 +207,7 @@ describe('Multi-datasources enabled', () => { }, }).then(() => { cy.visit( - `http://localhost:5601/app/security-admin-dashboards-plugin${externalDataSourceUrl}#/auditLogging` + `http://localhost:5601/app/security-dashboards-plugin${externalDataSourceUrl}#/auditLogging` ); cy.get('[class="euiSwitch__label"]').should('contain', 'Disabled'); }); @@ -231,7 +227,7 @@ describe('Multi-datasources enabled', () => { }, }).then(() => { cy.visit( - `http://localhost:5601/app/security-admin-dashboards-plugin${externalDataSourceUrl}#/roles` + `http://localhost:5601/app/security-dashboards-plugin${externalDataSourceUrl}#/roles` ); cy.get('[data-test-subj="dataSourceSelectableButton"]').should('contain', '9202'); diff --git a/test/cypress/e2e/oidc/oidc_auth_test.spec.js b/test/cypress/e2e/oidc/oidc_auth_test.spec.js index 893c743e0..2228d2fbf 100644 --- a/test/cypress/e2e/oidc/oidc_auth_test.spec.js +++ b/test/cypress/e2e/oidc/oidc_auth_test.spec.js @@ -69,7 +69,7 @@ describe('Log in via OIDC', () => { }); it('Login to Dashboard with Hash', () => { - const urlWithHash = `http://localhost:5601${basePath}/app/security-admin-dashboards-plugin#/getstarted`; + const urlWithHash = `http://localhost:5601${basePath}/app/security-dashboards-plugin#/getstarted`; cy.visit(urlWithHash, { failOnStatusCode: false, diff --git a/test/cypress/e2e/saml/saml_auth_test.spec.js b/test/cypress/e2e/saml/saml_auth_test.spec.js index 923771a2d..34f58da2b 100644 --- a/test/cypress/e2e/saml/saml_auth_test.spec.js +++ b/test/cypress/e2e/saml/saml_auth_test.spec.js @@ -82,7 +82,7 @@ describe('Log in via SAML', () => { localStorage.setItem('opendistro::security::tenant::saved', '"__user__"'); localStorage.setItem('home:newThemeModal:show', 'false'); - const urlWithHash = `http://localhost:5601${basePath}/app/security-admin-dashboards-plugin#/getstarted`; + const urlWithHash = `http://localhost:5601${basePath}/app/security-dashboards-plugin#/getstarted`; cy.visit(urlWithHash, { failOnStatusCode: false, From 0868d19c4a502c313522902f3cf6b19503914974 Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Thu, 23 May 2024 15:32:45 -0400 Subject: [PATCH 37/43] Revert "Test change" This reverts commit 018cc7fcd6f8118e5889e6efd2786e72b50c57ff. --- .../setup-opensearch-dashboards/action.yml | 4 ++-- common/index.ts | 4 ++-- .../multi_datasources_disabled.spec.js | 2 +- .../multi_datasources_enabled.spec.js | 20 +++++++++++-------- test/cypress/e2e/oidc/oidc_auth_test.spec.js | 2 +- test/cypress/e2e/saml/saml_auth_test.spec.js | 2 +- 6 files changed, 19 insertions(+), 15 deletions(-) diff --git a/.github/actions/setup-opensearch-dashboards/action.yml b/.github/actions/setup-opensearch-dashboards/action.yml index 1eab4a9cb..cfd4eb15b 100644 --- a/.github/actions/setup-opensearch-dashboards/action.yml +++ b/.github/actions/setup-opensearch-dashboards/action.yml @@ -115,8 +115,8 @@ runs: if: ${{ runner.os == 'Linux' }} shell: bash run: | - rm ./OpenSearch-Dashboards/plugins/security-dashboards-plugin/opensearch_dashboards.json - cat << 'EOT' > ./OpenSearch-Dashboards/plugins/security-dashboards-plugin/opensearch_dashboards.json + rm ./OpenSearch-Dashboards/plugins/security-admin-dashboards-plugin/opensearch_dashboards.json + cat << 'EOT' > ./OpenSearch-Dashboards/plugins/security-admin-dashboards-plugin/opensearch_dashboards.json { "id": "securityAdminDashboards", "version": "3.0.0.0", diff --git a/common/index.ts b/common/index.ts index 84445486d..6336b645d 100644 --- a/common/index.ts +++ b/common/index.ts @@ -13,8 +13,8 @@ * permissions and limitations under the License. */ -export const PLUGIN_ID = 'opensearchDashboardsSecurity'; -export const PLUGIN_NAME = 'security-dashboards-plugin'; +export const PLUGIN_ID = 'opensearchDashboardsSecurityAdmin'; +export const PLUGIN_NAME = 'security-admin-dashboards-plugin'; export const APP_ID_LOGIN = 'login'; export const APP_ID_CUSTOMERROR = 'customerror'; diff --git a/test/cypress/e2e/multi-datasources/multi_datasources_disabled.spec.js b/test/cypress/e2e/multi-datasources/multi_datasources_disabled.spec.js index b26079fe7..694a4721f 100644 --- a/test/cypress/e2e/multi-datasources/multi_datasources_disabled.spec.js +++ b/test/cypress/e2e/multi-datasources/multi_datasources_disabled.spec.js @@ -27,7 +27,7 @@ describe('Multi-datasources disabled', () => { it('Checks Get Started Tab', () => { // Remote cluster purge cache - cy.visit(`http://localhost:5601/app/security-dashboards-plugin#/getstarted`); + cy.visit(`http://localhost:5601/app/security-admin-dashboards-plugin#/getstarted`); cy.contains('h1', 'Get started'); cy.get('[data-test-subj="dataSourceSelectableButton"]').should('not.exist'); diff --git a/test/cypress/e2e/multi-datasources/multi_datasources_enabled.spec.js b/test/cypress/e2e/multi-datasources/multi_datasources_enabled.spec.js index 2a1a0c4f7..841aef666 100644 --- a/test/cypress/e2e/multi-datasources/multi_datasources_enabled.spec.js +++ b/test/cypress/e2e/multi-datasources/multi_datasources_enabled.spec.js @@ -93,7 +93,7 @@ describe('Multi-datasources enabled', () => { it('Checks Get Started Tab', () => { // Remote cluster purge cache cy.visit( - `http://localhost:5601/app/security-dashboards-plugin${externalDataSourceUrl}#/getstarted` + `http://localhost:5601/app/security-admin-dashboards-plugin${externalDataSourceUrl}#/getstarted` ); cy.contains('h1', 'Get started'); @@ -106,7 +106,9 @@ describe('Multi-datasources enabled', () => { }); it('Checks Auth Tab', () => { - cy.visit(`http://localhost:5601/app/security-dashboards-plugin${externalDataSourceUrl}#/auth`); + cy.visit( + `http://localhost:5601/app/security-admin-dashboards-plugin${externalDataSourceUrl}#/auth` + ); cy.get('.panel-header-count').first().invoke('text').should('contain', '(1)'); }); @@ -125,7 +127,7 @@ describe('Multi-datasources enabled', () => { }, }).then(() => { cy.visit( - `http://localhost:5601/app/security-dashboards-plugin${externalDataSourceUrl}#/users` + `http://localhost:5601/app/security-admin-dashboards-plugin${externalDataSourceUrl}#/users` ); cy.get('[data-test-subj="tableHeaderCell_username_0"]').click(); @@ -145,7 +147,7 @@ describe('Multi-datasources enabled', () => { }, }).then(() => { cy.visit( - `http://localhost:5601/app/security-dashboards-plugin${externalDataSourceUrl}#/permissions` + `http://localhost:5601/app/security-admin-dashboards-plugin${externalDataSourceUrl}#/permissions` ); // Permission exists on the remote data source @@ -156,7 +158,9 @@ describe('Multi-datasources enabled', () => { it('Checks Tenancy Tab', () => { // Datasource is locked to local cluster for tenancy tab - cy.visit(`http://localhost:5601/app/security-dashboards-plugin${localDataSourceUrl}#/tenants`); + cy.visit( + `http://localhost:5601/app/security-admin-dashboards-plugin${localDataSourceUrl}#/tenants` + ); cy.contains('h1', 'Dashboards multi-tenancy'); cy.get('[data-test-subj="dataSourceViewButton"]').should('contain', 'Local cluster'); @@ -165,7 +169,7 @@ describe('Multi-datasources enabled', () => { it('Checks Service Accounts Tab', () => { // Datasource is locked to local cluster for service accounts tab cy.visit( - `http://localhost:5601/app/security-dashboards-plugin${localDataSourceUrl}#/serviceAccounts` + `http://localhost:5601/app/security-admin-dashboards-plugin${localDataSourceUrl}#/serviceAccounts` ); cy.get('[data-test-subj="dataSourceViewButton"]').should('contain', 'Local cluster'); @@ -207,7 +211,7 @@ describe('Multi-datasources enabled', () => { }, }).then(() => { cy.visit( - `http://localhost:5601/app/security-dashboards-plugin${externalDataSourceUrl}#/auditLogging` + `http://localhost:5601/app/security-admin-dashboards-plugin${externalDataSourceUrl}#/auditLogging` ); cy.get('[class="euiSwitch__label"]').should('contain', 'Disabled'); }); @@ -227,7 +231,7 @@ describe('Multi-datasources enabled', () => { }, }).then(() => { cy.visit( - `http://localhost:5601/app/security-dashboards-plugin${externalDataSourceUrl}#/roles` + `http://localhost:5601/app/security-admin-dashboards-plugin${externalDataSourceUrl}#/roles` ); cy.get('[data-test-subj="dataSourceSelectableButton"]').should('contain', '9202'); diff --git a/test/cypress/e2e/oidc/oidc_auth_test.spec.js b/test/cypress/e2e/oidc/oidc_auth_test.spec.js index 2228d2fbf..893c743e0 100644 --- a/test/cypress/e2e/oidc/oidc_auth_test.spec.js +++ b/test/cypress/e2e/oidc/oidc_auth_test.spec.js @@ -69,7 +69,7 @@ describe('Log in via OIDC', () => { }); it('Login to Dashboard with Hash', () => { - const urlWithHash = `http://localhost:5601${basePath}/app/security-dashboards-plugin#/getstarted`; + const urlWithHash = `http://localhost:5601${basePath}/app/security-admin-dashboards-plugin#/getstarted`; cy.visit(urlWithHash, { failOnStatusCode: false, diff --git a/test/cypress/e2e/saml/saml_auth_test.spec.js b/test/cypress/e2e/saml/saml_auth_test.spec.js index 34f58da2b..923771a2d 100644 --- a/test/cypress/e2e/saml/saml_auth_test.spec.js +++ b/test/cypress/e2e/saml/saml_auth_test.spec.js @@ -82,7 +82,7 @@ describe('Log in via SAML', () => { localStorage.setItem('opendistro::security::tenant::saved', '"__user__"'); localStorage.setItem('home:newThemeModal:show', 'false'); - const urlWithHash = `http://localhost:5601${basePath}/app/security-dashboards-plugin#/getstarted`; + const urlWithHash = `http://localhost:5601${basePath}/app/security-admin-dashboards-plugin#/getstarted`; cy.visit(urlWithHash, { failOnStatusCode: false, From 65a1c1c8b620e158715ea894c60fc6f358598f47 Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Thu, 23 May 2024 15:41:10 -0400 Subject: [PATCH 38/43] Update FTR branch Signed-off-by: Craig Perkins --- .github/workflows/cypress-test-tenancy-disabled.yml | 4 ++-- .github/workflows/cypress-test.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/cypress-test-tenancy-disabled.yml b/.github/workflows/cypress-test-tenancy-disabled.yml index 7badd731f..4c988815b 100644 --- a/.github/workflows/cypress-test-tenancy-disabled.yml +++ b/.github/workflows/cypress-test-tenancy-disabled.yml @@ -80,7 +80,7 @@ jobs: cd ./OpenSearch-Dashboards nohup yarn start --no-base-path --no-watch & sleep 500 - git clone https://github.com/opensearch-project/opensearch-dashboards-functional-test.git + git clone -b security-admin-dashboards-plugin https://github.com/cwperks/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/.github/workflows/cypress-test.yml b/.github/workflows/cypress-test.yml index 5de9d3ea3..f242c6229 100644 --- a/.github/workflows/cypress-test.yml +++ b/.github/workflows/cypress-test.yml @@ -82,7 +82,7 @@ jobs: cd ./OpenSearch-Dashboards nohup yarn start --no-base-path --no-watch & sleep 500 - git clone https://github.com/opensearch-project/opensearch-dashboards-functional-test.git + git clone -b security-admin-dashboards-plugin https://github.com/cwperks/opensearch-dashboards-functional-test.git cd opensearch-dashboards-functional-test npm install cypress --save-dev yarn cypress:run-with-security-and-aggregation-view --browser chrome --spec "cypress/integration/plugins/security-dashboards-plugin/aggregation_view.js" From 2767f69cc2232f6143522831ad14969a367b20ac Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Thu, 23 May 2024 20:36:38 -0400 Subject: [PATCH 39/43] Only run on push Signed-off-by: Craig Perkins --- .../action.yml | 170 ++++++++++++++++++ .../workflows/cypress-test-multiauth-e2e.yml | 2 +- ...ess-test-multidatasources-disabled-e2e.yml | 2 +- ...ress-test-multidatasources-enabled-e2e.yml | 2 +- .github/workflows/cypress-test-oidc-e2e.yml | 2 +- .github/workflows/cypress-test-saml-e2e.yml | 2 +- .../cypress-test-tenancy-disabled.yml | 2 +- .github/workflows/cypress-test.yml | 2 +- .github/workflows/integration-test.yml | 7 +- .github/workflows/unit-test.yml | 2 +- .../workflows/verify-binary-installation.yml | 2 +- 11 files changed, 185 insertions(+), 10 deletions(-) create mode 100644 .github/actions/setup-opensearch-dashboards-for-integ-tests/action.yml diff --git a/.github/actions/setup-opensearch-dashboards-for-integ-tests/action.yml b/.github/actions/setup-opensearch-dashboards-for-integ-tests/action.yml new file mode 100644 index 000000000..ac06ef036 --- /dev/null +++ b/.github/actions/setup-opensearch-dashboards-for-integ-tests/action.yml @@ -0,0 +1,170 @@ +name: 'Starts Dashboards with Plugin' +description: 'Installs OpenSearch Dashboard with a plugin from github, then checks out the correct dashboards version for the plugin, configures npm/yarn, bootstraps Dashboards, and starts the instance' + +inputs: + plugin_name: + description: 'The name of the plugin to use, such as security-dashboards-plugin' + required: true + + built_plugin_name: + description: 'The name of the plugin after building, if doing zip install' + required: false + + built_plugin_suffix: + description: 'The suffix of the plugin after building, if doing zip instll. Leave empty to indicate default OSD build behavior' + required: false + + install_zip: + description: 'Whether the setup should be done using the install plugin flow. Leave empty to indicate dev setup. Only applicable for linux for now' + required: false + + plugin_repo: + description: 'The repo of the plugin to use. Leave empty to indicate the repo which is running this action' + required: false + + opensearch_dashboards_yml: + description: 'The file to replace opensearch_dashboards.yml. Leave empty to use the default' + required: false + + plugin_branch: + description: 'The branch of the repo of the plugin to use. Leave empty to indicate the branch which is running this action' + required: false + +outputs: + dashboards-directory: + description: "The directory where the dashboards has been configured" + value: ${{ steps.determine-dashboards-directory.outputs.dashboards-directory }} + + dashboards-binary-directory: + description: "The directory of the dashboards binary that was configured" + value: ${{ steps.determine-dashboards-directory-zip-install.outputs.dashboards-directory }} + + plugin-directory: + description: "The directory where the plugin has been configured" + value: ${{ steps.determine-plugin-directory.outputs.plugin-directory }} + + +runs: + using: "composite" + steps: + - id: determine-dashboards-directory + if: ${{ inputs.install_zip != 'true' }} + run: echo "dashboards-directory=OpenSearch-Dashboards" >> $GITHUB_OUTPUT + shell: bash + + - id: determine-plugin-directory + run: echo "::set-output name=plugin-directory::./OpenSearch-Dashboards/plugins/${{ inputs.plugin_name }}" + shell: bash + + - uses: actions/checkout@v2 + with: + path: OpenSearch-Dashboards + repository: cwperks/OpenSearch-Dashboards + ref: 'support-repeated-plugin' + fetch-depth: 0 + filter: | + cypress + test + + - uses: actions/checkout@v2 + with: + path: ${{ steps.determine-plugin-directory.outputs.plugin-directory }} + + - id: osd-version + run: | + echo "::set-output name=osd-version::$(jq -r '.opensearchDashboards.version | split(".") | .[:2] | join(".")' package.json)" + echo "::set-output name=osd-x-version::$(jq -r '.opensearchDashboards.version | split(".") | .[0]' package.json).x" + echo "::set-output name=plugin-version::$(jq -r '.opensearchDashboardsVersion' opensearch_dashboards.json)" + working-directory: ${{ steps.determine-plugin-directory.outputs.plugin-directory }} + shell: bash + + - id: branch-switch-if-possible + continue-on-error: true # Defaults onto main if the branch switch doesn't work + if: ${{ steps.osd-version.outputs.osd-version }} + run: git checkout ${{ steps.osd-version.outputs.osd-version }} || git checkout ${{ steps.osd-version.outputs.osd-x-version }} + working-directory: ./OpenSearch-Dashboards + shell: bash + + - id: tool-versions + run: | + echo "node_version=$(cat .node-version)" >> $GITHUB_OUTPUT + echo "yarn_version=$(jq -r '.engines.yarn' package.json)" >> $GITHUB_OUTPUT + working-directory: OpenSearch-Dashboards + shell: bash + + - uses: actions/setup-node@v1 + with: + node-version: ${{ steps.tool-versions.outputs.node_version }} + registry-url: 'https://registry.npmjs.org' + + - name: Setup Opensearch Dashboards + run: | + npm uninstall -g yarn + echo "Installing yarn ${{ steps.tool-versions.outputs.yarn_version }}" + npm i -g yarn@${{ steps.tool-versions.outputs.yarn_version }} + yarn cache clean + yarn add sha.js + working-directory: OpenSearch-Dashboards + shell: bash + + - name: Bootstrap the OpenSearch Dashboard + uses: nick-fields/retry@v2 + with: + timeout_minutes: 20 + max_attempts: 2 + command: yarn --cwd OpenSearch-Dashboards osd bootstrap --oss --single-version=loose # loose is passed in to ignore version conflicts on cypress version used in OSD and this repo + + - name: Download OpenSearch for Linux + uses: peternied/download-file@v2 + if: ${{ inputs.install_zip == 'true' && runner.os == 'Linux' }} + with: + url: https://ci.opensearch.org/ci/dbc/distribution-build-opensearch-dashboards/${{ steps.osd-version.outputs.plugin-version }}/latest/linux/x64/tar/builds/opensearch-dashboards/dist/opensearch-dashboards-min-${{ steps.osd-version.outputs.plugin-version }}-linux-x64.tar.gz + + # Extract downloaded zip + - name: Extract downloaded tar + if: ${{ inputs.install_zip == 'true' && runner.os == 'Linux' }} + run: | + tar -xzf opensearch-*.tar.gz + rm -f opensearch-*.tar.gz + shell: bash + + - id: determine-dashboards-directory-zip-install + if: ${{ inputs.install_zip == 'true' }} + run: echo "dashboards-directory=opensearch-dashboards-${{ steps.osd-version.outputs.plugin-version }}-linux-x64" >> $GITHUB_OUTPUT + shell: bash + + - name: Build the plugin + if: ${{ inputs.install_zip == 'true' }} + run: | + yarn build + working-directory: ${{ steps.determine-plugin-directory.outputs.plugin-directory }} + shell: bash + + - name: Install the plugin zip + if: ${{ inputs.install_zip == 'true' && inputs.built_plugin_suffix == '' }} + run: | + ${{ steps.determine-dashboards-directory-zip-install.outputs.dashboards-directory }}/bin/opensearch-dashboards-plugin install file:$(pwd)/OpenSearch-Dashboards/plugins/${{ inputs.plugin_name }}/build/${{ inputs.built_plugin_name }}-${{env.PLUGIN_VERSION}}.zip + shell: bash + + - name: Install the plugin zip + if: ${{ inputs.install_zip == 'true' && inputs.built_plugin_suffix != '' }} + run: | + ${{ steps.determine-dashboards-directory-zip-install.outputs.dashboards-directory }}/bin/opensearch-dashboards-plugin install file:$(pwd)/OpenSearch-Dashboards/plugins/${{ inputs.plugin_name }}/build/${{ inputs.built_plugin_name }}-${{inputs.built_plugin_suffix}}.zip + shell: bash + + - name: Replace opensearch_dashboards.yml file if applicable binary + if: ${{ inputs.opensearch_dashboards_yml != '' && inputs.install_zip == 'true' }} + run: | + mv ${{ inputs.opensearch_dashboards_yml }} ${{ steps.determine-dashboards-directory-zip-install.outputs.dashboards-directory }}/config/opensearch_dashboards.yml + shell: bash + + - name: Replace opensearch_dashboards.yml file if applicable dev + if: ${{ inputs.opensearch_dashboards_yml != '' && inputs.install_zip != 'true' }} + run: | + mv ${{ inputs.opensearch_dashboards_yml }} ./OpenSearch-Dashboards/config/opensearch_dashboards.yml + shell: bash + + - name: Print contents + run: | + cat ./OpenSearch-Dashboards/config/opensearch_dashboards.yml + shell: bash \ No newline at end of file diff --git a/.github/workflows/cypress-test-multiauth-e2e.yml b/.github/workflows/cypress-test-multiauth-e2e.yml index bb7985f9b..9f06f5e88 100644 --- a/.github/workflows/cypress-test-multiauth-e2e.yml +++ b/.github/workflows/cypress-test-multiauth-e2e.yml @@ -1,6 +1,6 @@ name: Snapshot based E2E SAML multi-auth tests workflow -on: [ push, pull_request ] +on: [ pull_request ] env: OPENSEARCH_VERSION: '3.0.0' diff --git a/.github/workflows/cypress-test-multidatasources-disabled-e2e.yml b/.github/workflows/cypress-test-multidatasources-disabled-e2e.yml index e41de5fab..4e0fd5af3 100644 --- a/.github/workflows/cypress-test-multidatasources-disabled-e2e.yml +++ b/.github/workflows/cypress-test-multidatasources-disabled-e2e.yml @@ -1,6 +1,6 @@ name: E2E multi datasources disabled workflow -on: [ push, pull_request ] +on: [ pull_request ] env: OPENSEARCH_VERSION: '3.0.0' diff --git a/.github/workflows/cypress-test-multidatasources-enabled-e2e.yml b/.github/workflows/cypress-test-multidatasources-enabled-e2e.yml index 162941cf1..8cda1c92f 100644 --- a/.github/workflows/cypress-test-multidatasources-enabled-e2e.yml +++ b/.github/workflows/cypress-test-multidatasources-enabled-e2e.yml @@ -1,6 +1,6 @@ name: E2E multi datasources enabled workflow -on: [ push, pull_request ] +on: [ pull_request ] env: OPENSEARCH_VERSION: '3.0.0' diff --git a/.github/workflows/cypress-test-oidc-e2e.yml b/.github/workflows/cypress-test-oidc-e2e.yml index 11e51d390..b99725d2d 100644 --- a/.github/workflows/cypress-test-oidc-e2e.yml +++ b/.github/workflows/cypress-test-oidc-e2e.yml @@ -1,6 +1,6 @@ name: Snapshot based E2E OIDC tests workflow -on: [ push, pull_request ] +on: [ pull_request ] env: OPENSEARCH_VERSION: '3.0.0' diff --git a/.github/workflows/cypress-test-saml-e2e.yml b/.github/workflows/cypress-test-saml-e2e.yml index 3025c2cde..c3a93f39a 100644 --- a/.github/workflows/cypress-test-saml-e2e.yml +++ b/.github/workflows/cypress-test-saml-e2e.yml @@ -1,6 +1,6 @@ name: Snapshot based E2E SAML tests workflow -on: [ push, pull_request ] +on: [ pull_request ] env: OPENSEARCH_VERSION: '3.0.0' diff --git a/.github/workflows/cypress-test-tenancy-disabled.yml b/.github/workflows/cypress-test-tenancy-disabled.yml index 4c988815b..ec2f72a68 100644 --- a/.github/workflows/cypress-test-tenancy-disabled.yml +++ b/.github/workflows/cypress-test-tenancy-disabled.yml @@ -1,6 +1,6 @@ name: Cypress Tests Multitenancy Disabled -on: [ push, pull_request ] +on: [ pull_request ] env: TEST_BROWSER_HEADLESS: 1 diff --git a/.github/workflows/cypress-test.yml b/.github/workflows/cypress-test.yml index f242c6229..4aabf5445 100644 --- a/.github/workflows/cypress-test.yml +++ b/.github/workflows/cypress-test.yml @@ -1,6 +1,6 @@ name: Cypress Tests -on: [ push, pull_request ] +on: [ pull_request ] env: TEST_BROWSER_HEADLESS: 1 diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml index f264f5c4f..9c914bcc5 100644 --- a/.github/workflows/integration-test.yml +++ b/.github/workflows/integration-test.yml @@ -1,6 +1,6 @@ name: Integration Tests -on: [push, pull_request] +on: [pull_request] env: TEST_BROWSER_HEADLESS: 1 @@ -85,6 +85,11 @@ jobs: with: plugin_name: security-dashboards-plugin + - id: install-dashboards + uses: ./.github/actions/setup-opensearch-dashboards-for-integ-tests + with: + plugin_name: security-dashboards-plugin + - name: Start Dashboards in background run: node scripts/build_opensearch_dashboards_platform_plugins.js working-directory: ${{ steps.install-dashboards.outputs.dashboards-directory }} diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index 2a8537acb..0bd72a33c 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -1,6 +1,6 @@ name: Unit Tests -on: [ push, pull_request ] +on: [ pull_request ] jobs: unit-tests: diff --git a/.github/workflows/verify-binary-installation.yml b/.github/workflows/verify-binary-installation.yml index 779bc148d..721732ff4 100644 --- a/.github/workflows/verify-binary-installation.yml +++ b/.github/workflows/verify-binary-installation.yml @@ -1,6 +1,6 @@ name: 'Install Dashboards with Plugin via Binary' -on: [push, pull_request] +on: [pull_request] env: OPENSEARCH_VERSION: '3.0.0' CI: 1 From c4a9a84eaf40359ce7a0bc3591038a17be61d33f Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Thu, 23 May 2024 22:41:03 -0400 Subject: [PATCH 40/43] Remove duplication Signed-off-by: Craig Perkins --- .github/workflows/integration-test.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml index 9c914bcc5..fced99d34 100644 --- a/.github/workflows/integration-test.yml +++ b/.github/workflows/integration-test.yml @@ -80,11 +80,6 @@ jobs: sudo rm -rf /usr/share/dotnet sudo rm -rf "$AGENT_TOOLSDIRECTORY" - - id: install-dashboards - uses: ./.github/actions/setup-opensearch-dashboards - with: - plugin_name: security-dashboards-plugin - - id: install-dashboards uses: ./.github/actions/setup-opensearch-dashboards-for-integ-tests with: From cd69d859e2887519fa62baa12941a21aec95973a Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Thu, 23 May 2024 22:59:02 -0400 Subject: [PATCH 41/43] Minor change Signed-off-by: Craig Perkins --- .github/actions/setup-opensearch-dashboards/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/setup-opensearch-dashboards/action.yml b/.github/actions/setup-opensearch-dashboards/action.yml index cfd4eb15b..fa691a079 100644 --- a/.github/actions/setup-opensearch-dashboards/action.yml +++ b/.github/actions/setup-opensearch-dashboards/action.yml @@ -257,7 +257,7 @@ runs: mv ${{ inputs.opensearch_dashboards_yml }} ./OpenSearch-Dashboards/config/opensearch_dashboards.yml shell: bash - - name: Add to opensearch_dashboards.yml config + - name: Add to opensearch_dashboards.yml config file run: | echo 'opensearch_security.configuration.admin_pages_enabled: false' >> ./OpenSearch-Dashboards/config/opensearch_dashboards.yml echo 'opensearch_security_admin.configuration.session_management_enabled: false' >> ./OpenSearch-Dashboards/config/opensearch_dashboards.yml From e491ea77f7157189c619e74aa7681a1f0cae1b68 Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Fri, 24 May 2024 09:27:54 -0400 Subject: [PATCH 42/43] Remove agg view test Signed-off-by: Craig Perkins --- .github/workflows/cypress-test.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/cypress-test.yml b/.github/workflows/cypress-test.yml index 4aabf5445..90ee1b766 100644 --- a/.github/workflows/cypress-test.yml +++ b/.github/workflows/cypress-test.yml @@ -68,7 +68,6 @@ jobs: opensearch_security.multitenancy.tenants.preferred: ["Private", "Global"] opensearch_security.readonly_mode.roles: ["kibana_read_only"] opensearch_security.cookie.secure: false - opensearch_security.multitenancy.enable_aggregation_view: true EOT - name: Run Dashboard with Security Dashboards Plugin @@ -85,6 +84,5 @@ jobs: git clone -b security-admin-dashboards-plugin https://github.com/cwperks/opensearch-dashboards-functional-test.git cd opensearch-dashboards-functional-test npm install cypress --save-dev - yarn cypress:run-with-security-and-aggregation-view --browser chrome --spec "cypress/integration/plugins/security-dashboards-plugin/aggregation_view.js" yarn cypress:run-with-security --browser chrome --spec "cypress/integration/plugins/security-dashboards-plugin/multi_tenancy.js" yarn cypress:run-with-security --browser chrome --spec "cypress/integration/plugins/security-dashboards-plugin/default_tenant.js" From 8655becb5aa6d4f2a05349d724d2d7801eb0bafe Mon Sep 17 00:00:00 2001 From: Craig Perkins Date: Fri, 24 May 2024 09:44:16 -0400 Subject: [PATCH 43/43] Use correct prefix Signed-off-by: Craig Perkins --- .github/workflows/cypress-test-multidatasources-enabled-e2e.yml | 1 + .github/workflows/cypress-test.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/cypress-test-multidatasources-enabled-e2e.yml b/.github/workflows/cypress-test-multidatasources-enabled-e2e.yml index 8cda1c92f..2ebb27bc5 100644 --- a/.github/workflows/cypress-test-multidatasources-enabled-e2e.yml +++ b/.github/workflows/cypress-test-multidatasources-enabled-e2e.yml @@ -94,6 +94,7 @@ jobs: opensearch.password: "kibanaserver" opensearch.requestHeadersWhitelist: [ authorization,securitytenant ] opensearch_security.multitenancy.enabled: true + opensearch_security_admin.multitenancy.enabled: true opensearch_security.multitenancy.tenants.preferred: ["Private", "Global"] opensearch_security.readonly_mode.roles: ["kibana_read_only"] opensearch_security.cookie.secure: false diff --git a/.github/workflows/cypress-test.yml b/.github/workflows/cypress-test.yml index 90ee1b766..63610e9da 100644 --- a/.github/workflows/cypress-test.yml +++ b/.github/workflows/cypress-test.yml @@ -68,6 +68,7 @@ jobs: opensearch_security.multitenancy.tenants.preferred: ["Private", "Global"] opensearch_security.readonly_mode.roles: ["kibana_read_only"] opensearch_security.cookie.secure: false + opensearch_security_admin.multitenancy.enabled: true EOT - name: Run Dashboard with Security Dashboards Plugin