Skip to content

Commit

Permalink
Adds UT for auth registry in data source plugin
Browse files Browse the repository at this point in the history
Signed-off-by: Bandini Bhopi <[email protected]>
  • Loading branch information
bandinib-amzn committed Feb 12, 2024
1 parent c80df0b commit f52481f
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 13 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import { AuthenticationMethodRegistery } from './authentication_methods_registry';
import { AuthMethodType } from '../../server/types';
import { AuthType } from '../../common/data_sources';

const createAuthenticationMethod = (authMethod: Partial<AuthMethodType>): AuthMethodType => ({
name: 'unknown',
authType: AuthType.NoAuth,
credentialProvider: jest.fn(),
...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',
authType: AuthType.NoAuth,
})
);

const typeA = registry.getAuthenticationMethod('typeA')!;

expect(() => {
typeA.authType = AuthType.SigV4;
}).toThrow();
expect(() => {
typeA.name = 'foo';
}).toThrow();
expect(() => {
typeA.credentialProvider = jest.fn();
}).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,24 +3,25 @@
* SPDX-License-Identifier: Apache-2.0
*/

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

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

export class AuthenticationMethodRegistery {
private readonly authMethods = new Map<string, AuthMethodValues>();
private readonly authMethods = new Map<string, AuthMethodType>();
/**
* 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, authMethodValues: AuthMethodValues) {
if (this.authMethods.has(name)) {
throw new Error(`Authentication method '${name}' is already registered`);
public registerAuthenticationMethod(method: AuthMethodType) {
if (this.authMethods.has(method.name)) {
throw new Error(`Authentication method '${method.name}' is already registered`);
}
this.authMethods.set(name, authMethodValues);
this.authMethods.set(method.name, deepFreeze(method) as AuthMethodType);
}

public getAllAuthenticationMethods() {
Expand Down
8 changes: 4 additions & 4 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 { AuthMethodValues, DataSourcePluginSetup, DataSourcePluginStart } from './types';
import { AuthMethodType, 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,12 +124,12 @@ export class DataSourcePlugin implements Plugin<DataSourcePluginSetup, DataSourc
authRegistryPromise
);

const registerCredentialProvider = (name: string, authMethodValues: AuthMethodValues) => {
this.logger.debug(`Registered Credential Provider for authType = ${name}`);
const registerCredentialProvider = (method: AuthMethodType) => {
this.logger.debug(`Registered Credential Provider for authType = ${method.name}`);
if (this.started) {
throw new Error('cannot call `registerCredentialProvider` after service startup.');
}
this.authMethodsRegistry.registerAuthenticationMethod(name, authMethodValues);
this.authMethodsRegistry.registerAuthenticationMethod(method);
};

return {
Expand Down
7 changes: 4 additions & 3 deletions src/plugins/data_source/server/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,10 @@ export type DataSourceCredentialsProvider = (
options: DataSourceCredentialsProviderOptions
) => Promise<UsernamePasswordTypedContent | SigV4Content>;

export interface AuthMethodValues {
credentialProvider: DataSourceCredentialsProvider;
export interface AuthMethodType {
name: string;
authType: AuthType;
credentialProvider: DataSourceCredentialsProvider;
}

export interface DataSourcePluginRequestContext {
Expand Down Expand Up @@ -77,7 +78,7 @@ export interface DataSourcePluginSetup {
createDataSourceError: (err: any) => DataSourceError;
dataSourceEnabled: () => boolean;
defaultClusterEnabled: () => boolean;
registerCredentialProvider: (name: string, authMethodValues: AuthMethodValues) => void;
registerCredentialProvider: (method: AuthMethodType) => void;
}

export interface DataSourcePluginStart {
Expand Down

0 comments on commit f52481f

Please sign in to comment.