Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Proposal] Refactoring data source plugin to support add-on authentication method with plug-in module #5692

Closed
Tracked by #5752
bandinib-amzn opened this issue Jan 16, 2024 · 0 comments · Fixed by #5851, #5881, #6002, #6049 or #6062
Assignees
Labels
dashboards anywhere label for dashboards anywhere enhancement New feature or request multiple datasource multiple datasource project v2.13.0

Comments

@bandinib-amzn
Copy link
Member

bandinib-amzn commented Jan 16, 2024

Overview

The purpose of this design document is to outline the approach and specifications for adding an interface in multi data source plugin that allows the seamless integration of authentication method through plugin. This enhancement aims to provide a standardized and user-friendly mechanism for extending the authentication in multi data source plugin.

User stories

Currently in multi data source, we support these auth types

  • No Auth
  • Basic Auth
  • AWS SigV4

In AWS SigV4, we only support user based access policies where IAM user comes with IAM access key & secret key that can be used to sign the request.

As a user I want additional support for role based authentication for my OpenSearch domain. Data source plugin already supports AWS SigV4 for authenticating request. I want to add required UI fields and generate access toke, secret key and session token in external plugin and pass them to data source plugin to authenticating request. As a user I want interface in data source plugin to register my own authentication method.

Requirement

Functional

  • Modularity: Enable OpenSearch-Dashboards to easily accommodate additional authentication methods through external plugins.
  • Standardization: Establish set of APIs to ensure uniformity and compatibility with existing authentication method.

Non-functional

  • Scalability: The system should be able to handle a growing number of authentication methods through plugins without a significant degradation in performance. It should support a scalable architecture that can accommodate an increasing concurrently active plugins.
  • Usability: The interface should have a consistent and intuitive design, making it easy for users to manage plugins.
  • Security and Compliance: Implement security best practices to protect against potential vulnerabilities in plugins. Ensure that the interface and plugins comply with relevant security standards and regulations.

Architecture Proposal

To meet the outlined requirements, we will introduce a new interface in data source and data source management plugins. These plugins are OpenSearch Dashboards core plugins. This will extend the current multi data source plugin’s capabilities, enabling it to be able to support multiple authentication method.

Components

  • Server side auth registry : This map will hold all the details required for signing the request.
  • DataSourcePlugin : The proposed architecture will add method to DataSourcePluginSetup to register authentication method and to DataSourcePluginStart to retrieve auth registry . This will be in charge of returning OpenSearch client for provided credentials by plugin.
  • Client side auth registry : This map will hold UI component required for authentication method.
  • DataSourceManagementPlugin: The proposed architecture will add method to DataSourceManagementPluginSetup to register authentication method with UI elements and to DataSourceManagementPluginStart to retrieve auth registry. This will be in charge of mounting UI component for user input.
  • Plugin: Plugin should add dataSource and dataSourceManagement as requiredPlugins in plugin’s JSON manifest file named opensearch_dashboards.json. requiredPlugins is an optional list of the other plugins that must be installed and enabled for this plugin to function properly. During setup in both browser-side and server-side plugin, it should register the authentication method to above core plugin.

Location of Datasource Codebase

The proposed architecture will comprise a set of back-end functionalities sitting within the server folder of the core data_source plugin and a set of front-end functionalities sitting within the public folder of the core data_source_management plugin. The interfaces to register authentication method into the registry and get authentication registry will be made accessible via the setup and start hook in plugin.ts. Any core components or plugins that list dataSource and dataSourceManagement as a dependency will have access to these interfaces. During plugin setup stage, it initiates registry to store authentication methods and provide function to register authentication method. When the plugin starts, it returns function to retrieve the authentication registry. During setup stage of both dataSource
and dataSourceManagement plugins, we need authentication registry to pass to async handler. We are using core. getStartService which allows plugins to get access to APIs available in start inside async handlers. Promise will not resolve until Core and plugin dependencies have completed start. This should only be used inside handlers registered during setup that will only be executed after start lifecycle.

# src/plugins/data_source/server/plugin.ts

public async setup(core: CoreSetup<DataSourcePluginStart>) {
    const authRegistryPromise = core.getStartServices().then(([, , selfStart]) => {
      const dataSourcePluginStart = selfStart as DataSourcePluginStart;
      return dataSourcePluginStart.getAuthenticationMethodRegistery();
    });
    .
    .
    .
    const registerCredentialProvider = (method: AuthenticationMethod) => {
      if (this.started) {
        throw new Error('cannot call `registerCredentialProvider` after service startup.');
      }
      this.authMethodsRegistry.registerAuthenticationMethod(method);
    };

    return {
      createDataSourceError: (e: any) => createDataSourceError(e),
      registerCredentialProvider, #This is new API
    };
}

public start(core: CoreStart) {
    this.started = true;
    return {
      getAuthenticationMethodRegistery: () => this.authMethodsRegistry,
    };
  }
# src/plugins/data_source_management/public/plugin.ts

public setup(core: CoreSetup<DataSourceManagementPluginStart>) {
    const registerAuthenticationMethod = (authMethod: AuthenticationMethod) => {
      if (this.started) {
        throw new Error(
          'cannot call `registerAuthenticationMethod` after data source management startup.'
        );
      }
      this.authMethodsRegistry.registerAuthenticationMethod(authMethod);
    };

    return { registerAuthenticationMethod };
}

