Skip to content

Commit

Permalink
[Search][Onboarding] api-key plugin (#191926)
Browse files Browse the repository at this point in the history
## Summary
Kibana plugin that helps manage the session for the api-key that
provides two exports:
- React custom hook to read the api-key stored in session. This hook
should return the api-key if it exists, otherwise null.
- Component to present either the api key in storage or action to create
api key and store into sessionStorage after callback.
<img width="1255" alt="Screenshot 2024-09-27 at 20 52 52"
src="https://github.com/user-attachments/assets/dc5bcd39-7fe6-433c-8aaa-ad3578a68b62">
<img width="1248" alt="Screenshot 2024-09-27 at 20 52 39"
src="https://github.com/user-attachments/assets/d760c163-9017-4f57-ba1a-38ee8ee21534">
<img width="676" alt="Screenshot 2024-09-27 at 20 52 28"
src="https://github.com/user-attachments/assets/e908d20a-7e0c-4f3b-9ea2-8e2d1a74c9eb">
  • Loading branch information
yansavitski authored Oct 2, 2024
1 parent d1f24b0 commit c5aa739
Show file tree
Hide file tree
Showing 52 changed files with 1,208 additions and 126 deletions.
3 changes: 3 additions & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -753,6 +753,8 @@ src/plugins/screenshot_mode @elastic/appex-sharedux
x-pack/examples/screenshotting_example @elastic/appex-sharedux
x-pack/plugins/screenshotting @elastic/kibana-reporting-services
packages/kbn-screenshotting-server @elastic/appex-sharedux
packages/kbn-search-api-keys-components @elastic/search-kibana
packages/kbn-search-api-keys-server @elastic/search-kibana
packages/kbn-search-api-panels @elastic/search-kibana
x-pack/plugins/search_assistant @elastic/search-kibana
packages/kbn-search-connectors @elastic/search-kibana
Expand All @@ -766,6 +768,7 @@ x-pack/plugins/search_inference_endpoints @elastic/search-kibana
x-pack/plugins/search_notebooks @elastic/search-kibana
x-pack/plugins/search_playground @elastic/search-kibana
packages/kbn-search-response-warnings @elastic/kibana-data-discovery
x-pack/packages/search/shared_ui @elastic/search-kibana
packages/kbn-search-types @elastic/kibana-data-discovery
x-pack/plugins/searchprofiler @elastic/kibana-management
x-pack/test/security_api_integration/packages/helpers @elastic/kibana-security
Expand Down
1 change: 1 addition & 0 deletions .i18nrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@
"server": "src/legacy/server",
"share": ["src/plugins/share", "packages/kbn-reporting-share"],
"sharedUXPackages": "packages/shared-ux",
"searchApiKeysComponents": "packages/kbn-search-api-keys-components",
"searchApiPanels": "packages/kbn-search-api-panels/",
"searchErrors": "packages/kbn-search-errors",
"searchIndexDocuments": "packages/kbn-search-index-documents",
Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -771,6 +771,8 @@
"@kbn/screenshotting-example-plugin": "link:x-pack/examples/screenshotting_example",
"@kbn/screenshotting-plugin": "link:x-pack/plugins/screenshotting",
"@kbn/screenshotting-server": "link:packages/kbn-screenshotting-server",
"@kbn/search-api-keys-components": "link:packages/kbn-search-api-keys-components",
"@kbn/search-api-keys-server": "link:packages/kbn-search-api-keys-server",
"@kbn/search-api-panels": "link:packages/kbn-search-api-panels",
"@kbn/search-assistant": "link:x-pack/plugins/search_assistant",
"@kbn/search-connectors": "link:packages/kbn-search-connectors",
Expand All @@ -784,6 +786,7 @@
"@kbn/search-notebooks": "link:x-pack/plugins/search_notebooks",
"@kbn/search-playground": "link:x-pack/plugins/search_playground",
"@kbn/search-response-warnings": "link:packages/kbn-search-response-warnings",
"@kbn/search-shared-ui": "link:x-pack/packages/search/shared_ui",
"@kbn/search-types": "link:packages/kbn-search-types",
"@kbn/searchprofiler-plugin": "link:x-pack/plugins/searchprofiler",
"@kbn/security-api-key-management": "link:x-pack/packages/security/api_key_management",
Expand Down
3 changes: 3 additions & 0 deletions packages/kbn-search-api-keys-components/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Search API Key Components

The Search API Keys components package is a shared components and utilities to simplify managing the API Keys experience for elasticsearch users across stack and serverless search solutions.
13 changes: 13 additions & 0 deletions packages/kbn-search-api-keys-components/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

export * from './src/components/api_key_flyout_wrapper';
export * from './src/components/api_key_form';
export * from './src/hooks/use_search_api_key';
export * from './src/providers/search_api_key_provider';
20 changes: 20 additions & 0 deletions packages/kbn-search-api-keys-components/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

module.exports = {
preset: '@kbn/test',
rootDir: '../..',
roots: ['<rootDir>/packages/kbn-search-api-keys-components'],
coverageDirectory:
'<rootDir>/target/kibana-coverage/jest/packages/kbn-search-api-keys-components',
coverageReporters: ['text', 'html'],
collectCoverageFrom: [
'<rootDir>/packages/kbn-search-api-keys-components/public/{components,hooks}/**/*.{ts,tsx}',
],
};
5 changes: 5 additions & 0 deletions packages/kbn-search-api-keys-components/kibana.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"type": "shared-browser",
"id": "@kbn/search-api-keys-components",
"owner": "@elastic/search-kibana"
}
6 changes: 6 additions & 0 deletions packages/kbn-search-api-keys-components/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"name": "@kbn/search-api-keys-components",
"private": true,
"version": "1.0.0",
"license": "Elastic License 2.0 OR AGPL-3.0-only OR SSPL-1.0"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

