diff --git a/CHANGELOG.md b/CHANGELOG.md index ee4f091d3d7..ac6c30e58d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - [Discover] Enhanced the data source selector with added sorting functionality ([#5609](https://github.com/opensearch-project/OpenSearch-Dashboards/issues/5609)) - [Multiple Datasource] Add datasource picker component and use it in devtools and tutorial page when multiple datasource is enabled ([#5756](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5756)) - [Multiple Datasource] Add datasource picker to import saved object flyout when multiple data source is enabled ([#5781](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5781)) +- [Multiple Datasource] Add interfaces to register add-on authentication method from plug-in module ([#5851](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5851)) ### 🐛 Bug Fixes diff --git a/src/plugins/data_source/server/auth_registry/authentication_methods_registry.test.ts b/src/plugins/data_source/server/auth_registry/authentication_methods_registry.test.ts index fa589321d29..c7692acee78 100644 --- a/src/plugins/data_source/server/auth_registry/authentication_methods_registry.test.ts +++ b/src/plugins/data_source/server/auth_registry/authentication_methods_registry.test.ts @@ -4,10 +4,12 @@ */ import { AuthenticationMethodRegistery } from './authentication_methods_registry'; -import { AuthMethodType } from '../../server/types'; +import { AuthenticationMethod } from '../../server/types'; import { AuthType } from '../../common/data_sources'; -const createAuthenticationMethod = (authMethod: Partial): AuthMethodType => ({ +const createAuthenticationMethod = ( + authMethod: Partial +): AuthenticationMethod => ({ name: 'unknown', authType: AuthType.NoAuth, credentialProvider: jest.fn(), diff --git a/src/plugins/data_source/server/auth_registry/authentication_methods_registry.ts b/src/plugins/data_source/server/auth_registry/authentication_methods_registry.ts index 329ee0c6590..e2f39498e00 100644 --- a/src/plugins/data_source/server/auth_registry/authentication_methods_registry.ts +++ b/src/plugins/data_source/server/auth_registry/authentication_methods_registry.ts @@ -4,7 +4,7 @@ */ import { deepFreeze } from '@osd/std'; -import { AuthMethodType } from '../../server/types'; +import { AuthenticationMethod } from '../../server/types'; export type IAuthenticationMethodRegistery = Omit< AuthenticationMethodRegistery, @@ -12,16 +12,16 @@ export type IAuthenticationMethodRegistery = Omit< >; export class AuthenticationMethodRegistery { - private readonly authMethods = new Map(); + private readonly authMethods = new Map(); /** * Register a authMethods with function to return credentials inside the registry. * Authentication Method can only be registered once. subsequent calls with the same method name will throw an error. */ - public registerAuthenticationMethod(method: AuthMethodType) { + public registerAuthenticationMethod(method: AuthenticationMethod) { if (this.authMethods.has(method.name)) { throw new Error(`Authentication method '${method.name}' is already registered`); } - this.authMethods.set(method.name, deepFreeze(method) as AuthMethodType); + this.authMethods.set(method.name, deepFreeze(method) as AuthenticationMethod); } public getAllAuthenticationMethods() { diff --git a/src/plugins/data_source/server/plugin.ts b/src/plugins/data_source/server/plugin.ts index 6afa378c5a0..1a90d22960e 100644 --- a/src/plugins/data_source/server/plugin.ts +++ b/src/plugins/data_source/server/plugin.ts @@ -23,7 +23,7 @@ import { LoggingAuditor } from './audit/logging_auditor'; import { CryptographyService, CryptographyServiceSetup } from './cryptography_service'; import { DataSourceService, DataSourceServiceSetup } from './data_source_service'; import { DataSourceSavedObjectsClientWrapper, dataSource } from './saved_objects'; -import { AuthMethodType, DataSourcePluginSetup, DataSourcePluginStart } from './types'; +import { AuthenticationMethod, DataSourcePluginSetup, DataSourcePluginStart } from './types'; import { DATA_SOURCE_SAVED_OBJECT_TYPE } from '../common'; // eslint-disable-next-line @osd/eslint/no-restricted-paths @@ -124,7 +124,7 @@ export class DataSourcePlugin implements Plugin { + const registerCredentialProvider = (method: AuthenticationMethod) => { this.logger.debug(`Registered Credential Provider for authType = ${method.name}`); if (this.started) { throw new Error('cannot call `registerCredentialProvider` after service startup.'); diff --git a/src/plugins/data_source/server/types.ts b/src/plugins/data_source/server/types.ts index a97c1fc8536..b54eb5db0eb 100644 --- a/src/plugins/data_source/server/types.ts +++ b/src/plugins/data_source/server/types.ts @@ -46,7 +46,7 @@ export type DataSourceCredentialsProvider = ( options: DataSourceCredentialsProviderOptions ) => Promise; -export interface AuthMethodType { +export interface AuthenticationMethod { name: string; authType: AuthType; credentialProvider: DataSourceCredentialsProvider; @@ -78,7 +78,7 @@ export interface DataSourcePluginSetup { createDataSourceError: (err: any) => DataSourceError; dataSourceEnabled: () => boolean; defaultClusterEnabled: () => boolean; - registerCredentialProvider: (method: AuthMethodType) => void; + registerCredentialProvider: (method: AuthenticationMethod) => void; } export interface DataSourcePluginStart { diff --git a/src/plugins/data_source_management/public/auth_registry/authentication_methods_registry.test.ts b/src/plugins/data_source_management/public/auth_registry/authentication_methods_registry.test.ts new file mode 100644 index 00000000000..b98cc4cefee --- /dev/null +++ b/src/plugins/data_source_management/public/auth_registry/authentication_methods_registry.test.ts @@ -0,0 +1,110 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { + AuthenticationMethodRegistery, + AuthenticationMethod, +} from './authentication_methods_registry'; +import React from 'react'; + +const createAuthenticationMethod = ( + authMethod: Partial +): AuthenticationMethod => ({ + name: 'unknown', + credentialForm: React.createElement('div', {}, 'Hello, world!'), + credentialSourceOption: { + value: 'unknown', + }, + ...authMethod, +}); + +describe('AuthenticationMethodRegistery', () => { + let registry: AuthenticationMethodRegistery; + + beforeEach(() => { + registry = new AuthenticationMethodRegistery(); + }); + + it('allows to register authentication method', () => { + registry.registerAuthenticationMethod(createAuthenticationMethod({ name: 'typeA' })); + registry.registerAuthenticationMethod(createAuthenticationMethod({ name: 'typeB' })); + registry.registerAuthenticationMethod(createAuthenticationMethod({ name: 'typeC' })); + + expect( + registry + .getAllAuthenticationMethods() + .map((type) => type.name) + .sort() + ).toEqual(['typeA', 'typeB', 'typeC']); + }); + + it('throws when trying to register the same authentication method twice', () => { + registry.registerAuthenticationMethod(createAuthenticationMethod({ name: 'typeA' })); + registry.registerAuthenticationMethod(createAuthenticationMethod({ name: 'typeB' })); + expect(() => { + registry.registerAuthenticationMethod(createAuthenticationMethod({ name: 'typeA' })); + }).toThrowErrorMatchingInlineSnapshot(`"Authentication method 'typeA' is already registered"`); + }); + + describe('#getAuthenticationMethod', () => { + it(`retrieve a type by it's name`, () => { + const typeA = createAuthenticationMethod({ name: 'typeA' }); + const typeB = createAuthenticationMethod({ name: 'typeB' }); + registry.registerAuthenticationMethod(typeA); + registry.registerAuthenticationMethod(typeB); + registry.registerAuthenticationMethod(createAuthenticationMethod({ name: 'typeC' })); + + expect(registry.getAuthenticationMethod('typeA')).toEqual(typeA); + expect(registry.getAuthenticationMethod('typeB')).toEqual(typeB); + expect(registry.getAuthenticationMethod('unknownType')).toBeUndefined(); + }); + + it('forbids to mutate the registered types', () => { + registry.registerAuthenticationMethod( + createAuthenticationMethod({ + name: 'typeA', + }) + ); + + const typeA = registry.getAuthenticationMethod('typeA')!; + + expect(() => { + typeA.credentialForm = React.createElement('div', {}, 'Welcome!'); + }).toThrow(); + expect(() => { + typeA.credentialSourceOption = { + value: 'typeA', + }; + }).toThrow(); + }); + }); + + describe('#getAllTypes', () => { + it('returns all registered types', () => { + const typeA = createAuthenticationMethod({ name: 'typeA' }); + const typeB = createAuthenticationMethod({ name: 'typeB' }); + const typeC = createAuthenticationMethod({ name: 'typeC' }); + registry.registerAuthenticationMethod(typeA); + registry.registerAuthenticationMethod(typeB); + + const registered = registry.getAllAuthenticationMethods(); + expect(registered.length).toEqual(2); + expect(registered).toContainEqual(typeA); + expect(registered).toContainEqual(typeB); + expect(registered).not.toContainEqual(typeC); + }); + + it('does not mutate the registered types when altering the list', () => { + registry.registerAuthenticationMethod(createAuthenticationMethod({ name: 'typeA' })); + registry.registerAuthenticationMethod(createAuthenticationMethod({ name: 'typeB' })); + registry.registerAuthenticationMethod(createAuthenticationMethod({ name: 'typeC' })); + + const types = registry.getAllAuthenticationMethods(); + types.splice(0, 3); + + expect(registry.getAllAuthenticationMethods().length).toEqual(3); + }); + }); +}); diff --git a/src/plugins/data_source_management/public/auth_registry/authentication_methods_registry.ts b/src/plugins/data_source_management/public/auth_registry/authentication_methods_registry.ts index 6f8e81ae9ec..98cff913483 100644 --- a/src/plugins/data_source_management/public/auth_registry/authentication_methods_registry.ts +++ b/src/plugins/data_source_management/public/auth_registry/authentication_methods_registry.ts @@ -3,13 +3,13 @@ * SPDX-License-Identifier: Apache-2.0 */ +import { deepFreeze } from '@osd/std'; import { EuiSuperSelectOption } from '@elastic/eui'; -import { AuthTypeContent } from 'src/plugins/data_source/common/data_sources'; -export interface AuthMethodUIElements { +export interface AuthenticationMethod { + name: string; credentialForm: React.JSX.Element; credentialSourceOption: EuiSuperSelectOption; - credentialsFormValues: AuthTypeContent; } export type IAuthenticationMethodRegistery = Omit< @@ -18,16 +18,16 @@ export type IAuthenticationMethodRegistery = Omit< >; export class AuthenticationMethodRegistery { - private readonly authMethods = new Map(); + private readonly authMethods = new Map(); /** * Register a authMethods with function to return credentials inside the registry. * Authentication Method can only be registered once. subsequent calls with the same method name will throw an error. */ - public registerAuthenticationMethod(name: string, authMethodUIElements: AuthMethodUIElements) { - if (this.authMethods.has(name)) { - throw new Error(`Authentication method '${name}' is already registered`); + public registerAuthenticationMethod(method: AuthenticationMethod) { + if (this.authMethods.has(method.name)) { + throw new Error(`Authentication method '${method.name}' is already registered`); } - this.authMethods.set(name, authMethodUIElements); + this.authMethods.set(method.name, deepFreeze(method) as AuthenticationMethod); } public getAllAuthenticationMethods() { diff --git a/src/plugins/data_source_management/public/auth_registry/index.ts b/src/plugins/data_source_management/public/auth_registry/index.ts index f731b652839..5cbadd12a51 100644 --- a/src/plugins/data_source_management/public/auth_registry/index.ts +++ b/src/plugins/data_source_management/public/auth_registry/index.ts @@ -5,6 +5,6 @@ export { IAuthenticationMethodRegistery, - AuthMethodUIElements, + AuthenticationMethod, AuthenticationMethodRegistery, } from './authentication_methods_registry'; diff --git a/src/plugins/data_source_management/public/plugin.ts b/src/plugins/data_source_management/public/plugin.ts index c915e6d79d1..0c7123e47a9 100644 --- a/src/plugins/data_source_management/public/plugin.ts +++ b/src/plugins/data_source_management/public/plugin.ts @@ -11,7 +11,7 @@ import { ManagementSetup } from '../../management/public'; import { IndexPatternManagementSetup } from '../../index_pattern_management/public'; import { DataSourceColumn } from './components/data_source_column/data_source_column'; import { - AuthMethodUIElements, + AuthenticationMethod, IAuthenticationMethodRegistery, AuthenticationMethodRegistery, } from './auth_registry'; @@ -22,7 +22,7 @@ export interface DataSourceManagementSetupDependencies { } export interface DataSourceManagementPluginSetup { - registerAuthenticationMethod: (name: string, authMethodValues: AuthMethodUIElements) => void; + registerAuthenticationMethod: (authMethodValues: AuthenticationMethod) => void; } export interface DataSourceManagementPluginStart { @@ -69,16 +69,13 @@ export class DataSourceManagementPlugin }, }); - const registerAuthenticationMethod = ( - name: string, - authMethodUIElements: AuthMethodUIElements - ) => { + const registerAuthenticationMethod = (authMethod: AuthenticationMethod) => { if (this.started) { throw new Error( 'cannot call `registerAuthenticationMethod` after data source management startup.' ); } - this.authMethodsRegistry.registerAuthenticationMethod(name, authMethodUIElements); + this.authMethodsRegistry.registerAuthenticationMethod(authMethod); }; return { registerAuthenticationMethod };