Skip to content

Commit

Permalink
Create credential management plugin (opensearch-project#1)
Browse files Browse the repository at this point in the history
* [POC] Create credential management plugin

1. Create credential saved object via API with multiple credential types
2. Store encrypted information on metadata index
3. Config crypto material path
4. Generate crypto material via script
  • Loading branch information
noCharger authored Jun 30, 2022
1 parent 68f1964 commit f274d23
Show file tree
Hide file tree
Showing 27 changed files with 722 additions and 1 deletion.
2 changes: 2 additions & 0 deletions config/opensearch_dashboards.yml
Original file line number Diff line number Diff line change
Expand Up @@ -183,3 +183,5 @@
# Set the value of this setting to false to suppress search usage telemetry
# for reducing the load of OpenSearch cluster.
# data.search.usageTelemetry.enabled: false

multiDataSource.materialPath: "src/plugins/credential_management/server/crypto/crypto_material"
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"dashboarding"
],
"private": true,
"version": "2.1.0",
"version": "2.0.1",
"branch": "main",
"types": "./opensearch_dashboards.d.ts",
"tsdocMetadata": "./build/tsdoc-metadata.json",
Expand Down Expand Up @@ -107,6 +107,7 @@
]
},
"dependencies": {
"@aws-crypto/client-node": "^3.1.1",
"@elastic/datemath": "5.0.3",
"@elastic/eui": "34.6.0",
"@elastic/good": "^9.0.1-kibana3",
Expand Down
7 changes: 7 additions & 0 deletions src/plugins/credential_management/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports = {
root: true,
extends: ['@elastic/eslint-config-kibana', 'plugin:@elastic/eui/recommended'],
rules: {
'@osd/eslint/require-license-header': 'warning',
},
};
7 changes: 7 additions & 0 deletions src/plugins/credential_management/.i18nrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"prefix": "credentialManagement",
"paths": {
"credentialManagement": "."
},
"translations": ["translations/ja-JP.json"]
}
25 changes: 25 additions & 0 deletions src/plugins/credential_management/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Credential Management

A OpenSearch Dashboards plugin

---

## Development

See the [OpenSearch Dashboards contributing
guide](https://github.com/opensearch-project/OpenSearch-Dashboards/blob/master/CONTRIBUTING.md) for instructions
setting up your development environment.


## Build and Run crypto_materials_generator

```
npm install @types/yargs
npm install -g ts-node typescript '@types/node'
```

```
cd <root dir>
ts-node src/plugins/credential_management/server/crypto/crypto_materials_generator.ts --keyName='aes-name' --keyNamespace='aes-namespace'
```
13 changes: 13 additions & 0 deletions src/plugins/credential_management/common/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
* 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.
*
* Any modifications Copyright OpenSearch Contributors. See
* GitHub history for details.
*/

export const PLUGIN_ID = 'credentialManagement';
export const PLUGIN_NAME = 'credentialManagement';
9 changes: 9 additions & 0 deletions src/plugins/credential_management/opensearch_dashboards.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"id": "credentialManagement",
"version": "1.0.0",
"opensearchDashboardsVersion": "opensearchDashboards",
"server": true,
"ui": true,
"requiredPlugins": ["management", "data", "navigation"],
"optionalPlugins": []
}
23 changes: 23 additions & 0 deletions src/plugins/credential_management/public/application.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { AppMountParameters, CoreStart } from '../../../core/public';
import { AppPluginStartDependencies } from './types';
import { CredentialManagementApp } from './components/app';

export const renderApp = (
{ notifications, http }: CoreStart,
{ navigation }: AppPluginStartDependencies,
{ appBasePath, element }: AppMountParameters
) => {
ReactDOM.render(
<CredentialManagementApp
basename={appBasePath}
notifications={notifications}
http={http}
navigation={navigation}
/>,
element
);

return () => ReactDOM.unmountComponentAtNode(element);
};
119 changes: 119 additions & 0 deletions src/plugins/credential_management/public/components/app.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import React, { useState } from 'react';
import { i18n } from '@osd/i18n';
import { FormattedMessage, I18nProvider } from '@osd/i18n/react';
import { BrowserRouter as Router } from 'react-router-dom';

import {
EuiButton,
EuiHorizontalRule,
EuiPage,
EuiPageBody,
EuiPageContent,
EuiPageContentBody,
EuiPageContentHeader,
EuiPageHeader,
EuiTitle,
EuiText,
} from '@elastic/eui';

import { CoreStart } from '../../../../core/public';
import { NavigationPublicPluginStart } from '../../../navigation/public';

import { PLUGIN_ID, PLUGIN_NAME } from '../../common';

interface CredentialManagementAppDeps {
basename: string;
notifications: CoreStart['notifications'];
http: CoreStart['http'];
navigation: NavigationPublicPluginStart;
}