public start(core: CoreStart) {
    this.started = true;
    return {
      getAuthenticationMethodRegistery: () => this.authMethodsRegistry,
    };
}

Interfaces and Classes

Authentication method registry:
We will maintain two registry to hold authentication method information. One in server which holds the credential provider and one in public which holds the UI elements to mount on web page.

# Registry for server-side

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

export class AuthenticationMethodRegistery {
  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: 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 AuthenticationMethod);
  }

  public getAllAuthenticationMethods() {
    return [...this.authMethods.values()];
  }

  public getAuthenticationMethod(name: string) {
    return this.authMethods.get(name);
  }
}
# Registry for browser-side

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

export class AuthenticationMethodRegistery {
  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: 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 AuthenticationMethod);
  }

  public getAllAuthenticationMethods() {
    return [...this.authMethods.values()];
  }

  public getAuthenticationMethod(name: string) {
    return this.authMethods.get(name);
  }
}

data_source plugin
1] DataSourceCredentialsProvider: Instance of DataSourceCredentialsProvider will be responsible for providing credentials to sign the request either with Basic auth or AWS SigV4 which are only supported request signing mechanism.

export interface DataSourceCredentialsProviderOptions {
  dataSourceAttr: DataSourceAttributes;
  request?: OpenSearchDashboardsRequest;
  cryptography?: CryptographyServiceSetup;
}

export type DataSourceCredentialsProvider = (
  options: DataSourceCredentialsProviderOptions
) => Promise<UsernamePasswordTypedContent | SigV4Content>;

2] DataSourceAttributes: Right now we only support UsernamePasswordTypedContent, SigV4Content and undefined as type for auth credentials. We will need to support more generic type along with existing types to allow credentials in the form of key:value pair. Also we will add new field name: AuthType | string in DataSourceAttributes to identify the authentication method. To ensure compatibility with existing auth methods, we will make default value same as type for Basic and SigV4 authentication method. auth.type in DataSourceAttributes will have only values of AuthType. auth.type will tell data_source plugin, using which method request needs to be signed. Right now we only have two ways: Basic authentication and AWS SigV4 authentication to sign the request.

export interface AuthTypeContent {
  [key: string]: string;
}

export interface DataSourceAttributes extends SavedObjectAttributes {
  title: string;
  description?: string;
  endpoint: string;
  auth: {
    type: AuthType;
    credentials: UsernamePasswordTypedContent | SigV4Content | undefined | **AuthTypeContent**;
  };
  lastUpdatedTime?: string;
  **name: AuthType | string;**
}

3] SigV4Content: This proposed architecture will also extend the ability of multi data source to support authentication using IAM role. Current architecture only supports authenticating as IAM user. As IAM role session requires temporary security credentials through assuming role, we will need additional field for sessionToken in SigV4Content interface.

export interface SigV4Content extends SavedObjectAttributes {
  accessKey: string;
  secretKey: string;
  region: string;
  service?: SigV4ServiceName;
  **sessionToken?: string;** # Newly added field to support temporary security credentials.
}

4] Client: Based on authentication method type, client will look for authentication method in auth registry to get credentials. Client pooling and caching will be handled by data_source core plugin.

# src/plugins/data_source/server/client/configure_client.ts

export const getQueryClient = async (): Promise<Client> => {
   awsCredential = authMethod?.credentialProvider({ dataSourceAttr, request, cryptography });
   type = authMethod?.authType;
   .
   .
   .
   return client; # bases on auth type.
}

data_source_management plugin
1] AuthenticationMethod: To support custom authentication method from plugin, browser side we need option for selection and then form to enter fields which would be credentials or fields which required to retrieve credentials in case role based authentication. Plugins or any component which will be using these API, need to take care of validations as well for those fields. Core data_source_management plugin will be in charge to mount those UI component in parent form and calls sever side API to test connection and save the data source.

export interface AuthenticationMethod {
  name: string;
  credentialForm: React.JSX.Element;
  credentialSourceOption: EuiSuperSelectOption<string>;
}

# Map storing UI elements for custom authentication method
authMethods = new Map<string, AuthenticationMethod>();

Tracking issue

#5838

Follow up Issue

#5694

@bandinib-amzn bandinib-amzn added the enhancement New feature or request label Jan 16, 2024
@bandinib-amzn bandinib-amzn self-assigned this Jan 16, 2024
@bandinib-amzn bandinib-amzn added dashboards anywhere label for dashboards anywhere v2.13.0 multiple datasource multiple datasource project and removed untriaged labels Jan 16, 2024
@bandinib-amzn bandinib-amzn changed the title [Proposal] Add interface in multi data source plugin to register authentication method by plugin. [Proposal] Refactoring data source plugin to support add-on authentication method with plug-in module Feb 6, 2024
@bandinib-amzn bandinib-amzn changed the title [Proposal] Refactoring data source plugin to support add-on authentication method with plug-in module [Proposal][WIP] Refactoring data source plugin to support add-on authentication method with plug-in module Feb 6, 2024
@bandinib-amzn bandinib-amzn changed the title [Proposal][WIP] Refactoring data source plugin to support add-on authentication method with plug-in module [Proposal] Refactoring data source plugin to support add-on authentication method with plug-in module Feb 7, 2024
@bandinib-amzn bandinib-amzn reopened this Feb 14, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment