diff --git a/x-pack/legacy/plugins/encrypted_saved_objects/index.ts b/x-pack/legacy/plugins/encrypted_saved_objects/index.ts index 85aa5c22135b6..69058a7a33f59 100644 --- a/x-pack/legacy/plugins/encrypted_saved_objects/index.ts +++ b/x-pack/legacy/plugins/encrypted_saved_objects/index.ts @@ -21,7 +21,9 @@ export const encryptedSavedObjects = (kibana: { // Some legacy plugins still use `enabled` config key, so we keep it here, but the rest of the // keys is handled by the New Platform plugin. config: (Joi: Root) => - Joi.object({ enabled: Joi.boolean().default(true) }) + Joi.object({ + enabled: Joi.boolean().default(true), + }) .unknown(true) .default(), diff --git a/x-pack/legacy/plugins/siem/index.ts b/x-pack/legacy/plugins/siem/index.ts index cd9b7f59226b0..0a3e447ac64a1 100644 --- a/x-pack/legacy/plugins/siem/index.ts +++ b/x-pack/legacy/plugins/siem/index.ts @@ -5,6 +5,7 @@ */ import { i18n } from '@kbn/i18n'; +import { get } from 'lodash/fp'; import { resolve } from 'path'; import { Server } from 'hapi'; import { Root } from 'joi'; @@ -155,6 +156,9 @@ export const siem = (kibana: any) => { const initializerContext = { ...coreContext, env } as PluginInitializerContext; const serverFacade = { config, + usingEphemeralEncryptionKey: + get('usingEphemeralEncryptionKey', newPlatform.setup.plugins.encryptedSavedObjects) ?? + false, plugins: { alerting: plugins.alerting, actions: newPlatform.start.plugins.actions, diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_pre_packaged_rules.tsx b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_pre_packaged_rules.tsx index 14d40f9ffbc37..d77d6283692a2 100644 --- a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_pre_packaged_rules.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_pre_packaged_rules.tsx @@ -29,6 +29,7 @@ interface UsePrePackagedRuleProps { hasIndexWrite: boolean | null; hasManageApiKey: boolean | null; isAuthenticated: boolean | null; + hasEncryptionKey: boolean | null; isSignalIndexExists: boolean | null; } @@ -38,6 +39,7 @@ interface UsePrePackagedRuleProps { * @param hasIndexWrite boolean * @param hasManageApiKey boolean * @param isAuthenticated boolean + * @param hasEncryptionKey boolean * @param isSignalIndexExists boolean * */ @@ -46,6 +48,7 @@ export const usePrePackagedRules = ({ hasIndexWrite, hasManageApiKey, isAuthenticated, + hasEncryptionKey, isSignalIndexExists, }: UsePrePackagedRuleProps): Return => { const [rulesStatus, setRuleStatus] = useState< @@ -117,6 +120,7 @@ export const usePrePackagedRules = ({ hasIndexWrite && hasManageApiKey && isAuthenticated && + hasEncryptionKey && isSignalIndexExists ) { setLoadingCreatePrePackagedRules(true); @@ -180,7 +184,14 @@ export const usePrePackagedRules = ({ isSubscribed = false; abortCtrl.abort(); }; - }, [canUserCRUD, hasIndexWrite, hasManageApiKey, isAuthenticated, isSignalIndexExists]); + }, [ + canUserCRUD, + hasIndexWrite, + hasManageApiKey, + isAuthenticated, + hasEncryptionKey, + isSignalIndexExists, + ]); return { loading, diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/types.ts b/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/types.ts index ea4860dafd40f..752de13567e5c 100644 --- a/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/types.ts +++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/types.ts @@ -97,4 +97,5 @@ export interface Privilege { }; }; is_authenticated: boolean; + has_encryption_key: boolean; } diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/use_privilege_user.tsx b/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/use_privilege_user.tsx index b93009c8ce2c2..55f3386b503d8 100644 --- a/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/use_privilege_user.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/use_privilege_user.tsx @@ -14,6 +14,7 @@ import * as i18n from './translations'; interface Return { loading: boolean; isAuthenticated: boolean | null; + hasEncryptionKey: boolean | null; hasIndexManage: boolean | null; hasManageApiKey: boolean | null; hasIndexWrite: boolean | null; @@ -25,9 +26,17 @@ interface Return { export const usePrivilegeUser = (): Return => { const [loading, setLoading] = useState(true); const [privilegeUser, setPrivilegeUser] = useState< - Pick + Pick< + Return, + | 'isAuthenticated' + | 'hasEncryptionKey' + | 'hasIndexManage' + | 'hasManageApiKey' + | 'hasIndexWrite' + > >({ isAuthenticated: null, + hasEncryptionKey: null, hasIndexManage: null, hasManageApiKey: null, hasIndexWrite: null, @@ -50,6 +59,7 @@ export const usePrivilegeUser = (): Return => { const indexName = Object.keys(privilege.index)[0]; setPrivilegeUser({ isAuthenticated: privilege.is_authenticated, + hasEncryptionKey: privilege.has_encryption_key, hasIndexManage: privilege.index[indexName].manage, hasIndexWrite: privilege.index[indexName].create || @@ -67,6 +77,7 @@ export const usePrivilegeUser = (): Return => { if (isSubscribed) { setPrivilegeUser({ isAuthenticated: false, + hasEncryptionKey: false, hasIndexManage: false, hasManageApiKey: false, hasIndexWrite: false, diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/no_api_integration_callout/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/no_api_integration_callout/index.tsx new file mode 100644 index 0000000000000..2d517717ac59d --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/no_api_integration_callout/index.tsx @@ -0,0 +1,26 @@ +/* + * 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 { EuiCallOut, EuiButton } from '@elastic/eui'; +import React, { memo, useCallback, useState } from 'react'; + +import * as i18n from './translations'; + +const NoApiIntegrationKeyCallOutComponent = () => { + const [showCallOut, setShowCallOut] = useState(true); + const handleCallOut = useCallback(() => setShowCallOut(false), [setShowCallOut]); + + return showCallOut ? ( + +

{i18n.NO_API_INTEGRATION_KEY_CALLOUT_MSG}

+ + {i18n.DISMISS_CALLOUT} + +
+ ) : null; +}; + +export const NoApiIntegrationKeyCallOut = memo(NoApiIntegrationKeyCallOutComponent); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/no_api_integration_callout/translations.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/no_api_integration_callout/translations.ts new file mode 100644 index 0000000000000..84804af8840f9 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/no_api_integration_callout/translations.ts @@ -0,0 +1,28 @@ +/* + * 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 { i18n } from '@kbn/i18n'; + +export const NO_API_INTEGRATION_KEY_CALLOUT_TITLE = i18n.translate( + 'xpack.siem.detectionEngine.noApiIntegrationKeyCallOutTitle', + { + defaultMessage: 'API integration key required', + } +); + +export const NO_API_INTEGRATION_KEY_CALLOUT_MSG = i18n.translate( + 'xpack.siem.detectionEngine.noApiIntegrationKeyCallOutMsg', + { + defaultMessage: `A new encryption key is generated for saved objects each time you start Kibana. Without a persistent key, you cannot delete or modify rules after Kibana restarts. To set a persistent key, add the xpack.encryptedSavedObjects.encryptionKey setting with any text value of 32 or more characters to the kibana.yml file.`, + } +); + +export const DISMISS_CALLOUT = i18n.translate( + 'xpack.siem.detectionEngine.dismissNoApiIntegrationKeyButton', + { + defaultMessage: 'Dismiss', + } +); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/user_info/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/user_info/index.tsx index 0f6a51e52cd2e..a96913f2ad541 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/user_info/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/user_info/index.tsx @@ -18,6 +18,7 @@ export interface State { hasManageApiKey: boolean | null; isSignalIndexExists: boolean | null; isAuthenticated: boolean | null; + hasEncryptionKey: boolean | null; loading: boolean; signalIndexName: string | null; } @@ -29,6 +30,7 @@ const initialState: State = { hasManageApiKey: null, isSignalIndexExists: null, isAuthenticated: null, + hasEncryptionKey: null, loading: true, signalIndexName: null, }; @@ -55,6 +57,10 @@ export type Action = type: 'updateIsAuthenticated'; isAuthenticated: boolean | null; } + | { + type: 'updateHasEncryptionKey'; + hasEncryptionKey: boolean | null; + } | { type: 'updateCanUserCRUD'; canUserCRUD: boolean | null; @@ -102,6 +108,12 @@ export const userInfoReducer = (state: State, action: Action): State => { isAuthenticated: action.isAuthenticated, }; } + case 'updateHasEncryptionKey': { + return { + ...state, + hasEncryptionKey: action.hasEncryptionKey, + }; + } case 'updateCanUserCRUD': { return { ...state, @@ -142,6 +154,7 @@ export const useUserInfo = (): State => { hasManageApiKey, isSignalIndexExists, isAuthenticated, + hasEncryptionKey, loading, signalIndexName, }, @@ -150,6 +163,7 @@ export const useUserInfo = (): State => { const { loading: privilegeLoading, isAuthenticated: isApiAuthenticated, + hasEncryptionKey: isApiEncryptionKey, hasIndexManage: hasApiIndexManage, hasIndexWrite: hasApiIndexWrite, hasManageApiKey: hasApiManageApiKey, @@ -205,6 +219,12 @@ export const useUserInfo = (): State => { } }, [loading, isAuthenticated, isApiAuthenticated]); + useEffect(() => { + if (!loading && hasEncryptionKey !== isApiEncryptionKey && isApiEncryptionKey != null) { + dispatch({ type: 'updateHasEncryptionKey', hasEncryptionKey: isApiEncryptionKey }); + } + }, [loading, hasEncryptionKey, isApiEncryptionKey]); + useEffect(() => { if (!loading && canUserCRUD !== capabilitiesCanUserCRUD && capabilitiesCanUserCRUD != null) { dispatch({ type: 'updateCanUserCRUD', canUserCRUD: capabilitiesCanUserCRUD }); @@ -220,6 +240,7 @@ export const useUserInfo = (): State => { useEffect(() => { if ( isAuthenticated && + hasEncryptionKey && hasIndexManage && isSignalIndexExists != null && !isSignalIndexExists && @@ -227,12 +248,13 @@ export const useUserInfo = (): State => { ) { createSignalIndex(); } - }, [createSignalIndex, isAuthenticated, isSignalIndexExists, hasIndexManage]); + }, [createSignalIndex, isAuthenticated, hasEncryptionKey, isSignalIndexExists, hasIndexManage]); return { loading, isSignalIndexExists, isAuthenticated, + hasEncryptionKey, canUserCRUD, hasIndexManage, hasIndexWrite, diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine.tsx index b6ddb4de9fd39..d854c377e6ec8 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine.tsx @@ -18,7 +18,10 @@ import { GlobalTime } from '../../containers/global_time'; import { indicesExistOrDataTemporarilyUnavailable, WithSource } from '../../containers/source'; import { AlertsTable } from '../../components/alerts_viewer/alerts_table'; import { FiltersGlobal } from '../../components/filters_global'; -import { DETECTION_ENGINE_PAGE_NAME } from '../../components/link_to/redirect_to_detection_engine'; +import { + getDetectionEngineTabUrl, + getRulesUrl, +} from '../../components/link_to/redirect_to_detection_engine'; import { SiemSearchBar } from '../../components/search_bar'; import { WrapperPage } from '../../components/wrapper_page'; import { State } from '../../store'; @@ -30,6 +33,7 @@ import { InputsRange } from '../../store/inputs/model'; import { AlertsByCategory } from '../overview/alerts_by_category'; import { useSignalInfo } from './components/signals_info'; import { SignalsTable } from './components/signals'; +import { NoApiIntegrationKeyCallOut } from './components/no_api_integration_callout'; import { NoWriteSignalsCallOut } from './components/no_write_signals_callout'; import { SignalsHistogramPanel } from './components/signals_histogram_panel'; import { signalsHistogramOptions } from './components/signals_histogram_panel/config'; @@ -79,6 +83,7 @@ const DetectionEnginePageComponent: React.FC loading, isSignalIndexExists, isAuthenticated: isUserAuthenticated, + hasEncryptionKey, canUserCRUD, signalIndexName, hasIndexWrite, @@ -101,7 +106,7 @@ const DetectionEnginePageComponent: React.FC isSelected={tab.id === tabName} disabled={tab.disabled} key={tab.id} - href={`#/${DETECTION_ENGINE_PAGE_NAME}/${tab.id}`} + href={getDetectionEngineTabUrl(tab.id)} > {tab.name} @@ -134,6 +139,7 @@ const DetectionEnginePageComponent: React.FC return ( <> + {hasEncryptionKey != null && !hasEncryptionKey && } {hasIndexWrite != null && !hasIndexWrite && } {({ indicesExist, indexPattern }) => { @@ -155,7 +161,7 @@ const DetectionEnginePageComponent: React.FC } title={i18n.PAGE_TITLE} > - + {i18n.BUTTON_MANAGE_RULES} @@ -184,7 +190,7 @@ const DetectionEnginePageComponent: React.FC { loading, isSignalIndexExists, isAuthenticated, + hasEncryptionKey, canUserCRUD, hasManageApiKey, } = useUserInfo(); @@ -239,11 +241,7 @@ const CreateRulePageComponent: React.FC = () => { return ; } - if ( - isSignalIndexExists != null && - isAuthenticated != null && - (!isSignalIndexExists || !isAuthenticated) - ) { + if (redirectToDetections(isSignalIndexExists, isAuthenticated, hasEncryptionKey)) { return ; } else if (userHasNoPermissions) { return ; diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/details/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/details/index.tsx index 7b615d5f159c2..bac1494c4fdd8 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/details/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/details/index.tsx @@ -25,9 +25,9 @@ import { connect } from 'react-redux'; import { FiltersGlobal } from '../../../../components/filters_global'; import { FormattedDate } from '../../../../components/formatted_date'; import { - getDetectionEngineUrl, getEditRuleUrl, getRulesUrl, + DETECTION_ENGINE_PAGE_NAME, } from '../../../../components/link_to/redirect_to_detection_engine'; import { SiemSearchBar } from '../../../../components/search_bar'; import { WrapperPage } from '../../../../components/wrapper_page'; @@ -54,7 +54,7 @@ import * as detectionI18n from '../../translations'; import { ReadOnlyCallOut } from '../components/read_only_callout'; import { RuleSwitch } from '../components/rule_switch'; import { StepPanel } from '../components/step_panel'; -import { getStepsData } from '../helpers'; +import { getStepsData, redirectToDetections } from '../helpers'; import * as ruleI18n from '../translations'; import * as i18n from './translations'; import { GlobalTime } from '../../../../containers/global_time'; @@ -113,6 +113,7 @@ const RuleDetailsPageComponent: FC = ({ loading, isSignalIndexExists, isAuthenticated, + hasEncryptionKey, canUserCRUD, hasManageApiKey, hasIndexWrite, @@ -236,12 +237,8 @@ const RuleDetailsPageComponent: FC = ({ [ruleEnabled, setRuleEnabled] ); - if ( - isSignalIndexExists != null && - isAuthenticated != null && - (!isSignalIndexExists || !isAuthenticated) - ) { - return ; + if (redirectToDetections(isSignalIndexExists, isAuthenticated, hasEncryptionKey)) { + return ; } return ( diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/edit/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/edit/index.tsx index 65f4bd2edf7cd..99fcff6b8d2fd 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/edit/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/edit/index.tsx @@ -32,7 +32,7 @@ import { StepAboutRule } from '../components/step_about_rule'; import { StepDefineRule } from '../components/step_define_rule'; import { StepScheduleRule } from '../components/step_schedule_rule'; import { formatRule } from '../create/helpers'; -import { getStepsData } from '../helpers'; +import { getStepsData, redirectToDetections } from '../helpers'; import * as ruleI18n from '../translations'; import { RuleStep, DefineStepRule, AboutStepRule, ScheduleStepRule } from '../types'; import * as i18n from './translations'; @@ -56,6 +56,7 @@ const EditRulePageComponent: FC = () => { loading: initLoading, isSignalIndexExists, isAuthenticated, + hasEncryptionKey, canUserCRUD, hasManageApiKey, } = useUserInfo(); @@ -270,11 +271,7 @@ const EditRulePageComponent: FC = () => { return ; } - if ( - isSignalIndexExists != null && - isAuthenticated != null && - (!isSignalIndexExists || !isAuthenticated) - ) { + if (redirectToDetections(isSignalIndexExists, isAuthenticated, hasEncryptionKey)) { return ; } else if (userHasNoPermissions) { return ; diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/helpers.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/helpers.tsx index ce0d50d9b6106..4e98fc17404c9 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/helpers.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/helpers.tsx @@ -138,3 +138,13 @@ export const setFieldValue = ( form.setFieldValue(key, val); } }); + +export const redirectToDetections = ( + isSignalIndexExists: boolean | null, + isAuthenticated: boolean | null, + hasEncryptionKey: boolean | null +) => + isSignalIndexExists != null && + isAuthenticated != null && + hasEncryptionKey != null && + (!isSignalIndexExists || !isAuthenticated || !hasEncryptionKey); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/index.tsx index 1c0ed34e92793..0c53ad19a3574 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/index.tsx @@ -10,6 +10,7 @@ import { Redirect } from 'react-router-dom'; import { usePrePackagedRules } from '../../../containers/detection_engine/rules'; import { + DETECTION_ENGINE_PAGE_NAME, getDetectionEngineUrl, getCreateRuleUrl, } from '../../../components/link_to/redirect_to_detection_engine'; @@ -22,7 +23,7 @@ import { AllRules } from './all'; import { ImportRuleModal } from './components/import_rule_modal'; import { ReadOnlyCallOut } from './components/read_only_callout'; import { UpdatePrePackagedRulesCallOut } from './components/pre_packaged_rules/update_callout'; -import { getPrePackagedRuleStatus } from './helpers'; +import { getPrePackagedRuleStatus, redirectToDetections } from './helpers'; import * as i18n from './translations'; type Func = () => void; @@ -35,6 +36,7 @@ const RulesPageComponent: React.FC = () => { loading, isSignalIndexExists, isAuthenticated, + hasEncryptionKey, canUserCRUD, hasIndexWrite, hasManageApiKey, @@ -54,6 +56,7 @@ const RulesPageComponent: React.FC = () => { hasManageApiKey, isSignalIndexExists, isAuthenticated, + hasEncryptionKey, }); const prePackagedRuleStatus = getPrePackagedRuleStatus( rulesInstalled, @@ -83,12 +86,8 @@ const RulesPageComponent: React.FC = () => { refreshRulesData.current = refreshRule; }, []); - if ( - isSignalIndexExists != null && - isAuthenticated != null && - (!isSignalIndexExists || !isAuthenticated) - ) { - return ; + if (redirectToDetections(isSignalIndexExists, isAuthenticated, hasEncryptionKey)) { + return ; } return ( diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_responses.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_responses.ts index eea25a1e89cc8..6a42aed123fa3 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_responses.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_responses.ts @@ -390,6 +390,7 @@ export const getMockPrivileges = () => ({ }, application: {}, is_authenticated: false, + has_encryption_key: true, }); export const getFindResultStatus = (): SavedObjectsFindResponse => ({ diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/privileges/read_privileges_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/privileges/read_privileges_route.ts index 803d9d645aadb..5ea4dc7595b2b 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/privileges/read_privileges_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/privileges/read_privileges_route.ts @@ -29,8 +29,10 @@ export const createReadPrivilegesRulesRoute = (server: ServerFacade): Hapi.Serve const callWithRequest = callWithRequestFactory(request, server); const index = getIndex(request, server); const permissions = await readPrivileges(callWithRequest, index); + const usingEphemeralEncryptionKey = server.usingEphemeralEncryptionKey; return merge(permissions, { is_authenticated: request?.auth?.isAuthenticated ?? false, + has_encryption_key: !usingEphemeralEncryptionKey, }); } catch (err) { return transformError(err); diff --git a/x-pack/legacy/plugins/siem/server/types.ts b/x-pack/legacy/plugins/siem/server/types.ts index 3fa2268afe92c..7c07e63404eaa 100644 --- a/x-pack/legacy/plugins/siem/server/types.ts +++ b/x-pack/legacy/plugins/siem/server/types.ts @@ -8,6 +8,7 @@ import { Legacy } from 'kibana'; export interface ServerFacade { config: Legacy.Server['config']; + usingEphemeralEncryptionKey: boolean; plugins: { // eslint-disable-next-line @typescript-eslint/no-explicit-any actions: any; // We have to do this at the moment because the types are not compatible diff --git a/x-pack/plugins/encrypted_saved_objects/server/config.test.ts b/x-pack/plugins/encrypted_saved_objects/server/config.test.ts index 7d6632aa56cb1..e05d8d687d05a 100644 --- a/x-pack/plugins/encrypted_saved_objects/server/config.test.ts +++ b/x-pack/plugins/encrypted_saved_objects/server/config.test.ts @@ -49,7 +49,7 @@ describe('config schema', () => { }); describe('createConfig$()', () => { - it('should log a warning and set xpack.encryptedSavedObjects.encryptionKey if not set', async () => { + it('should log a warning, set xpack.encryptedSavedObjects.encryptionKey and usingEphemeralEncryptionKey=true when encryptionKey is not set', async () => { const mockRandomBytes = jest.requireMock('crypto').randomBytes; mockRandomBytes.mockReturnValue('ab'.repeat(16)); @@ -57,7 +57,10 @@ describe('createConfig$()', () => { const config = await createConfig$(contextMock) .pipe(first()) .toPromise(); - expect(config).toEqual({ encryptionKey: 'ab'.repeat(16) }); + expect(config).toEqual({ + config: { encryptionKey: 'ab'.repeat(16) }, + usingEphemeralEncryptionKey: true, + }); expect(loggingServiceMock.collect(contextMock.logger).warn).toMatchInlineSnapshot(` Array [ @@ -67,4 +70,19 @@ describe('createConfig$()', () => { ] `); }); + + it('should not log a warning and set usingEphemeralEncryptionKey=false when encryptionKey is set', async () => { + const contextMock = coreMock.createPluginInitializerContext({ + encryptionKey: 'supersecret', + }); + const config = await createConfig$(contextMock) + .pipe(first()) + .toPromise(); + expect(config).toEqual({ + config: { encryptionKey: 'supersecret' }, + usingEphemeralEncryptionKey: false, + }); + + expect(loggingServiceMock.collect(contextMock.logger).warn).toEqual([]); + }); }); diff --git a/x-pack/plugins/encrypted_saved_objects/server/config.ts b/x-pack/plugins/encrypted_saved_objects/server/config.ts index c755b7dd9f205..2f01850520724 100644 --- a/x-pack/plugins/encrypted_saved_objects/server/config.ts +++ b/x-pack/plugins/encrypted_saved_objects/server/config.ts @@ -5,15 +5,10 @@ */ import crypto from 'crypto'; -import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { schema, TypeOf } from '@kbn/config-schema'; import { PluginInitializerContext } from 'src/core/server'; -export type ConfigType = ReturnType extends Observable - ? P - : ReturnType; - export const ConfigSchema = schema.object({ enabled: schema.boolean({ defaultValue: true }), encryptionKey: schema.conditional( @@ -30,6 +25,7 @@ export function createConfig$(context: PluginInitializerContext) { const logger = context.logger.get('config'); let encryptionKey = config.encryptionKey; + const usingEphemeralEncryptionKey = encryptionKey === undefined; if (encryptionKey === undefined) { logger.warn( 'Generating a random key for xpack.encryptedSavedObjects.encryptionKey. ' + @@ -40,7 +36,10 @@ export function createConfig$(context: PluginInitializerContext) { encryptionKey = crypto.randomBytes(16).toString('hex'); } - return { ...config, encryptionKey }; + return { + config: { ...config, encryptionKey }, + usingEphemeralEncryptionKey, + }; }) ); } diff --git a/x-pack/plugins/encrypted_saved_objects/server/mocks.ts b/x-pack/plugins/encrypted_saved_objects/server/mocks.ts index 87c36381a841a..7f53f47760f12 100644 --- a/x-pack/plugins/encrypted_saved_objects/server/mocks.ts +++ b/x-pack/plugins/encrypted_saved_objects/server/mocks.ts @@ -10,6 +10,7 @@ function createEncryptedSavedObjectsSetupMock() { return { registerType: jest.fn(), __legacyCompat: { registerLegacyAPI: jest.fn() }, + usingEphemeralEncryptionKey: true, } as jest.Mocked; } diff --git a/x-pack/plugins/encrypted_saved_objects/server/plugin.test.ts b/x-pack/plugins/encrypted_saved_objects/server/plugin.test.ts index 534ed13ba0acb..5228734e4a773 100644 --- a/x-pack/plugins/encrypted_saved_objects/server/plugin.test.ts +++ b/x-pack/plugins/encrypted_saved_objects/server/plugin.test.ts @@ -18,6 +18,7 @@ describe('EncryptedSavedObjects Plugin', () => { "registerLegacyAPI": [Function], }, "registerType": [Function], + "usingEphemeralEncryptionKey": true, } `); }); diff --git a/x-pack/plugins/encrypted_saved_objects/server/plugin.ts b/x-pack/plugins/encrypted_saved_objects/server/plugin.ts index ecd917ff90d00..d9185251ca466 100644 --- a/x-pack/plugins/encrypted_saved_objects/server/plugin.ts +++ b/x-pack/plugins/encrypted_saved_objects/server/plugin.ts @@ -23,6 +23,7 @@ import { SavedObjectsSetup, setupSavedObjects } from './saved_objects'; export interface PluginSetupContract { registerType: (typeRegistration: EncryptedSavedObjectTypeRegistration) => void; __legacyCompat: { registerLegacyAPI: (legacyAPI: LegacyAPI) => void }; + usingEphemeralEncryptionKey: boolean; } export interface PluginStartContract extends SavedObjectsSetup { @@ -59,7 +60,7 @@ export class Plugin { } public async setup(core: CoreSetup): Promise { - const config = await createConfig$(this.initializerContext) + const { config, usingEphemeralEncryptionKey } = await createConfig$(this.initializerContext) .pipe(first()) .toPromise(); @@ -81,6 +82,7 @@ export class Plugin { registerType: (typeRegistration: EncryptedSavedObjectTypeRegistration) => service.registerType(typeRegistration), __legacyCompat: { registerLegacyAPI: (legacyAPI: LegacyAPI) => (this.legacyAPI = legacyAPI) }, + usingEphemeralEncryptionKey, }; }