export const CredentialManagementApp = ({
basename,
notifications,
http,
navigation,
}: CredentialManagementAppDeps) => {
// Use React hooks to manage state.
const [timestamp, setTimestamp] = useState<string | undefined>();

const onClickHandler = () => {
// Use the core http service to make a response to the server API.
http.get('/api/credential_management/example').then((res) => {
setTimestamp(res.time);
// Use the core notifications service to display a success message.
notifications.toasts.addSuccess(
i18n.translate('credentialManagement.dataUpdated', {
defaultMessage: 'Data updated',
})
);
});
};

// Render the application DOM.
// Note that `navigation.ui.TopNavMenu` is a stateful component exported on the `navigation` plugin's start contract.
return (
<Router basename={basename}>
<I18nProvider>
<>
<navigation.ui.TopNavMenu
appName={PLUGIN_ID}
showSearchBar={true}
useDefaultBehaviors={true}
/>
<EuiPage restrictWidth="1000px">
<EuiPageBody component="main">
<EuiPageHeader>
<EuiTitle size="l">
<h1>
<FormattedMessage
id="credentialManagement.helloWorldText"
defaultMessage="{name}"
values={{ name: PLUGIN_NAME }}
/>
</h1>
</EuiTitle>
</EuiPageHeader>
<EuiPageContent>
<EuiPageContentHeader>
<EuiTitle>
<h2>
<FormattedMessage
id="credentialManagement.congratulationsTitle"
defaultMessage="Credential Management Page"
/>
</h2>
</EuiTitle>
</EuiPageContentHeader>
<EuiPageContentBody>
<EuiText>
<p>
<FormattedMessage
id="credentialManagement.content"
defaultMessage="TODO: List all credential names"
/>
</p>
<EuiHorizontalRule />
<p>
<FormattedMessage
id="credentialManagement.timestampText"
defaultMessage="Last timestamp: {time}"
values={{ time: timestamp ? timestamp : 'Unknown' }}
/>
</p>
<EuiButton type="primary" size="s" onClick={onClickHandler}>
<FormattedMessage
id="credentialManagement.buttonText"
defaultMessage="Get data"
/>
</EuiButton>
</EuiText>
</EuiPageContentBody>
</EuiPageContent>
</EuiPageBody>
</EuiPage>
</>
</I18nProvider>
</Router>
);
};
1 change: 1 addition & 0 deletions src/plugins/credential_management/public/index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/* stylelint-disable no-empty-source */
10 changes: 10 additions & 0 deletions src/plugins/credential_management/public/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import './index.scss';

import { CredentialManagementPlugin } from './plugin';

// This exports static code and TypeScript types,
// as well as, OpenSearch Dashboards Platform `plugin()` initializer.
export function plugin() {
return new CredentialManagementPlugin();
}
export { CredentialManagementPluginSetup, CredentialManagementPluginStart } from './types';
45 changes: 45 additions & 0 deletions src/plugins/credential_management/public/plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { i18n } from '@osd/i18n';
import { AppMountParameters, CoreSetup, CoreStart, Plugin } from '../../../core/public';
import {
CredentialManagementPluginSetup,
CredentialManagementPluginStart,
AppPluginStartDependencies,
} from './types';
import { PLUGIN_NAME } from '../common';

export class CredentialManagementPlugin
implements Plugin<CredentialManagementPluginSetup, CredentialManagementPluginStart> {
public setup(core: CoreSetup): CredentialManagementPluginSetup {
// Register an application into the side navigation menu
core.application.register({
id: 'credentialManagement',
title: PLUGIN_NAME,
async mount(params: AppMountParameters) {
// Load application bundle
const { renderApp } = await import('./application');
// Get start services as specified in opensearch_dashboards.json
const [coreStart, depsStart] = await core.getStartServices();
// Render the application
return renderApp(coreStart, depsStart as AppPluginStartDependencies, params);
},
});

// Return methods that should be available to other plugins
return {
getGreeting() {
return i18n.translate('credentialManagement.greetingText', {
defaultMessage: 'Hello from {name}!',
values: {
name: PLUGIN_NAME,
},
});
},
};
}

public start(core: CoreStart): CredentialManagementPluginStart {
return {};
}

public stop() {}
}
11 changes: 11 additions & 0 deletions src/plugins/credential_management/public/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { NavigationPublicPluginStart } from '../../navigation/public';

export interface CredentialManagementPluginSetup {
getGreeting: () => string;
}
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface CredentialManagementPluginStart {}

export interface AppPluginStartDependencies {
navigation: NavigationPublicPluginStart;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* 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.
*
* Any modifications Copyright OpenSearch Contributors. See
* GitHub history for details.
*/

import { OpenSearchDashboardsRequest } from 'opensearch-dashboards/server';
import { CryptoCli } from '../crypto';

interface Credential {
readonly credential_name: string,
readonly credential_type: string,
readonly credential_material: BasicAuthCredentialMaterial | AWSIAMCredentialMaterial,
}

interface BasicAuthCredentialMaterial{
readonly user_name: string
readonly password: string
}

interface AWSIAMCredentialMaterial {
readonly encrypted_aws_iam_credential: string
}

// TODO: Refactor handler, add logger, etc
export async function createHandler(request: OpenSearchDashboardsRequest) {
const cryptoCli = CryptoCli.getInstance()
if (request.body.credential_type == 'basic_auth') {
const basicAuthCredentialMaterial: BasicAuthCredentialMaterial = {
user_name: request.body.basic_auth_credential_JSON.user_name,
password: await cryptoCli.encrypt(request.body.basic_auth_credential_JSON.password),
}
return {
credential_name: request.body.credential_name,
credential_type: request.body.credential_type,
credential_material: basicAuthCredentialMaterial
}
} else if (request.body.credential_type == 'aws_iam_credential') {
const aWSIAMCredentialMaterial: AWSIAMCredentialMaterial = {
encrypted_aws_iam_credential: await cryptoCli.encrypt(request.body.basic_auth_credential_JSON.password)
}
return {
credential_name: request.body.credential_name,
credential_type: request.body.credential_type,
credential_material: aWSIAMCredentialMaterial
}
}
}
12 changes: 12 additions & 0 deletions src/plugins/credential_management/server/credential/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
* 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.
*
* Any modifications Copyright OpenSearch Contributors. See
* GitHub history for details.
*/

export { createHandler } from './credential_manager';
Loading

0 comments on commit f274d23

Please sign in to comment.