@@ -80,13 +80,13 @@ export const SearchApplicationAPI = () => {
@@ -98,7 +98,7 @@ export const SearchApplicationAPI = () => {
>
),
title: i18n.translate(
- 'xpack.enterpriseSearch.content.searchApplication.safeSearchApi.step1.setUpSearchtemplate.title',
+ 'xpack.enterpriseSearch.content.searchApplication.searchApi.step1.setUpSearchtemplate.title',
{
defaultMessage: 'Set up your search template',
}
@@ -110,13 +110,13 @@ export const SearchApplicationAPI = () => {
@@ -132,10 +132,10 @@ export const SearchApplicationAPI = () => {
iconSide="left"
iconType="plusInCircleFilled"
onClick={openGenerateModal}
- data-telemetry-id="entSearchApplications-safeSearchApi-step2-createApiKeyButton"
+ data-telemetry-id="entSearchApplications-searchApi-step2-createApiKeyButton"
>
{i18n.translate(
- 'xpack.enterpriseSearch.content.searchApplication.safeSearchApi.step2.createAPIKeyButton',
+ 'xpack.enterpriseSearch.content.searchApplication.searchApi.step2.createAPIKeyButton',
{
defaultMessage: 'Create API Key',
}
@@ -146,7 +146,7 @@ export const SearchApplicationAPI = () => {
KibanaLogic.values.navigateToUrl('/app/management/security/api_keys', {
shouldNotCreateHref: true,
@@ -154,7 +154,7 @@ export const SearchApplicationAPI = () => {
}
>
{i18n.translate(
- 'xpack.enterpriseSearch.content.searchApplication.safeSearchApi.step2.viewKeysButton',
+ 'xpack.enterpriseSearch.content.searchApplication.searchApi.step2.viewKeysButton',
{
defaultMessage: 'View Keys',
}
@@ -165,7 +165,7 @@ export const SearchApplicationAPI = () => {
>
),
title: i18n.translate(
- 'xpack.enterpriseSearch.content.searchApplication.safeSearchApi.step2.title',
+ 'xpack.enterpriseSearch.content.searchApplication.searchApi.step2.title',
{
defaultMessage: 'Generate and save API key',
}
@@ -177,7 +177,7 @@ export const SearchApplicationAPI = () => {
{i18n.translate(
- 'xpack.enterpriseSearch.content.searchApplication.safeSearchApi.step3.copyEndpointDescription',
+ 'xpack.enterpriseSearch.content.searchApplication.searchApi.step3.copyEndpointDescription',
{
defaultMessage: "Here's the URL for your endpoint:",
}
@@ -197,16 +197,16 @@ export const SearchApplicationAPI = () => {
>
),
title: i18n.translate(
- 'xpack.enterpriseSearch.content.searchApplication.safeSearchApi.step3.title',
+ 'xpack.enterpriseSearch.content.searchApplication.searchApi.step3.title',
{
- defaultMessage: 'Copy your Safe Search endpoint',
+ defaultMessage: 'Copy your Search endpoint',
}
),
},
{
children: ,
title: i18n.translate(
- 'xpack.enterpriseSearch.content.searchApplication.safeSearchApi.step4.title',
+ 'xpack.enterpriseSearch.content.searchApplication.searchApi.step4.title',
{
defaultMessage: 'Learn how to call your endpoint',
}
@@ -226,25 +226,25 @@ export const SearchApplicationAPI = () => {
iconType="iInCircle"
title={
}
>
{i18n.translate(
- 'xpack.enterpriseSearch.content.searchApplication.safeSearchApi.safeSearchCallout.body.safeSearchDocLink',
+ 'xpack.enterpriseSearch.content.searchApplication.searchApi.searchApiCallout.body.searchApiDocLink',
{
- defaultMessage: 'Safe Search API',
+ defaultMessage: 'Search API',
}
)}
@@ -256,7 +256,7 @@ export const SearchApplicationAPI = () => {
data-telemetry-id="entSearchApplications-searchTemplate-documentation-viewDocumentaion"
>
{i18n.translate(
- 'xpack.enterpriseSearch.content.searchApplication.safeSearchApi.safeSearchCallout.body.searchTemplateDocLink',
+ 'xpack.enterpriseSearch.content.searchApplication.searchApi.searchApiCallout.body.searchTemplateDocLink',
{
defaultMessage: 'search template',
}
@@ -267,20 +267,20 @@ export const SearchApplicationAPI = () => {
/>
{i18n.translate(
- 'xpack.enterpriseSearch.content.searchApplication.safeSearchApi.safeSearchCallout.body.safeSearchDocumentationLink',
+ 'xpack.enterpriseSearch.content.searchApplication.searchApi.searchApiCallout.body.searchApiDocumentationLink',
{
- defaultMessage: 'Learn more about the Safe Search API',
+ defaultMessage: 'Learn more about the Search API',
}
)}
diff --git a/x-pack/plugins/enterprise_search/public/applications/applications/components/engine/engine_search_preview/engine_search_preview.tsx b/x-pack/plugins/enterprise_search/public/applications/applications/components/engine/engine_search_preview/engine_search_preview.tsx
index 428283df3cd86..bfec3086c5e87 100644
--- a/x-pack/plugins/enterprise_search/public/applications/applications/components/engine/engine_search_preview/engine_search_preview.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/applications/components/engine/engine_search_preview/engine_search_preview.tsx
@@ -9,6 +9,8 @@ import React, { useState, useMemo } from 'react';
import { useActions, useValues } from 'kea';
+import useLocalStorage from 'react-use/lib/useLocalStorage';
+
import {
EuiButtonEmpty,
EuiContextMenuItem,
@@ -24,6 +26,7 @@ import {
EuiText,
EuiTextColor,
EuiTitle,
+ EuiTourStep,
} from '@elastic/eui';
import {
PagingInfo,
@@ -128,6 +131,11 @@ const ConfigurationPopover: React.FC = ({
const { engineData } = useValues(EngineViewLogic);
const { openDeleteEngineModal } = useActions(EngineViewLogic);
const { sendEnterpriseSearchTelemetry } = useActions(TelemetryLogic);
+ const [isTourClosed, setTourClosed] = useLocalStorage(
+ 'search-application-tour-closed',
+ false
+ );
+
return (
<>
= ({
closePopover={setCloseConfiguration}
button={
- {hasSchemaConflicts && }
-
- {i18n.translate(
- 'xpack.enterpriseSearch.content.engine.searchPreview.configuration.buttonTitle',
- {
- defaultMessage: 'Configuration',
+ {hasSchemaConflicts && (
+ <>
+
+
+
+ {!isTourClosed && }
+ >
+ )}
+
+
+
+ {i18n.translate(
+ 'xpack.enterpriseSearch.content.searchApplication.searchPreview.configuration.tourContent',
+ {
+ defaultMessage:
+ 'Create your API key, learn about using language clients and find more resources in Connect.',
+ }
+ )}
+
+
}
- )}
-
+ isStepOpen={!isTourClosed}
+ maxWidth={360}
+ hasArrow
+ step={1}
+ onFinish={() => {
+ setTourClosed(true);
+ }}
+ stepsTotal={1}
+ anchorPosition="downCenter"
+ title={i18n.translate(
+ 'xpack.enterpriseSearch.content.searchApplication.searchPreview.configuration.tourTitle',
+ {
+ defaultMessage: 'Review our API page to start using your search application',
+ }
+ )}
+ >
+ <>>
+
+
+
+
+
+ {i18n.translate(
+ 'xpack.enterpriseSearch.content.engine.searchPreview.configuration.buttonTitle',
+ {
+ defaultMessage: 'Configuration',
+ }
+ )}
+
+
}
>
@@ -235,7 +289,7 @@ const ConfigurationPopover: React.FC = ({
onClick={() =>
navigateToUrl(
generateEncodedPath(SEARCH_APPLICATION_CONNECT_PATH, {
- connectTabId: SearchApplicationConnectTabs.SAFESEARCHAPI,
+ connectTabId: SearchApplicationConnectTabs.SEARCHAPI,
engineName,
})
)
diff --git a/x-pack/plugins/enterprise_search/public/applications/applications/components/engine/engine_view.tsx b/x-pack/plugins/enterprise_search/public/applications/applications/components/engine/engine_view.tsx
index 419b8a9d21e82..6b66c94f5ecf6 100644
--- a/x-pack/plugins/enterprise_search/public/applications/applications/components/engine/engine_view.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/applications/components/engine/engine_view.tsx
@@ -97,7 +97,7 @@ export const EngineView: React.FC = () => {
{
cluster: [],
index: [
{
- names: [indexName],
+ names: [indexName, `.search-acl-filter-my-index`],
+ privileges: ['all'],
+ },
+ ],
+ },
+ },
+ });
+ });
+
+ it('works with search-* prefixed indices', async () => {
+ await createApiKey(request, security, 'search-test', keyName);
+ expect(security.authc.apiKeys.create).toHaveBeenCalledWith(request, {
+ name: keyName,
+ role_descriptors: {
+ ['search-test-key-role']: {
+ cluster: [],
+ index: [
+ {
+ names: ['search-test', `.search-acl-filter-test`],
privileges: ['all'],
},
],
diff --git a/x-pack/plugins/enterprise_search/server/lib/indices/create_api_key.ts b/x-pack/plugins/enterprise_search/server/lib/indices/create_api_key.ts
index 0c1a62c4d30db..13d6aa142e50c 100644
--- a/x-pack/plugins/enterprise_search/server/lib/indices/create_api_key.ts
+++ b/x-pack/plugins/enterprise_search/server/lib/indices/create_api_key.ts
@@ -17,6 +17,9 @@ export const createApiKey = async (
indexName: string,
keyName: string
) => {
+ // removes the "search-" prefix if present, and applies the new prefix
+ const aclIndexName = indexName.replace(/^(?:search-)?(.*)$/, '.search-acl-filter-$1');
+
return await security.authc.apiKeys.create(request, {
name: keyName,
role_descriptors: {
@@ -24,7 +27,7 @@ export const createApiKey = async (
cluster: [],
index: [
{
- names: [indexName],
+ names: [indexName, aclIndexName],
privileges: ['all'],
},
],
diff --git a/x-pack/plugins/fleet/server/services/output.test.ts b/x-pack/plugins/fleet/server/services/output.test.ts
index 16a27abb0b330..588f686a6eaf7 100644
--- a/x-pack/plugins/fleet/server/services/output.test.ts
+++ b/x-pack/plugins/fleet/server/services/output.test.ts
@@ -82,6 +82,12 @@ function getMockedSoClient(
type: 'elasticsearch',
});
}
+ case outputIdToUuid('existing-default-and-default-monitoring-output'): {
+ return mockOutputSO('existing-default-and-default-monitoring-output', {
+ is_default: true,
+ is_default_monitoring: true,
+ });
+ }
case outputIdToUuid('existing-preconfigured-default-output'): {
return mockOutputSO('existing-preconfigured-default-output', {
is_default: true,
@@ -569,6 +575,46 @@ describe('Output Service', () => {
);
});
+ it('should not set default output to false when the output is already the default one', async () => {
+ const soClient = getMockedSoClient({
+ defaultOutputId: 'existing-default-and-default-monitoring-output',
+ });
+
+ await expect(
+ outputService.update(
+ soClient,
+ esClientMock,
+ 'existing-default-and-default-monitoring-output',
+ {
+ is_default: false,
+ name: 'Test',
+ }
+ )
+ ).rejects.toThrow(
+ `Default output existing-default-and-default-monitoring-output cannot be set to is_default=false or is_default_monitoring=false manually. Make another output the default first.`
+ );
+ });
+
+ it('should not set default monitoring output to false when the output is already the default one', async () => {
+ const soClient = getMockedSoClient({
+ defaultOutputId: 'existing-default-and-default-monitoring-output',
+ });
+
+ await expect(
+ outputService.update(
+ soClient,
+ esClientMock,
+ 'existing-default-and-default-monitoring-output',
+ {
+ is_default_monitoring: false,
+ name: 'Test',
+ }
+ )
+ ).rejects.toThrow(
+ `Default output existing-default-and-default-monitoring-output cannot be set to is_default=false or is_default_monitoring=false manually. Make another output the default first.`
+ );
+ });
+
it('should update existing default monitoring output when updating an output to become the default monitoring output', async () => {
const soClient = getMockedSoClient({
defaultOutputMonitoringId: 'existing-default-monitoring-output',
diff --git a/x-pack/plugins/fleet/server/services/output.ts b/x-pack/plugins/fleet/server/services/output.ts
index 50fb7b54a5346..afa92127dae97 100644
--- a/x-pack/plugins/fleet/server/services/output.ts
+++ b/x-pack/plugins/fleet/server/services/output.ts
@@ -9,27 +9,27 @@ import { omit } from 'lodash';
import { safeLoad } from 'js-yaml';
import deepEqual from 'fast-deep-equal';
-import { SavedObjectsUtils } from '@kbn/core/server';
import type {
+ ElasticsearchClient,
KibanaRequest,
SavedObject,
SavedObjectsClientContract,
- ElasticsearchClient,
} from '@kbn/core/server';
+import { SavedObjectsUtils } from '@kbn/core/server';
-import type { NewOutput, Output, OutputSOAttributes, AgentPolicy } from '../types';
+import type { AgentPolicy, NewOutput, Output, OutputSOAttributes } from '../types';
import {
+ AGENT_POLICY_SAVED_OBJECT_TYPE,
DEFAULT_OUTPUT,
DEFAULT_OUTPUT_ID,
OUTPUT_SAVED_OBJECT_TYPE,
- AGENT_POLICY_SAVED_OBJECT_TYPE,
} from '../constants';
-import { SO_SEARCH_LIMIT, outputType } from '../../common/constants';
+import { outputType, SO_SEARCH_LIMIT } from '../../common/constants';
import { normalizeHostsForAgents } from '../../common/services';
import {
- OutputUnauthorizedError,
- OutputInvalidError,
FleetEncryptedSavedObjectEncryptionKeyRequired,
+ OutputInvalidError,
+ OutputUnauthorizedError,
} from '../errors';
import { agentPolicyService } from './agent_policy';
@@ -261,6 +261,59 @@ class OutputService {
return outputs;
}
+ private async _updateDefaultOutput(
+ soClient: SavedObjectsClientContract,
+ defaultDataOutputId: string,
+ updateData: { is_default: boolean } | { is_default_monitoring: boolean },
+ fromPreconfiguration: boolean
+ ) {
+ const originalOutput = await this.get(soClient, defaultDataOutputId);
+ this._validateFieldsAreEditable(
+ originalOutput,
+ updateData,
+ defaultDataOutputId,
+ fromPreconfiguration
+ );
+
+ auditLoggingService.writeCustomSoAuditLog({
+ action: 'update',
+ id: outputIdToUuid(defaultDataOutputId),
+ savedObjectType: OUTPUT_SAVED_OBJECT_TYPE,
+ });
+
+ return await this.encryptedSoClient.update>(
+ SAVED_OBJECT_TYPE,
+ outputIdToUuid(defaultDataOutputId),
+ updateData
+ );
+ }
+
+ private _validateFieldsAreEditable(
+ originalOutput: Output,
+ data: Partial