Skip to content

Commit

Permalink
Adds UT for auth registry in data source management plugin
Browse files Browse the repository at this point in the history
Signed-off-by: Bandini Bhopi <bandinib@amazon.com>
  • Loading branch information
bandinib-amzn committed Feb 9, 2024
1 parent ab6244b commit 3711d37
Show file tree
Hide file tree
Showing 9 changed files with 136 additions and 26 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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>): AuthMethodType => ({
const createAuthenticationMethod = (
authMethod: Partial<AuthenticationMethod>
): AuthenticationMethod => ({
name: 'unknown',
authType: AuthType.NoAuth,
credentialProvider: jest.fn(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,24 @@
*/

import { deepFreeze } from '@osd/std';
import { AuthMethodType } from '../../server/types';
import { AuthenticationMethod } from '../../server/types';

export type IAuthenticationMethodRegistery = Omit<
AuthenticationMethodRegistery,
'registerAuthenticationMethod'
>;

export class AuthenticationMethodRegistery {
private readonly authMethods = new Map<string, AuthMethodType>();
private readonly authMethods = new Map<string, AuthenticationMethod>();
/**
* 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() {
Expand Down
4 changes: 2 additions & 2 deletions src/plugins/data_source/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -124,7 +124,7 @@ export class DataSourcePlugin implements Plugin<DataSourcePluginSetup, DataSourc
authRegistryPromise
);

const registerCredentialProvider = (method: AuthMethodType) => {
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.');
Expand Down
4 changes: 2 additions & 2 deletions src/plugins/data_source/server/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export type DataSourceCredentialsProvider = (
options: DataSourceCredentialsProviderOptions
) => Promise<UsernamePasswordTypedContent | SigV4Content>;

export interface AuthMethodType {
export interface AuthenticationMethod {
name: string;
authType: AuthType;
credentialProvider: DataSourceCredentialsProvider;
Expand Down Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
@@ -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>
): 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);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -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<string>;
credentialsFormValues: AuthTypeContent;
}

export type IAuthenticationMethodRegistery = Omit<
Expand All @@ -18,16 +18,16 @@ export type IAuthenticationMethodRegistery = Omit<
>;

export class AuthenticationMethodRegistery {
private readonly authMethods = new Map<string, AuthMethodUIElements>();
private readonly authMethods = new Map<string, AuthenticationMethod>();
/**
* 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() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@

export {
IAuthenticationMethodRegistery,
AuthMethodUIElements,
AuthenticationMethod,
AuthenticationMethodRegistery,
} from './authentication_methods_registry';
11 changes: 4 additions & 7 deletions src/plugins/data_source_management/public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -22,7 +22,7 @@ export interface DataSourceManagementSetupDependencies {
}

export interface DataSourceManagementPluginSetup {
registerAuthenticationMethod: (name: string, authMethodValues: AuthMethodUIElements) => void;
registerAuthenticationMethod: (authMethodValues: AuthenticationMethod) => void;
}

export interface DataSourceManagementPluginStart {
Expand Down Expand Up @@ -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 };
Expand Down

0 comments on commit 3711d37

Please sign in to comment.