diff --git a/x-pack/legacy/plugins/fleet/public/hooks/use_input.tsx b/x-pack/legacy/plugins/fleet/public/hooks/use_input.tsx new file mode 100644 index 0000000000000..4aa0ad7155d2f --- /dev/null +++ b/x-pack/legacy/plugins/fleet/public/hooks/use_input.tsx @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; + +export function useInput(defaultValue = '') { + const [value, setValue] = React.useState(defaultValue); + + return { + value, + props: { + onChange: (e: React.ChangeEvent) => { + setValue(e.target.value); + }, + value, + }, + clear: () => { + setValue(''); + }, + }; +} diff --git a/x-pack/legacy/plugins/fleet/public/hooks/use_pagination.tsx b/x-pack/legacy/plugins/fleet/public/hooks/use_pagination.tsx index 16c9262934478..b478c039dad64 100644 --- a/x-pack/legacy/plugins/fleet/public/hooks/use_pagination.tsx +++ b/x-pack/legacy/plugins/fleet/public/hooks/use_pagination.tsx @@ -7,11 +7,13 @@ import { useState } from 'react'; import { DEFAULT_AGENTS_PAGE_SIZE, AGENTS_PAGE_SIZE_OPTIONS } from '../../common/constants'; +export interface Pagination { + currentPage: number; + pageSize: number; +} + export function usePagination() { - const [pagination, setPagination] = useState<{ - currentPage: number; - pageSize: number; - }>({ + const [pagination, setPagination] = useState({ currentPage: 1, pageSize: DEFAULT_AGENTS_PAGE_SIZE, }); diff --git a/x-pack/legacy/plugins/fleet/public/lib/compose/kibana.ts b/x-pack/legacy/plugins/fleet/public/lib/compose/kibana.ts index bf5d456e528ca..84f5e36cce478 100644 --- a/x-pack/legacy/plugins/fleet/public/lib/compose/kibana.ts +++ b/x-pack/legacy/plugins/fleet/public/lib/compose/kibana.ts @@ -25,6 +25,7 @@ import { FrontendLibs } from '../types'; import { PLUGIN } from '../../../common/constants/plugin'; import { FrameworkLib } from '../framework'; import { INDEX_NAMES } from '../../../common/constants'; +import { EnrollmentApiKeyLib } from '../enrollment_api_key'; // A super early spot in kibana loading that we can use to hook before most other things const onKibanaReady = chrome.dangerouslyGetActiveInjector; @@ -35,6 +36,7 @@ export function compose(): FrontendLibs { const elasticsearchLib = new ElasticsearchLib(esAdapter); const agents = new AgentsLib(new RestAgentAdapter(api)); const policies = new PoliciesLib(new RestPolicyAdapter(api)); + const enrollmentApiKeys = new EnrollmentApiKeyLib(api); const framework = new FrameworkLib( new KibanaFrameworkAdapter( @@ -54,6 +56,7 @@ export function compose(): FrontendLibs { elasticsearch: elasticsearchLib, agents, policies, + enrollmentApiKeys, }; return libs; } diff --git a/x-pack/legacy/plugins/fleet/public/lib/compose/memory.ts b/x-pack/legacy/plugins/fleet/public/lib/compose/memory.ts deleted file mode 100644 index 60f7ffd689759..0000000000000 --- a/x-pack/legacy/plugins/fleet/public/lib/compose/memory.ts +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import 'ui/autoload/all'; -// @ts-ignore: path dynamic for kibana -import { management } from 'ui/management'; -// @ts-ignore: path dynamic for kibana -import { toastNotifications } from 'ui/notify'; -// @ts-ignore: path dynamic for kibana -import { uiModules } from 'ui/modules'; -// @ts-ignore: path dynamic for kibana -import routes from 'ui/routes'; -// @ts-ignore: path dynamic for kibana -import { MemoryAgentAdapter } from '../adapters/agent/memory_agent_adapter'; -import { PolicyAdapter } from '../adapters/policy/memory_policy_adapter'; -import { KibanaFrameworkAdapter } from '../adapters/framework/kibana_framework_adapter'; -import { AgentsLib } from '../agent'; -import { PoliciesLib } from '../policy'; -import { FrameworkLib } from '../framework'; -import { FrontendLibs } from '../types'; -import { MemoryElasticsearchAdapter } from '../adapters/elasticsearch/memory'; -import { AutocompleteSuggestion } from '../../../../../../../src/plugins/data/public'; -import { ElasticsearchLib } from '../elasticsearch'; - -const onKibanaReady = uiModules.get('kibana').run; - -export function compose( - mockIsKueryValid: (kuery: string) => boolean, - mockKueryToEsQuery: (kuery: string) => string, - suggestions: AutocompleteSuggestion[] -): FrontendLibs { - const esAdapter = new MemoryElasticsearchAdapter( - mockIsKueryValid, - mockKueryToEsQuery, - suggestions - ); - const elasticsearchLib = new ElasticsearchLib(esAdapter); - - const agents = new AgentsLib(new MemoryAgentAdapter([])); - const policies = new PoliciesLib(new PolicyAdapter([])); - - const pluginUIModule = uiModules.get('app/fleet'); - - const framework = new FrameworkLib( - new KibanaFrameworkAdapter( - pluginUIModule, - management, - routes, - () => '', - onKibanaReady, - null, - '7.0.0', - toastNotifications - ) - ); - const libs: FrontendLibs = { - framework, - elasticsearch: elasticsearchLib, - agents, - policies, - }; - return libs; -} diff --git a/x-pack/legacy/plugins/fleet/public/lib/compose/scripts.ts b/x-pack/legacy/plugins/fleet/public/lib/compose/scripts.ts deleted file mode 100644 index f6a0b3bc9fbab..0000000000000 --- a/x-pack/legacy/plugins/fleet/public/lib/compose/scripts.ts +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { RestAgentAdapter } from '../adapters/agent/rest_agent_adapter'; -import { RestPolicyAdapter } from '../adapters/policy/rest_policy_adapter'; -import { MemoryElasticsearchAdapter } from '../adapters/elasticsearch/memory'; -import { TestingFrameworkAdapter } from '../adapters/framework/testing_framework_adapter'; -import { NodeAxiosAPIAdapter } from '../adapters/rest_api/node_axios_api_adapter'; -import { AgentsLib } from '../agent'; -import { PoliciesLib } from '../policy'; -import { ElasticsearchLib } from '../elasticsearch'; -import { FrameworkLib } from '../framework'; -import { FrontendLibs } from '../types'; - -export function compose(basePath: string): FrontendLibs { - const api = new NodeAxiosAPIAdapter('elastic', 'changeme', basePath); - const esAdapter = new MemoryElasticsearchAdapter( - () => true, - () => '', - [] - ); - const elasticsearchLib = new ElasticsearchLib(esAdapter); - - const agents = new AgentsLib(new RestAgentAdapter(api)); - const policies = new PoliciesLib(new RestPolicyAdapter(api)); - - const framework = new FrameworkLib( - new TestingFrameworkAdapter( - { - basePath, - license: { - type: 'gold', - expired: false, - expiry_date_in_millis: 34353453452345, - }, - security: { - enabled: true, - available: true, - }, - settings: {}, - }, - { - username: 'joeuser', - roles: ['fleet_admin'], - enabled: true, - full_name: null, - email: null, - }, - '6.7.0' - ) - ); - - const libs: FrontendLibs = { - framework, - elasticsearch: elasticsearchLib, - agents, - policies, - }; - return libs; -} diff --git a/x-pack/legacy/plugins/fleet/public/lib/enrollment_api_key.ts b/x-pack/legacy/plugins/fleet/public/lib/enrollment_api_key.ts new file mode 100644 index 0000000000000..512ed367bc65b --- /dev/null +++ b/x-pack/legacy/plugins/fleet/public/lib/enrollment_api_key.ts @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + ReturnTypeList, + ReturnTypeGet, + ReturnTypeCreate, + ReturnTypeDelete, +} from '../../common/return_types'; +import { RestAPIAdapter } from './adapters/rest_api/adapter_types'; +import { Pagination } from '../hooks/use_pagination'; +import { EnrollmentApiKey } from '../../common/types/domain_data'; + +export class EnrollmentApiKeyLib { + constructor(private readonly rest: RestAPIAdapter) {} + + public async listKeys(pagination: Pagination) { + return await this.rest.get>('/api/fleet/enrollment-api-keys', { + page: pagination.currentPage, + perPage: pagination.pageSize, + }); + } + + public async get(keyId: string) { + return await this.rest.get>( + `/api/fleet/enrollment-api-keys/${keyId}` + ); + } + + public async delete(keyId: string) { + return await this.rest.delete(`/api/fleet/enrollment-api-keys/${keyId}`); + } + + public async create(data: { name: string; policyId: string }) { + return await this.rest.post>( + `/api/fleet/enrollment-api-keys`, + { + name: data.name, + policy_id: data.policyId, + } + ); + } +} diff --git a/x-pack/legacy/plugins/fleet/public/lib/types.ts b/x-pack/legacy/plugins/fleet/public/lib/types.ts index c7b33e1b8a2ea..eebb2a806407c 100644 --- a/x-pack/legacy/plugins/fleet/public/lib/types.ts +++ b/x-pack/legacy/plugins/fleet/public/lib/types.ts @@ -10,12 +10,14 @@ import { AgentsLib } from './agent'; import { PoliciesLib } from './policy'; import { ElasticsearchLib } from './elasticsearch'; import { FrameworkLib } from './framework'; +import { EnrollmentApiKeyLib } from './enrollment_api_key'; export interface FrontendLibs { elasticsearch: ElasticsearchLib; framework: FrameworkLib; agents: AgentsLib; policies: PoliciesLib; + enrollmentApiKeys: EnrollmentApiKeyLib; } export type FramworkAdapterConstructable = new (uiModule: IModule) => FrameworkAdapter; diff --git a/x-pack/legacy/plugins/fleet/public/pages/agent_details/components/metadata_form.tsx b/x-pack/legacy/plugins/fleet/public/pages/agent_details/components/metadata_form.tsx index 6df53cd0ec1d7..45b62a0823497 100644 --- a/x-pack/legacy/plugins/fleet/public/pages/agent_details/components/metadata_form.tsx +++ b/x-pack/legacy/plugins/fleet/public/pages/agent_details/components/metadata_form.tsx @@ -21,20 +21,7 @@ import { AxiosError } from 'axios'; import { Agent } from '../../../../common/types/domain_data'; import { useLibs } from '../../../hooks/use_libs'; import { useAgentRefresh } from '../hooks/use_agent'; - -function useInput() { - const [value, setValue] = useState(''); - - return { - value, - onChange: (e: React.ChangeEvent) => { - setValue(e.target.value); - }, - clear: () => { - setValue(''); - }, - }; -} +import { useInput } from '../../../hooks/use_input'; function useAddMetadataForm(agent: Agent, done: () => void) { const libs = useLibs(); @@ -132,11 +119,7 @@ export const MetadataForm: SFC<{ agent: Agent }> = ({ agent }) => { defaultMessage: 'Key', })} > - + @@ -145,11 +128,7 @@ export const MetadataForm: SFC<{ agent: Agent }> = ({ agent }) => { defaultMessage: 'Value', })} > - + diff --git a/x-pack/legacy/plugins/fleet/public/pages/agent_list/components/agent_enrollment.tsx b/x-pack/legacy/plugins/fleet/public/pages/agent_list/components/agent_enrollment.tsx index 18914f957a887..98a1ce4a612e9 100644 --- a/x-pack/legacy/plugins/fleet/public/pages/agent_list/components/agent_enrollment.tsx +++ b/x-pack/legacy/plugins/fleet/public/pages/agent_list/components/agent_enrollment.tsx @@ -19,6 +19,8 @@ import { EuiText, EuiFilterGroup, EuiFilterButton, + EuiSelect, + EuiHorizontalRule, } from '@elastic/eui'; import { ShellEnrollmentInstructions, @@ -26,6 +28,8 @@ import { ToolsEnrollmentInstructions, } from './enrollment_instructions'; import { useLibs } from '../../../hooks/use_libs'; +import { useEnrollmentApiKeys, useEnrollmentApiKey } from './enrollment_api_keys/hooks'; +import { EnrollmentApiKeysTable } from './enrollment_api_keys'; interface RouterProps { onClose: () => void; @@ -36,8 +40,21 @@ export const AgentEnrollmentFlyout: React.SFC = ({ onClose }) => { const [quickInstallType, setQuickInstallType] = useState<'shell' | 'container' | 'tools'>( 'shell' ); + // api keys + const enrollmentApiKeys = useEnrollmentApiKeys({ + currentPage: 1, + pageSize: 1000, + }); + const [selectedApiKeyId, setSelectedApiKeyId] = useState(null); + React.useEffect(() => { + if (!selectedApiKeyId && enrollmentApiKeys.data && enrollmentApiKeys.data.list.length > 0) { + setSelectedApiKeyId(enrollmentApiKeys.data.list[0].id); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [enrollmentApiKeys.data]); + const apiKey = useEnrollmentApiKey(selectedApiKeyId); - const renderHeader = () => ( + const header = (

@@ -59,7 +76,63 @@ export const AgentEnrollmentFlyout: React.SFC = ({ onClose }) => { ); - const renderInstructions = () => ( + const policyOptions = enrollmentApiKeys.data + ? enrollmentApiKeys.data.list.map(key => ({ + value: key.id, + text: key.name, + })) + : []; + + const [apiKeyListVisible, setApiKeyListVisble] = useState(false); + const renderedPolicySelect = ( + <> + +
+ +
+
+ + setSelectedApiKeyId(e.target.value)} + /> + + { + setApiKeyListVisble(!apiKeyListVisible); + }} + iconType={apiKeyListVisible ? 'arrowUp' : 'arrowDown'} + iconSide="right" + size="xs" + flush="left" + > + {apiKeyListVisible ? ( + + ) : ( + + )} + + {apiKeyListVisible && ( + <> + + + + )} + + ); + + const renderedInstructions = apiKey.data && (
@@ -102,6 +175,7 @@ export const AgentEnrollmentFlyout: React.SFC = ({ onClose }) => { {quickInstallType === 'shell' ? ( ) : null} @@ -110,9 +184,16 @@ export const AgentEnrollmentFlyout: React.SFC = ({ onClose }) => { ); - const renderBody = () => {renderInstructions()}; + const body = ( + + {renderedPolicySelect} + + + {renderedInstructions} + + ); - const renderFooter = () => ( + const footer = ( @@ -130,10 +211,10 @@ export const AgentEnrollmentFlyout: React.SFC = ({ onClose }) => { ); return ( - - {renderHeader()} - {renderBody()} - {renderFooter()} + + {header} + {body} + {footer} ); }; diff --git a/x-pack/legacy/plugins/fleet/public/pages/agent_list/components/enrollment_api_keys/confirm_delete_modal.tsx b/x-pack/legacy/plugins/fleet/public/pages/agent_list/components/enrollment_api_keys/confirm_delete_modal.tsx new file mode 100644 index 0000000000000..0d24f1fe28e9e --- /dev/null +++ b/x-pack/legacy/plugins/fleet/public/pages/agent_list/components/enrollment_api_keys/confirm_delete_modal.tsx @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { EuiOverlayMask, EuiConfirmModal } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; + +export const ConfirmDeleteModal: React.SFC<{ + onConfirm: () => void; + onCancel: () => void; + apiKeyId: string; +}> = ({ onConfirm, onCancel, apiKeyId }) => { + return ( + + + } + onCancel={onCancel} + onConfirm={onConfirm} + cancelButtonText={ + + } + confirmButtonText={ + + } + buttonColor="danger" + /> + + ); +}; diff --git a/x-pack/legacy/plugins/fleet/public/pages/agent_list/components/enrollment_api_keys/create_api_key_form.tsx b/x-pack/legacy/plugins/fleet/public/pages/agent_list/components/enrollment_api_keys/create_api_key_form.tsx new file mode 100644 index 0000000000000..2451ec2fe01c9 --- /dev/null +++ b/x-pack/legacy/plugins/fleet/public/pages/agent_list/components/enrollment_api_keys/create_api_key_form.tsx @@ -0,0 +1,86 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiFormRow, + EuiFieldText, + EuiButton, + EuiSelect, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { useInput } from '../../../../hooks/use_input'; +import { useLibs } from '../../../../hooks/use_libs'; +import { usePolicies } from './hooks'; + +export const CreateApiKeyForm: React.SFC<{ onChange: () => void }> = ({ onChange }) => { + const { data: policies } = usePolicies(); + const { inputs, onSubmit, submitted } = useCreateApiKey(() => onChange()); + + return ( + + + + + + + + + ({ + value: policy.id, + text: policy.name, + }))} + /> + + + + + onSubmit()}> + + + + + + ); +}; + +function useCreateApiKey(onSuccess: () => void) { + const { enrollmentApiKeys } = useLibs(); + const [submitted, setSubmitted] = React.useState(false); + const inputs = { + nameInput: useInput(), + policyIdInput: useInput('default'), + }; + + const onSubmit = async () => { + setSubmitted(true); + await enrollmentApiKeys.create({ + name: inputs.nameInput.value, + policyId: inputs.policyIdInput.value, + }); + setSubmitted(false); + onSuccess(); + }; + + return { + inputs, + onSubmit, + submitted, + }; +} diff --git a/x-pack/legacy/plugins/fleet/public/pages/agent_list/components/enrollment_api_keys/hooks.tsx b/x-pack/legacy/plugins/fleet/public/pages/agent_list/components/enrollment_api_keys/hooks.tsx new file mode 100644 index 0000000000000..c65b7012979b4 --- /dev/null +++ b/x-pack/legacy/plugins/fleet/public/pages/agent_list/components/enrollment_api_keys/hooks.tsx @@ -0,0 +1,121 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { useEffect, useState } from 'react'; +import { useLibs } from '../../../../hooks/use_libs'; +import { Pagination } from '../../../../hooks/use_pagination'; +import { ReturnTypeList, ReturnTypeGet } from '../../../../../common/return_types'; +import { EnrollmentApiKey } from '../../../../../common/types/domain_data'; +import { Policy } from '../../../../../scripts/mock_spec/types'; + +export function useEnrollmentApiKeys(pagination: Pagination) { + const { enrollmentApiKeys } = useLibs(); + const [state, setState] = useState<{ + data: ReturnTypeList | null; + isLoading: boolean; + }>({ + isLoading: true, + data: null, + }); + async function fetchApiKeys() { + try { + const data = await enrollmentApiKeys.listKeys(pagination); + setState({ + isLoading: false, + data, + }); + } catch (error) { + setState({ + isLoading: false, + data: null, + }); + } + } + useEffect(() => { + fetchApiKeys(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + return { + ...state, + refresh: () => fetchApiKeys(), + }; +} + +export function usePolicies() { + const { policies } = useLibs(); + const [state, setState] = useState<{ + data: Policy[]; + isLoading: boolean; + }>({ + isLoading: true, + data: [], + }); + + async function fetchPolicies() { + try { + const data = await policies.getAll(); + setState({ + data, + isLoading: false, + }); + } catch (err) { + setState({ + data: [], + isLoading: false, + }); + } + } + + useEffect(() => { + fetchPolicies(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + return { + ...state, + }; +} + +export function useEnrollmentApiKey(apiKeyId: string | null) { + const { enrollmentApiKeys } = useLibs(); + const [state, setState] = useState<{ + data: ReturnTypeGet | null; + isLoading: boolean; + }>({ + isLoading: true, + data: null, + }); + useEffect(() => { + async function fetchApiKey() { + if (!apiKeyId) { + setState({ + isLoading: false, + data: null, + }); + return; + } + try { + const data = await enrollmentApiKeys.get(apiKeyId); + setState({ + isLoading: false, + data, + }); + } catch (error) { + setState({ + isLoading: false, + data: null, + }); + } + } + fetchApiKey(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [apiKeyId]); + + return { + ...state, + }; +} diff --git a/x-pack/legacy/plugins/fleet/public/pages/agent_list/components/enrollment_api_keys/index.tsx b/x-pack/legacy/plugins/fleet/public/pages/agent_list/components/enrollment_api_keys/index.tsx new file mode 100644 index 0000000000000..55c1272766612 --- /dev/null +++ b/x-pack/legacy/plugins/fleet/public/pages/agent_list/components/enrollment_api_keys/index.tsx @@ -0,0 +1,143 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React, { useState } from 'react'; +import { EuiBasicTable, EuiButtonEmpty, EuiSpacer, EuiPopover } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { i18n } from '@kbn/i18n'; +import { useEnrollmentApiKeys, useEnrollmentApiKey } from './hooks'; +import { EnrollmentApiKey } from '../../../../../common/types/domain_data'; +import { useLibs } from '../../../../hooks/use_libs'; +import { usePagination } from '../../../../hooks/use_pagination'; +import { ConfirmDeleteModal } from './confirm_delete_modal'; +import { CreateApiKeyForm } from './create_api_key_form'; + +export const EnrollmentApiKeysTable: React.SFC = () => { + const { enrollmentApiKeys } = useLibs(); + const [confirmDeleteApiKeyId, setConfirmDeleteApiKeyId] = useState(null); + const { pagination } = usePagination(); + const { data, isLoading, refresh } = useEnrollmentApiKeys(pagination); + + const columns: any[] = [ + { + field: 'name', + name: i18n.translate('xpack.fleet.apiKeysList.nameColumnTitle', { + defaultMessage: 'Name', + }), + width: '300px', + }, + { + field: 'policy_id', + name: i18n.translate('xpack.fleet.agentList.policyColumnTitle', { + defaultMessage: 'Policy', + }), + width: '100px', + }, + { + field: null, + name: i18n.translate('xpack.fleet.agentList.apiKeyColumnTitle', { + defaultMessage: 'Api Key', + }), + render: (key: EnrollmentApiKey) => , + }, + { + field: null, + width: '50px', + render: (key: EnrollmentApiKey) => { + return ( + setConfirmDeleteApiKeyId(key.id)} iconType={'trash'} /> + ); + }, + }, + ]; + + return ( + <> + {confirmDeleteApiKeyId && ( + setConfirmDeleteApiKeyId(null)} + onConfirm={async () => { + await enrollmentApiKeys.delete(confirmDeleteApiKeyId); + setConfirmDeleteApiKeyId(null); + refresh(); + }} + /> + )} + + } + items={data ? data.list : []} + itemId="id" + columns={columns} + /> + + refresh()} /> + + ); +}; + +const CreateApiKeyButton: React.SFC<{ onChange: () => void }> = ({ onChange }) => { + const [isOpen, setIsOpen] = React.useState(false); + + return ( + setIsOpen(true)} + color="text" + iconType={'plusInCircle'} + size="xs" + > + + + } + isOpen={isOpen} + closePopover={() => setIsOpen(false)} + > + { + setIsOpen(false); + onChange(); + }} + /> + + ); + return <>; +}; + +const ApiKeyField: React.SFC<{ apiKeyId: string }> = ({ apiKeyId }) => { + const [visible, setVisible] = useState(false); + const { data } = useEnrollmentApiKey(apiKeyId); + + return ( + <> + {visible && data ? data.item.api_key : '••••••••••••••••••••••••••••'} + setVisible(!visible)}> + {visible ? ( + + ) : ( + + )} + {' '} + + ); +}; diff --git a/x-pack/legacy/plugins/fleet/public/pages/agent_list/components/enrollment_instructions/shell/index.tsx b/x-pack/legacy/plugins/fleet/public/pages/agent_list/components/enrollment_instructions/shell/index.tsx index a518a75ccdb9a..6955f4f69ffa3 100644 --- a/x-pack/legacy/plugins/fleet/public/pages/agent_list/components/enrollment_instructions/shell/index.tsx +++ b/x-pack/legacy/plugins/fleet/public/pages/agent_list/components/enrollment_instructions/shell/index.tsx @@ -17,6 +17,7 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; import { ManualEnrollmentInstructions, ManualEnrollmentSteps } from '../'; import * as MAC_COMMANDS from './mac_commands'; +import { EnrollmentApiKey } from '../../../../../../common/types/domain_data'; // No need for i18n as these are platform names const PLATFORMS = { @@ -56,16 +57,17 @@ const PLATFORM_INSTRUCTIONS: { interface Props { kibanaUrl: string; + apiKey: EnrollmentApiKey; } -export const ShellEnrollmentInstructions: React.SFC = ({ kibanaUrl }) => { +export const ShellEnrollmentInstructions: React.SFC = ({ kibanaUrl, apiKey }) => { // Platform state const [currentPlatform, setCurrentPlatform] = useState('macos'); const [isPlatformOptionsOpen, setIsPlatformOptionsOpen] = useState(false); const [isManualInstallationOpen, setIsManualInstallationOpen] = useState(false); // Build quick installation command - const quickInstallInstructions = `curl ${kibanaUrl}/api/fleet/install/${currentPlatform} | bash`; + const quickInstallInstructions = `API_KEY=${apiKey.api_key} curl ${kibanaUrl}/api/fleet/install/${currentPlatform} | bash`; return ( diff --git a/x-pack/legacy/plugins/fleet/server/libs/api_keys.ts b/x-pack/legacy/plugins/fleet/server/libs/api_keys.ts index 3135f6d3d48f1..7d261f6601eaf 100644 --- a/x-pack/legacy/plugins/fleet/server/libs/api_keys.ts +++ b/x-pack/legacy/plugins/fleet/server/libs/api_keys.ts @@ -208,13 +208,11 @@ export class ApiKeyLib { } private _getEnrollmentApiKeyName(id: string, name?: string, policyId?: string): string { - const generatedName = `Fleet:EnrollmentApiKey:${id}${policyId ? `:${policyId}` : ''}`; - - return name ? `${name} (${generatedName})` : generatedName; + return name ? `${name} (${id})` : id; } private _getAccesstApiKeyName(agentId: string): string { - return `Fleet:AccessApiKey:${agentId}`; + return agentId; } public async addEnrollmentRule(