diff --git a/x-pack/plugins/fields_metadata/server/plugin.ts b/x-pack/plugins/fields_metadata/server/plugin.ts index 70ccb7b5d30a3..144b03d6f6786 100644 --- a/x-pack/plugins/fields_metadata/server/plugin.ts +++ b/x-pack/plugins/fields_metadata/server/plugin.ts @@ -56,8 +56,8 @@ export class FieldsMetadataPlugin }; } - public start(_core: CoreStart, _plugins: FieldsMetadataServerPluginStartDeps) { - const fieldsMetadata = this.fieldsMetadataService.start(); + public start(core: CoreStart, _plugins: FieldsMetadataServerPluginStartDeps) { + const fieldsMetadata = this.fieldsMetadataService.start(core); return { getClient: fieldsMetadata.getClient }; } diff --git a/x-pack/plugins/fields_metadata/server/routes/fields_metadata/find_fields_metadata.ts b/x-pack/plugins/fields_metadata/server/routes/fields_metadata/find_fields_metadata.ts index 422c16a726843..9d186b57dd8dc 100644 --- a/x-pack/plugins/fields_metadata/server/routes/fields_metadata/find_fields_metadata.ts +++ b/x-pack/plugins/fields_metadata/server/routes/fields_metadata/find_fields_metadata.ts @@ -27,7 +27,8 @@ export const initFindFieldsMetadataRoute = ({ security: { authz: { enabled: false, - reason: 'This route is opted out from authorization', + reason: + 'This route is opted out from authorization to keep available the access to static fields metadata such as ECS fields. For other sources (fleet integrations), appropriate checks are performed at the API level.', }, }, validate: { @@ -38,9 +39,9 @@ export const initFindFieldsMetadataRoute = ({ }, async (_requestContext, request, response) => { const { attributes, fieldNames, integration, dataset } = request.query; - const [_core, _startDeps, startContract] = await getStartServices(); - const fieldsMetadataClient = startContract.getClient(); + + const fieldsMetadataClient = await startContract.getClient(request); try { const fieldsDictionary = await fieldsMetadataClient.find({ diff --git a/x-pack/plugins/fields_metadata/server/services/fields_metadata/fields_metadata_client.test.ts b/x-pack/plugins/fields_metadata/server/services/fields_metadata/fields_metadata_client.test.ts index 4ef2e3c693fb5..f59380b948e2d 100644 --- a/x-pack/plugins/fields_metadata/server/services/fields_metadata/fields_metadata_client.test.ts +++ b/x-pack/plugins/fields_metadata/server/services/fields_metadata/fields_metadata_client.test.ts @@ -103,10 +103,11 @@ describe('FieldsMetadataClient class', () => { integrationListExtractor, }); fieldsMetadataClient = FieldsMetadataClient.create({ + capabilities: { fleet: { read: true }, fleetv2: { read: true } }, + logger, ecsFieldsRepository, integrationFieldsRepository, metadataFieldsRepository, - logger, }); }); @@ -184,6 +185,21 @@ describe('FieldsMetadataClient class', () => { expect(integrationFieldsExtractor).not.toHaveBeenCalled(); expect(unknownFieldInstance).toBeUndefined(); }); + + it('should not resolve the field from an integration if the user has not the fleet privileges to access it', async () => { + const clientWithouthPrivileges = FieldsMetadataClient.create({ + capabilities: { fleet: { read: false }, fleetv2: { read: false } }, + logger, + ecsFieldsRepository, + integrationFieldsRepository, + metadataFieldsRepository, + }); + + const fieldInstance = await clientWithouthPrivileges.getByName('mysql.slowlog.filesort'); + + expect(integrationFieldsExtractor).not.toHaveBeenCalled(); + expect(fieldInstance).toBeUndefined(); + }); }); describe('#find', () => { diff --git a/x-pack/plugins/fields_metadata/server/services/fields_metadata/fields_metadata_client.ts b/x-pack/plugins/fields_metadata/server/services/fields_metadata/fields_metadata_client.ts index baaac903a7b3a..4aa0d8c1a4c71 100644 --- a/x-pack/plugins/fields_metadata/server/services/fields_metadata/fields_metadata_client.ts +++ b/x-pack/plugins/fields_metadata/server/services/fields_metadata/fields_metadata_client.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { Logger } from '@kbn/core/server'; +import { Capabilities, Logger } from '@kbn/core/server'; import { FieldName, FieldMetadata, FieldsMetadataDictionary } from '../../../common'; import { EcsFieldsRepository } from './repositories/ecs_fields_repository'; import { IntegrationFieldsRepository } from './repositories/integration_fields_repository'; @@ -13,7 +13,13 @@ import { MetadataFieldsRepository } from './repositories/metadata_fields_reposit import { IntegrationFieldsSearchParams } from './repositories/types'; import { FindFieldsMetadataOptions, IFieldsMetadataClient } from './types'; +interface FleetCapabilities { + fleet: Capabilities[string]; + fleetv2: Capabilities[string]; +} + interface FieldsMetadataClientDeps { + capabilities: FleetCapabilities; logger: Logger; ecsFieldsRepository: EcsFieldsRepository; metadataFieldsRepository: MetadataFieldsRepository; @@ -22,6 +28,7 @@ interface FieldsMetadataClientDeps { export class FieldsMetadataClient implements IFieldsMetadataClient { private constructor( + private readonly capabilities: FleetCapabilities, private readonly logger: Logger, private readonly ecsFieldsRepository: EcsFieldsRepository, private readonly metadataFieldsRepository: MetadataFieldsRepository, @@ -43,7 +50,7 @@ export class FieldsMetadataClient implements IFieldsMetadataClient { } // 2. Try searching for the fiels in the Elastic Package Registry - if (!field) { + if (!field && this.hasFleetPermissions(this.capabilities)) { field = await this.integrationFieldsRepository.getByName(fieldName, { integration, dataset }); } @@ -74,13 +81,21 @@ export class FieldsMetadataClient implements IFieldsMetadataClient { return FieldsMetadataDictionary.create(fields); } + private hasFleetPermissions(capabilities: FleetCapabilities) { + const { fleet, fleetv2 } = capabilities; + + return fleet.read && fleetv2.read; + } + public static create({ + capabilities, logger, ecsFieldsRepository, metadataFieldsRepository, integrationFieldsRepository, }: FieldsMetadataClientDeps) { return new FieldsMetadataClient( + capabilities, logger, ecsFieldsRepository, metadataFieldsRepository, diff --git a/x-pack/plugins/fields_metadata/server/services/fields_metadata/fields_metadata_service.mock.ts b/x-pack/plugins/fields_metadata/server/services/fields_metadata/fields_metadata_service.mock.ts index b6395d4c96f6b..62ffc231fe837 100644 --- a/x-pack/plugins/fields_metadata/server/services/fields_metadata/fields_metadata_service.mock.ts +++ b/x-pack/plugins/fields_metadata/server/services/fields_metadata/fields_metadata_service.mock.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { KibanaRequest } from '@kbn/core-http-server'; import { createFieldsMetadataClientMock } from './fields_metadata_client.mock'; import { FieldsMetadataServiceSetup, FieldsMetadataServiceStart } from './types'; @@ -16,5 +17,7 @@ export const createFieldsMetadataServiceSetupMock = export const createFieldsMetadataServiceStartMock = (): jest.Mocked => ({ - getClient: jest.fn(() => createFieldsMetadataClientMock()), + getClient: jest.fn((_request: KibanaRequest) => + Promise.resolve(createFieldsMetadataClientMock()) + ), }); diff --git a/x-pack/plugins/fields_metadata/server/services/fields_metadata/fields_metadata_service.ts b/x-pack/plugins/fields_metadata/server/services/fields_metadata/fields_metadata_service.ts index dc8aa976e34be..6e00572c21070 100644 --- a/x-pack/plugins/fields_metadata/server/services/fields_metadata/fields_metadata_service.ts +++ b/x-pack/plugins/fields_metadata/server/services/fields_metadata/fields_metadata_service.ts @@ -6,7 +6,7 @@ */ import { EcsFlat as ecsFields } from '@elastic/ecs'; -import { Logger } from '@kbn/core/server'; +import { CoreStart, Logger } from '@kbn/core/server'; import { FieldsMetadataClient } from './fields_metadata_client'; import { EcsFieldsRepository } from './repositories/ecs_fields_repository'; import { IntegrationFieldsRepository } from './repositories/integration_fields_repository'; @@ -32,7 +32,7 @@ export class FieldsMetadataService { }; } - public start(): FieldsMetadataServiceStart { + public start(core: CoreStart): FieldsMetadataServiceStart { const { logger, integrationFieldsExtractor, integrationListExtractor } = this; const ecsFieldsRepository = EcsFieldsRepository.create({ ecsFields }); @@ -43,8 +43,13 @@ export class FieldsMetadataService { }); return { - getClient() { + getClient: async (request) => { + const { fleet, fleetv2 } = await core.capabilities.resolveCapabilities(request, { + capabilityPath: '*', + }); + return FieldsMetadataClient.create({ + capabilities: { fleet, fleetv2 }, logger, ecsFieldsRepository, metadataFieldsRepository, diff --git a/x-pack/plugins/fields_metadata/server/services/fields_metadata/types.ts b/x-pack/plugins/fields_metadata/server/services/fields_metadata/types.ts index 533b4fd0bb2c2..7e094fbbbf8b5 100644 --- a/x-pack/plugins/fields_metadata/server/services/fields_metadata/types.ts +++ b/x-pack/plugins/fields_metadata/server/services/fields_metadata/types.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { KibanaRequest } from '@kbn/core/server'; import { FieldName, FieldMetadata, FieldsMetadataDictionary } from '../../../common'; import { IntegrationFieldsExtractor, @@ -23,7 +24,7 @@ export interface FieldsMetadataServiceSetup { } export interface FieldsMetadataServiceStart { - getClient(): IFieldsMetadataClient; + getClient(request: KibanaRequest): Promise; } export interface FindFieldsMetadataOptions extends Partial {