Skip to content

Commit e6cff25

Browse files
[Resource Sharing] Adds resource access management dashboard (opensearch-project#2304)
* Adds resource access management dashboard Signed-off-by: Darshit Chanpura <dchanp@amazon.com> * Updates share button to only be displayed when user can actually share Signed-off-by: Darshit Chanpura <dchanp@amazon.com> * Adds UI unit tests Signed-off-by: Darshit Chanpura <dchanp@amazon.com> * Adds cypress e2e test for resource-access-management dashboard Signed-off-by: Darshit Chanpura <dchanp@amazon.com> * Updates cypress workflow Signed-off-by: Darshit Chanpura <dchanp@amazon.com> * Set action version to v8 Signed-off-by: Darshit Chanpura <dchanp@amazon.com> * Adds changelog entry Signed-off-by: Darshit Chanpura <dchanp@amazon.com> * Adds AD plugin to setup and creates sample detector Signed-off-by: Darshit Chanpura <dchanp@amazon.com> * Fix AD dashboard installation Signed-off-by: Darshit Chanpura <dchanp@amazon.com> * Adds sample plugin and removes AD completely Signed-off-by: Darshit Chanpura <dchanp@amazon.com> * Fix cypress workflow for resource-access-management Signed-off-by: Darshit Chanpura <dchanp@amazon.com> * Adds explicit dashboard config in workflow Signed-off-by: Darshit Chanpura <dchanp@amazon.com> * Corrects file name in cypress workflow Signed-off-by: Darshit Chanpura <dchanp@amazon.com> * Addresses fixes to backend api Signed-off-by: Darshit Chanpura <dchanp@amazon.com> * Fix unit test failure to address backend change for resource-type Signed-off-by: Darshit Chanpura <dchanp@amazon.com> * Uses library for normalization of resource types and changes subtitle position Signed-off-by: Darshit Chanpura <dchanp@amazon.com> * Adds EuiEmptyPrompt and update api usage Signed-off-by: Darshit Chanpura <dchanp@amazon.com> * Fix tests Signed-off-by: Darshit Chanpura <dchanp@amazon.com> * Change to lodash Signed-off-by: Darshit Chanpura <dchanp@amazon.com> --------- Signed-off-by: Darshit Chanpura <dchanp@amazon.com>
1 parent ad7ade9 commit e6cff25

16 files changed

+2054
-2
lines changed
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
name: E2E Resource Access Management Cypress Tests
2+
3+
on: [ push, pull_request ]
4+
5+
env:
6+
CI: 1
7+
# avoid warnings like "tput: No value for $TERM and no -T specified"
8+
TERM: xterm
9+
PLUGIN_NAME: opensearch-security
10+
OPENSEARCH_INITIAL_ADMIN_PASSWORD: myStrongPassword123!
11+
12+
jobs:
13+
tests:
14+
name: Run Cypress resource-access-management tests
15+
strategy:
16+
fail-fast: false
17+
matrix:
18+
os: [ ubuntu-latest ]
19+
runs-on: ${{ matrix.os }}
20+
21+
steps:
22+
- name: Checkout Branch
23+
uses: actions/checkout@v5
24+
25+
- name: Set up JDK 21 for build
26+
uses: actions/setup-java@v4
27+
with:
28+
distribution: temurin
29+
java-version: '21'
30+
31+
- name: Set env
32+
run: |
33+
opensearch_version=$(node -p "require('./package.json').opensearchDashboards.version")
34+
plugin_version=$(node -p "require('./package.json').version")
35+
echo "OPENSEARCH_VERSION=$opensearch_version" >> $GITHUB_ENV
36+
echo "PLUGIN_VERSION=$plugin_version" >> $GITHUB_ENV
37+
shell: bash
38+
39+
- name: Download security plugin and create setup scripts
40+
uses: ./.github/actions/download-plugin
41+
with:
42+
opensearch-version: ${{ env.OPENSEARCH_VERSION }}
43+
plugin-name: ${{ env.PLUGIN_NAME }}
44+
plugin-version: ${{ env.PLUGIN_VERSION }}
45+
download-location: ${{env.PLUGIN_NAME}}
46+
47+
# build sample-resource-plugin from source (Linux runner)
48+
- name: Build sample-resource-plugin (server)
49+
shell: bash
50+
run: |
51+
set -euo pipefail
52+
OSV="${OPENSEARCH_VERSION}"
53+
# Map 3.2.0[-anything] -> 3.2
54+
SEC_REF="$(echo "$OSV" | sed -E 's/^([0-9]+)\.([0-9]+).*/\1.\2/')"
55+
echo "Derived security repo ref: $SEC_REF (from $OSV)"
56+
57+
# Prefer branch/tag = X.Y; fall back to main if not found
58+
if git ls-remote --exit-code --heads https://github.com/opensearch-project/security.git "$SEC_REF" >/dev/null 2>&1 \
59+
|| git ls-remote --exit-code --tags https://github.com/opensearch-project/security.git "$SEC_REF" >/dev/null 2>&1; then
60+
REF="$SEC_REF"
61+
else
62+
echo "Ref $SEC_REF not found; falling back to main"
63+
REF="main"
64+
fi
65+
66+
git clone --depth 1 --branch "$REF" https://github.com/opensearch-project/security.git security-src
67+
68+
pushd security-src
69+
chmod +x ./gradlew
70+
# Build the sample resource plugin
71+
./gradlew :opensearch-sample-resource-plugin:assemble
72+
ZIP_PATH=$(ls -t sample-resource-plugin/build/distributions/*.zip | head -n1)
73+
echo "Built sample plugin: $ZIP_PATH"
74+
cp "$ZIP_PATH" "$GITHUB_WORKSPACE/sample-resource-plugin.zip"
75+
popd
76+
77+
echo "SAMPLE_PLUGIN_ZIP=$GITHUB_WORKSPACE/sample-resource-plugin.zip" >> "$GITHUB_ENV"
78+
79+
80+
- name: Run Opensearch with security + sample resource plugin
81+
uses: derek-ho/start-opensearch@v8
82+
with:
83+
opensearch-version: ${{ env.OPENSEARCH_VERSION }}
84+
plugins: "file:$(pwd)/opensearch-security.zip,file:${{ env.SAMPLE_PLUGIN_ZIP }}"
85+
security-enabled: true
86+
admin-password: ${{ env.OPENSEARCH_INITIAL_ADMIN_PASSWORD }}
87+
jdk-version: 21
88+
resource-sharing-enabled: true
89+
90+
- name: Check OpenSearch is running
91+
run: |
92+
curl https://localhost:9200/_cat/plugins -v -u admin:${{ env.OPENSEARCH_INITIAL_ADMIN_PASSWORD }} -k
93+
shell: bash
94+
95+
# OSD bootstrap
96+
- name: Setup Dashboard with Security Dashboards Plugin
97+
uses: derek-ho/setup-opensearch-dashboards@v1
98+
with:
99+
plugin_name: security-dashboards-plugin
100+
101+
- name: Compile OpenSearch Dashboards
102+
run: |
103+
node scripts/build_opensearch_dashboards_platform_plugins --no-examples --workers=10 --verbose
104+
shell: bash
105+
working-directory: OpenSearch-Dashboards
106+
107+
- name: Create OpenSearch Dashboards Config
108+
if: ${{ runner.os == 'Linux' }}
109+
run: |
110+
cat << 'EOT' > resource_sharing_config.yml
111+
server.host: "localhost"
112+
opensearch.hosts: ["https://localhost:9200"]
113+
opensearch.ssl.verificationMode: none
114+
opensearch.username: "kibanaserver"
115+
opensearch.password: "kibanaserver"
116+
opensearch.requestHeadersWhitelist: [ authorization,securitytenant ]
117+
opensearch_security.multitenancy.enabled: true
118+
opensearch_security.multitenancy.tenants.preferred: ["Private", "Global"]
119+
opensearch_security.readonly_mode.roles: ["kibana_read_only"]
120+
opensearch_security.cookie.secure: false
121+
home.disableWelcomeScreen: true
122+
EOT
123+
shell: bash
124+
125+
- name: Replace opensearch_dashboards.yml
126+
run: |
127+
mv resource_sharing_config.yml $GITHUB_WORKSPACE/OpenSearch-Dashboards/config/opensearch_dashboards.yml
128+
shell: bash
129+
130+
- name: Run OpenSearch Dashboards
131+
run: |
132+
nohup yarn start --no-base-path --no-watch --csp.warnLegacyBrowsers=false | tee dashboard.log &
133+
shell: bash
134+
working-directory: OpenSearch-Dashboards
135+
136+
# Check if OSD is ready with a max timeout of 300 seconds
137+
- name: Wait for OpenSearch Dashboards (status API)
138+
shell: bash
139+
working-directory: OpenSearch-Dashboards
140+
env:
141+
OSD_URL: http://localhost:5601
142+
OSD_USER: admin
143+
OSD_PASS: ${{ env.OPENSEARCH_INITIAL_ADMIN_PASSWORD }}
144+
run: |
145+
set -euo pipefail
146+
TIMEOUT=300
147+
INTERVAL=5
148+
end=$((SECONDS + TIMEOUT))
149+
150+
echo "Waiting up to ${TIMEOUT}s for ${OSD_URL}/api/status ..."
151+
while (( SECONDS < end )); do
152+
out="$(curl -s -k -u "${OSD_USER}:${OSD_PASS}" -H 'kbn-xsrf: true' \
153+
-w 'HTTPSTATUS:%{http_code}' "${OSD_URL}/api/status" || true)"
154+
code="${out##*HTTPSTATUS:}"
155+
body="${out%HTTPSTATUS:*}"
156+
157+
# Ready when HTTP 200 AND overall.state === "green"
158+
if [ "${code:-}" = "200" ] && printf '%s' "${body:-}" | grep -Eiq '"state"[[:space:]]*:[[:space:]]*"green"'; then
159+
echo "OpenSearch Dashboards status is green (HTTP ${code})."
160+
exit 0
161+
fi
162+
sleep "${INTERVAL}"
163+
done
164+
165+
echo "Timed out after ${TIMEOUT}s waiting for OpenSearch Dashboards status."
166+
echo "Last 200 lines of dashboard.log:"
167+
tail -n 200 dashboard.log || true
168+
exit 1
169+
170+
171+
- name: Run Cypress Tests with retry
172+
uses: Wandalen/wretry.action@v3.3.0
173+
with:
174+
attempt_limit: 5
175+
attempt_delay: 2000
176+
command: |
177+
cd ./OpenSearch-Dashboards/plugins/security-dashboards-plugin
178+
yarn add cypress --save-dev
179+
eval 'CYPRESS_VERIFY_TIMEOUT=60000 yarn cypress:run --browser chrome --headless --env LOGIN_AS_ADMIN=true --spec "test/cypress/e2e/resource-sharing/resource_access_management.spec.ts"'
180+

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
77

88
### Features
99

10+
* [Resource Sharing] Adds resource access management dashboard ([#2304](https://github.com/opensearch-project/security-dashboards-plugin/pull/2304))
1011

1112
### Enhancements
1213
- Add experimental direct query permissions ([#2315](https://github.com/opensearch-project/security-dashboards-plugin/pull/2315))

common/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,11 @@ export const PLUGIN_USERS_APP_ID = `${PLUGIN_NAME}_users`;
2222
export const PLUGIN_PERMISSIONS_APP_ID = `${PLUGIN_NAME}_permissions`;
2323
export const PLUGIN_TENANTS_APP_ID = `${PLUGIN_NAME}_tenants`;
2424
export const PLUGIN_AUDITLOG_APP_ID = `${PLUGIN_NAME}_auditlog`;
25+
export const PLUGIN_RESOURCE_ACCESS_MANAGEMENT_APP_ID = `${PLUGIN_NAME}_resource_access_management`;
2526

2627
export const APP_ID_LOGIN = 'login';
2728
export const APP_ID_CUSTOMERROR = 'customerror';
29+
export const APP_ID_RESOURCE_ACCESS_MANAGEMENT = 'resource_access_management';
2830
export const OPENDISTRO_SECURITY_ANONYMOUS = 'opendistro_security_anonymous';
2931

3032
export const API_PREFIX = '/api/v1';
@@ -34,6 +36,7 @@ export const API_ENDPOINT_DASHBOARDSINFO = API_PREFIX + '/auth/dashboardsinfo';
3436
export const API_ENDPOINT_AUTHTYPE = API_PREFIX + '/auth/type';
3537
export const LOGIN_PAGE_URI = '/app/' + APP_ID_LOGIN;
3638
export const CUSTOM_ERROR_PAGE_URI = '/app/' + APP_ID_CUSTOMERROR;
39+
export const RESOURCE_ACCESS_MANAGEMENT_URI = '/app/' + APP_ID_RESOURCE_ACCESS_MANAGEMENT;
3740
export const API_AUTH_LOGIN = '/auth/login';
3841
export const API_AUTH_LOGOUT = '/auth/logout';
3942
export const OPENID_AUTH_LOGIN = '/auth/openid/login';

cypress.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ module.exports = defineConfig({
2525
setupNodeEvents(on, config) {},
2626
supportFile: 'test/cypress/support/e2e.js',
2727
baseUrl: 'http://localhost:5601',
28-
specPattern: 'test/cypress/e2e/**/*.spec.js',
28+
specPattern: ['test/cypress/e2e/**/*.spec.js', 'test/cypress/e2e/**/*.spec.ts'],
2929
},
3030
env: {
3131
openSearchUrl: 'https://localhost:9200',
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
// stylelint-disable-next-line @osd/stylelint/no_modifying_global_selectors
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
import './_index.scss';
17+
18+
import React from 'react';
19+
import ReactDOM from 'react-dom';
20+
import { I18nProvider } from '@osd/i18n/react';
21+
22+
import {
23+
EuiPage,
24+
EuiPageBody,
25+
EuiFlexGroup,
26+
EuiFlexItem,
27+
EuiPageHeader,
28+
EuiTitle,
29+
EuiText,
30+
EuiSpacer,
31+
} from '@elastic/eui';
32+
33+
import { AppMountParameters, CoreStart } from '../../../../../src/core/public';
34+
import { DataSourceManagementPluginSetup } from '../../../../../src/plugins/data_source_management/public';
35+
import { SecurityPluginStartDependencies, ClientConfigType } from '../../types';
36+
37+
import { ResourceSharingPanel } from './resource-sharing-panel';
38+
import { buildResourceApi } from '../../utils/resource-sharing-utils';
39+
40+
interface Props {
41+
coreStart: CoreStart;
42+
depsStart: SecurityPluginStartDependencies;
43+
params: AppMountParameters;
44+
config: ClientConfigType;
45+
redirect: string;
46+
dataSourceManagement?: DataSourceManagementPluginSetup;
47+
}
48+
49+
const ResourceAccessManagementApp: React.FC<Props> = ({ coreStart, depsStart }) => {
50+
const {
51+
http,
52+
notifications: { toasts },
53+
} = coreStart;
54+
const TopNav = depsStart?.navigation?.ui?.TopNavMenu;
55+
56+
return (
57+
<>
58+
{TopNav ? (
59+
<TopNav appName="resource-access" showSearchBar={false} useDefaultBehaviors={true} />
60+
) : null}
61+
<EuiPage restrictWidth="2000px">
62+
<EuiPageBody component="main">
63+
<EuiPageHeader>
64+
<EuiFlexGroup direction="column" gutterSize="xs">
65+
<EuiFlexItem grow={false}>
66+
<EuiTitle size="l">
67+
<h1>Resource Access Management</h1>
68+
</EuiTitle>
69+
</EuiFlexItem>
70+
<EuiFlexItem grow={false}>
71+
<EuiText color="subdued" size="s">
72+
Manage sharing for detectors, forecasters, and more.
73+
</EuiText>
74+
</EuiFlexItem>
75+
</EuiFlexGroup>
76+
</EuiPageHeader>
77+
78+
<EuiSpacer size="m" />
79+
80+
<ResourceSharingPanel api={buildResourceApi(http)} toasts={toasts} />
81+
</EuiPageBody>
82+
</EuiPage>
83+
</>
84+
);
85+
};
86+
87+
export function renderApp(
88+
coreStart: CoreStart,
89+
depsStart: SecurityPluginStartDependencies,
90+
params: AppMountParameters,
91+
config: ClientConfigType,
92+
redirect: string,
93+
dataSourceManagement?: DataSourceManagementPluginSetup
94+
) {
95+
const deps: Props = {
96+
coreStart,
97+
depsStart,
98+
params,
99+
config,
100+
dataSourceManagement,
101+
redirect,
102+
};
103+
104+
ReactDOM.render(
105+
<I18nProvider>
106+
<ResourceAccessManagementApp {...deps} />
107+
</I18nProvider>,
108+
params.element
109+
);
110+
111+
return () => ReactDOM.unmountComponentAtNode(params.element);
112+
}

0 commit comments

Comments
 (0)