From 293490dce6f0d769d8f36b0d5fd93b99fe176d51 Mon Sep 17 00:00:00 2001 From: yuboluo <15242088755@163.com> Date: Mon, 24 Jun 2024 22:06:27 +0800 Subject: [PATCH 1/9] Fix the bug of capabilities request not supporting carrying authinfo (#2014) * capabilities api support authinfo Signed-off-by: yubonluo * optimize the annotation Signed-off-by: yubonluo * optimize the code Signed-off-by: yubonluo --------- Signed-off-by: yubonluo --- server/auth/types/authentication_type.test.ts | 54 +++++++++++++++++++ server/auth/types/authentication_type.ts | 19 +++++-- 2 files changed, 69 insertions(+), 4 deletions(-) diff --git a/server/auth/types/authentication_type.test.ts b/server/auth/types/authentication_type.test.ts index fdf0397cf..0274251dc 100644 --- a/server/auth/types/authentication_type.test.ts +++ b/server/auth/types/authentication_type.test.ts @@ -111,3 +111,57 @@ describe('test tenant header', () => { expect(result.requestHeaders.securitytenant).toEqual('dummy_tenant'); }); }); + +describe('test capabilities request authinfo', () => { + const config = { + auth: { + unauthenticated_routes: [] as string[], + }, + session: { + keepalive: false, + }, + } as SecurityPluginConfigType; + const sessionStorageFactory = { + asScoped: jest.fn(() => { + return { + clear: jest.fn(), + get: jest.fn().mockResolvedValue({}), + }; + }), + }; + const router = jest.fn(); + const esClient = { + asScoped: jest.fn().mockImplementation(() => { + return { + callAsCurrentUser: jest.fn().mockImplementation(() => { + return { username: 'capabilities-username' }; + }), + }; + }), + }; + const coreSetup = jest.fn(); + const logger = { + error: jest.fn(), + }; + + const dummyAuthType = new DummyAuthType( + config, + sessionStorageFactory, + router, + esClient, + coreSetup, + logger + ); + + it(`Capabilities API includes authinfo`, async () => { + const request = httpServerMock.createOpenSearchDashboardsRequest({ + path: '/api/core/capabilities', + }); + const response = jest.fn(); + const toolkit = { + authenticated: jest.fn((value) => value), + }; + const result = await dummyAuthType.authHandler(request, response, toolkit); + expect(result.state.authInfo.username).toEqual('capabilities-username'); + }); +}); diff --git a/server/auth/types/authentication_type.ts b/server/auth/types/authentication_type.ts index 6dd3db59d..783bdd142 100755 --- a/server/auth/types/authentication_type.ts +++ b/server/auth/types/authentication_type.ts @@ -73,10 +73,8 @@ export interface OpenSearchDashboardsAuthState { } export abstract class AuthenticationType implements IAuthenticationType { - protected static readonly ROUTES_TO_IGNORE: string[] = [ - '/api/core/capabilities', // FIXME: need to figure out how to bypass this API call - '/app/login', - ]; + protected static readonly ROUTES_TO_IGNORE: string[] = ['/app/login']; + protected static readonly ROUTES_AUTH_OPTIONAL: string[] = ['/api/core/capabilities']; protected static readonly REST_API_CALL_HEADER = 'osd-xsrf'; @@ -153,6 +151,11 @@ export abstract class AuthenticationType implements IAuthenticationType { return toolkit.notHandled(); } + // allow optional authentication + if (this.authOptional(request)) { + return toolkit.authenticated(); + } + // send to auth workflow return this.handleUnauthedRequest(request, response, toolkit); } @@ -236,6 +239,14 @@ export abstract class AuthenticationType implements IAuthenticationType { return false; } + authOptional(request: OpenSearchDashboardsRequest): boolean { + const pathname = request.url.pathname; + if (!pathname) { + return false; + } + return AuthenticationType.ROUTES_AUTH_OPTIONAL.includes(pathname!); + } + async resolveTenant( request: OpenSearchDashboardsRequest, cookie: SecuritySessionCookie, From e35f7f767b69e88dbedab50b609ef84361572ae8 Mon Sep 17 00:00:00 2001 From: Derek Ho Date: Fri, 12 Jul 2024 16:32:41 -0400 Subject: [PATCH 2/9] Add custom build script to support different cypress version (#2027) Signed-off-by: Derek Ho --- scripts/build.sh | 107 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 scripts/build.sh diff --git a/scripts/build.sh b/scripts/build.sh new file mode 100644 index 000000000..06dab67b8 --- /dev/null +++ b/scripts/build.sh @@ -0,0 +1,107 @@ +#!/bin/bash + +# Copyright OpenSearch Contributors +# SPDX-License-Identifier: Apache-2.0 +# +# The OpenSearch Contributors require contributions made to +# this file be licensed under the Apache-2.0 license or a +# compatible open source license. + +set -ex + +# vars / libs +SCRIPT_DIR=`dirname $(realpath $0)` +. $SCRIPT_DIR/../../../lib/shell/file_management.sh +PLUGIN_NAME=$(basename "$PWD") +PLUGIN_PATH=`realpath ../OpenSearch-Dashboards/plugins/$PLUGIN_NAME` + +function usage() { + echo "Usage: $0 [args]" + echo "" + echo "Arguments:" + echo -e "-v VERSION\t[Required] OpenSearch version." + echo -e "-q QUALIFIER\t[Optional] Version qualifier." + echo -e "-s SNAPSHOT\t[Optional] Build a snapshot, default is 'false'." + echo -e "-p PLATFORM\t[Optional] Platform, ignored." + echo -e "-a ARCHITECTURE\t[Optional] Build architecture, ignored." + echo -e "-o OUTPUT\t[Optional] Output path, default is 'artifacts'." + echo -e "-h help" +} + + +function cleanup_all() { + File_Delete $PLUGIN_PATH +} + +trap cleanup_all TERM INT EXIT + +while getopts ":h:v:q:s:o:p:a:" arg; do + case $arg in + h) + usage + exit 1 + ;; + v) + VERSION=$OPTARG + ;; + q) + QUALIFIER=$OPTARG + ;; + s) + SNAPSHOT=$OPTARG + ;; + o) + OUTPUT=$OPTARG + ;; + p) + PLATFORM=$OPTARG + ;; + a) + ARCHITECTURE=$OPTARG + ;; + :) + echo "Error: -${OPTARG} requires an argument" + usage + exit 1 + ;; + ?) + echo "Invalid option: -${arg}" + exit 1 + ;; + esac +done + +if [ -z "$VERSION" ]; then + echo "Error: You must specify the OpenSearch Dashboards version" + usage + exit 1 +fi + +[ -z "$OUTPUT" ] && OUTPUT=artifacts +[ ! -z "$QUALIFIER" ] && QUALIFIER_IDENTIFIER="-$QUALIFIER" + +NVM_CMD="source $NVM_DIR/nvm.sh && nvm use" +if [ "$PLATFORM" = "windows" ]; then + NVM_CMD="volta install node@`cat ../OpenSearch-Dashboards/.nvmrc` && volta install yarn@`jq -r '.engines.yarn' ../OpenSearch-Dashboards/package.json`" +fi + +HELPER_CMD=$(jq -r '.scripts.plugin_helpers' package.json) +if [ "$HELPER_CMD" != null ]; then + HELPER_STRING=plugin_helpers +else + HELPER_STRING=plugin-helpers +fi + +mkdir -p $OUTPUT/plugins +# TODO: [CLEANUP] Needed OpenSearch Dashboards git repo to build the required modules for plugins +# This makes it so there is a dependency on having Dashboards pulled already. +cp -r ../$PLUGIN_NAME/ ../OpenSearch-Dashboards/plugins +echo "BUILD MODULES FOR $PLUGIN_NAME" +CURR_DIR=`pwd` +# Bootstrap loose to allow different cypress version +cd ../OpenSearch-Dashboards; eval $NVM_CMD; yarn osd bootstrap --single-version=loose +echo "BUILD RELEASE ZIP FOR $PLUGIN_NAME" +cd plugins/$PLUGIN_NAME; yarn $HELPER_STRING build --opensearch-dashboards-version=$VERSION$QUALIFIER_IDENTIFIER +cd $CURR_DIR +echo "COPY $PLUGIN_NAME.zip" +cp -r ../OpenSearch-Dashboards/plugins/$PLUGIN_NAME/build/$PLUGIN_NAME-$VERSION$QUALIFIER_IDENTIFIER.zip $OUTPUT/plugins/ \ No newline at end of file From b0d36ffbef00bccf51c2efaf697ede92657d8534 Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Mon, 15 Jul 2024 18:12:08 -0400 Subject: [PATCH 3/9] Remove dependency on opensearch build repo libs from custom build.sh (#2033) Signed-off-by: Peter Zhu --- scripts/build.sh | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/scripts/build.sh b/scripts/build.sh index 06dab67b8..55c8ef657 100644 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -10,8 +10,6 @@ set -ex # vars / libs -SCRIPT_DIR=`dirname $(realpath $0)` -. $SCRIPT_DIR/../../../lib/shell/file_management.sh PLUGIN_NAME=$(basename "$PWD") PLUGIN_PATH=`realpath ../OpenSearch-Dashboards/plugins/$PLUGIN_NAME` @@ -30,7 +28,7 @@ function usage() { function cleanup_all() { - File_Delete $PLUGIN_PATH + rm -rf -- $PLUGIN_PATH } trap cleanup_all TERM INT EXIT @@ -104,4 +102,4 @@ echo "BUILD RELEASE ZIP FOR $PLUGIN_NAME" cd plugins/$PLUGIN_NAME; yarn $HELPER_STRING build --opensearch-dashboards-version=$VERSION$QUALIFIER_IDENTIFIER cd $CURR_DIR echo "COPY $PLUGIN_NAME.zip" -cp -r ../OpenSearch-Dashboards/plugins/$PLUGIN_NAME/build/$PLUGIN_NAME-$VERSION$QUALIFIER_IDENTIFIER.zip $OUTPUT/plugins/ \ No newline at end of file +cp -r ../OpenSearch-Dashboards/plugins/$PLUGIN_NAME/build/$PLUGIN_NAME-$VERSION$QUALIFIER_IDENTIFIER.zip $OUTPUT/plugins/ From 757f4cec8cb8d903ab0ef1f0a150a3220f24c9ef Mon Sep 17 00:00:00 2001 From: "opensearch-trigger-bot[bot]" <98922864+opensearch-trigger-bot[bot]@users.noreply.github.com> Date: Tue, 16 Jul 2024 12:10:11 -0400 Subject: [PATCH 4/9] Increment version to 3.0.0.0 (#2035) Signed-off-by: opensearch-ci-bot Co-authored-by: opensearch-ci-bot --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 438121b4f..5aad7a155 100644 --- a/package.json +++ b/package.json @@ -53,4 +53,4 @@ "ejs": "^3.1.10", "express": "^4.19.2" } -} +} \ No newline at end of file From 2e53b3c481506bef14f4823ea55224fd4bfa8f55 Mon Sep 17 00:00:00 2001 From: Derek Ho Date: Wed, 17 Jul 2024 14:44:42 -0400 Subject: [PATCH 5/9] Run release time FTR tests on PR runs (#1941) * Run release time FTR tests on PR runs Signed-off-by: Derek Ho * Explicitly list out all spec files Signed-off-by: Derek Ho * Move tests out Signed-off-by: Derek Ho * Add no command log env variable Signed-off-by: Derek Ho --------- Signed-off-by: Derek Ho --- .github/workflows/cypress-test.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.github/workflows/cypress-test.yml b/.github/workflows/cypress-test.yml index 5698fa03a..e63532f9c 100644 --- a/.github/workflows/cypress-test.yml +++ b/.github/workflows/cypress-test.yml @@ -11,6 +11,7 @@ env: SPEC: 'cypress/integration/plugins/security-dashboards-plugin/aggregation_view.js,' PLUGIN_NAME: opensearch-security OPENSEARCH_INITIAL_ADMIN_PASSWORD: myStrongPassword123! + CYPRESS_NO_COMMAND_LOG: 1 jobs: cypress-tests: @@ -84,3 +85,15 @@ jobs: 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" + + - name: Configure and Run OpenSearch Dashboards with Cypress Test Cases Release Tests + run: | + cd ./OpenSearch-Dashboards + cd opensearch-dashboards-functional-test + yarn cypress:run-with-security --browser chrome --spec "cypress/integration/plugins/security/audit_log_spec.js" + yarn cypress:run-with-security --browser chrome --spec "cypress/integration/plugins/security/auth_spec.js" + yarn cypress:run-with-security --browser chrome --spec "cypress/integration/plugins/security/get_started_spec.js" + yarn cypress:run-with-security --browser chrome --spec "cypress/integration/plugins/security/internalusers_spec.js" + yarn cypress:run-with-security --browser chrome --spec "cypress/integration/plugins/security/permissions_spec.js" + yarn cypress:run-with-security --browser chrome --spec "cypress/integration/plugins/security/roles_spec.js" + yarn cypress:run-with-security --browser chrome --spec "cypress/integration/plugins/security/tenants_spec.js" From 0592b68d8cc2d6fb83ab47209e08f6c6b7e46e5a Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Wed, 17 Jul 2024 15:56:31 -0400 Subject: [PATCH 6/9] Addresses CVE-2024-4068 and updates yarn.lock (#2039) * Updates yarn.lock Signed-off-by: Darshit Chanpura * Forces braces to 3.0.3 Signed-off-by: Darshit Chanpura * Runs yarn Signed-off-by: Darshit Chanpura --------- Signed-off-by: Darshit Chanpura --- package.json | 3 +- yarn.lock | 102 +++++++++++---------------------------------------- 2 files changed, 23 insertions(+), 82 deletions(-) diff --git a/package.json b/package.json index 5aad7a155..1f2349a40 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,7 @@ "glob-parent": "^5.1.2", "debug": "^4.3.4", "ejs": "^3.1.10", - "express": "^4.19.2" + "express": "^4.19.2", + "braces": "^3.0.3" } } \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 20a357290..dc945c7bb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -542,11 +542,6 @@ arr-diff@^4.0.0: resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" integrity sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA== -arr-flatten@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" - integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== - arr-union@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" @@ -811,28 +806,12 @@ brace-expansion@^2.0.1: dependencies: balanced-match "^1.0.0" -braces@^2.3.1, braces@^2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" - integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== - dependencies: - arr-flatten "^1.1.0" - array-unique "^0.3.2" - extend-shallow "^2.0.1" - fill-range "^4.0.0" - isobject "^3.0.1" - repeat-element "^1.1.2" - snapdragon "^0.8.1" - snapdragon-node "^2.0.1" - split-string "^3.0.2" - to-regex "^3.0.1" - -braces@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" - integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== +braces@^2.3.1, braces@^2.3.2, braces@^3.0.3, braces@~3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== dependencies: - fill-range "^7.0.1" + fill-range "^7.1.1" brorand@^1.0.1, brorand@^1.1.0: version "1.1.0" @@ -1980,20 +1959,10 @@ filelist@^1.0.4: dependencies: minimatch "^5.0.1" -fill-range@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" - integrity sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ== - dependencies: - extend-shallow "^2.0.1" - is-number "^3.0.0" - repeat-string "^1.6.1" - to-regex-range "^2.1.0" - -fill-range@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" - integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== dependencies: to-regex-range "^5.0.1" @@ -2257,6 +2226,11 @@ glob-parent@^3.1.0, glob-parent@^5.1.2, glob-parent@~5.1.2: dependencies: is-glob "^4.0.1" +glob-to-regexp@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" + integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== + glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.2.3: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" @@ -2913,7 +2887,7 @@ jszip@^3.10.1: readable-stream "~2.3.6" setimmediate "^1.0.5" -kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: +kind-of@^3.0.2, kind-of@^3.0.3: version "3.2.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" integrity sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ== @@ -3923,16 +3897,6 @@ remove-trailing-separator@^1.0.1: resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" integrity sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw== -repeat-element@^1.1.2: - version "1.1.4" - resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9" - integrity sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ== - -repeat-string@^1.6.1: - version "1.6.1" - resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" - integrity sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w== - request-progress@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/request-progress/-/request-progress-3.0.0.tgz#4ca754081c7fec63f505e4faa825aa06cd669dbe" @@ -4289,22 +4253,6 @@ slice-ansi@^4.0.0: astral-regex "^2.0.0" is-fullwidth-code-point "^3.0.0" -snapdragon-node@^2.0.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" - integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== - dependencies: - define-property "^1.0.0" - isobject "^3.0.0" - snapdragon-util "^3.0.1" - -snapdragon-util@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" - integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== - dependencies: - kind-of "^3.2.0" - snapdragon@^0.8.1: version "0.8.2" resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" @@ -4358,7 +4306,7 @@ source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -split-string@^3.0.1, split-string@^3.0.2: +split-string@^3.0.1: version "3.1.0" resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== @@ -4609,14 +4557,6 @@ to-object-path@^0.3.0: dependencies: kind-of "^3.0.2" -to-regex-range@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" - integrity sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg== - dependencies: - is-number "^3.0.0" - repeat-string "^1.6.1" - to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" @@ -4956,10 +4896,10 @@ webpack-sources@^1.4.0, webpack-sources@^1.4.1: source-list-map "^2.0.0" source-map "~0.6.1" -"webpack@npm:@amoo-miki/webpack@4.46.0-rc.2": - version "4.46.0-rc.2" - resolved "https://registry.yarnpkg.com/@amoo-miki/webpack/-/webpack-4.46.0-rc.2.tgz#36824597c14557a7bb0a8e13203e30275e7b02bd" - integrity sha512-Y/ZqxTHOoDF1kz3SR63Y9SZGTDUpZNNFrisTRHofWhP8QvNX3LMN+TCmEP56UfLaiLVKMcaiFjx8kFb2TgyBaQ== +"webpack@npm:@amoo-miki/webpack@4.46.0-xxhash.1": + version "4.46.0-xxhash.1" + resolved "https://registry.yarnpkg.com/@amoo-miki/webpack/-/webpack-4.46.0-xxhash.1.tgz#e8fdb0399d36715e558ad35e3ff70fd1d4ea47a7" + integrity sha512-gPwQMqfrE9326g+rZoU4BPOfcYvEcAR0XxfpV9AGUcRwl2oHoqEwq7nxSWchilpGV1i9XI7mCa8u8k4ePz6ziA== dependencies: "@node-rs/xxhash" "^1.3.0" "@webassemblyjs/ast" "1.9.0" @@ -4972,11 +4912,11 @@ webpack-sources@^1.4.0, webpack-sources@^1.4.1: chrome-trace-event "^1.0.2" enhanced-resolve "^4.5.0" eslint-scope "^4.0.3" + glob-to-regexp "^0.4.1" json-parse-better-errors "^1.0.2" loader-runner "^2.4.0" loader-utils "^2.0.4" memory-fs "^0.4.1" - micromatch "^3.1.10" mkdirp "^0.5.3" neo-async "^2.6.1" node-libs-browser "^2.2.1" From c523578812f85e6d08b9db85a1490b048e134b3f Mon Sep 17 00:00:00 2001 From: Derek Ho Date: Thu, 18 Jul 2024 13:23:15 -0400 Subject: [PATCH 7/9] Remove service accounts from Main (#2041) --- DEVELOPER_GUIDE.md | 8 +- common/index.ts | 1 - public/apps/configuration/app-router.tsx | 13 - public/apps/configuration/constants.tsx | 2 - .../role-mapping/role-edit-mapped-user.tsx | 4 +- .../panels/service-account-list.tsx | 248 ------------------ .../service-account-list.test.tsx.snap | 37 --- .../panels/test/service-account-list.test.tsx | 216 --------------- .../apps/configuration/panels/user-list.tsx | 8 +- .../__snapshots__/app-router.test.tsx.snap | 90 ------- .../utils/internal-user-list-utils.tsx | 28 +- .../test/internal-user-list-utils.test.tsx | 40 +-- ...pensearch_security_configuration_plugin.ts | 11 - server/routes/index.ts | 27 +- .../multi_datasources_enabled.spec.js | 9 - 15 files changed, 28 insertions(+), 714 deletions(-) delete mode 100644 public/apps/configuration/panels/service-account-list.tsx delete mode 100644 public/apps/configuration/panels/test/__snapshots__/service-account-list.test.tsx.snap delete mode 100644 public/apps/configuration/panels/test/service-account-list.test.tsx diff --git a/DEVELOPER_GUIDE.md b/DEVELOPER_GUIDE.md index 972f96c2d..c2388af7d 100644 --- a/DEVELOPER_GUIDE.md +++ b/DEVELOPER_GUIDE.md @@ -92,11 +92,13 @@ Next, go to the base directory (`cd ../..`) and run `yarn osd bootstrap` to inst From the base directory, run `yarn start`. This should start dashboard UI successfully. `Cmd+click` the url in the console output (It should look something like `http://0:5601/omf`). Once the page loads, you should be able to log in with user `admin` and password `admin`. -## Integration Tests +## Testing -To run selenium based integration tests, download and export the firefox web-driver to your PATH. Also, run `node scripts/build_opensearch_dashboards_platform_plugins.js` or `yarn start` before running the tests. This is essential to generate the bundles. +The security-dashboards-plugin project uses Jest for unit and integration tests and Cypress for end to end tests. To run frontend unit tests run `yarn test:jest_ui`. To run Cypress tests that live in this repo either use `yarn cypress:run` or `yarn cypress:open`. To run the Cypress tests that live in the [OpenSearch Dashboards Functional Test]( https://github.com/opensearch-project/opensearch-dashboards-functional-test) project first make sure you have OpenSearch and OpenSearch Dashboards running with the Security Plugin and that you can log in to it using a web browser. Then clone [OpenSearch Dashboards Functional Test]( https://github.com/opensearch-project/opensearch-dashboards-functional-test) in your local machine and follow the instructions in its DEVELOPER_GUIDE.md -The integration tests take advantage of [npm "pre" scripts](https://docs.npmjs.com/cli/v9/using-npm/scripts) to run a node based SAML IdP for integration tests related to SAML authentication. This will run a background process that listens on port 7000. +### Integration Tests + +The integration tests take advantage of [npm "pre" scripts](https://docs.npmjs.com/cli/v9/using-npm/scripts) to run a node based SAML IdP for integration tests related to SAML authentication. This will run a background process that listens on port 7000. Then run `yarn test:jest_server`. ## Submitting Changes diff --git a/common/index.ts b/common/index.ts index 84445486d..23ad3e1b6 100644 --- a/common/index.ts +++ b/common/index.ts @@ -70,7 +70,6 @@ export enum AuthType { export enum ResourceType { roles = 'roles', users = 'users', - serviceAccounts = 'serviceAccounts', permissions = 'permissions', tenants = 'tenants', tenantsManageTab = 'tenantsManageTab', diff --git a/public/apps/configuration/app-router.tsx b/public/apps/configuration/app-router.tsx index bb317fda6..9164bbc29 100644 --- a/public/apps/configuration/app-router.tsx +++ b/public/apps/configuration/app-router.tsx @@ -36,7 +36,6 @@ import { RoleEditMappedUser } from './panels/role-mapping/role-edit-mapped-user' import { RoleView } from './panels/role-view/role-view'; import { TenantList } from './panels/tenant-list/tenant-list'; import { UserList } from './panels/user-list'; -import { ServiceAccountList } from './panels/service-account-list'; import { Action, RouteItem, SubAction } from './types'; import { ResourceType } from '../../../common'; import { buildHashUrl, buildUrl } from './utils/url-builder'; @@ -58,10 +57,6 @@ const ROUTE_MAP: { [key: string]: RouteItem } = { name: 'Internal users', href: buildUrl(ResourceType.users), }, - [ResourceType.serviceAccounts]: { - name: 'Service Accounts', - href: buildUrl(ResourceType.serviceAccounts), - }, [ResourceType.permissions]: { name: 'Permissions', href: buildUrl(ResourceType.permissions), @@ -90,7 +85,6 @@ const getRouteList = (multitenancyEnabled: boolean) => { ROUTE_MAP[ResourceType.auth], ROUTE_MAP[ResourceType.roles], ROUTE_MAP[ResourceType.users], - ROUTE_MAP[ResourceType.serviceAccounts], ROUTE_MAP[ResourceType.permissions], ...(multitenancyEnabled ? [ROUTE_MAP[ResourceType.tenants]] : []), ROUTE_MAP[ResourceType.auditLogging], @@ -234,13 +228,6 @@ export function AppRouter(props: AppDependencies) { return ; }} /> - { - setGlobalBreadcrumbs(ResourceType.serviceAccounts); - return ; - }} - /> { diff --git a/public/apps/configuration/constants.tsx b/public/apps/configuration/constants.tsx index 3f73ba5b3..a1a79cb06 100644 --- a/public/apps/configuration/constants.tsx +++ b/public/apps/configuration/constants.tsx @@ -25,8 +25,6 @@ export const API_ENDPOINT_MULTITENANCY = API_PREFIX + '/multitenancy/tenant'; export const API_ENDPOINT_TENANCY_CONFIGS = API_ENDPOINT + '/tenancy/config'; export const API_ENDPOINT_SECURITYCONFIG = API_ENDPOINT + '/securityconfig'; export const API_ENDPOINT_INTERNALUSERS = API_ENDPOINT + '/internalusers'; -export const API_ENDPOINT_INTERNALACCOUNTS = API_ENDPOINT + '/internalaccounts'; -export const API_ENDPOINT_SERVICEACCOUNTS = API_ENDPOINT + '/serviceaccounts'; export const API_ENDPOINT_AUDITLOGGING = API_ENDPOINT + '/audit'; export const API_ENDPOINT_AUDITLOGGING_UPDATE = API_ENDPOINT_AUDITLOGGING + '/config'; export const API_ENDPOINT_PERMISSIONS_INFO = API_PREFIX + '/restapiinfo'; diff --git a/public/apps/configuration/panels/role-mapping/role-edit-mapped-user.tsx b/public/apps/configuration/panels/role-mapping/role-edit-mapped-user.tsx index 65e384275..d00faf3cb 100644 --- a/public/apps/configuration/panels/role-mapping/role-edit-mapped-user.tsx +++ b/public/apps/configuration/panels/role-mapping/role-edit-mapped-user.tsx @@ -91,9 +91,7 @@ export function RoleEditMappedUser(props: RoleEditMappedUserProps) { React.useEffect(() => { const fetchInternalUserNames = async () => { try { - setUserNames( - await fetchUserNameList(props.coreStart.http, ResourceType.users, dataSource.id) - ); + setUserNames(await fetchUserNameList(props.coreStart.http, dataSource.id)); } catch (e) { addToast(createUnknownErrorToast('fetchInternalUserNames', 'load data')); console.error(e); diff --git a/public/apps/configuration/panels/service-account-list.tsx b/public/apps/configuration/panels/service-account-list.tsx deleted file mode 100644 index e689e9f26..000000000 --- a/public/apps/configuration/panels/service-account-list.tsx +++ /dev/null @@ -1,248 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -import { - EuiBadge, - EuiButtonEmpty, - EuiFlexGroup, - EuiFlexItem, - EuiInMemoryTable, - EuiLink, - EuiLoadingContent, - EuiPageBody, - EuiPageContent, - EuiPageContentHeader, - EuiPageContentHeaderSection, - EuiPageHeader, - EuiText, - EuiTitle, - Query, -} from '@elastic/eui'; -import { Dictionary, isEmpty, map } from 'lodash'; -import React, { useState } from 'react'; -import { getAuthInfo } from '../../../utils/auth-info-utils'; -import { AppDependencies } from '../../types'; -import { API_ENDPOINT_SERVICEACCOUNTS, DocLinks } from '../constants'; -import { Action } from '../types'; -import { LOCAL_CLUSTER_ID, ResourceType } from '../../../../common'; -import { EMPTY_FIELD_VALUE } from '../ui-constants'; -import { useContextMenuState } from '../utils/context-menu'; -import { ExternalLink, tableItemsUIProps, truncatedListView } from '../utils/display-utils'; -import { getUserList, InternalUsersListing } from '../utils/internal-user-list-utils'; -import { showTableStatusMessage } from '../utils/loading-spinner-utils'; -import { buildHashUrl } from '../utils/url-builder'; -import { LocalCluster } from '../app-router'; -import { SecurityPluginTopNavMenu } from '../top-nav-menu'; -import { AccessErrorComponent } from '../access-error-component'; - -export function dictView(items: Dictionary) { - if (isEmpty(items)) { - return EMPTY_FIELD_VALUE; - } - return ( - - {map(items, (v, k) => ( - - {k}: {`"${v}"`} - - ))} - - ); -} - -export function getColumns(currentUsername: string) { - return [ - { - field: 'username', - name: 'Username', - render: (username: string) => ( - <> - {username} - {username === currentUsername && ( - <> -   - Current - - )} - - ), - sortable: true, - }, - { - field: 'backend_roles', - name: 'Backend roles', - render: truncatedListView(tableItemsUIProps), - }, - { - field: 'attributes', - name: 'Attributes', - render: dictView, - truncateText: true, - }, - ]; -} - -export function ServiceAccountList(props: AppDependencies) { - const [userData, setUserData] = React.useState([]); - const [selection, setSelection] = React.useState([]); - const [errorFlag, setErrorFlag] = React.useState(false); - const [accessErrorFlag, setAccessErrorFlag] = React.useState(false); - const [currentUsername, setCurrentUsername] = useState(''); - const [loading, setLoading] = useState(false); - const [query, setQuery] = useState(null); - - React.useEffect(() => { - const fetchData = async () => { - try { - setLoading(true); - const userDataPromise = getUserList( - props.coreStart.http, - ResourceType.serviceAccounts, - LOCAL_CLUSTER_ID - ); - setCurrentUsername((await getAuthInfo(props.coreStart.http)).user_name); - setUserData(await userDataPromise); - setErrorFlag(false); - setAccessErrorFlag(false); - } catch (e) { - console.log(e); - // requests with existing credentials but insufficient permissions result in 403, remote data-source requests with non-existing credentials result in 400 - if (e.response && [400, 403].includes(e.response.status)) { - setAccessErrorFlag(true); - } - setErrorFlag(true); - } finally { - setLoading(false); - } - }; - - fetchData(); - }, [props.coreStart.http]); - - const actionsMenuItems = [ - { - window.location.href = buildHashUrl(ResourceType.users, Action.edit, selection[0].username); - }} - disabled={selection.length !== 1} - > - Edit - , - { - window.location.href = buildHashUrl( - ResourceType.users, - Action.duplicate, - selection[0].username - ); - }} - disabled={selection.length !== 1} - > - Duplicate - , - - Export JSON - , - ]; - - const [actionsMenu, closeActionsMenu] = useContextMenuState('Actions', {}, actionsMenuItems); - - return ( - <> - {}} - selectedDataSource={LocalCluster} - /> - - -

Service accounts

-
-
- {loading ? ( - - ) : accessErrorFlag ? ( - - ) : ( - - - - -

- Service accounts - - {' '} - ({Query.execute(query || '', userData).length}) - -

-
- - Here you have a list of special accounts that represent services like extensions, - plugins or other third party applications. You can map an account to a role from - Roles - “Manage mapping” - - -
- - - {actionsMenu} - - -
- - { - setQuery(arg.query); - return true; - }, - }} - // @ts-ignore - selection={{ onSelectionChange: setSelection }} - sorting - error={ - errorFlag ? 'Load data failed, please check the console log for more details.' : '' - } - message={showTableStatusMessage(loading, userData)} - /> - -
- )} - - ); -} diff --git a/public/apps/configuration/panels/test/__snapshots__/service-account-list.test.tsx.snap b/public/apps/configuration/panels/test/__snapshots__/service-account-list.test.tsx.snap deleted file mode 100644 index 0617b7a8b..000000000 --- a/public/apps/configuration/panels/test/__snapshots__/service-account-list.test.tsx.snap +++ /dev/null @@ -1,37 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Service Account list AccessError component should load access error component 1`] = ` - - - - -