import React from 'react';
import { ApiKeyFlyout, ApiKeyFlyoutProps } from '@kbn/security-api-key-management';
import type { SecurityCreateApiKeyResponse } from '@elastic/elasticsearch/lib/api/typesWithBodyKey';

const API_KEY_NAME = 'Unrestricted API Key';

type ApiKeyFlyoutWrapperProps = Pick<ApiKeyFlyoutProps, 'onCancel'> & {
onSuccess?: (createApiKeyResponse: SecurityCreateApiKeyResponse) => void;
};

export const ApiKeyFlyoutWrapper: React.FC<ApiKeyFlyoutWrapperProps> = ({
onCancel,
onSuccess,
}) => {
return <ApiKeyFlyout onCancel={onCancel} onSuccess={onSuccess} defaultName={API_KEY_NAME} />;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

import React, { useState } from 'react';
import {
EuiBadge,
EuiButton,
EuiButtonIcon,
EuiFlexGroup,
EuiFlexItem,
EuiTitle,
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import { i18n } from '@kbn/i18n';
import { FormInfoField } from '@kbn/search-shared-ui';
import { ApiKeyFlyoutWrapper } from './api_key_flyout_wrapper';
import { useSearchApiKey } from '../hooks/use_search_api_key';
import { Status } from '../constants';

interface ApiKeyFormProps {
hasTitle?: boolean;
}

export const ApiKeyForm: React.FC<ApiKeyFormProps> = ({ hasTitle = true }) => {
const [showFlyout, setShowFlyout] = useState(false);
const { apiKey, status, updateApiKey, toggleApiKeyVisibility, displayedApiKey, apiKeyIsVisible } =
useSearchApiKey();

const titleLocale = i18n.translate('searchApiKeysComponents.apiKeyForm.title', {
defaultMessage: 'API Key',
});

if (apiKey && displayedApiKey) {
return (
<FormInfoField
label={hasTitle ? titleLocale : undefined}
value={displayedApiKey}
copyValue={apiKey}
dataTestSubj="apiKeyFormAPIKey"
actions={[
<EuiButtonIcon
iconType={apiKeyIsVisible ? 'eyeClosed' : 'eye'}
color="success"
onClick={toggleApiKeyVisibility}
data-test-subj="showAPIKeyButton"
aria-label={i18n.translate('searchApiKeysComponents.apiKeyForm.showApiKey', {
defaultMessage: 'Show API Key',
})}
/>,
]}
/>
);
}

return (
<EuiFlexGroup alignItems="center" gutterSize="s" justifyContent="flexStart" responsive={false}>
{hasTitle && (
<EuiFlexItem grow={0}>
<EuiTitle size="xxxs" css={{ whiteSpace: 'nowrap' }}>
<h6>{titleLocale}</h6>
</EuiTitle>
</EuiFlexItem>
)}
{status === Status.showUserPrivilegesError && (
<EuiFlexItem grow={0}>
<EuiBadge data-test-subj="apiKeyFormNoUserPrivileges">
{i18n.translate('searchApiKeysComponents.apiKeyForm.noUserPrivileges', {
defaultMessage: "You don't have access to manage API keys",
})}
</EuiBadge>
</EuiFlexItem>
)}
{status === Status.showCreateButton && (
<EuiFlexItem grow={0}>
<EuiButton
color="primary"
size="s"
iconSide="left"
iconType="key"
onClick={() => setShowFlyout(true)}
data-test-subj="createAPIKeyButton"
>
<FormattedMessage
id="searchApiKeysComponents.apiKeyForm.createButton"
defaultMessage="Create an API Key"
/>
</EuiButton>
{showFlyout && (
<ApiKeyFlyoutWrapper onCancel={() => setShowFlyout(false)} onSuccess={updateApiKey} />
)}
</EuiFlexItem>
)}
</EuiFlexGroup>
);
};
17 changes: 17 additions & 0 deletions packages/kbn-search-api-keys-components/src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

export enum Status {
uninitialized = 'uninitialized',
loading = 'loading',
showCreateButton = 'showCreateButton',
showHiddenKey = 'showHiddenKey',
showPreviewKey = 'showPreviewKey',
showUserPrivilegesError = 'showUserPrivilegesError',
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

import { useContext, useEffect } from 'react';
import { ApiKeyContext } from '../providers/search_api_key_provider';

export const useSearchApiKey = () => {
const { initialiseKey, ...context } = useContext(ApiKeyContext);
useEffect(() => {
initialiseKey();
}, [initialiseKey]);
return context;
};
Loading

0 comments on commit c5aa739

Please sign in to comment.