- Service accounts -

-
-
- -
-`; diff --git a/public/apps/configuration/panels/test/service-account-list.test.tsx b/public/apps/configuration/panels/test/service-account-list.test.tsx deleted file mode 100644 index 6e33a46be..000000000 --- a/public/apps/configuration/panels/test/service-account-list.test.tsx +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -import { EuiBadge, EuiText, EuiInMemoryTable } from '@elastic/eui'; -import { shallow } from 'enzyme'; -import React from 'react'; -import { EMPTY_FIELD_VALUE } from '../../ui-constants'; -import { getUserList, InternalUsersListing } from '../../utils/internal-user-list-utils'; -import { dictView, getColumns, ServiceAccountList } from '../service-account-list'; - -jest.mock('../../utils/internal-user-list-utils', () => ({ - getUserList: jest.fn(), -})); -jest.mock('../../../../utils/auth-info-utils', () => ({ - getAuthInfo: jest.fn().mockReturnValue({ user_name: 'user' }), -})); -jest.mock('../../utils/context-menu', () => ({ - useContextMenuState: jest - .fn() - .mockImplementation((buttonText, buttonProps, children) => [children, jest.fn()]), -})); - -import { getAuthInfo } from '../../../../utils/auth-info-utils'; -import { buildHashUrl } from '../../utils/url-builder'; -import { Action } from '../../types'; -import { ResourceType } from '../../../../../common'; - -describe('Service Account list', () => { - describe('dictView', () => { - it('- empty', () => { - const result = dictView({}); - - expect(result).toEqual(EMPTY_FIELD_VALUE); - }); - - it('dictView - non-empty', () => { - const attr1 = 'attr1'; - const attr2 = 'attr2'; - const value1 = 'value1'; - const value2 = 'value2'; - const result = shallow(dictView({ [attr1]: value1, [attr2]: value2 })); - - expect(result.find(EuiText).at(0).prop('children')).toEqual([attr1, ': ', `"${value1}"`]); - expect(result.find(EuiText).at(1).prop('children')).toEqual([attr2, ': ', `"${value2}"`]); - }); - }); - - describe('getColumns', () => { - it('current user', () => { - const columns = getColumns('user1'); - const usernameRenderer = columns[0].render as (usename: string) => JSX.Element; - const Container = (props: { username: string }) => usernameRenderer(props.username); - const result = shallow(); - - expect(result.find(EuiBadge).length).toBe(1); - }); - - it('not current user', () => { - const columns = getColumns('user1'); - const usernameRenderer = columns[0].render as (usename: string) => JSX.Element; - const Container = (props: { username: string }) => usernameRenderer(props.username); - const result = shallow(); - - expect(result.find(EuiBadge).length).toBe(0); - }); - }); - - describe('ServiceAccountList', () => { - const mockCoreStart = { - http: 1, - }; - const setState = jest.fn(); - jest.spyOn(React, 'useState').mockImplementation((initValue) => [initValue, setState]); - - it('render empty', () => { - const component = shallow( - - ); - - expect(component.find(EuiInMemoryTable).prop('items')).toEqual([]); - }); - - it('fetch data', () => { - jest.spyOn(React, 'useEffect').mockImplementationOnce((f) => f()); - shallow( - - ); - - expect(getUserList).toBeCalled(); - expect(getAuthInfo).toBeCalled(); - }); - - it('fetch data error', () => { - jest.spyOn(React, 'useEffect').mockImplementationOnce((f) => f()); - getUserList.mockImplementationOnce(() => { - throw new Error(); - }); - // Hide the error message - jest.spyOn(console, 'log').mockImplementationOnce(() => {}); - shallow( - - ); - - // Expect error flag set to true - expect(setState).toBeCalledWith(true); - }); - }); - - describe('Action menu Component', () => { - const mockCoreStart = { - http: { - basePath: { - serverBasePath: '', - }, - }, - }; - let component; - const mockSAListData: InternalUsersListing = { - username: 'user_1', - attributes: { service: 'true' }, - backend_roles: ['backend_role1'], - }; - beforeEach(() => { - jest.spyOn(React, 'useState').mockRestore(); - jest - .spyOn(React, 'useState') - .mockImplementationOnce(() => [[mockSAListData], jest.fn()]) - .mockImplementationOnce(() => [[mockSAListData], jest.fn()]) - .mockImplementationOnce(() => [false, jest.fn()]) - .mockImplementationOnce(() => ['', jest.fn()]) - .mockImplementationOnce(() => [false, jest.fn()]) - .mockImplementationOnce(() => [null, jest.fn()]); - component = shallow( - - ); - }); - - it('Edit click', () => { - component.find('[data-test-subj="edit"]').simulate('click'); - expect(window.location.hash).toBe( - buildHashUrl(ResourceType.users, Action.edit, mockSAListData.username) - ); - }); - - it('Duplicate click', () => { - component.find('[data-test-subj="duplicate"]').simulate('click'); - expect(window.location.hash).toBe( - buildHashUrl(ResourceType.users, Action.duplicate, mockSAListData.username) - ); - }); - }); - - describe('AccessError component', () => { - let component; - const mockCoreStart = { - http: 1, - }; - beforeEach(() => { - getUserList.mockRejectedValue({ response: { status: 403 } }); - jest.spyOn(React, 'useState').mockRestore(); - jest - .spyOn(React, 'useState') - .mockImplementationOnce(() => [[], jest.fn()]) - .mockImplementationOnce(() => [[], jest.fn()]) - .mockImplementationOnce(() => [false, jest.fn()]) - .mockImplementationOnce(() => [true, jest.fn()]) - .mockImplementationOnce(() => ['', jest.fn()]) - .mockImplementationOnce(() => [false, jest.fn()]) - .mockImplementationOnce(() => [null, jest.fn()]); - }); - - it('should load access error component', () => { - component = shallow( - - ); - expect(component).toMatchSnapshot(); - }); - }); -}); diff --git a/public/apps/configuration/panels/user-list.tsx b/public/apps/configuration/panels/user-list.tsx index 6b605af3a..f9388fb53 100644 --- a/public/apps/configuration/panels/user-list.tsx +++ b/public/apps/configuration/panels/user-list.tsx @@ -114,11 +114,7 @@ export function UserList(props: AppDependencies) { const fetchData = async () => { try { setLoading(true); - const userDataPromise = getUserList( - props.coreStart.http, - ResourceType.users, - dataSource.id - ); + const userDataPromise = getUserList(props.coreStart.http, dataSource.id); setCurrentUsername((await getAuthInfo(props.coreStart.http)).user_name); setUserData(await userDataPromise); setErrorFlag(false); @@ -241,7 +237,7 @@ export function UserList(props: AppDependencies) { The Security plugin includes an internal user database. Use this database in place of, or in addition to, an external authentication system such as LDAP server or - Active Directory. You can map an user account to a role from{' '} + Active Directory. You can map an internal user to a role from{' '} Roles . First, click into the detail page of the role. Then, under “Mapped users”, click “Manage mapping” diff --git a/public/apps/configuration/test/__snapshots__/app-router.test.tsx.snap b/public/apps/configuration/test/__snapshots__/app-router.test.tsx.snap index 0c19e737f..21aa4e515 100644 --- a/public/apps/configuration/test/__snapshots__/app-router.test.tsx.snap +++ b/public/apps/configuration/test/__snapshots__/app-router.test.tsx.snap @@ -39,10 +39,6 @@ exports[`SecurityPluginTopNavMenu renders DataSourceMenu when dataSource is enab "href": "/users", "name": "Internal users", }, - Object { - "href": "/serviceAccounts", - "name": "Service Accounts", - }, Object { "href": "/permissions", "name": "Permissions", @@ -85,10 +81,6 @@ exports[`SecurityPluginTopNavMenu renders DataSourceMenu when dataSource is enab "href": "/users", "name": "Internal users", }, - Object { - "href": "/serviceAccounts", - "name": "Service Accounts", - }, Object { "href": "/permissions", "name": "Permissions", @@ -131,10 +123,6 @@ exports[`SecurityPluginTopNavMenu renders DataSourceMenu when dataSource is enab "href": "/users", "name": "Internal users", }, - Object { - "href": "/serviceAccounts", - "name": "Service Accounts", - }, Object { "href": "/permissions", "name": "Permissions", @@ -177,56 +165,6 @@ exports[`SecurityPluginTopNavMenu renders DataSourceMenu when dataSource is enab "href": "/users", "name": "Internal users", }, - Object { - "href": "/serviceAccounts", - "name": "Service Accounts", - }, - Object { - "href": "/permissions", - "name": "Permissions", - }, - Object { - "href": "/tenants", - "name": "Tenants", - }, - Object { - "href": "/auditLogging", - "name": "Audit logs", - }, - ] - } - /> - -
- - - - > { - let ENDPOINT = API_ENDPOINT_INTERNALACCOUNTS; - if (userType === ResourceType.serviceAccounts) { - ENDPOINT = API_ENDPOINT_SERVICEACCOUNTS; - } - return await createRequestContextWithDataSourceId(dataSourceId).httpGet< ObjectsMessage - >({ http, url: ENDPOINT }); + >({ http, url: API_ENDPOINT_INTERNALUSERS }); } export async function getUserList( http: HttpStart, - userType: string, dataSourceId: string ): Promise { - const rawData = await getUserListRaw(http, userType, dataSourceId); + const rawData = await getUserListRaw(http, dataSourceId); return transformUserData(rawData.data); } -export async function fetchUserNameList( - http: HttpStart, - userType: string, - dataSourceId: string -): Promise { - return Object.keys((await getUserListRaw(http, userType, dataSourceId)).data); +export async function fetchUserNameList(http: HttpStart, dataSourceId: string): Promise { + return Object.keys((await getUserListRaw(http, dataSourceId)).data); } diff --git a/public/apps/configuration/utils/test/internal-user-list-utils.test.tsx b/public/apps/configuration/utils/test/internal-user-list-utils.test.tsx index 7a0a819a4..fca702691 100644 --- a/public/apps/configuration/utils/test/internal-user-list-utils.test.tsx +++ b/public/apps/configuration/utils/test/internal-user-list-utils.test.tsx @@ -42,54 +42,26 @@ describe('Internal user list utils', () => { expect(result).toEqual(expectedUserList); }); - it('getUserList calls httpGet with the correct parameters for internal users', async () => { - const httpMock = {}; // Mock HttpStart object - const userType = 'internalaccounts'; - - const test = await getUserList(httpMock, userType, 'test'); - - expect(mockedHttpGet).toHaveBeenCalledWith({ - http: httpMock, - url: '/api/v1/configuration/internalaccounts', - }); - expect(test).toEqual([]); - }); - - it('getUserList calls httpGet with the correct parameters for service accounts', async () => { - const httpMock = {}; - const userType = 'serviceAccounts'; - - const test = await getUserList(httpMock, userType, 'test'); - - expect(mockedHttpGet).toHaveBeenCalledWith({ - http: httpMock, - url: '/api/v1/configuration/serviceaccounts', - }); - expect(test).toEqual([]); - }); - - it('fetchUserNameList calls httpGet with the correct parameters for service accounts', async () => { + it('getUserList calls httpGet with the correct parameters', async () => { const httpMock = {}; - const userType = 'serviceAccounts'; - const test = await fetchUserNameList(httpMock, userType, ''); + const test = await getUserList(httpMock, 'test'); expect(mockedHttpGet).toHaveBeenCalledWith({ http: httpMock, - url: '/api/v1/configuration/serviceaccounts', + url: '/api/v1/configuration/internalusers', }); expect(test).toEqual([]); }); - it('fetchUserNameList calls httpGet with the correct parameters for internal users', async () => { + it('fetchUserNameList calls httpGet with the correct parameters', async () => { const httpMock = {}; - const userType = 'internalaccounts'; - const test = await fetchUserNameList(httpMock, userType, ''); + const test = await fetchUserNameList(httpMock, ''); expect(mockedHttpGet).toHaveBeenCalledWith({ http: httpMock, - url: '/api/v1/configuration/internalaccounts', + url: '/api/v1/configuration/internalusers', }); expect(test).toEqual([]); }); diff --git a/server/backend/opensearch_security_configuration_plugin.ts b/server/backend/opensearch_security_configuration_plugin.ts index 3b87863b8..ff0ccfe21 100644 --- a/server/backend/opensearch_security_configuration_plugin.ts +++ b/server/backend/opensearch_security_configuration_plugin.ts @@ -59,17 +59,6 @@ export default function (Client: any, config: any, components: any) { }, }); - Client.prototype.opensearch_security.prototype.listInternalAccounts = ca({ - url: { - fmt: '/_plugins/_security/api/internalusers?filterBy=internal', - }, - }); - Client.prototype.opensearch_security.prototype.listServiceAccounts = ca({ - url: { - fmt: '/_plugins/_security/api/internalusers?filterBy=service', - }, - }); - /** * Creates a Security resource instance. * diff --git a/server/routes/index.ts b/server/routes/index.ts index 6faa939f4..270d05f71 100644 --- a/server/routes/index.ts +++ b/server/routes/index.ts @@ -23,7 +23,6 @@ import { OpenSearchDashboardsRequest, } from 'opensearch-dashboards/server'; import { API_PREFIX, CONFIGURATION_API_PREFIX, isValidResourceName } from '../../common'; -import { ResourceType } from '../../common'; // TODO: consider to extract entity CRUD operations and put it into a client class export function defineRoutes(router: IRouter, dataSourceEnabled: boolean) { @@ -254,24 +253,14 @@ export function defineRoutes(router: IRouter, dataSourceEnabled: boolean) { 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 } - ); - } + esResp = await wrapRouteWithDataSource( + dataSourceEnabled, + context, + request, + 'opensearch_security.listResource', + { resourceName: request.params.resourceName } + ); + return response.ok({ body: { total: Object.keys(esResp).length, 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..f850c6572 100644 --- a/test/cypress/e2e/multi-datasources/multi_datasources_enabled.spec.js +++ b/test/cypress/e2e/multi-datasources/multi_datasources_enabled.spec.js @@ -162,15 +162,6 @@ describe('Multi-datasources enabled', () => { cy.get('[data-test-subj="dataSourceViewButton"]').should('contain', 'Local cluster'); }); - 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` - ); - - cy.get('[data-test-subj="dataSourceViewButton"]').should('contain', 'Local cluster'); - }); - it('Checks Audit Logs Tab', () => { cy.request({ method: 'POST', From fe847af1a54180ce43c3e8910b904b5364b9296d Mon Sep 17 00:00:00 2001 From: Manuelraa Date: Thu, 18 Jul 2024 21:08:44 +0200 Subject: [PATCH 8/9] feat: http proxy support for oidc (#2024) * feat: http proxy support for oidc Resolves: #911 Signed-off-by: manuelraa * chore: reduce code duplication for agent configuration Signed-off-by: Manuelraa --------- Signed-off-by: manuelraa Signed-off-by: Manuelraa --- package.json | 1 + server/auth/types/openid/openid_auth.ts | 29 ++-- yarn.lock | 192 +++++++++++++++++++++++- 3 files changed, 208 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index 1f2349a40..675291e2f 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "@hapi/cryptiles": "5.0.0", "@hapi/wreck": "^17.1.0", "html-entities": "1.3.1", + "proxy-agent": "^6.4.0", "zxcvbn": "^4.4.2" }, "resolutions": { diff --git a/server/auth/types/openid/openid_auth.ts b/server/auth/types/openid/openid_auth.ts index 3292b68e1..b1f69bd64 100644 --- a/server/auth/types/openid/openid_auth.ts +++ b/server/auth/types/openid/openid_auth.ts @@ -27,10 +27,9 @@ import { IOpenSearchDashboardsResponse, AuthResult, } from 'opensearch-dashboards/server'; -import HTTP from 'http'; -import HTTPS from 'https'; import { PeerCertificate } from 'tls'; import { Server, ServerStateCookieOptions } from '@hapi/hapi'; +import { ProxyAgent } from 'proxy-agent'; import { SecurityPluginConfigType } from '../../..'; import { SecuritySessionCookie, @@ -175,19 +174,23 @@ export class OpenIdAuthentication extends AuthenticationType { }; } this.logger.info(getObjectProperties(this.wreckHttpsOption, 'WreckHttpsOptions')); + + // Use proxy agent to allow usage of e.g. http_proxy environment variable + const httpAgent = new ProxyAgent(); + const httpsAllowUnauthorizedAgent = new ProxyAgent({ + rejectUnauthorized: false, + }); + let httpsAgent = new ProxyAgent(); if (Object.keys(this.wreckHttpsOption).length > 0) { - return wreck.defaults({ - agents: { - http: new HTTP.Agent(), - https: new HTTPS.Agent(this.wreckHttpsOption), - httpsAllowUnauthorized: new HTTPS.Agent({ - rejectUnauthorized: false, - }), - }, - }); - } else { - return wreck; + httpsAgent = new ProxyAgent(this.wreckHttpsOption); } + return wreck.defaults({ + agents: { + http: httpAgent, + https: httpsAgent, + httpsAllowUnauthorized: httpsAllowUnauthorizedAgent, + }, + }); } getWreckHttpsOptions(): WreckHttpsOptions { diff --git a/yarn.lock b/yarn.lock index dc945c7bb..3c3eef2bf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -200,6 +200,11 @@ "@types/react-test-renderer" ">=16.9.0" react-error-boundary "^3.1.0" +"@tootallnate/quickjs-emscripten@^0.23.0": + version "0.23.0" + resolved "https://registry.yarnpkg.com/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz#db4ecfd499a9765ab24002c3b696d02e6d32a12c" + integrity sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA== + "@types/hapi__boom@*": version "9.0.1" resolved "https://registry.yarnpkg.com/@types/hapi__boom/-/hapi__boom-9.0.1.tgz#8fbf719d0358e361b831802fb05fb4feced2ac29" @@ -459,6 +464,13 @@ acorn@^6.4.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== +agent-base@^7.0.2, agent-base@^7.1.0, agent-base@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.1.tgz#bdbded7dfb096b751a2a087eeeb9664725b2e317" + integrity sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA== + dependencies: + debug "^4.3.4" + aggregate-error@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" @@ -623,6 +635,13 @@ assign-symbols@^1.0.0: resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" integrity sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw== +ast-types@^0.13.4: + version "0.13.4" + resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.4.tgz#ee0d77b343263965ecc3fb62da16e7222b2b6782" + integrity sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w== + dependencies: + tslib "^2.0.1" + astral-regex@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" @@ -708,6 +727,11 @@ basic-auth@~2.0.1: dependencies: safe-buffer "5.1.2" +basic-ftp@^5.0.2: + version "5.0.5" + resolved "https://registry.yarnpkg.com/basic-ftp/-/basic-ftp-5.0.5.tgz#14a474f5fffecca1f4f406f1c26b18f800225ac0" + integrity sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg== + bcrypt-pbkdf@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" @@ -1388,6 +1412,11 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" +data-uri-to-buffer@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz#8a58bb67384b261a38ef18bea1810cb01badd28b" + integrity sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw== + dateformat@^4.5.1: version "4.6.3" resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-4.6.3.tgz#556fa6497e5217fedb78821424f8a1c22fa3f4b5" @@ -1398,7 +1427,7 @@ dayjs@^1.10.4: resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.10.tgz#68acea85317a6e164457d6d6947564029a6a16a0" integrity sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ== -debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9, debug@^3.1.0, debug@^3.2.7, debug@^4.1.1, debug@^4.3.4, debug@~4.1.1: +debug@2.6.9, debug@4, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9, debug@^3.1.0, debug@^3.2.7, debug@^4.1.1, debug@^4.3.4, debug@~4.1.1: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -1455,6 +1484,15 @@ define-property@^2.0.2: is-descriptor "^1.0.2" isobject "^3.0.1" +degenerator@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/degenerator/-/degenerator-5.0.1.tgz#9403bf297c6dad9a1ece409b37db27954f91f2f5" + integrity sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ== + dependencies: + ast-types "^0.13.4" + escodegen "^2.1.0" + esprima "^4.0.1" + delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" @@ -1685,6 +1723,17 @@ escape-string-regexp@^1.0.5: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== +escodegen@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.1.0.tgz#ba93bbb7a43986d29d6041f99f5262da773e2e17" + integrity sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w== + dependencies: + esprima "^4.0.1" + estraverse "^5.2.0" + esutils "^2.0.2" + optionalDependencies: + source-map "~0.6.1" + eslint-import-resolver-node@0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz#58f15fb839b8d0576ca980413476aab2472db66a" @@ -1737,6 +1786,11 @@ eslint-scope@^4.0.3: esrecurse "^4.1.0" estraverse "^4.1.1" +esprima@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + esrecurse@^4.1.0: version "4.3.0" resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" @@ -1754,6 +1808,11 @@ estraverse@^5.2.0: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + etag@~1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" @@ -2090,6 +2149,15 @@ fs-extra@^10.0.0, fs-extra@^10.0.1: jsonfile "^6.0.1" universalify "^2.0.0" +fs-extra@^11.2.0: + version "11.2.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.2.0.tgz#e70e17dfad64232287d01929399e0ea7c86b0e5b" + integrity sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + fs-extra@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" @@ -2192,6 +2260,16 @@ get-symbol-description@^1.0.0: call-bind "^1.0.2" get-intrinsic "^1.1.1" +get-uri@^6.0.1: + version "6.0.3" + resolved "https://registry.yarnpkg.com/get-uri/-/get-uri-6.0.3.tgz#0d26697bc13cf91092e519aa63aa60ee5b6f385a" + integrity sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw== + dependencies: + basic-ftp "^5.0.2" + data-uri-to-buffer "^6.0.2" + debug "^4.3.4" + fs-extra "^11.2.0" + get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" @@ -2426,6 +2504,14 @@ http-errors@2.0.0: statuses "2.0.1" toidentifier "1.0.1" +http-proxy-agent@^7.0.0, http-proxy-agent@^7.0.1: + version "7.0.2" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz#9a8b1f246866c028509486585f62b8f2c18c270e" + integrity sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig== + dependencies: + agent-base "^7.1.0" + debug "^4.3.4" + http-signature@~1.3.6: version "1.3.6" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.3.6.tgz#cb6fbfdf86d1c974f343be94e87f7fc128662cf9" @@ -2440,6 +2526,14 @@ https-browserify@^1.0.0: resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" integrity sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg== +https-proxy-agent@^7.0.3, https-proxy-agent@^7.0.5: + version "7.0.5" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz#9e8b5013873299e11fab6fd548405da2d6c602b2" + integrity sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw== + dependencies: + agent-base "^7.0.2" + debug "4" + human-signals@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" @@ -2524,6 +2618,14 @@ interpret@^1.4.0: resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== +ip-address@^9.0.5: + version "9.0.5" + resolved "https://registry.yarnpkg.com/ip-address/-/ip-address-9.0.5.tgz#117a960819b08780c3bd1f14ef3c1cc1d3f3ea5a" + integrity sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g== + dependencies: + jsbn "1.1.0" + sprintf-js "^1.1.3" + ipaddr.js@1.9.1: version "1.9.1" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" @@ -2821,6 +2923,11 @@ jose@^5.2.4: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== +jsbn@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-1.1.0.tgz#b01307cb29b618a1ed26ec79e911f803c4da0040" + integrity sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A== + jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" @@ -3038,6 +3145,11 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" +lru-cache@^7.14.1: + version "7.18.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" + integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== + make-dir@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" @@ -3330,6 +3442,11 @@ neo-async@^2.5.0, neo-async@^2.6.0, neo-async@^2.6.1: resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== +netmask@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/netmask/-/netmask-2.0.2.tgz#8b01a07644065d536383835823bc52004ebac5e7" + integrity sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg== + node-forge@^1: version "1.3.1" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" @@ -3512,6 +3629,28 @@ p-try@^2.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== +pac-proxy-agent@^7.0.1: + version "7.0.2" + resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-7.0.2.tgz#0fb02496bd9fb8ae7eb11cfd98386daaac442f58" + integrity sha512-BFi3vZnO9X5Qt6NRz7ZOaPja3ic0PhlsmCRYLOpN11+mWBCR6XJDqW5RF3j8jm4WGGQZtBA+bTfxYzeKW73eHg== + dependencies: + "@tootallnate/quickjs-emscripten" "^0.23.0" + agent-base "^7.0.2" + debug "^4.3.4" + get-uri "^6.0.1" + http-proxy-agent "^7.0.0" + https-proxy-agent "^7.0.5" + pac-resolver "^7.0.1" + socks-proxy-agent "^8.0.4" + +pac-resolver@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/pac-resolver/-/pac-resolver-7.0.1.tgz#54675558ea368b64d210fd9c92a640b5f3b8abb6" + integrity sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg== + dependencies: + degenerator "^5.0.0" + netmask "^2.0.2" + pako@~1.0.2, pako@~1.0.5: version "1.0.11" resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" @@ -3667,11 +3806,30 @@ proxy-addr@~2.0.7: forwarded "0.2.0" ipaddr.js "1.9.1" +proxy-agent@^6.4.0: + version "6.4.0" + resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-6.4.0.tgz#b4e2dd51dee2b377748aef8d45604c2d7608652d" + integrity sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ== + dependencies: + agent-base "^7.0.2" + debug "^4.3.4" + http-proxy-agent "^7.0.1" + https-proxy-agent "^7.0.3" + lru-cache "^7.14.1" + pac-proxy-agent "^7.0.1" + proxy-from-env "^1.1.0" + socks-proxy-agent "^8.0.2" + proxy-from-env@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.0.0.tgz#33c50398f70ea7eb96d21f7b817630a55791c7ee" integrity sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A== +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + prr@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" @@ -4253,6 +4411,11 @@ slice-ansi@^4.0.0: astral-regex "^2.0.0" is-fullwidth-code-point "^3.0.0" +smart-buffer@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" + integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== + snapdragon@^0.8.1: version "0.8.2" resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" @@ -4267,6 +4430,23 @@ snapdragon@^0.8.1: source-map-resolve "^0.5.0" use "^3.1.0" +socks-proxy-agent@^8.0.2, socks-proxy-agent@^8.0.4: + version "8.0.4" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-8.0.4.tgz#9071dca17af95f483300316f4b063578fa0db08c" + integrity sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw== + dependencies: + agent-base "^7.1.1" + debug "^4.3.4" + socks "^2.8.3" + +socks@^2.8.3: + version "2.8.3" + resolved "https://registry.yarnpkg.com/socks/-/socks-2.8.3.tgz#1ebd0f09c52ba95a09750afe3f3f9f724a800cb5" + integrity sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw== + dependencies: + ip-address "^9.0.5" + smart-buffer "^4.2.0" + source-list-map@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" @@ -4313,6 +4493,11 @@ split-string@^3.0.1: dependencies: extend-shallow "^3.0.0" +sprintf-js@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.3.tgz#4914b903a2f8b685d17fdf78a70e917e872e444a" + integrity sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA== + sshpk@^1.14.1: version "1.18.0" resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.18.0.tgz#1663e55cddf4d688b86a46b77f0d5fe363aba028" @@ -4589,6 +4774,11 @@ tough-cookie@^4.1.3: universalify "^0.2.0" url-parse "^1.5.3" +tslib@^2.0.1: + version "2.6.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.3.tgz#0438f810ad7a9edcde7a241c3d80db693c8cbfe0" + integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ== + tslib@^2.1.0: version "2.6.2" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" From 2f8c6f4a683d5405e9770059ddd54cce3a676782 Mon Sep 17 00:00:00 2001 From: Derek Ho Date: Fri, 19 Jul 2024 10:23:40 -0400 Subject: [PATCH 9/9] Conform to Navigation changes from OSD core (#2022) * Adopt to nav changes in core Signed-off-by: Derek Ho * Lint Signed-off-by: Derek Ho * feat: enable data source info in new left navigation Signed-off-by: SuZhou-Joe * feat: enable data source info in new left navigation Signed-off-by: SuZhou-Joe * Address PR feedback Signed-off-by: Derek Ho * Lint Signed-off-by: Derek Ho * Fix lint and tests Signed-off-by: Derek Ho --------- Signed-off-by: Derek Ho Signed-off-by: SuZhou-Joe Co-authored-by: SuZhou-Joe --- public/apps/configuration/app-router.tsx | 25 ++- .../apps/configuration/configuration-app.tsx | 2 + .../panels/tenant-list/configure_tab1.tsx | 2 +- .../panels/tenant-list/manage_tab.tsx | 3 +- .../panels/tenant-list/tenant-list.tsx | 2 +- .../tenant-list/test/tenant-list.test.tsx | 3 + .../__snapshots__/app-router.test.tsx.snap | 1 - .../configuration/test/app-router.test.tsx | 4 + public/apps/configuration/top-nav-menu.tsx | 6 +- public/apps/types.ts | 1 + public/plugin.ts | 163 +++++++++++++++++- public/utils/datasource-utils.ts | 19 +- 12 files changed, 209 insertions(+), 22 deletions(-) diff --git a/public/apps/configuration/app-router.tsx b/public/apps/configuration/app-router.tsx index 9164bbc29..f0dc0cae2 100644 --- a/public/apps/configuration/app-router.tsx +++ b/public/apps/configuration/app-router.tsx @@ -40,11 +40,11 @@ import { Action, RouteItem, SubAction } from './types'; import { ResourceType } from '../../../common'; import { buildHashUrl, buildUrl } from './utils/url-builder'; import { CrossPageToast } from './cross-page-toast'; -import { getDataSourceFromUrl } from '../../utils/datasource-utils'; +import { getDataSourceFromUrl, LocalCluster } from '../../utils/datasource-utils'; const LANDING_PAGE_URL = '/getstarted'; -const ROUTE_MAP: { [key: string]: RouteItem } = { +export const ROUTE_MAP: { [key: string]: RouteItem } = { getStarted: { name: 'Get Started', href: LANDING_PAGE_URL, @@ -145,8 +145,6 @@ export interface DataSourceContextType { setDataSource: React.Dispatch>; } -export const LocalCluster = { label: 'Local cluster', id: '' }; - export const DataSourceContext = createContext(null); export function AppRouter(props: AppDependencies) { @@ -161,14 +159,15 @@ export function AppRouter(props: AppDependencies) { - {allNavPanelUrls(multitenancyEnabled).map((route) => ( - // Create different routes to update the 'selected' nav item . - - - - - - ))} + {!props.coreStart.chrome.navGroup.getNavGroupEnabled() && + allNavPanelUrls(multitenancyEnabled).map((route) => ( + // Create different routes to update the 'selected' nav item . + + + + + + ))} )} - + diff --git a/public/apps/configuration/configuration-app.tsx b/public/apps/configuration/configuration-app.tsx index 1f341f781..e6c681921 100644 --- a/public/apps/configuration/configuration-app.tsx +++ b/public/apps/configuration/configuration-app.tsx @@ -28,6 +28,7 @@ export function renderApp( depsStart: SecurityPluginStartDependencies, params: AppMountParameters, config: ClientConfigType, + redirect: string, dataSourceManagement?: DataSourceManagementPluginSetup ) { const deps = { @@ -36,6 +37,7 @@ export function renderApp( params, config, dataSourceManagement, + redirect, }; ReactDOM.render( diff --git a/public/apps/configuration/panels/tenant-list/configure_tab1.tsx b/public/apps/configuration/panels/tenant-list/configure_tab1.tsx index b38f0a098..770496d85 100644 --- a/public/apps/configuration/panels/tenant-list/configure_tab1.tsx +++ b/public/apps/configuration/panels/tenant-list/configure_tab1.tsx @@ -52,7 +52,7 @@ import { import { getDashboardsInfo } from '../../../../utils/dashboards-info-utils'; import { LOCAL_CLUSTER_ID } from '../../../../../common'; import { AccessErrorComponent } from '../../access-error-component'; -import { LocalCluster } from '../../app-router'; +import { LocalCluster } from '../../../../utils/datasource-utils'; export function ConfigureTab1(props: AppDependencies) { const [isMultiTenancyEnabled, setIsMultiTenancyEnabled] = useState(false); diff --git a/public/apps/configuration/panels/tenant-list/manage_tab.tsx b/public/apps/configuration/panels/tenant-list/manage_tab.tsx index 544aff698..ab30bf0ee 100644 --- a/public/apps/configuration/panels/tenant-list/manage_tab.tsx +++ b/public/apps/configuration/panels/tenant-list/manage_tab.tsx @@ -69,7 +69,8 @@ import { useContextMenuState } from '../../utils/context-menu'; import { generateResourceName } from '../../utils/resource-utils'; import { DocLinks } from '../../constants'; import { TenantList } from './tenant-list'; -import { LocalCluster, getBreadcrumbs } from '../../app-router'; +import { getBreadcrumbs } from '../../app-router'; +import { LocalCluster } from '../../../../utils/datasource-utils'; import { buildUrl } from '../../utils/url-builder'; import { CrossPageToast } from '../../cross-page-toast'; import { getDashboardsInfo } from '../../../../utils/dashboards-info-utils'; diff --git a/public/apps/configuration/panels/tenant-list/tenant-list.tsx b/public/apps/configuration/panels/tenant-list/tenant-list.tsx index c624adcbe..cebfb4981 100644 --- a/public/apps/configuration/panels/tenant-list/tenant-list.tsx +++ b/public/apps/configuration/panels/tenant-list/tenant-list.tsx @@ -30,7 +30,7 @@ import { AppDependencies } from '../../../types'; import { ExternalLink } from '../../utils/display-utils'; import { DocLinks } from '../../constants'; import { getDashboardsInfo } from '../../../../utils/dashboards-info-utils'; -import { LocalCluster } from '../../app-router'; +import { LocalCluster } from '../../../../utils/datasource-utils'; import { SecurityPluginTopNavMenu } from '../../top-nav-menu'; interface TenantListProps extends AppDependencies { diff --git a/public/apps/configuration/panels/tenant-list/test/tenant-list.test.tsx b/public/apps/configuration/panels/tenant-list/test/tenant-list.test.tsx index ca4081fce..66117c398 100644 --- a/public/apps/configuration/panels/tenant-list/test/tenant-list.test.tsx +++ b/public/apps/configuration/panels/tenant-list/test/tenant-list.test.tsx @@ -64,6 +64,9 @@ describe('Tenant list', () => { setBreadcrumbs() { return 1; }, + navGroup: { + getNavGroupEnabled: jest.fn().mockReturnValue(false), + }, }, }; const config = { diff --git a/public/apps/configuration/test/__snapshots__/app-router.test.tsx.snap b/public/apps/configuration/test/__snapshots__/app-router.test.tsx.snap index 21aa4e515..d5ee503be 100644 --- a/public/apps/configuration/test/__snapshots__/app-router.test.tsx.snap +++ b/public/apps/configuration/test/__snapshots__/app-router.test.tsx.snap @@ -495,7 +495,6 @@ exports[`SecurityPluginTopNavMenu renders DataSourceMenu when dataSource is enab diff --git a/public/apps/configuration/test/app-router.test.tsx b/public/apps/configuration/test/app-router.test.tsx index 540f44614..4bb61888d 100644 --- a/public/apps/configuration/test/app-router.test.tsx +++ b/public/apps/configuration/test/app-router.test.tsx @@ -20,6 +20,7 @@ import { getDataSourceFromUrl } from '../../../utils/datasource-utils'; jest.mock('../../../utils/datasource-utils', () => ({ getDataSourceFromUrl: jest.fn(), + LocalCluster: { id: '', label: 'Local cluster' }, })); describe('SecurityPluginTopNavMenu', () => { @@ -30,6 +31,9 @@ describe('SecurityPluginTopNavMenu', () => { notifications: jest.fn(), chrome: { setBreadcrumbs: jest.fn(), + navGroup: { + getNavGroupEnabled: jest.fn().mockReturnValue(false), + }, }, }; diff --git a/public/apps/configuration/top-nav-menu.tsx b/public/apps/configuration/top-nav-menu.tsx index 5e5e51893..20197d69a 100644 --- a/public/apps/configuration/top-nav-menu.tsx +++ b/public/apps/configuration/top-nav-menu.tsx @@ -17,7 +17,10 @@ import React from 'react'; import { DataSourceSelectableConfig } from 'src/plugins/data_source_management/public'; import { DataSourceOption } from 'src/plugins/data_source_management/public/components/data_source_menu/types'; import { AppDependencies } from '../types'; -import { setDataSourceInUrl } from '../../utils/datasource-utils'; +import { + setDataSourceInUrl, + setDataSource as setDataSourceInSubscription, +} from '../../utils/datasource-utils'; export interface TopNavMenuProps extends AppDependencies { dataSourcePickerReadOnly: boolean; @@ -44,6 +47,7 @@ export const SecurityPluginTopNavMenu = React.memo( const wrapSetDataSourceWithUpdateUrl = (dataSources: DataSourceOption[]) => { setDataSourceInUrl(dataSources[0]); setDataSource(dataSources[0]); + setDataSourceInSubscription(dataSources[0]); }; return dataSourceEnabled ? ( diff --git a/public/apps/types.ts b/public/apps/types.ts index 276f1ce07..755431c33 100644 --- a/public/apps/types.ts +++ b/public/apps/types.ts @@ -24,6 +24,7 @@ export interface AppDependencies { config: ClientConfigType; dashboardsInfo: DashboardsInfo; dataSourceManagement: DataSourceManagementPluginSetup; + redirect?: string; } export interface BreadcrumbsPageDependencies extends AppDependencies { diff --git a/public/plugin.ts b/public/plugin.ts index 75a983ab9..fb71c9c88 100644 --- a/public/plugin.ts +++ b/public/plugin.ts @@ -17,14 +17,18 @@ import { BehaviorSubject } from 'rxjs'; import { SavedObjectsManagementColumn } from 'src/plugins/saved_objects_management/public'; import { i18n } from '@osd/i18n'; import { + AppCategory, AppMountParameters, + AppNavLinkStatus, AppStatus, AppUpdater, CoreSetup, CoreStart, DEFAULT_APP_CATEGORIES, + DEFAULT_NAV_GROUPS, Plugin, PluginInitializerContext, + WorkspaceAvailability, } from '../../../src/core/public'; import { APP_ID_LOGIN, CUSTOM_ERROR_PAGE_URI, LOGIN_PAGE_URI, PLUGIN_NAME } from '../common'; import { APP_ID_CUSTOMERROR } from '../common'; @@ -50,6 +54,11 @@ import { addTenantToShareURL } from './services/shared-link'; import { interceptError } from './utils/logout-utils'; import { tenantColumn, getNamespacesToRegister } from './apps/configuration/utils/tenant-utils'; import { getDashboardsInfoSafe } from './utils/dashboards-info-utils'; +import { + dataSource$, + getDataSourceEnabledUrl, + getDataSourceFromUrl, +} from './utils/datasource-utils'; async function hasApiPermission(core: CoreSetup): Promise { try { @@ -68,8 +77,13 @@ const APP_ID_DASHBOARDS = 'dashboards'; // OpenSearchDashboards app is for legacy url migration const APP_ID_OPENSEARCH_DASHBOARDS = 'kibana'; const APP_LIST_FOR_READONLY_ROLE = [APP_ID_HOME, APP_ID_DASHBOARDS, APP_ID_OPENSEARCH_DASHBOARDS]; -const GLOBAL_TENANT_RENDERING_TEXT = 'Global'; -const PRIVATE_TENANT_RENDERING_TEXT = 'Private'; + +const dataAccessUsersCategory: AppCategory & { group?: AppCategory } = { + id: 'dataAccessAndUsers', + label: 'Data Access and Users', + order: 9000, + euiIconType: 'managementApp', +}; export class SecurityPlugin implements @@ -82,6 +96,15 @@ export class SecurityPlugin // @ts-ignore : initializerContext not used constructor(private readonly initializerContext: PluginInitializerContext) {} + private updateDefaultRouteOfSecurityApplications: AppUpdater = () => { + const url = getDataSourceEnabledUrl(getDataSourceFromUrl()); + return { + defaultPath: `?${url.searchParams.toString()}`, + }; + }; + + private appStateUpdater = new BehaviorSubject(this.updateDefaultRouteOfSecurityApplications); + public async setup( core: CoreSetup, deps: SecurityPluginSetupDependencies @@ -97,11 +120,36 @@ export class SecurityPlugin (config.readonly_mode?.roles || DEFAULT_READONLY_ROLES).includes(role) ); + const mountWrapper = async (params: AppMountParameters, redirect: string) => { + 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, + redirect, + deps.dataSourceManagement + ); + }; + if (mdsEnabled || apiPermission) { core.application.register({ id: PLUGIN_NAME, title: 'Security', order: 9050, + workspaceAvailability: WorkspaceAvailability.outsideWorkspace, + navLinkStatus: core.chrome.navGroup.getNavGroupEnabled() + ? AppNavLinkStatus.hidden + : AppNavLinkStatus.visible, mount: async (params: AppMountParameters) => { const { renderApp } = await import('./apps/configuration/configuration-app'); const [coreStart, depsStart] = await core.getStartServices(); @@ -118,12 +166,117 @@ export class SecurityPlugin depsStart as SecurityPluginStartDependencies, params, config, + '/getstarted', deps.dataSourceManagement ); }, category: DEFAULT_APP_CATEGORIES.management, }); + if (core.chrome.navGroup.getNavGroupEnabled()) { + core.application.register({ + id: `security-dashboards-plugin_getstarted`, + title: 'Get Started', + order: 8040, + workspaceAvailability: WorkspaceAvailability.outsideWorkspace, + updater$: this.appStateUpdater, + mount: async (params: AppMountParameters) => { + return mountWrapper(params, '/getstarted'); + }, + }); + core.application.register({ + id: `security-dashboards-plugin_auth`, + title: 'Authentication', + order: 8040, + workspaceAvailability: WorkspaceAvailability.outsideWorkspace, + updater$: this.appStateUpdater, + mount: async (params: AppMountParameters) => { + return mountWrapper(params, '/auth'); + }, + }); + core.application.register({ + id: `security-dashboards-plugin_roles`, + title: 'Roles', + order: 8040, + workspaceAvailability: WorkspaceAvailability.outsideWorkspace, + updater$: this.appStateUpdater, + mount: async (params: AppMountParameters) => { + return mountWrapper(params, '/roles'); + }, + }); + core.application.register({ + id: `security-dashboards-plugin_users`, + title: 'Internal users', + order: 8040, + workspaceAvailability: WorkspaceAvailability.outsideWorkspace, + updater$: this.appStateUpdater, + mount: async (params: AppMountParameters) => { + return mountWrapper(params, '/users'); + }, + }); + core.application.register({ + id: `security-dashboards-plugin_permissions`, + title: 'Permissions', + order: 8040, + workspaceAvailability: WorkspaceAvailability.outsideWorkspace, + updater$: this.appStateUpdater, + mount: async (params: AppMountParameters) => { + return mountWrapper(params, '/permissions'); + }, + }); + core.application.register({ + id: `security-dashboards-plugin_tenants`, + title: 'Tenants', + order: 8040, + workspaceAvailability: WorkspaceAvailability.outsideWorkspace, + updater$: this.appStateUpdater, + mount: async (params: AppMountParameters) => { + return mountWrapper(params, '/tenants'); + }, + }); + core.application.register({ + id: `security-dashboards-plugin_auditlog`, + title: 'Audit logs', + order: 8040, + workspaceAvailability: WorkspaceAvailability.outsideWorkspace, + updater$: this.appStateUpdater, + mount: async (params: AppMountParameters) => { + return mountWrapper(params, '/auditLogging'); + }, + }); + } + + core.chrome.navGroup.addNavLinksToGroup(DEFAULT_NAV_GROUPS.dataAdministration, [ + { + id: `security-dashboards-plugin_getstarted`, + category: dataAccessUsersCategory, + }, + { + id: `security-dashboards-plugin_auth`, + category: dataAccessUsersCategory, + }, + { + id: `security-dashboards-plugin_roles`, + category: dataAccessUsersCategory, + }, + { + id: `security-dashboards-plugin_users`, + category: dataAccessUsersCategory, + }, + { + id: `security-dashboards-plugin_permissions`, + category: dataAccessUsersCategory, + }, + { + id: `security-dashboards-plugin_tenants`, + category: dataAccessUsersCategory, + }, + { + id: `security-dashboards-plugin_auditlog`, + category: dataAccessUsersCategory, + }, + ]); + if (deps.managementOverview) { deps.managementOverview.register({ id: PLUGIN_NAME, @@ -135,6 +288,12 @@ export class SecurityPlugin }), }); } + + dataSource$.subscribe((dataSourceOption) => { + if (dataSourceOption) { + this.appStateUpdater.next(this.updateDefaultRouteOfSecurityApplications); + } + }); } core.application.register({ diff --git a/public/utils/datasource-utils.ts b/public/utils/datasource-utils.ts index 638c515eb..508a72a78 100644 --- a/public/utils/datasource-utils.ts +++ b/public/utils/datasource-utils.ts @@ -13,6 +13,7 @@ * permissions and limitations under the License. */ +import { BehaviorSubject } from 'rxjs'; import { DataSourceOption } from 'src/plugins/data_source_management/public/components/data_source_menu/types'; const DATASOURCEURLKEY = 'dataSource'; @@ -35,8 +36,22 @@ export function getDataSourceFromUrl(): DataSourceOption { } } -export function setDataSourceInUrl(dataSource: DataSourceOption) { +export function getDataSourceEnabledUrl(dataSource: DataSourceOption) { const url = new URL(window.location.href); url.searchParams.set(DATASOURCEURLKEY, JSON.stringify(dataSource)); - window.history.replaceState({}, '', url.toString()); + return url; +} + +export function setDataSourceInUrl(dataSource: DataSourceOption) { + window.history.replaceState({}, '', getDataSourceEnabledUrl(dataSource).toString()); +} + +export const LocalCluster = { label: 'Local cluster', id: '' }; + +export const dataSource$ = new BehaviorSubject( + getDataSourceFromUrl() || LocalCluster +); + +export function setDataSource(dataSource: DataSourceOption) { + dataSource$.next(dataSource); }