diff --git a/x-pack/plugins/fleet/dev_docs/fleet_router.md b/x-pack/plugins/fleet/dev_docs/fleet_router.md new file mode 100644 index 0000000000000..03df8c19318ee --- /dev/null +++ b/x-pack/plugins/fleet/dev_docs/fleet_router.md @@ -0,0 +1,33 @@ +# Fleet router + +All the fleet API routes are wrapped with a custom handler see [fleet_router](../server/services/security/fleet_router.ts) that provides error handling and security. + +## Error handling + +All non catched errors in Fleet API will go throuh a default error handler, that will allow to transform known error in response with predefined status code. + +## Security + +Fleet router also provide an easy way to declare authorization rules for Fleet routes. This can be done via the `fleetAuthz` property via a function or an object with required roles. + +Examples: + +```typescript +router.versioned.get({ + path: OUTPUT_API_ROUTES.LIST_PATTERN, + fleetAuthz: (authz) => { + return authz.fleet.readSettings || authz.fleet.readAgentPolicies; + }, + summary: 'Get outputs', +}); +``` + +```typescript +router.versioned.post({ + path: OUTPUT_API_ROUTES.CREATE_PATTERN, + fleetAuthz: { + fleet: { allSettings: true }, + }, + summary: 'Create output', +}); +``` diff --git a/x-pack/plugins/fleet/server/routes/agent/actions_handlers.ts b/x-pack/plugins/fleet/server/routes/agent/actions_handlers.ts index b11dcb719e2d2..1871bbd74fd17 100644 --- a/x-pack/plugins/fleet/server/routes/agent/actions_handlers.ts +++ b/x-pack/plugins/fleet/server/routes/agent/actions_handlers.ts @@ -16,7 +16,6 @@ import type { } from '../../types/rest_spec'; import type { ActionsService } from '../../services/agents'; import type { PostNewAgentActionResponse } from '../../../common/types/rest_spec'; -import { defaultFleetErrorHandler } from '../../errors'; import { getCurrentNamespace } from '../../services/spaces/get_current_namespace'; export const postNewAgentActionHandlerBuilder = function ( @@ -27,30 +26,26 @@ export const postNewAgentActionHandlerBuilder = function ( TypeOf > { return async (context, request, response) => { - try { - const core = await context.core; - const esClient = core.elasticsearch.client.asInternalUser; - const soClient = core.savedObjects.client; + const core = await context.core; + const esClient = core.elasticsearch.client.asInternalUser; + const soClient = core.savedObjects.client; - const agent = await actionsService.getAgent(esClient, soClient, request.params.agentId); + const agent = await actionsService.getAgent(esClient, soClient, request.params.agentId); - const newAgentAction = request.body.action; + const newAgentAction = request.body.action; - const savedAgentAction = await actionsService.createAgentAction(esClient, { - created_at: new Date().toISOString(), - ...newAgentAction, - agents: [agent.id], - namespaces: [getCurrentNamespace(soClient)], - }); + const savedAgentAction = await actionsService.createAgentAction(esClient, { + created_at: new Date().toISOString(), + ...newAgentAction, + agents: [agent.id], + namespaces: [getCurrentNamespace(soClient)], + }); - const body: PostNewAgentActionResponse = { - item: savedAgentAction, - }; + const body: PostNewAgentActionResponse = { + item: savedAgentAction, + }; - return response.ok({ body }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } + return response.ok({ body }); }; }; @@ -58,24 +53,20 @@ export const postCancelActionHandlerBuilder = function ( actionsService: ActionsService ): RequestHandler, undefined, undefined> { return async (context, request, response) => { - try { - const core = await context.core; - const esClient = core.elasticsearch.client.asInternalUser; - const soClient = core.savedObjects.client; + const core = await context.core; + const esClient = core.elasticsearch.client.asInternalUser; + const soClient = core.savedObjects.client; - const action = await actionsService.cancelAgentAction( - esClient, - soClient, - request.params.actionId - ); + const action = await actionsService.cancelAgentAction( + esClient, + soClient, + request.params.actionId + ); - const body: PostNewAgentActionResponse = { - item: action, - }; + const body: PostNewAgentActionResponse = { + item: action, + }; - return response.ok({ body }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } + return response.ok({ body }); }; }; diff --git a/x-pack/plugins/fleet/server/routes/agent/handlers.ts b/x-pack/plugins/fleet/server/routes/agent/handlers.ts index faff25f9d5624..972a0f18b7ac3 100644 --- a/x-pack/plugins/fleet/server/routes/agent/handlers.ts +++ b/x-pack/plugins/fleet/server/routes/agent/handlers.ts @@ -42,7 +42,7 @@ import type { PostRetrieveAgentsByActionsRequestSchema, FleetRequestHandler, } from '../../types'; -import { defaultFleetErrorHandler, FleetNotFoundError } from '../../errors'; +import { FleetNotFoundError } from '../../errors'; import * as AgentService from '../../services/agents'; import { fetchAndAssignAgentMetrics } from '../../services/agents/agent_metrics'; import { getAgentStatusForAgentPolicy } from '../../services/agents'; @@ -83,7 +83,7 @@ export const getAgentHandler: FleetRequestHandler< }); } - return defaultFleetErrorHandler({ error, response }); + throw error; } }; @@ -112,7 +112,7 @@ export const deleteAgentHandler: FleetRequestHandler< }); } - return defaultFleetErrorHandler({ error, response }); + throw error; } }; @@ -150,7 +150,7 @@ export const updateAgentHandler: FleetRequestHandler< }); } - return defaultFleetErrorHandler({ error, response }); + throw error; } }; @@ -166,19 +166,15 @@ export const bulkUpdateAgentTagsHandler: RequestHandler< ? { agentIds: request.body.agents } : { kuery: request.body.agents, showInactive: request.body.includeInactive }; - try { - const results = await AgentService.updateAgentTags( - soClient, - esClient, - { ...agentOptions, batchSize: request.body.batchSize }, - request.body.tagsToAdd ?? [], - request.body.tagsToRemove ?? [] - ); - - return response.ok({ body: { actionId: results.actionId } }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } + const results = await AgentService.updateAgentTags( + soClient, + esClient, + { ...agentOptions, batchSize: request.body.batchSize }, + request.body.tagsToAdd ?? [], + request.body.tagsToRemove ?? [] + ); + + return response.ok({ body: { actionId: results.actionId } }); }; export const getAgentsHandler: FleetRequestHandler< @@ -189,38 +185,34 @@ export const getAgentsHandler: FleetRequestHandler< const { agentClient } = fleetContext; const esClientCurrentUser = coreContext.elasticsearch.client.asCurrentUser; - try { - const agentRes = await agentClient.asCurrentUser.listAgents({ - page: request.query.page, - perPage: request.query.perPage, - showInactive: request.query.showInactive, - showUpgradeable: request.query.showUpgradeable, - kuery: request.query.kuery, - sortField: request.query.sortField, - sortOrder: request.query.sortOrder, - getStatusSummary: request.query.getStatusSummary, - }); - - const { total, page, perPage, statusSummary } = agentRes; - let { agents } = agentRes; - - // Assign metrics - if (request.query.withMetrics) { - agents = await fetchAndAssignAgentMetrics(esClientCurrentUser, agents); - } - - const body: GetAgentsResponse = { - list: agents, // deprecated - items: agents, - total, - page, - perPage, - ...(statusSummary ? { statusSummary } : {}), - }; - return response.ok({ body }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); + const agentRes = await agentClient.asCurrentUser.listAgents({ + page: request.query.page, + perPage: request.query.perPage, + showInactive: request.query.showInactive, + showUpgradeable: request.query.showUpgradeable, + kuery: request.query.kuery, + sortField: request.query.sortField, + sortOrder: request.query.sortOrder, + getStatusSummary: request.query.getStatusSummary, + }); + + const { total, page, perPage, statusSummary } = agentRes; + let { agents } = agentRes; + + // Assign metrics + if (request.query.withMetrics) { + agents = await fetchAndAssignAgentMetrics(esClientCurrentUser, agents); } + + const body: GetAgentsResponse = { + list: agents, // deprecated + items: agents, + total, + page, + perPage, + ...(statusSummary ? { statusSummary } : {}), + }; + return response.ok({ body }); }; export const getAgentTagsHandler: RequestHandler< @@ -231,19 +223,15 @@ export const getAgentTagsHandler: RequestHandler< const esClient = coreContext.elasticsearch.client.asInternalUser; const soClient = coreContext.savedObjects.client; - try { - const tags = await AgentService.getAgentTags(soClient, esClient, { - showInactive: request.query.showInactive, - kuery: request.query.kuery, - }); + const tags = await AgentService.getAgentTags(soClient, esClient, { + showInactive: request.query.showInactive, + kuery: request.query.kuery, + }); - const body: GetAgentTagsResponse = { - items: tags, - }; - return response.ok({ body }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } + const body: GetAgentTagsResponse = { + items: tags, + }; + return response.ok({ body }); }; export const putAgentsReassignHandlerDeprecated: RequestHandler< @@ -254,19 +242,15 @@ export const putAgentsReassignHandlerDeprecated: RequestHandler< const coreContext = await context.core; const soClient = coreContext.savedObjects.client; const esClient = coreContext.elasticsearch.client.asInternalUser; - try { - await AgentService.reassignAgent( - soClient, - esClient, - request.params.agentId, - request.body.policy_id - ); - - const body: PutAgentReassignResponse = {}; - return response.ok({ body }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } + await AgentService.reassignAgent( + soClient, + esClient, + request.params.agentId, + request.body.policy_id + ); + + const body: PutAgentReassignResponse = {}; + return response.ok({ body }); }; export const postAgentReassignHandler: RequestHandler< @@ -277,19 +261,15 @@ export const postAgentReassignHandler: RequestHandler< const coreContext = await context.core; const soClient = coreContext.savedObjects.client; const esClient = coreContext.elasticsearch.client.asInternalUser; - try { - await AgentService.reassignAgent( - soClient, - esClient, - request.params.agentId, - request.body.policy_id - ); - - const body: PostAgentReassignResponse = {}; - return response.ok({ body }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } + await AgentService.reassignAgent( + soClient, + esClient, + request.params.agentId, + request.body.policy_id + ); + + const body: PostAgentReassignResponse = {}; + return response.ok({ body }); }; export const postBulkAgentReassignHandler: RequestHandler< @@ -304,52 +284,44 @@ export const postBulkAgentReassignHandler: RequestHandler< ? { agentIds: request.body.agents } : { kuery: request.body.agents, showInactive: request.body.includeInactive }; - try { - const results = await AgentService.reassignAgents( - soClient, - esClient, - { ...agentOptions, batchSize: request.body.batchSize }, - request.body.policy_id - ); - - return response.ok({ body: { actionId: results.actionId } }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } + const results = await AgentService.reassignAgents( + soClient, + esClient, + { ...agentOptions, batchSize: request.body.batchSize }, + request.body.policy_id + ); + + return response.ok({ body: { actionId: results.actionId } }); }; export const getAgentStatusForAgentPolicyHandler: FleetRequestHandler< undefined, TypeOf > = async (context, request, response) => { - try { - const [coreContext, fleetContext] = await Promise.all([context.core, context.fleet]); - const esClient = coreContext.elasticsearch.client.asInternalUser; - const soClient = fleetContext.internalSoClient; + const [coreContext, fleetContext] = await Promise.all([context.core, context.fleet]); + const esClient = coreContext.elasticsearch.client.asInternalUser; + const soClient = fleetContext.internalSoClient; - const parsePolicyIds = (policyIds: string | string[] | undefined): string[] | undefined => { - if (!policyIds || !policyIds.length) { - return undefined; - } + const parsePolicyIds = (policyIds: string | string[] | undefined): string[] | undefined => { + if (!policyIds || !policyIds.length) { + return undefined; + } - return Array.isArray(policyIds) ? policyIds : [policyIds]; - }; + return Array.isArray(policyIds) ? policyIds : [policyIds]; + }; - const results = await getAgentStatusForAgentPolicy( - esClient, - soClient, - request.query.policyId, - request.query.kuery, - coreContext.savedObjects.client.getCurrentNamespace(), - parsePolicyIds(request.query.policyIds) - ); + const results = await getAgentStatusForAgentPolicy( + esClient, + soClient, + request.query.policyId, + request.query.kuery, + coreContext.savedObjects.client.getCurrentNamespace(), + parsePolicyIds(request.query.policyIds) + ); - const body: GetAgentStatusResponse = { results }; + const body: GetAgentStatusResponse = { results }; - return response.ok({ body }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } + return response.ok({ body }); }; export const getAgentDataHandler: RequestHandler< @@ -358,24 +330,20 @@ export const getAgentDataHandler: RequestHandler< > = async (context, request, response) => { const coreContext = await context.core; const esClient = coreContext.elasticsearch.client.asCurrentUser; - try { - const returnDataPreview = request.query.previewData; - const agentIds = isStringArray(request.query.agentsIds) - ? request.query.agentsIds - : [request.query.agentsIds]; + const returnDataPreview = request.query.previewData; + const agentIds = isStringArray(request.query.agentsIds) + ? request.query.agentsIds + : [request.query.agentsIds]; - const { items, dataPreview } = await AgentService.getIncomingDataByAgentsId( - esClient, - agentIds, - returnDataPreview - ); + const { items, dataPreview } = await AgentService.getIncomingDataByAgentsId( + esClient, + agentIds, + returnDataPreview + ); - const body = { items, dataPreview }; + const body = { items, dataPreview }; - return response.ok({ body }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } + return response.ok({ body }); }; function isStringArray(arr: unknown | string[]): arr is string[] { @@ -387,24 +355,16 @@ export const getAgentStatusRuntimeFieldHandler: RequestHandler = async ( request, response ) => { - try { - const runtimeFields = await buildAgentStatusRuntimeField(); + const runtimeFields = await buildAgentStatusRuntimeField(); - return response.ok({ body: (runtimeFields.status.script as Script)!.source! }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } + return response.ok({ body: (runtimeFields.status.script as Script)!.source! }); }; export const getAvailableVersionsHandler: RequestHandler = async (context, request, response) => { - try { - const availableVersions = await AgentService.getAvailableVersions(); - const body: GetAvailableVersionsResponse = { items: availableVersions }; + const availableVersions = await AgentService.getAvailableVersions(); + const body: GetAvailableVersionsResponse = { items: availableVersions }; - return response.ok({ body }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } + return response.ok({ body }); }; export const getActionStatusHandler: RequestHandler< @@ -414,17 +374,13 @@ export const getActionStatusHandler: RequestHandler< const coreContext = await context.core; const esClient = coreContext.elasticsearch.client.asInternalUser; - try { - const actionStatuses = await AgentService.getActionStatuses( - esClient, - request.query, - getCurrentNamespace(coreContext.savedObjects.client) - ); - const body: GetActionStatusResponse = { items: actionStatuses }; - return response.ok({ body }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } + const actionStatuses = await AgentService.getActionStatuses( + esClient, + request.query, + getCurrentNamespace(coreContext.savedObjects.client) + ); + const body: GetActionStatusResponse = { items: actionStatuses }; + return response.ok({ body }); }; export const postRetrieveAgentsByActionsHandler: RequestHandler< @@ -435,13 +391,9 @@ export const postRetrieveAgentsByActionsHandler: RequestHandler< const coreContext = await context.core; const esClient = coreContext.elasticsearch.client.asInternalUser; - try { - const agents = await AgentService.getAgentsByActionsIds(esClient, request.body.actionIds); - const body: PostRetrieveAgentsByActionsResponse = { items: agents }; - return response.ok({ body }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } + const agents = await AgentService.getAgentsByActionsIds(esClient, request.body.actionIds); + const body: PostRetrieveAgentsByActionsResponse = { items: agents }; + return response.ok({ body }); }; export const getAgentUploadsHandler: RequestHandler< @@ -449,15 +401,11 @@ export const getAgentUploadsHandler: RequestHandler< > = async (context, request, response) => { const coreContext = await context.core; const esClient = coreContext.elasticsearch.client.asInternalUser; - try { - const body: GetAgentUploadsResponse = { - items: await AgentService.getAgentUploads(esClient, request.params.agentId), - }; + const body: GetAgentUploadsResponse = { + items: await AgentService.getAgentUploads(esClient, request.params.agentId), + }; - return response.ok({ body }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } + return response.ok({ body }); }; export const getAgentUploadFileHandler: RequestHandler< @@ -465,17 +413,13 @@ export const getAgentUploadFileHandler: RequestHandler< > = async (context, request, response) => { const coreContext = await context.core; const esClient = coreContext.elasticsearch.client.asInternalUser; - try { - const resp = await AgentService.getAgentUploadFile( - esClient, - request.params.fileId, - request.params.fileName - ); + const resp = await AgentService.getAgentUploadFile( + esClient, + request.params.fileId, + request.params.fileName + ); - return response.ok(resp); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } + return response.ok(resp); }; export const deleteAgentUploadFileHandler: RequestHandler< @@ -483,11 +427,7 @@ export const deleteAgentUploadFileHandler: RequestHandler< > = async (context, request, response) => { const coreContext = await context.core; const esClient = coreContext.elasticsearch.client.asInternalUser; - try { - const resp = await AgentService.deleteAgentUploadFile(esClient, request.params.fileId); + const resp = await AgentService.deleteAgentUploadFile(esClient, request.params.fileId); - return response.ok({ body: resp }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } + return response.ok({ body: resp }); }; diff --git a/x-pack/plugins/fleet/server/routes/agent/request_diagnostics_handler.test.ts b/x-pack/plugins/fleet/server/routes/agent/request_diagnostics_handler.test.ts index b0502c6a23cd5..fccc5790df087 100644 --- a/x-pack/plugins/fleet/server/routes/agent/request_diagnostics_handler.test.ts +++ b/x-pack/plugins/fleet/server/routes/agent/request_diagnostics_handler.test.ts @@ -7,7 +7,6 @@ import type { ElasticsearchClient, KibanaResponseFactory, - RequestHandlerContext, SavedObjectsClientContract, KibanaRequest, } from '@kbn/core/server'; @@ -18,12 +17,14 @@ import { } from '@kbn/core/server/mocks'; import type { RequestDiagnosticsAdditionalMetrics } from '../../../common/types'; - +import { withDefaultErrorHandler } from '../../services/security/fleet_router'; import { getAgentById } from '../../services/agents'; import * as AgentService from '../../services/agents'; +import { type FleetRequestHandlerContext } from '../..'; import { requestDiagnosticsHandler } from './request_diagnostics_handler'; +const requestDiagnosticsWithErrorHandler = withDefaultErrorHandler(requestDiagnosticsHandler); jest.mock('../../services/agents'); const mockGetAgentById = getAgentById as jest.Mock; @@ -32,7 +33,7 @@ describe('request diagnostics handler', () => { let mockResponse: jest.Mocked; let mockSavedObjectsClient: jest.Mocked; let mockElasticsearchClient: jest.Mocked; - let mockContext: RequestHandlerContext; + let mockContext: FleetRequestHandlerContext; let mockRequest: KibanaRequest< { agentId: string }, undefined, @@ -56,7 +57,7 @@ describe('request diagnostics handler', () => { }, }, }, - } as unknown as RequestHandlerContext; + } as unknown as FleetRequestHandlerContext; mockRequest = httpServerMock.createKibanaRequest({ params: { agentId: 'agent1' }, body: { additional_metrics: ['CPU'] }, @@ -69,7 +70,7 @@ describe('request diagnostics handler', () => { local_metadata: { elastic: { agent: { version: '8.7.0' } } }, }); - await requestDiagnosticsHandler(mockContext, mockRequest, mockResponse); + await requestDiagnosticsWithErrorHandler(mockContext, mockRequest, mockResponse); expect(mockResponse.ok).toHaveBeenCalledWith({ body: { actionId: '1' } }); }); @@ -80,7 +81,7 @@ describe('request diagnostics handler', () => { local_metadata: { elastic: { agent: { version: '8.6.0' } } }, }); - await requestDiagnosticsHandler(mockContext, mockRequest, mockResponse); + await requestDiagnosticsWithErrorHandler(mockContext, mockRequest, mockResponse); expect(mockResponse.customError).toHaveBeenCalledWith({ body: { message: 'Agent agent1 does not support request diagnostics action.' }, diff --git a/x-pack/plugins/fleet/server/routes/agent/request_diagnostics_handler.ts b/x-pack/plugins/fleet/server/routes/agent/request_diagnostics_handler.ts index b59d38d947860..63177e6eb57c1 100644 --- a/x-pack/plugins/fleet/server/routes/agent/request_diagnostics_handler.ts +++ b/x-pack/plugins/fleet/server/routes/agent/request_diagnostics_handler.ts @@ -5,20 +5,19 @@ * 2.0. */ -import type { RequestHandler } from '@kbn/core/server'; import type { TypeOf } from '@kbn/config-schema'; import { isAgentRequestDiagnosticsSupported } from '../../../common/services'; import * as AgentService from '../../services/agents'; import type { + FleetRequestHandler, PostBulkRequestDiagnosticsActionRequestSchema, PostRequestDiagnosticsActionRequestSchema, } from '../../types'; -import { defaultFleetErrorHandler } from '../../errors'; import { getAgentById } from '../../services/agents'; -export const requestDiagnosticsHandler: RequestHandler< +export const requestDiagnosticsHandler: FleetRequestHandler< TypeOf, undefined, TypeOf @@ -26,32 +25,28 @@ export const requestDiagnosticsHandler: RequestHandler< const coreContext = await context.core; const esClient = coreContext.elasticsearch.client.asInternalUser; const soClient = coreContext.savedObjects.client; - try { - const agent = await getAgentById(esClient, soClient, request.params.agentId); + const agent = await getAgentById(esClient, soClient, request.params.agentId); - if (!isAgentRequestDiagnosticsSupported(agent)) { - return response.customError({ - statusCode: 400, - body: { - message: `Agent ${request.params.agentId} does not support request diagnostics action.`, - }, - }); - } + if (!isAgentRequestDiagnosticsSupported(agent)) { + return response.customError({ + statusCode: 400, + body: { + message: `Agent ${request.params.agentId} does not support request diagnostics action.`, + }, + }); + } - const result = await AgentService.requestDiagnostics( - esClient, - soClient, - request.params.agentId, - request.body?.additional_metrics - ); + const result = await AgentService.requestDiagnostics( + esClient, + soClient, + request.params.agentId, + request.body?.additional_metrics + ); - return response.ok({ body: { actionId: result.actionId } }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } + return response.ok({ body: { actionId: result.actionId } }); }; -export const bulkRequestDiagnosticsHandler: RequestHandler< +export const bulkRequestDiagnosticsHandler: FleetRequestHandler< undefined, undefined, TypeOf @@ -62,15 +57,11 @@ export const bulkRequestDiagnosticsHandler: RequestHandler< const agentOptions = Array.isArray(request.body.agents) ? { agentIds: request.body.agents } : { kuery: request.body.agents }; - try { - const result = await AgentService.bulkRequestDiagnostics(esClient, soClient, { - ...agentOptions, - batchSize: request.body.batchSize, - additionalMetrics: request.body.additional_metrics, - }); + const result = await AgentService.bulkRequestDiagnostics(esClient, soClient, { + ...agentOptions, + batchSize: request.body.batchSize, + additionalMetrics: request.body.additional_metrics, + }); - return response.ok({ body: { actionId: result.actionId } }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } + return response.ok({ body: { actionId: result.actionId } }); }; diff --git a/x-pack/plugins/fleet/server/routes/agent/unenroll_handler.ts b/x-pack/plugins/fleet/server/routes/agent/unenroll_handler.ts index ec72f23a9876d..6f8b4c3d82fae 100644 --- a/x-pack/plugins/fleet/server/routes/agent/unenroll_handler.ts +++ b/x-pack/plugins/fleet/server/routes/agent/unenroll_handler.ts @@ -14,7 +14,6 @@ import type { PostBulkAgentUnenrollRequestSchema, } from '../../types'; import * as AgentService from '../../services/agents'; -import { defaultFleetErrorHandler } from '../../errors'; export const postAgentUnenrollHandler: RequestHandler< TypeOf, @@ -24,17 +23,13 @@ export const postAgentUnenrollHandler: RequestHandler< const coreContext = await context.core; const soClient = coreContext.savedObjects.client; const esClient = coreContext.elasticsearch.client.asInternalUser; - try { - await AgentService.unenrollAgent(soClient, esClient, request.params.agentId, { - force: request.body?.force, - revoke: request.body?.revoke, - }); + await AgentService.unenrollAgent(soClient, esClient, request.params.agentId, { + force: request.body?.force, + revoke: request.body?.revoke, + }); - const body: PostAgentUnenrollResponse = {}; - return response.ok({ body }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } + const body: PostAgentUnenrollResponse = {}; + return response.ok({ body }); }; export const postBulkAgentsUnenrollHandler: RequestHandler< @@ -49,17 +44,13 @@ export const postBulkAgentsUnenrollHandler: RequestHandler< ? { agentIds: request.body.agents } : { kuery: request.body.agents }; - try { - const results = await AgentService.unenrollAgents(soClient, esClient, { - ...agentOptions, - revoke: request.body?.revoke, - force: request.body?.force, - batchSize: request.body?.batchSize, - showInactive: request.body?.includeInactive, - }); + const results = await AgentService.unenrollAgents(soClient, esClient, { + ...agentOptions, + revoke: request.body?.revoke, + force: request.body?.force, + batchSize: request.body?.batchSize, + showInactive: request.body?.includeInactive, + }); - return response.ok({ body: { actionId: results.actionId } }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } + return response.ok({ body: { actionId: results.actionId } }); }; diff --git a/x-pack/plugins/fleet/server/routes/agent/upgrade_handler.ts b/x-pack/plugins/fleet/server/routes/agent/upgrade_handler.ts index c2a4361a8253e..aaacd5b4a1c59 100644 --- a/x-pack/plugins/fleet/server/routes/agent/upgrade_handler.ts +++ b/x-pack/plugins/fleet/server/routes/agent/upgrade_handler.ts @@ -19,7 +19,7 @@ import type { PostAgentUpgradeResponse } from '../../../common/types'; import type { PostAgentUpgradeRequestSchema, PostBulkAgentUpgradeRequestSchema } from '../../types'; import * as AgentService from '../../services/agents'; import { appContextService } from '../../services'; -import { defaultFleetErrorHandler, AgentRequestInvalidError } from '../../errors'; +import { AgentRequestInvalidError } from '../../errors'; import { getRecentUpgradeInfoForAgent, AGENT_UPGRADE_COOLDOWN_IN_MIN, @@ -55,88 +55,84 @@ export const postAgentUpgradeHandler: RequestHandler< }, }); } - try { - const agent = await getAgentById(esClient, soClient, request.params.agentId); - - const fleetServerAgents = await getAllFleetServerAgents(soClient, esClient); - const agentIsFleetServer = fleetServerAgents.some( - (fleetServerAgent) => fleetServerAgent.id === agent.id - ); - if (!agentIsFleetServer) { - try { - checkFleetServerVersion(version, fleetServerAgents); - } catch (err) { - return response.customError({ - statusCode: 400, - body: { - message: err.message, - }, - }); - } - } - - const { hasBeenUpgradedRecently, timeToWaitMs } = getRecentUpgradeInfoForAgent(agent); - const timeToWaitString = moment - .utc(moment.duration(timeToWaitMs).asMilliseconds()) - .format('mm[m]ss[s]'); - - if (!skipRateLimitCheck && hasBeenUpgradedRecently) { - return response.customError({ - statusCode: 429, - body: { - message: `agent ${request.params.agentId} was upgraded less than ${AGENT_UPGRADE_COOLDOWN_IN_MIN} minutes ago. Please wait ${timeToWaitString} before trying again to ensure the upgrade will not be rolled back.`, - }, - headers: { - // retry-after expects seconds - 'retry-after': Math.ceil(timeToWaitMs / 1000).toString(), - }, - }); - } - - if (agent.unenrollment_started_at || agent.unenrolled_at) { + const agent = await getAgentById(esClient, soClient, request.params.agentId); + + const fleetServerAgents = await getAllFleetServerAgents(soClient, esClient); + const agentIsFleetServer = fleetServerAgents.some( + (fleetServerAgent) => fleetServerAgent.id === agent.id + ); + if (!agentIsFleetServer) { + try { + checkFleetServerVersion(version, fleetServerAgents); + } catch (err) { return response.customError({ statusCode: 400, body: { - message: 'cannot upgrade an unenrolling or unenrolled agent', + message: err.message, }, }); } + } - if (!force && isAgentUpgrading(agent)) { - return response.customError({ - statusCode: 400, - body: { - message: `agent ${request.params.agentId} is already upgrading`, - }, - }); - } + const { hasBeenUpgradedRecently, timeToWaitMs } = getRecentUpgradeInfoForAgent(agent); + const timeToWaitString = moment + .utc(moment.duration(timeToWaitMs).asMilliseconds()) + .format('mm[m]ss[s]'); - if (!force && !skipRateLimitCheck && !isAgentUpgradeableToVersion(agent, version)) { - return response.customError({ - statusCode: 400, - body: { - message: `Agent ${request.params.agentId} is not upgradeable: ${getNotUpgradeableMessage( - agent, - latestAgentVersion, - version - )}`, - }, - }); - } + if (!skipRateLimitCheck && hasBeenUpgradedRecently) { + return response.customError({ + statusCode: 429, + body: { + message: `agent ${request.params.agentId} was upgraded less than ${AGENT_UPGRADE_COOLDOWN_IN_MIN} minutes ago. Please wait ${timeToWaitString} before trying again to ensure the upgrade will not be rolled back.`, + }, + headers: { + // retry-after expects seconds + 'retry-after': Math.ceil(timeToWaitMs / 1000).toString(), + }, + }); + } - await AgentService.sendUpgradeAgentAction({ - soClient, - esClient, - agentId: request.params.agentId, - version, - sourceUri, + if (agent.unenrollment_started_at || agent.unenrolled_at) { + return response.customError({ + statusCode: 400, + body: { + message: 'cannot upgrade an unenrolling or unenrolled agent', + }, }); + } + + if (!force && isAgentUpgrading(agent)) { + return response.customError({ + statusCode: 400, + body: { + message: `agent ${request.params.agentId} is already upgrading`, + }, + }); + } - const body: PostAgentUpgradeResponse = {}; - return response.ok({ body }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); + if (!force && !skipRateLimitCheck && !isAgentUpgradeableToVersion(agent, version)) { + return response.customError({ + statusCode: 400, + body: { + message: `Agent ${request.params.agentId} is not upgradeable: ${getNotUpgradeableMessage( + agent, + latestAgentVersion, + version + )}`, + }, + }); } + + await AgentService.sendUpgradeAgentAction({ + soClient, + esClient, + agentId: request.params.agentId, + version, + sourceUri, + }); + + const body: PostAgentUpgradeResponse = {}; + return response.ok({ body }); }; export const postBulkAgentsUpgradeHandler: RequestHandler< @@ -171,26 +167,22 @@ export const postBulkAgentsUpgradeHandler: RequestHandler< }); } - try { - const agentOptions = Array.isArray(agents) - ? { agentIds: agents } - : { kuery: agents, showInactive: request.body.includeInactive }; - const upgradeOptions = { - ...agentOptions, - sourceUri, - version, - force, - skipRateLimitCheck, - upgradeDurationSeconds, - startTime, - batchSize, - }; - const results = await AgentService.sendUpgradeAgentsActions(soClient, esClient, upgradeOptions); - - return response.ok({ body: { actionId: results.actionId } }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } + const agentOptions = Array.isArray(agents) + ? { agentIds: agents } + : { kuery: agents, showInactive: request.body.includeInactive }; + const upgradeOptions = { + ...agentOptions, + sourceUri, + version, + force, + skipRateLimitCheck, + upgradeDurationSeconds, + startTime, + batchSize, + }; + const results = await AgentService.sendUpgradeAgentsActions(soClient, esClient, upgradeOptions); + + return response.ok({ body: { actionId: results.actionId } }); }; export const checkKibanaVersion = (version: string, kibanaVersion: string, force = false) => { diff --git a/x-pack/plugins/fleet/server/routes/agent_policy/handlers.ts b/x-pack/plugins/fleet/server/routes/agent_policy/handlers.ts index 01c476eeb5ae8..1ea17e7734ef6 100644 --- a/x-pack/plugins/fleet/server/routes/agent_policy/handlers.ts +++ b/x-pack/plugins/fleet/server/routes/agent_policy/handlers.ts @@ -48,12 +48,7 @@ import type { GetFullAgentManifestResponse, BulkGetAgentPoliciesResponse, } from '../../../common/types'; -import { - defaultFleetErrorHandler, - AgentPolicyNotFoundError, - FleetUnauthorizedError, - FleetError, -} from '../../errors'; +import { AgentPolicyNotFoundError, FleetUnauthorizedError, FleetError } from '../../errors'; import { createAgentPolicyWithPackages } from '../../services/agent_policy_create'; import { updateAgentPolicySpaces } from '../../services/spaces/agent_policy'; import { packagePolicyToSimplifiedPackagePolicy } from '../../../common/services/simplified_package_policy_helper'; @@ -127,59 +122,55 @@ export const getAgentPoliciesHandler: FleetRequestHandler< undefined, TypeOf > = async (context, request, response) => { - try { - const [coreContext, fleetContext] = await Promise.all([context.core, context.fleet]); - const soClient = fleetContext.internalSoClient; - const esClient = coreContext.elasticsearch.client.asInternalUser; - const { - full: withPackagePolicies = false, - noAgentCount = false, - format, - ...restOfQuery - } = request.query; - if (!fleetContext.authz.fleet.readAgentPolicies && withPackagePolicies) { - throw new FleetUnauthorizedError( - 'full query parameter require agent policies read permissions' - ); - } - const agentPoliciesResponse = await agentPolicyService.list(soClient, { - withPackagePolicies, - esClient, - ...restOfQuery, - }); - let { items } = agentPoliciesResponse; - const { total, page, perPage } = agentPoliciesResponse; - - if (fleetContext.authz.fleet.readAgents && !noAgentCount) { - await populateAssignedAgentsCount(fleetContext.agentClient.asCurrentUser, items); - } + const [coreContext, fleetContext] = await Promise.all([context.core, context.fleet]); + const soClient = fleetContext.internalSoClient; + const esClient = coreContext.elasticsearch.client.asInternalUser; + const { + full: withPackagePolicies = false, + noAgentCount = false, + format, + ...restOfQuery + } = request.query; + if (!fleetContext.authz.fleet.readAgentPolicies && withPackagePolicies) { + throw new FleetUnauthorizedError( + 'full query parameter require agent policies read permissions' + ); + } + const agentPoliciesResponse = await agentPolicyService.list(soClient, { + withPackagePolicies, + esClient, + ...restOfQuery, + }); + let { items } = agentPoliciesResponse; + const { total, page, perPage } = agentPoliciesResponse; - if (!fleetContext.authz.fleet.readAgentPolicies) { - items = items.map(sanitizeItemForReadAgentOnly); - } else if (withPackagePolicies && format === inputsFormat.Simplified) { - items.map((item) => { - if (isEmpty(item.package_policies)) { - return item; - } - return { - ...item, - package_policies: item.package_policies!.map((packagePolicy) => - packagePolicyToSimplifiedPackagePolicy(packagePolicy) - ), - }; - }); - } + if (fleetContext.authz.fleet.readAgents && !noAgentCount) { + await populateAssignedAgentsCount(fleetContext.agentClient.asCurrentUser, items); + } - const body: GetAgentPoliciesResponse = { - items, - total, - page, - perPage, - }; - return response.ok({ body }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); + if (!fleetContext.authz.fleet.readAgentPolicies) { + items = items.map(sanitizeItemForReadAgentOnly); + } else if (withPackagePolicies && format === inputsFormat.Simplified) { + items.map((item) => { + if (isEmpty(item.package_policies)) { + return item; + } + return { + ...item, + package_policies: item.package_policies!.map((packagePolicy) => + packagePolicyToSimplifiedPackagePolicy(packagePolicy) + ), + }; + }); } + + const body: GetAgentPoliciesResponse = { + items, + total, + page, + perPage, + }; + return response.ok({ body }); }; export const bulkGetAgentPoliciesHandler: FleetRequestHandler< @@ -233,7 +224,7 @@ export const bulkGetAgentPoliciesHandler: FleetRequestHandler< }); } - return defaultFleetErrorHandler({ error, response }); + throw error; } }; @@ -241,43 +232,39 @@ export const getOneAgentPolicyHandler: FleetRequestHandler< TypeOf, TypeOf > = async (context, request, response) => { - try { - const [coreContext, fleetContext] = await Promise.all([context.core, context.fleet]); - const soClient = coreContext.savedObjects.client; + const [coreContext, fleetContext] = await Promise.all([context.core, context.fleet]); + const soClient = coreContext.savedObjects.client; - const agentPolicy = await agentPolicyService.get(soClient, request.params.agentPolicyId); - if (agentPolicy) { - if (fleetContext.authz.fleet.readAgents) { - await populateAssignedAgentsCount(fleetContext.agentClient.asCurrentUser, [agentPolicy]); - } - let item: any = agentPolicy; - if (!fleetContext.authz.fleet.readAgentPolicies) { - item = sanitizeItemForReadAgentOnly(agentPolicy); - } else if ( - request.query.format === inputsFormat.Simplified && - !isEmpty(agentPolicy.package_policies) - ) { - item = { - ...agentPolicy, - package_policies: agentPolicy.package_policies!.map((packagePolicy) => - packagePolicyToSimplifiedPackagePolicy(packagePolicy) - ), - }; - } - const body: GetOneAgentPolicyResponse = { - item, + const agentPolicy = await agentPolicyService.get(soClient, request.params.agentPolicyId); + if (agentPolicy) { + if (fleetContext.authz.fleet.readAgents) { + await populateAssignedAgentsCount(fleetContext.agentClient.asCurrentUser, [agentPolicy]); + } + let item: any = agentPolicy; + if (!fleetContext.authz.fleet.readAgentPolicies) { + item = sanitizeItemForReadAgentOnly(agentPolicy); + } else if ( + request.query.format === inputsFormat.Simplified && + !isEmpty(agentPolicy.package_policies) + ) { + item = { + ...agentPolicy, + package_policies: agentPolicy.package_policies!.map((packagePolicy) => + packagePolicyToSimplifiedPackagePolicy(packagePolicy) + ), }; - return response.ok({ - body, - }); - } else { - return response.customError({ - statusCode: 404, - body: { message: 'Agent policy not found' }, - }); } - } catch (error) { - return defaultFleetErrorHandler({ error, response }); + const body: GetOneAgentPolicyResponse = { + item, + }; + return response.ok({ + body, + }); + } else { + return response.customError({ + statusCode: 404, + body: { message: 'Agent policy not found' }, + }); } }; @@ -352,7 +339,7 @@ export const createAgentPolicyHandler: FleetRequestHandler< body: { message: error.message }, }); } - return defaultFleetErrorHandler({ error, response }); + throw error; } }; @@ -418,7 +405,7 @@ export const updateAgentPolicyHandler: FleetRequestHandler< body: { message: error.message }, }); } - return defaultFleetErrorHandler({ error, response }); + throw error; } }; @@ -431,35 +418,28 @@ export const copyAgentPolicyHandler: RequestHandler< const soClient = coreContext.savedObjects.client; const esClient = coreContext.elasticsearch.client.asInternalUser; const user = appContextService.getSecurityCore().authc.getCurrentUser(request) || undefined; - try { - const agentPolicy = await agentPolicyService.copy( - soClient, - esClient, - request.params.agentPolicyId, - request.body, - { user } - ); - - let item: any = agentPolicy; - if ( - request.query.format === inputsFormat.Simplified && - !isEmpty(agentPolicy.package_policies) - ) { - item = { - ...agentPolicy, - package_policies: agentPolicy.package_policies!.map((packagePolicy) => - packagePolicyToSimplifiedPackagePolicy(packagePolicy) - ), - }; - } + const agentPolicy = await agentPolicyService.copy( + soClient, + esClient, + request.params.agentPolicyId, + request.body, + { user } + ); - const body: CopyAgentPolicyResponse = { item }; - return response.ok({ - body, - }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); + let item: any = agentPolicy; + if (request.query.format === inputsFormat.Simplified && !isEmpty(agentPolicy.package_policies)) { + item = { + ...agentPolicy, + package_policies: agentPolicy.package_policies!.map((packagePolicy) => + packagePolicyToSimplifiedPackagePolicy(packagePolicy) + ), + }; } + + const body: CopyAgentPolicyResponse = { item }; + return response.ok({ + body, + }); }; export const deleteAgentPoliciesHandler: RequestHandler< @@ -471,19 +451,15 @@ export const deleteAgentPoliciesHandler: RequestHandler< const soClient = coreContext.savedObjects.client; const esClient = coreContext.elasticsearch.client.asInternalUser; const user = appContextService.getSecurityCore().authc.getCurrentUser(request) || undefined; - try { - const body: DeleteAgentPolicyResponse = await agentPolicyService.delete( - soClient, - esClient, - request.body.agentPolicyId, - { user, force: request.body.force } - ); - return response.ok({ - body, - }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } + const body: DeleteAgentPolicyResponse = await agentPolicyService.delete( + soClient, + esClient, + request.body.agentPolicyId, + { user, force: request.body.force } + ); + return response.ok({ + body, + }); }; export const getFullAgentPolicy: FleetRequestHandler< @@ -494,55 +470,47 @@ export const getFullAgentPolicy: FleetRequestHandler< const soClient = fleetContext.internalSoClient; if (request.query.kubernetes === true) { - try { - const agentVersion = - await fleetContext.agentClient.asInternalUser.getLatestAgentAvailableVersion(); - const fullAgentConfigMap = await agentPolicyService.getFullAgentConfigMap( - soClient, - request.params.agentPolicyId, - agentVersion, - { standalone: request.query.standalone === true } - ); - if (fullAgentConfigMap) { - const body: GetFullAgentConfigMapResponse = { - item: fullAgentConfigMap, - }; - return response.ok({ - body, - }); - } else { - return response.customError({ - statusCode: 404, - body: { message: 'Agent config map not found' }, - }); - } - } catch (error) { - return defaultFleetErrorHandler({ error, response }); + const agentVersion = + await fleetContext.agentClient.asInternalUser.getLatestAgentAvailableVersion(); + const fullAgentConfigMap = await agentPolicyService.getFullAgentConfigMap( + soClient, + request.params.agentPolicyId, + agentVersion, + { standalone: request.query.standalone === true } + ); + if (fullAgentConfigMap) { + const body: GetFullAgentConfigMapResponse = { + item: fullAgentConfigMap, + }; + return response.ok({ + body, + }); + } else { + return response.customError({ + statusCode: 404, + body: { message: 'Agent config map not found' }, + }); } } else { - try { - const fullAgentPolicy = await agentPolicyService.getFullAgentPolicy( - soClient, - request.params.agentPolicyId, - { - standalone: request.query.standalone === true, - } - ); - if (fullAgentPolicy) { - const body: GetFullAgentPolicyResponse = { - item: fullAgentPolicy, - }; - return response.ok({ - body, - }); - } else { - return response.customError({ - statusCode: 404, - body: { message: 'Agent policy not found' }, - }); + const fullAgentPolicy = await agentPolicyService.getFullAgentPolicy( + soClient, + request.params.agentPolicyId, + { + standalone: request.query.standalone === true, } - } catch (error) { - return defaultFleetErrorHandler({ error, response }); + ); + if (fullAgentPolicy) { + const body: GetFullAgentPolicyResponse = { + item: fullAgentPolicy, + }; + return response.ok({ + body, + }); + } else { + return response.customError({ + statusCode: 404, + body: { message: 'Agent policy not found' }, + }); } } }; @@ -558,112 +526,39 @@ export const downloadFullAgentPolicy: FleetRequestHandler< } = request; if (request.query.kubernetes === true) { - try { - const agentVersion = - await fleetContext.agentClient.asInternalUser.getLatestAgentAvailableVersion(); - const fullAgentConfigMap = await agentPolicyService.getFullAgentConfigMap( - soClient, - request.params.agentPolicyId, - agentVersion, - { standalone: request.query.standalone === true } - ); - if (fullAgentConfigMap) { - const body = fullAgentConfigMap; - const headers: ResponseHeaders = { - 'content-type': 'text/x-yaml', - 'content-disposition': `attachment; filename="elastic-agent-standalone-kubernetes.yml"`, - }; - return response.ok({ - body, - headers, - }); - } else { - return response.customError({ - statusCode: 404, - body: { message: 'Agent config map not found' }, - }); - } - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } - } else { - try { - const fullAgentPolicy = await agentPolicyService.getFullAgentPolicy(soClient, agentPolicyId, { - standalone: request.query.standalone === true, - }); - if (fullAgentPolicy) { - const body = fullAgentPolicyToYaml(fullAgentPolicy, safeDump); - const headers: ResponseHeaders = { - 'content-type': 'text/x-yaml', - 'content-disposition': `attachment; filename="elastic-agent.yml"`, - }; - return response.ok({ - body, - headers, - }); - } else { - return response.customError({ - statusCode: 404, - body: { message: 'Agent policy not found' }, - }); - } - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } - } -}; - -export const getK8sManifest: FleetRequestHandler< - undefined, - TypeOf -> = async (context, request, response) => { - try { - const fleetServer = request.query.fleetServer ?? ''; - const token = request.query.enrolToken ?? ''; - - const agentVersion = await getLatestAvailableAgentVersion(); - - const fullAgentManifest = await agentPolicyService.getFullAgentManifest( - fleetServer, - token, - agentVersion + const agentVersion = + await fleetContext.agentClient.asInternalUser.getLatestAgentAvailableVersion(); + const fullAgentConfigMap = await agentPolicyService.getFullAgentConfigMap( + soClient, + request.params.agentPolicyId, + agentVersion, + { standalone: request.query.standalone === true } ); - if (fullAgentManifest) { - const body: GetFullAgentManifestResponse = { - item: fullAgentManifest, + if (fullAgentConfigMap) { + const body = fullAgentConfigMap; + const headers: ResponseHeaders = { + 'content-type': 'text/x-yaml', + 'content-disposition': `attachment; filename="elastic-agent-standalone-kubernetes.yml"`, }; return response.ok({ body, + headers, }); } else { return response.customError({ statusCode: 404, - body: { message: 'Agent manifest not found' }, + body: { message: 'Agent config map not found' }, }); } - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } -}; - -export const downloadK8sManifest: FleetRequestHandler< - undefined, - TypeOf -> = async (context, request, response) => { - try { - const fleetServer = request.query.fleetServer ?? ''; - const token = request.query.enrolToken ?? ''; - const agentVersion = await getLatestAvailableAgentVersion(); - const fullAgentManifest = await agentPolicyService.getFullAgentManifest( - fleetServer, - token, - agentVersion - ); - if (fullAgentManifest) { - const body = fullAgentManifest; + } else { + const fullAgentPolicy = await agentPolicyService.getFullAgentPolicy(soClient, agentPolicyId, { + standalone: request.query.standalone === true, + }); + if (fullAgentPolicy) { + const body = fullAgentPolicyToYaml(fullAgentPolicy, safeDump); const headers: ResponseHeaders = { 'content-type': 'text/x-yaml', - 'content-disposition': `attachment; filename="elastic-agent-managed-kubernetes.yml"`, + 'content-disposition': `attachment; filename="elastic-agent.yml"`, }; return response.ok({ body, @@ -672,10 +567,67 @@ export const downloadK8sManifest: FleetRequestHandler< } else { return response.customError({ statusCode: 404, - body: { message: 'Agent manifest not found' }, + body: { message: 'Agent policy not found' }, }); } - } catch (error) { - return defaultFleetErrorHandler({ error, response }); + } +}; + +export const getK8sManifest: FleetRequestHandler< + undefined, + TypeOf +> = async (context, request, response) => { + const fleetServer = request.query.fleetServer ?? ''; + const token = request.query.enrolToken ?? ''; + + const agentVersion = await getLatestAvailableAgentVersion(); + + const fullAgentManifest = await agentPolicyService.getFullAgentManifest( + fleetServer, + token, + agentVersion + ); + if (fullAgentManifest) { + const body: GetFullAgentManifestResponse = { + item: fullAgentManifest, + }; + return response.ok({ + body, + }); + } else { + return response.customError({ + statusCode: 404, + body: { message: 'Agent manifest not found' }, + }); + } +}; + +export const downloadK8sManifest: FleetRequestHandler< + undefined, + TypeOf +> = async (context, request, response) => { + const fleetServer = request.query.fleetServer ?? ''; + const token = request.query.enrolToken ?? ''; + const agentVersion = await getLatestAvailableAgentVersion(); + const fullAgentManifest = await agentPolicyService.getFullAgentManifest( + fleetServer, + token, + agentVersion + ); + if (fullAgentManifest) { + const body = fullAgentManifest; + const headers: ResponseHeaders = { + 'content-type': 'text/x-yaml', + 'content-disposition': `attachment; filename="elastic-agent-managed-kubernetes.yml"`, + }; + return response.ok({ + body, + headers, + }); + } else { + return response.customError({ + statusCode: 404, + body: { message: 'Agent manifest not found' }, + }); } }; diff --git a/x-pack/plugins/fleet/server/routes/app/index.ts b/x-pack/plugins/fleet/server/routes/app/index.ts index bedd5ba1967a1..ad8dc5fa58dbf 100644 --- a/x-pack/plugins/fleet/server/routes/app/index.ts +++ b/x-pack/plugins/fleet/server/routes/app/index.ts @@ -14,7 +14,7 @@ import { APP_API_ROUTES } from '../../constants'; import { API_VERSIONS } from '../../../common/constants'; import { appContextService } from '../../services'; import type { CheckPermissionsResponse, GenerateServiceTokenResponse } from '../../../common/types'; -import { defaultFleetErrorHandler, GenerateServiceTokenError } from '../../errors'; +import { GenerateServiceTokenError } from '../../errors'; import type { FleetRequestHandler, GenerateServiceTokenRequestSchema } from '../../types'; import { CheckPermissionsRequestSchema } from '../../types'; import { enableSpaceAwarenessMigration } from '../../services/spaces/enable_space_awareness'; @@ -104,16 +104,11 @@ export const postEnableSpaceAwarenessHandler: FleetRequestHandler = async ( request, response ) => { - try { - await enableSpaceAwarenessMigration(); + await enableSpaceAwarenessMigration(); - return response.ok({ - body: {}, - }); - } catch (e) { - const error = new GenerateServiceTokenError(e); - return defaultFleetErrorHandler({ error, response }); - } + return response.ok({ + body: {}, + }); }; export const generateServiceTokenHandler: RequestHandler< @@ -143,11 +138,11 @@ export const generateServiceTokenHandler: RequestHandler< }); } else { const error = new GenerateServiceTokenError('Unable to generate service token'); - return defaultFleetErrorHandler({ error, response }); + throw error; } } catch (e) { const error = new GenerateServiceTokenError(e); - return defaultFleetErrorHandler({ error, response }); + throw error; } }; @@ -156,28 +151,24 @@ export const getAgentPoliciesSpacesHandler: FleetRequestHandler< null, TypeOf > = async (context, request, response) => { - try { - const spaces = await (await context.fleet).getAllSpaces(); - const security = appContextService.getSecurity(); - const spaceIds = spaces.map(({ id }) => id); - const res = await security.authz.checkPrivilegesWithRequest(request).atSpaces(spaceIds, { - kibana: [security.authz.actions.api.get(`fleet-agent-policies-all`)], - }); + const spaces = await (await context.fleet).getAllSpaces(); + const security = appContextService.getSecurity(); + const spaceIds = spaces.map(({ id }) => id); + const res = await security.authz.checkPrivilegesWithRequest(request).atSpaces(spaceIds, { + kibana: [security.authz.actions.api.get(`fleet-agent-policies-all`)], + }); - const authorizedSpaces = spaces.filter( - (space) => - res.privileges.kibana.find((privilege) => privilege.resource === space.id)?.authorized ?? - false - ); + const authorizedSpaces = spaces.filter( + (space) => + res.privileges.kibana.find((privilege) => privilege.resource === space.id)?.authorized ?? + false + ); - return response.ok({ - body: { - items: authorizedSpaces, - }, - }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } + return response.ok({ + body: { + items: authorizedSpaces, + }, + }); }; const serviceTokenBodyValidation = (data: any, validationResult: RouteValidationResultFactory) => { diff --git a/x-pack/plugins/fleet/server/routes/data_streams/handlers.ts b/x-pack/plugins/fleet/server/routes/data_streams/handlers.ts index 7cbc9d9274032..7ed4b5bacf336 100644 --- a/x-pack/plugins/fleet/server/routes/data_streams/handlers.ts +++ b/x-pack/plugins/fleet/server/routes/data_streams/handlers.ts @@ -8,19 +8,18 @@ import type { Dictionary } from 'lodash'; import { keyBy, keys, merge } from 'lodash'; import type { RequestHandler } from '@kbn/core/server'; import pMap from 'p-map'; +import type { IndicesDataStreamsStatsDataStreamsStatsItem } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { ByteSizeValue } from '@kbn/config-schema'; import type { DataStream } from '../../types'; import { KibanaSavedObjectType } from '../../../common/types'; import type { GetDataStreamsResponse } from '../../../common/types'; import { getPackageSavedObjects } from '../../services/epm/packages/get'; -import { defaultFleetErrorHandler } from '../../errors'; import type { MeteringStats } from '../../services/data_streams'; import { dataStreamService } from '../../services/data_streams'; +import { appContextService } from '../../services'; import { getDataStreamsQueryMetadata } from './get_data_streams_query_metadata'; -import type { IndicesDataStreamsStatsDataStreamsStatsItem } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { ByteSizeValue } from '@kbn/config-schema'; -import { appContextService } from '../../services'; const MANAGED_BY = 'fleet'; const LEGACY_MANAGED_BY = 'ingest-manager'; @@ -55,158 +54,154 @@ export const getListHandler: RequestHandler = async (context, request, response) data_streams: [], }; - try { - const useMeteringApi = appContextService.getConfig()?.internal?.useMeteringApi; - - // Get matching data streams, their stats, and package SOs - const [ - dataStreamsInfo, - dataStreamStatsOrUndefined, - dataStreamMeteringStatsorUndefined, - packageSavedObjects, - ] = await Promise.all([ - dataStreamService.getAllFleetDataStreams(esClient), - useMeteringApi - ? undefined - : dataStreamService.getAllFleetDataStreamsStats(elasticsearch.client.asSecondaryAuthUser), - useMeteringApi - ? dataStreamService.getAllFleetMeteringStats(elasticsearch.client.asSecondaryAuthUser) - : undefined, - getPackageSavedObjects(savedObjects.client), - ]); - - // managed_by property 'ingest-manager' added to allow for legacy data streams to be displayed - // See https://github.com/elastic/elastic-agent/issues/654 - - const filteredDataStreamsInfo = dataStreamsInfo.filter( - (ds) => ds?._meta?.managed_by === MANAGED_BY || ds?._meta?.managed_by === LEGACY_MANAGED_BY + const useMeteringApi = appContextService.getConfig()?.internal?.useMeteringApi; + + // Get matching data streams, their stats, and package SOs + const [ + dataStreamsInfo, + dataStreamStatsOrUndefined, + dataStreamMeteringStatsorUndefined, + packageSavedObjects, + ] = await Promise.all([ + dataStreamService.getAllFleetDataStreams(esClient), + useMeteringApi + ? undefined + : dataStreamService.getAllFleetDataStreamsStats(elasticsearch.client.asSecondaryAuthUser), + useMeteringApi + ? dataStreamService.getAllFleetMeteringStats(elasticsearch.client.asSecondaryAuthUser) + : undefined, + getPackageSavedObjects(savedObjects.client), + ]); + + // managed_by property 'ingest-manager' added to allow for legacy data streams to be displayed + // See https://github.com/elastic/elastic-agent/issues/654 + + const filteredDataStreamsInfo = dataStreamsInfo.filter( + (ds) => ds?._meta?.managed_by === MANAGED_BY || ds?._meta?.managed_by === LEGACY_MANAGED_BY + ); + + const dataStreamsInfoByName = keyBy(filteredDataStreamsInfo, 'name'); + + let dataStreamsStatsByName: Dictionary = {}; + if (dataStreamStatsOrUndefined) { + const filteredDataStreamsStats = dataStreamStatsOrUndefined.filter( + (dss) => !!dataStreamsInfoByName[dss.data_stream] ); + dataStreamsStatsByName = keyBy(filteredDataStreamsStats, 'data_stream'); + } + let dataStreamsMeteringStatsByName: Dictionary = {}; + if (dataStreamMeteringStatsorUndefined) { + dataStreamsMeteringStatsByName = keyBy(dataStreamMeteringStatsorUndefined, 'name'); + } - const dataStreamsInfoByName = keyBy(filteredDataStreamsInfo, 'name'); - - let dataStreamsStatsByName: Dictionary = {}; - if (dataStreamStatsOrUndefined) { - const filteredDataStreamsStats = dataStreamStatsOrUndefined.filter( - (dss) => !!dataStreamsInfoByName[dss.data_stream] - ); - dataStreamsStatsByName = keyBy(filteredDataStreamsStats, 'data_stream'); - } - let dataStreamsMeteringStatsByName: Dictionary = {}; - if (dataStreamMeteringStatsorUndefined) { - dataStreamsMeteringStatsByName = keyBy(dataStreamMeteringStatsorUndefined, 'name'); - } - - // Combine data stream info - const dataStreams = merge( - dataStreamsInfoByName, - dataStreamsStatsByName, - dataStreamsMeteringStatsByName - ); - const dataStreamNames = keys(dataStreams); - - // Map package SOs - const packageSavedObjectsByName = keyBy(packageSavedObjects.saved_objects, 'id'); - const packageMetadata: any = {}; - - // Get dashboard information for all packages - const dashboardIdsByPackageName = packageSavedObjects.saved_objects.reduce< - Record - >((allDashboards, pkgSavedObject) => { - const dashboards: string[] = []; - (pkgSavedObject.attributes?.installed_kibana || []).forEach((o) => { - if (o.type === KibanaSavedObjectType.dashboard) { - dashboards.push(o.id); - } - }); - allDashboards[pkgSavedObject.id] = dashboards; - return allDashboards; - }, {}); - const allDashboardSavedObjectsResponse = await savedObjects.client.bulkGet<{ - title?: string; - }>( - Object.values(dashboardIdsByPackageName).flatMap((dashboardIds) => - dashboardIds.map((id) => ({ - id, - type: KibanaSavedObjectType.dashboard, - fields: ['title'], - })) - ) - ); - // Ignore dashboards not found - const allDashboardSavedObjects = allDashboardSavedObjectsResponse.saved_objects.filter((so) => { - if (so.error) { - if (so.error.statusCode === 404) { - return false; - } - throw so.error; + // Combine data stream info + const dataStreams = merge( + dataStreamsInfoByName, + dataStreamsStatsByName, + dataStreamsMeteringStatsByName + ); + const dataStreamNames = keys(dataStreams); + + // Map package SOs + const packageSavedObjectsByName = keyBy(packageSavedObjects.saved_objects, 'id'); + const packageMetadata: any = {}; + + // Get dashboard information for all packages + const dashboardIdsByPackageName = packageSavedObjects.saved_objects.reduce< + Record + >((allDashboards, pkgSavedObject) => { + const dashboards: string[] = []; + (pkgSavedObject.attributes?.installed_kibana || []).forEach((o) => { + if (o.type === KibanaSavedObjectType.dashboard) { + dashboards.push(o.id); } - return true; }); + allDashboards[pkgSavedObject.id] = dashboards; + return allDashboards; + }, {}); + const allDashboardSavedObjectsResponse = await savedObjects.client.bulkGet<{ + title?: string; + }>( + Object.values(dashboardIdsByPackageName).flatMap((dashboardIds) => + dashboardIds.map((id) => ({ + id, + type: KibanaSavedObjectType.dashboard, + fields: ['title'], + })) + ) + ); + // Ignore dashboards not found + const allDashboardSavedObjects = allDashboardSavedObjectsResponse.saved_objects.filter((so) => { + if (so.error) { + if (so.error.statusCode === 404) { + return false; + } + throw so.error; + } + return true; + }); + + const allDashboardSavedObjectsById = keyBy( + allDashboardSavedObjects, + (dashboardSavedObject) => dashboardSavedObject.id + ); + + // Query additional information for each data stream + const queryDataStreamInfo = async (dataStreamName: string) => { + const dataStream = dataStreams[dataStreamName]; + + const dataStreamResponse: DataStream = { + index: dataStreamName, + dataset: '', + namespace: '', + type: '', + package: dataStream._meta?.package?.name || '', + package_version: '', + last_activity_ms: dataStream.maximum_timestamp, // overridden below if maxIngestedTimestamp agg returns a result + size_in_bytes: dataStream.store_size_bytes || dataStream.size_in_bytes, + // `store_size` should be available from ES due to ?human=true flag + // but fallback to bytes just in case + size_in_bytes_formatted: + dataStream.store_size || + new ByteSizeValue(dataStream.store_size_bytes || dataStream.size_in_bytes || 0).toString(), + dashboards: [], + serviceDetails: null, + }; - const allDashboardSavedObjectsById = keyBy( - allDashboardSavedObjects, - (dashboardSavedObject) => dashboardSavedObject.id - ); - - // Query additional information for each data stream - const queryDataStreamInfo = async (dataStreamName: string) => { - const dataStream = dataStreams[dataStreamName]; - - const dataStreamResponse: DataStream = { - index: dataStreamName, - dataset: '', - namespace: '', - type: '', - package: dataStream._meta?.package?.name || '', - package_version: '', - last_activity_ms: dataStream.maximum_timestamp, // overridden below if maxIngestedTimestamp agg returns a result - size_in_bytes: dataStream.store_size_bytes || dataStream.size_in_bytes, - // `store_size` should be available from ES due to ?human=true flag - // but fallback to bytes just in case - size_in_bytes_formatted: - dataStream.store_size || - new ByteSizeValue( - dataStream.store_size_bytes || dataStream.size_in_bytes || 0 - ).toString(), - dashboards: [], - serviceDetails: null, - }; - - const { maxIngested, namespace, dataset, type, serviceNames, environments } = - await getDataStreamsQueryMetadata({ dataStreamName: dataStream.name, esClient }); + const { maxIngested, namespace, dataset, type, serviceNames, environments } = + await getDataStreamsQueryMetadata({ dataStreamName: dataStream.name, esClient }); - // some integrations e.g custom logs don't have event.ingested - if (maxIngested) { - dataStreamResponse.last_activity_ms = maxIngested; - } + // some integrations e.g custom logs don't have event.ingested + if (maxIngested) { + dataStreamResponse.last_activity_ms = maxIngested; + } - if (serviceNames?.length === 1) { - const serviceDetails = { - serviceName: serviceNames[0], - environment: environments?.length === 1 ? environments[0] : 'ENVIRONMENT_ALL', - }; - dataStreamResponse.serviceDetails = serviceDetails; - } + if (serviceNames?.length === 1) { + const serviceDetails = { + serviceName: serviceNames[0], + environment: environments?.length === 1 ? environments[0] : 'ENVIRONMENT_ALL', + }; + dataStreamResponse.serviceDetails = serviceDetails; + } - dataStreamResponse.dataset = dataset; - dataStreamResponse.namespace = namespace; - dataStreamResponse.type = type; - - // Find package saved object - const pkgName = dataStreamResponse.package; - const pkgSavedObject = pkgName ? packageSavedObjectsByName[pkgName] : null; - - if (pkgSavedObject) { - // if - // - the data stream is associated with a package - // - and the package has been installed through EPM - // - and we didn't pick the metadata in an earlier iteration of this map() - if (!packageMetadata[pkgName]) { - // then pick the dashboards from the package saved object - const packageDashboardIds = dashboardIdsByPackageName[pkgName] || []; - const packageDashboards = packageDashboardIds.reduce< - Array<{ id: string; title: string }> - >((dashboards, dashboardId) => { + dataStreamResponse.dataset = dataset; + dataStreamResponse.namespace = namespace; + dataStreamResponse.type = type; + + // Find package saved object + const pkgName = dataStreamResponse.package; + const pkgSavedObject = pkgName ? packageSavedObjectsByName[pkgName] : null; + + if (pkgSavedObject) { + // if + // - the data stream is associated with a package + // - and the package has been installed through EPM + // - and we didn't pick the metadata in an earlier iteration of this map() + if (!packageMetadata[pkgName]) { + // then pick the dashboards from the package saved object + const packageDashboardIds = dashboardIdsByPackageName[pkgName] || []; + const packageDashboards = packageDashboardIds.reduce>( + (dashboards, dashboardId) => { const dashboard = allDashboardSavedObjectsById[dashboardId]; if (dashboard) { dashboards.push({ @@ -215,37 +210,36 @@ export const getListHandler: RequestHandler = async (context, request, response) }); } return dashboards; - }, []); - - packageMetadata[pkgName] = { - version: pkgSavedObject.attributes?.version || '', - dashboards: packageDashboards, - }; - } - - // Set values from package information - dataStreamResponse.package = pkgName; - dataStreamResponse.package_version = packageMetadata[pkgName].version; - dataStreamResponse.dashboards = packageMetadata[pkgName].dashboards; + }, + [] + ); + + packageMetadata[pkgName] = { + version: pkgSavedObject.attributes?.version || '', + dashboards: packageDashboards, + }; } - return dataStreamResponse; - }; + // Set values from package information + dataStreamResponse.package = pkgName; + dataStreamResponse.package_version = packageMetadata[pkgName].version; + dataStreamResponse.dashboards = packageMetadata[pkgName].dashboards; + } - // Return final data streams objects sorted by last activity, descending - // After filtering out data streams that are missing dataset/namespace/type/package fields - body.data_streams = ( - await pMap(dataStreamNames, (dataStreamName) => queryDataStreamInfo(dataStreamName), { - concurrency: 50, - }) - ) - .filter(({ dataset, namespace, type }) => dataset && namespace && type) - .sort((a, b) => b.last_activity_ms - a.last_activity_ms); + return dataStreamResponse; + }; - return response.ok({ - body, - }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } + // Return final data streams objects sorted by last activity, descending + // After filtering out data streams that are missing dataset/namespace/type/package fields + body.data_streams = ( + await pMap(dataStreamNames, (dataStreamName) => queryDataStreamInfo(dataStreamName), { + concurrency: 50, + }) + ) + .filter(({ dataset, namespace, type }) => dataset && namespace && type) + .sort((a, b) => b.last_activity_ms - a.last_activity_ms); + + return response.ok({ + body, + }); }; diff --git a/x-pack/plugins/fleet/server/routes/download_source/handler.ts b/x-pack/plugins/fleet/server/routes/download_source/handler.ts index 8807106de441e..0b2254d85f867 100644 --- a/x-pack/plugins/fleet/server/routes/download_source/handler.ts +++ b/x-pack/plugins/fleet/server/routes/download_source/handler.ts @@ -21,25 +21,20 @@ import type { GetDownloadSourceResponse, } from '../../../common/types'; import { downloadSourceService } from '../../services/download_source'; -import { defaultFleetErrorHandler } from '../../errors'; import { agentPolicyService } from '../../services'; export const getDownloadSourcesHandler: RequestHandler = async (context, request, response) => { const soClient = (await context.core).savedObjects.client; - try { - const downloadSources = await downloadSourceService.list(soClient); + const downloadSources = await downloadSourceService.list(soClient); - const body: GetDownloadSourceResponse = { - items: downloadSources.items, - page: downloadSources.page, - perPage: downloadSources.perPage, - total: downloadSources.total, - }; + const body: GetDownloadSourceResponse = { + items: downloadSources.items, + page: downloadSources.page, + perPage: downloadSources.perPage, + total: downloadSources.total, + }; - return response.ok({ body }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } + return response.ok({ body }); }; export const getOneDownloadSourcesHandler: RequestHandler< @@ -61,7 +56,7 @@ export const getOneDownloadSourcesHandler: RequestHandler< }); } - return defaultFleetErrorHandler({ error, response }); + throw error; } }; @@ -94,7 +89,7 @@ export const putDownloadSourcesHandler: RequestHandler< }); } - return defaultFleetErrorHandler({ error, response }); + throw error; } }; @@ -106,21 +101,17 @@ export const postDownloadSourcesHandler: RequestHandler< const coreContext = await context.core; const soClient = coreContext.savedObjects.client; const esClient = coreContext.elasticsearch.client.asInternalUser; - try { - const { id, ...data } = request.body; - const downloadSource = await downloadSourceService.create(soClient, data, { id }); - if (downloadSource.is_default) { - await agentPolicyService.bumpAllAgentPolicies(esClient); - } + const { id, ...data } = request.body; + const downloadSource = await downloadSourceService.create(soClient, data, { id }); + if (downloadSource.is_default) { + await agentPolicyService.bumpAllAgentPolicies(esClient); + } - const body: GetOneDownloadSourceResponse = { - item: downloadSource, - }; + const body: GetOneDownloadSourceResponse = { + item: downloadSource, + }; - return response.ok({ body }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } + return response.ok({ body }); }; export const deleteDownloadSourcesHandler: RequestHandler< @@ -142,6 +133,6 @@ export const deleteDownloadSourcesHandler: RequestHandler< }); } - return defaultFleetErrorHandler({ error, response }); + throw error; } }; diff --git a/x-pack/plugins/fleet/server/routes/enrollment_api_key/handler.ts b/x-pack/plugins/fleet/server/routes/enrollment_api_key/handler.ts index a38f5bdadc617..5f225fc5da709 100644 --- a/x-pack/plugins/fleet/server/routes/enrollment_api_key/handler.ts +++ b/x-pack/plugins/fleet/server/routes/enrollment_api_key/handler.ts @@ -22,7 +22,7 @@ import type { } from '../../../common/types'; import * as APIKeyService from '../../services/api_keys'; import { agentPolicyService } from '../../services/agent_policy'; -import { defaultFleetErrorHandler, AgentPolicyNotFoundError } from '../../errors'; +import { AgentPolicyNotFoundError } from '../../errors'; import { getCurrentNamespace } from '../../services/spaces/get_current_namespace'; import { isSpaceAwarenessEnabled } from '../../services/spaces/helpers'; @@ -34,26 +34,22 @@ export const getEnrollmentApiKeysHandler: RequestHandler< const esClient = (await context.core).elasticsearch.client.asInternalUser; const soClient = (await context.core).savedObjects.client; - try { - const useSpaceAwareness = await isSpaceAwarenessEnabled(); - const { items, total, page, perPage } = await APIKeyService.listEnrollmentApiKeys(esClient, { - page: request.query.page, - perPage: request.query.perPage, - kuery: request.query.kuery, - spaceId: useSpaceAwareness ? getCurrentNamespace(soClient) : undefined, - }); - const body: GetEnrollmentAPIKeysResponse = { - list: items, // deprecated - items, - total, - page, - perPage, - }; - - return response.ok({ body }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } + const useSpaceAwareness = await isSpaceAwarenessEnabled(); + const { items, total, page, perPage } = await APIKeyService.listEnrollmentApiKeys(esClient, { + page: request.query.page, + perPage: request.query.perPage, + kuery: request.query.kuery, + spaceId: useSpaceAwareness ? getCurrentNamespace(soClient) : undefined, + }); + const body: GetEnrollmentAPIKeysResponse = { + list: items, // deprecated + items, + total, + page, + perPage, + }; + + return response.ok({ body }); }; export const postEnrollmentApiKeyHandler: RequestHandler< undefined, @@ -63,28 +59,24 @@ export const postEnrollmentApiKeyHandler: RequestHandler< const { elasticsearch, savedObjects } = await context.core; const soClient = savedObjects.client; const esClient = elasticsearch.client.asInternalUser; - try { - // validate policy exists in the current space - await agentPolicyService.get(soClient, request.body.policy_id).catch((err) => { - if (SavedObjectsErrorHelpers.isNotFoundError(err)) { - throw new AgentPolicyNotFoundError(`Agent policy "${request.body.policy_id}" not found`); - } + // validate policy exists in the current space + await agentPolicyService.get(soClient, request.body.policy_id).catch((err) => { + if (SavedObjectsErrorHelpers.isNotFoundError(err)) { + throw new AgentPolicyNotFoundError(`Agent policy "${request.body.policy_id}" not found`); + } - throw err; - }); + throw err; + }); - const apiKey = await APIKeyService.generateEnrollmentAPIKey(soClient, esClient, { - name: request.body.name, - expiration: request.body.expiration, - agentPolicyId: request.body.policy_id, - }); + const apiKey = await APIKeyService.generateEnrollmentAPIKey(soClient, esClient, { + name: request.body.name, + expiration: request.body.expiration, + agentPolicyId: request.body.policy_id, + }); - const body: PostEnrollmentAPIKeyResponse = { item: apiKey, action: 'created' }; + const body: PostEnrollmentAPIKeyResponse = { item: apiKey, action: 'created' }; - return response.ok({ body }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } + return response.ok({ body }); }; export const deleteEnrollmentApiKeyHandler: RequestHandler< @@ -111,7 +103,7 @@ export const deleteEnrollmentApiKeyHandler: RequestHandler< body: { message: `EnrollmentAPIKey ${request.params.keyId} not found` }, }); } - return defaultFleetErrorHandler({ error, response }); + throw error; } }; @@ -141,6 +133,6 @@ export const getOneEnrollmentApiKeyHandler: RequestHandler< }); } - return defaultFleetErrorHandler({ error, response }); + throw error; } }; diff --git a/x-pack/plugins/fleet/server/routes/epm/file_handler.ts b/x-pack/plugins/fleet/server/routes/epm/file_handler.ts index 994f52a71c224..701915d384f47 100644 --- a/x-pack/plugins/fleet/server/routes/epm/file_handler.ts +++ b/x-pack/plugins/fleet/server/routes/epm/file_handler.ts @@ -13,7 +13,6 @@ import type { ResponseHeaders, KnownHeaders, HttpResponseOptions } from '@kbn/co import type { GetFileRequestSchema, FleetRequestHandler } from '../../types'; import { getFile, getInstallation } from '../../services/epm/packages'; -import { defaultFleetErrorHandler } from '../../errors'; import { getAsset } from '../../services/epm/archive/storage'; import { getBundledPackageByPkgKey } from '../../services/epm/packages/bundled_packages'; import { pkgToPkgKey } from '../../services/epm/registry'; @@ -25,104 +24,100 @@ const CACHE_CONTROL_10_MINUTES_HEADER: HttpResponseOptions['headers'] = { export const getFileHandler: FleetRequestHandler< TypeOf > = async (context, request, response) => { - try { - const { pkgName, pkgVersion, filePath } = request.params; - const savedObjectsClient = (await context.fleet).internalSoClient; - - const installation = await getInstallation({ savedObjectsClient, pkgName }); - const isPackageInstalled = pkgVersion === installation?.version; - const assetPath = `${pkgName}-${pkgVersion}/${filePath}`; - - if (isPackageInstalled) { - const storedAsset = await getAsset({ savedObjectsClient, path: assetPath }); - - if (!storedAsset) { - return response.custom({ - body: `installed package file not found: ${filePath}`, - statusCode: 404, - }); - } + const { pkgName, pkgVersion, filePath } = request.params; + const savedObjectsClient = (await context.fleet).internalSoClient; - const contentType = storedAsset.media_type; - const buffer = storedAsset.data_utf8 - ? Buffer.from(storedAsset.data_utf8, 'utf8') - : Buffer.from(storedAsset.data_base64, 'base64'); + const installation = await getInstallation({ savedObjectsClient, pkgName }); + const isPackageInstalled = pkgVersion === installation?.version; + const assetPath = `${pkgName}-${pkgVersion}/${filePath}`; - if (!contentType) { - return response.custom({ - body: `unknown content type for file: ${filePath}`, - statusCode: 400, - }); - } + if (isPackageInstalled) { + const storedAsset = await getAsset({ savedObjectsClient, path: assetPath }); + + if (!storedAsset) { + return response.custom({ + body: `installed package file not found: ${filePath}`, + statusCode: 404, + }); + } + const contentType = storedAsset.media_type; + const buffer = storedAsset.data_utf8 + ? Buffer.from(storedAsset.data_utf8, 'utf8') + : Buffer.from(storedAsset.data_base64, 'base64'); + + if (!contentType) { return response.custom({ - body: buffer, - statusCode: 200, - headers: { - ...CACHE_CONTROL_10_MINUTES_HEADER, - 'content-type': contentType, - }, + body: `unknown content type for file: ${filePath}`, + statusCode: 400, }); } - const bundledPackage = await getBundledPackageByPkgKey( - pkgToPkgKey({ name: pkgName, version: pkgVersion }) + return response.custom({ + body: buffer, + statusCode: 200, + headers: { + ...CACHE_CONTROL_10_MINUTES_HEADER, + 'content-type': contentType, + }, + }); + } + + const bundledPackage = await getBundledPackageByPkgKey( + pkgToPkgKey({ name: pkgName, version: pkgVersion }) + ); + if (bundledPackage) { + const bufferEntries = await unpackArchiveEntriesIntoMemory( + await bundledPackage.getBuffer(), + 'application/zip' ); - if (bundledPackage) { - const bufferEntries = await unpackArchiveEntriesIntoMemory( - await bundledPackage.getBuffer(), - 'application/zip' - ); - - const fileBuffer = bufferEntries.find((entry) => entry.path === assetPath)?.buffer; - - if (!fileBuffer) { - return response.custom({ - body: `bundled package file not found: ${filePath}`, - statusCode: 404, - }); - } - // if storedAsset is not available, fileBuffer *must* be - // b/c we error if we don't have at least one, and storedAsset is the least likely - const { buffer, contentType } = { - contentType: mime.contentType(path.extname(assetPath)), - buffer: fileBuffer, - }; - - if (!contentType) { - return response.custom({ - body: `unknown content type for file: ${filePath}`, - statusCode: 400, - }); - } + const fileBuffer = bufferEntries.find((entry) => entry.path === assetPath)?.buffer; + if (!fileBuffer) { return response.custom({ - body: buffer, - statusCode: 200, - headers: { - ...CACHE_CONTROL_10_MINUTES_HEADER, - 'content-type': contentType, - }, + body: `bundled package file not found: ${filePath}`, + statusCode: 404, }); - } else { - const registryResponse = await getFile(pkgName, pkgVersion, filePath); - const headersToProxy: KnownHeaders[] = ['content-type']; - const proxiedHeaders = headersToProxy.reduce((headers, knownHeader) => { - const value = registryResponse.headers.get(knownHeader); - if (value !== null) { - headers[knownHeader] = value; - } - return headers; - }, {} as ResponseHeaders); + } + // if storedAsset is not available, fileBuffer *must* be + // b/c we error if we don't have at least one, and storedAsset is the least likely + const { buffer, contentType } = { + contentType: mime.contentType(path.extname(assetPath)), + buffer: fileBuffer, + }; + + if (!contentType) { return response.custom({ - body: registryResponse.body, - statusCode: registryResponse.status, - headers: { ...CACHE_CONTROL_10_MINUTES_HEADER, ...proxiedHeaders }, + body: `unknown content type for file: ${filePath}`, + statusCode: 400, }); } - } catch (error) { - return defaultFleetErrorHandler({ error, response }); + + return response.custom({ + body: buffer, + statusCode: 200, + headers: { + ...CACHE_CONTROL_10_MINUTES_HEADER, + 'content-type': contentType, + }, + }); + } else { + const registryResponse = await getFile(pkgName, pkgVersion, filePath); + const headersToProxy: KnownHeaders[] = ['content-type']; + const proxiedHeaders = headersToProxy.reduce((headers, knownHeader) => { + const value = registryResponse.headers.get(knownHeader); + if (value !== null) { + headers[knownHeader] = value; + } + return headers; + }, {} as ResponseHeaders); + + return response.custom({ + body: registryResponse.body, + statusCode: registryResponse.status, + headers: { ...CACHE_CONTROL_10_MINUTES_HEADER, ...proxiedHeaders }, + }); } }; diff --git a/x-pack/plugins/fleet/server/routes/epm/handlers.ts b/x-pack/plugins/fleet/server/routes/epm/handlers.ts index 47bdbd972b4c7..fdd145ba165aa 100644 --- a/x-pack/plugins/fleet/server/routes/epm/handlers.ts +++ b/x-pack/plugins/fleet/server/routes/epm/handlers.ts @@ -67,12 +67,7 @@ import { getTemplateInputs, } from '../../services/epm/packages'; import type { BulkInstallResponse } from '../../services/epm/packages'; -import { - defaultFleetErrorHandler, - fleetErrorToResponseOptions, - FleetError, - FleetTooManyRequestsError, -} from '../../errors'; +import { fleetErrorToResponseOptions, FleetError, FleetTooManyRequestsError } from '../../errors'; import { appContextService, checkAllowedPackages, packagePolicyService } from '../../services'; import { getPackageUsageStats } from '../../services/epm/packages/get'; import { updatePackage } from '../../services/epm/packages/update'; @@ -97,93 +92,77 @@ export const getCategoriesHandler: FleetRequestHandler< undefined, TypeOf > = async (context, request, response) => { - try { - const res = await getCategories({ - ...request.query, - }); - const body: GetCategoriesResponse = { - items: res, - response: res, - }; - return response.ok({ body, headers: { ...CACHE_CONTROL_10_MINUTES_HEADER } }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } + const res = await getCategories({ + ...request.query, + }); + const body: GetCategoriesResponse = { + items: res, + response: res, + }; + return response.ok({ body, headers: { ...CACHE_CONTROL_10_MINUTES_HEADER } }); }; export const getListHandler: FleetRequestHandler< undefined, TypeOf > = async (context, request, response) => { - try { - const savedObjectsClient = (await context.fleet).internalSoClient; - const res = await getPackages({ - savedObjectsClient, - ...request.query, - }); - const flattenedRes = res.map((pkg) => soToInstallationInfo(pkg)) as PackageList; - const body: GetPackagesResponse = { - items: flattenedRes, - response: res, - }; - return response.ok({ - body, - // Only cache responses where the installation status is excluded, otherwise the request - // needs up-to-date information on whether the package is installed so we can't cache it - headers: request.query.excludeInstallStatus ? { ...CACHE_CONTROL_10_MINUTES_HEADER } : {}, - }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } + const savedObjectsClient = (await context.fleet).internalSoClient; + const res = await getPackages({ + savedObjectsClient, + ...request.query, + }); + const flattenedRes = res.map((pkg) => soToInstallationInfo(pkg)) as PackageList; + const body: GetPackagesResponse = { + items: flattenedRes, + response: res, + }; + return response.ok({ + body, + // Only cache responses where the installation status is excluded, otherwise the request + // needs up-to-date information on whether the package is installed so we can't cache it + headers: request.query.excludeInstallStatus ? { ...CACHE_CONTROL_10_MINUTES_HEADER } : {}, + }); }; export const getInstalledListHandler: FleetRequestHandler< undefined, TypeOf > = async (context, request, response) => { - try { - const [fleetContext, coreContext] = await Promise.all([context.fleet, context.core]); - const savedObjectsClient = fleetContext.internalSoClient; - const esClient = coreContext.elasticsearch.client.asCurrentUser; - const res = await getInstalledPackages({ - savedObjectsClient, - esClient, - ...request.query, - }); + const [fleetContext, coreContext] = await Promise.all([context.fleet, context.core]); + const savedObjectsClient = fleetContext.internalSoClient; + const esClient = coreContext.elasticsearch.client.asCurrentUser; + const res = await getInstalledPackages({ + savedObjectsClient, + esClient, + ...request.query, + }); - const body: GetInstalledPackagesResponse = { ...res }; + const body: GetInstalledPackagesResponse = { ...res }; - return response.ok({ - body, - }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } + return response.ok({ + body, + }); }; export const getDataStreamsHandler: FleetRequestHandler< undefined, TypeOf > = async (context, request, response) => { - try { - const coreContext = await context.core; - // Query datastreams as the current user as the Kibana internal user may not have all the required permissions - const esClient = coreContext.elasticsearch.client.asCurrentUser; - const res = await getDataStreams({ - esClient, - ...request.query, - }); + const coreContext = await context.core; + // Query datastreams as the current user as the Kibana internal user may not have all the required permissions + const esClient = coreContext.elasticsearch.client.asCurrentUser; + const res = await getDataStreams({ + esClient, + ...request.query, + }); - const body: GetEpmDataStreamsResponse = { - ...res, - }; + const body: GetEpmDataStreamsResponse = { + ...res, + }; - return response.ok({ - body, - }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } + return response.ok({ + body, + }); }; export const getLimitedListHandler: FleetRequestHandler< @@ -191,71 +170,63 @@ export const getLimitedListHandler: FleetRequestHandler< TypeOf, undefined > = async (context, request, response) => { - try { - const savedObjectsClient = (await context.fleet).internalSoClient; - const res = await getLimitedPackages({ - savedObjectsClient, - prerelease: request.query.prerelease, - }); - const body: GetLimitedPackagesResponse = { - items: res, - response: res, - }; - return response.ok({ - body, - }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } + const savedObjectsClient = (await context.fleet).internalSoClient; + const res = await getLimitedPackages({ + savedObjectsClient, + prerelease: request.query.prerelease, + }); + const body: GetLimitedPackagesResponse = { + items: res, + response: res, + }; + return response.ok({ + body, + }); }; export const getInfoHandler: FleetRequestHandler< TypeOf, TypeOf > = async (context, request, response) => { - try { - const savedObjectsClient = (await context.fleet).internalSoClient; - const { limitedToPackages } = await context.fleet; - const { pkgName, pkgVersion } = request.params; + const savedObjectsClient = (await context.fleet).internalSoClient; + const { limitedToPackages } = await context.fleet; + const { pkgName, pkgVersion } = request.params; - checkAllowedPackages([pkgName], limitedToPackages); + checkAllowedPackages([pkgName], limitedToPackages); - const { ignoreUnverified = false, full = false, prerelease } = request.query; - if (pkgVersion && !semverValid(pkgVersion)) { - throw new FleetError('Package version is not a valid semver'); - } - const res = await getPackageInfo({ - savedObjectsClient, - pkgName, - pkgVersion: pkgVersion || '', - skipArchive: !full, - ignoreUnverified, - prerelease, + const { ignoreUnverified = false, full = false, prerelease } = request.query; + if (pkgVersion && !semverValid(pkgVersion)) { + throw new FleetError('Package version is not a valid semver'); + } + const res = await getPackageInfo({ + savedObjectsClient, + pkgName, + pkgVersion: pkgVersion || '', + skipArchive: !full, + ignoreUnverified, + prerelease, + }); + const flattenedRes = soToInstallationInfo(res) as PackageInfo; + + let metadata: any; + if (request.query.withMetadata) { + const allSpaceSoClient = appContextService.getInternalUserSOClientWithoutSpaceExtension(); + const { total } = await packagePolicyService.list(allSpaceSoClient, { + kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name:${pkgName}`, + page: 1, + perPage: 0, + spaceId: '*', }); - const flattenedRes = soToInstallationInfo(res) as PackageInfo; - - let metadata: any; - if (request.query.withMetadata) { - const allSpaceSoClient = appContextService.getInternalUserSOClientWithoutSpaceExtension(); - const { total } = await packagePolicyService.list(allSpaceSoClient, { - kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name:${pkgName}`, - page: 1, - perPage: 0, - spaceId: '*', - }); - metadata = { - has_policies: total > 0, - }; - } - - const body: GetInfoResponse = { - item: flattenedRes, - metadata, + metadata = { + has_policies: total > 0, }; - return response.ok({ body }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); } + + const body: GetInfoResponse = { + item: flattenedRes, + metadata, + }; + return response.ok({ body }); }; export const getBulkAssetsHandler: FleetRequestHandler< @@ -264,23 +235,19 @@ export const getBulkAssetsHandler: FleetRequestHandler< TypeOf > = async (context, request, response) => { const coreContext = await context.core; - try { - const { assetIds } = request.body; - const savedObjectsClient = coreContext.savedObjects.client; - const savedObjectsTypeRegistry = coreContext.savedObjects.typeRegistry; - const assets = await getBulkAssets( - savedObjectsClient, - savedObjectsTypeRegistry, - assetIds as AssetSOObject[] - ); + const { assetIds } = request.body; + const savedObjectsClient = coreContext.savedObjects.client; + const savedObjectsTypeRegistry = coreContext.savedObjects.typeRegistry; + const assets = await getBulkAssets( + savedObjectsClient, + savedObjectsTypeRegistry, + assetIds as AssetSOObject[] + ); - const body: GetBulkAssetsResponse = { - items: assets, - }; - return response.ok({ body }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } + const body: GetBulkAssetsResponse = { + items: assets, + }; + return response.ok({ body }); }; export const updatePackageHandler: FleetRequestHandler< @@ -288,34 +255,26 @@ export const updatePackageHandler: FleetRequestHandler< unknown, TypeOf > = async (context, request, response) => { - try { - const savedObjectsClient = (await context.fleet).internalSoClient; - const { pkgName } = request.params; + const savedObjectsClient = (await context.fleet).internalSoClient; + const { pkgName } = request.params; - const res = await updatePackage({ savedObjectsClient, pkgName, ...request.body }); - const body: UpdatePackageResponse = { - item: res, - }; + const res = await updatePackage({ savedObjectsClient, pkgName, ...request.body }); + const body: UpdatePackageResponse = { + item: res, + }; - return response.ok({ body }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } + return response.ok({ body }); }; export const getStatsHandler: FleetRequestHandler< TypeOf > = async (context, request, response) => { - try { - const { pkgName } = request.params; - const savedObjectsClient = (await context.fleet).internalSoClient; - const body: GetStatsResponse = { - response: await getPackageUsageStats({ savedObjectsClient, pkgName }), - }; - return response.ok({ body }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } + const { pkgName } = request.params; + const savedObjectsClient = (await context.fleet).internalSoClient; + const body: GetStatsResponse = { + response: await getPackageUsageStats({ savedObjectsClient, pkgName }), + }; + return response.ok({ body }); }; export const installPackageFromRegistryHandler: FleetRequestHandler< @@ -357,7 +316,7 @@ export const installPackageFromRegistryHandler: FleetRequestHandler< }; return response.ok({ body }); } else { - return await defaultFleetErrorHandler({ error: res.error, response }); + throw res.error; } }; @@ -398,7 +357,7 @@ export const createCustomIntegrationHandler: FleetRequestHandler< }; return response.ok({ body }); } else { - return await defaultFleetErrorHandler({ error: res.error, response }); + throw res.error; } } catch (error) { if (error instanceof NamingCollisionError) { @@ -416,7 +375,7 @@ export const createCustomIntegrationHandler: FleetRequestHandler< }, }); } - return await defaultFleetErrorHandler({ error, response }); + throw error; } }; @@ -513,7 +472,7 @@ export const installPackageByUploadHandler: FleetRequestHandler< }, }); } - return defaultFleetErrorHandler({ error: res.error, response }); + throw res.error; } }; @@ -522,26 +481,22 @@ export const deletePackageHandler: FleetRequestHandler< TypeOf, TypeOf > = async (context, request, response) => { - try { - const { pkgName, pkgVersion } = request.params; - const coreContext = await context.core; - const fleetContext = await context.fleet; - const savedObjectsClient = fleetContext.internalSoClient; - const esClient = coreContext.elasticsearch.client.asInternalUser; - const res = await removeInstallation({ - savedObjectsClient, - pkgName, - pkgVersion, - esClient, - force: request.query?.force, - }); - const body: DeletePackageResponse = { - items: res, - }; - return response.ok({ body }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } + const { pkgName, pkgVersion } = request.params; + const coreContext = await context.core; + const fleetContext = await context.fleet; + const savedObjectsClient = fleetContext.internalSoClient; + const esClient = coreContext.elasticsearch.client.asInternalUser; + const res = await removeInstallation({ + savedObjectsClient, + pkgName, + pkgVersion, + esClient, + force: request.query?.force, + }); + const body: DeletePackageResponse = { + items: res, + }; + return response.ok({ body }); }; export const getVerificationKeyIdHandler: FleetRequestHandler = async ( @@ -549,15 +504,11 @@ export const getVerificationKeyIdHandler: FleetRequestHandler = async ( request, response ) => { - try { - const packageVerificationKeyId = await getGpgKeyIdOrUndefined(); - const body: GetVerificationKeyIdResponse = { - id: packageVerificationKeyId || null, - }; - return response.ok({ body }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } + const packageVerificationKeyId = await getGpgKeyIdOrUndefined(); + const body: GetVerificationKeyIdResponse = { + id: packageVerificationKeyId || null, + }; + return response.ok({ body }); }; /** @@ -589,32 +540,28 @@ export const reauthorizeTransformsHandler: FleetRequestHandler< // User might not have permission to get username, or security is not enabled, and that's okay. } - try { - const logger = appContextService.getLogger(); - const authorizationHeader = HTTPAuthorizationHeader.parseFromRequest(request, username); - const secondaryAuth = await generateTransformSecondaryAuthHeaders({ - authorizationHeader, - logger, - username, - pkgName, - pkgVersion, - }); + const logger = appContextService.getLogger(); + const authorizationHeader = HTTPAuthorizationHeader.parseFromRequest(request, username); + const secondaryAuth = await generateTransformSecondaryAuthHeaders({ + authorizationHeader, + logger, + username, + pkgName, + pkgVersion, + }); - const resp = await handleTransformReauthorizeAndStart({ - esClient, - savedObjectsClient, - logger, - pkgName, - pkgVersion, - transforms, - secondaryAuth, - username, - }); + const resp = await handleTransformReauthorizeAndStart({ + esClient, + savedObjectsClient, + logger, + pkgName, + pkgVersion, + transforms, + secondaryAuth, + username, + }); - return response.ok({ body: resp }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } + return response.ok({ body: resp }); }; export const getInputsHandler: FleetRequestHandler< @@ -624,33 +571,29 @@ export const getInputsHandler: FleetRequestHandler< > = async (context, request, response) => { const soClient = (await context.fleet).internalSoClient; - try { - const { pkgName, pkgVersion } = request.params; - const { format, prerelease, ignoreUnverified } = request.query; - let body; - if (format === 'json') { - body = await getTemplateInputs( - soClient, - pkgName, - pkgVersion, - 'json', - prerelease, - ignoreUnverified - ); - } else if (format === 'yml' || format === 'yaml') { - body = await getTemplateInputs( - soClient, - pkgName, - pkgVersion, - 'yml', - prerelease, - ignoreUnverified - ); - } - return response.ok({ body }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); + const { pkgName, pkgVersion } = request.params; + const { format, prerelease, ignoreUnverified } = request.query; + let body; + if (format === 'json') { + body = await getTemplateInputs( + soClient, + pkgName, + pkgVersion, + 'json', + prerelease, + ignoreUnverified + ); + } else if (format === 'yml' || format === 'yaml') { + body = await getTemplateInputs( + soClient, + pkgName, + pkgVersion, + 'yml', + prerelease, + ignoreUnverified + ); } + return response.ok({ body }); }; // Don't expose the whole SO in the API response, only selected fields diff --git a/x-pack/plugins/fleet/server/routes/epm/kibana_assets_handler.ts b/x-pack/plugins/fleet/server/routes/epm/kibana_assets_handler.ts index ad0bec6397ee8..57880d5f08397 100644 --- a/x-pack/plugins/fleet/server/routes/epm/kibana_assets_handler.ts +++ b/x-pack/plugins/fleet/server/routes/epm/kibana_assets_handler.ts @@ -7,7 +7,7 @@ import type { TypeOf } from '@kbn/config-schema'; -import { defaultFleetErrorHandler, FleetNotFoundError } from '../../errors'; +import { FleetNotFoundError } from '../../errors'; import { appContextService } from '../../services'; import { deleteKibanaAssetsAndReferencesForSpace, @@ -29,87 +29,79 @@ export const installPackageKibanaAssetsHandler: FleetRequestHandler< undefined, TypeOf > = async (context, request, response) => { - try { - const fleetContext = await context.fleet; - const savedObjectsClient = fleetContext.internalSoClient; - const logger = appContextService.getLogger(); - const spaceId = fleetContext.spaceId; - const { pkgName, pkgVersion } = request.params; + const fleetContext = await context.fleet; + const savedObjectsClient = fleetContext.internalSoClient; + const logger = appContextService.getLogger(); + const spaceId = fleetContext.spaceId; + const { pkgName, pkgVersion } = request.params; - const installedPkgWithAssets = await getInstalledPackageWithAssets({ - savedObjectsClient, - pkgName, - logger, - }); + const installedPkgWithAssets = await getInstalledPackageWithAssets({ + savedObjectsClient, + pkgName, + logger, + }); - const installation = await getInstallationObject({ - pkgName, - savedObjectsClient, - }); + const installation = await getInstallationObject({ + pkgName, + savedObjectsClient, + }); - if ( - !installation || - !installedPkgWithAssets || - installedPkgWithAssets?.installation.version !== pkgVersion - ) { - throw new FleetNotFoundError('Requested version is not installed'); - } + if ( + !installation || + !installedPkgWithAssets || + installedPkgWithAssets?.installation.version !== pkgVersion + ) { + throw new FleetNotFoundError('Requested version is not installed'); + } - const { packageInfo } = installedPkgWithAssets; + const { packageInfo } = installedPkgWithAssets; - await installKibanaAssetsAndReferences({ - savedObjectsClient, - logger, - pkgName, - pkgTitle: packageInfo.title, - installAsAdditionalSpace: true, - spaceId, - assetTags: installedPkgWithAssets.packageInfo?.asset_tags, - installedPkg: installation, - packageInstallContext: { - packageInfo, - paths: installedPkgWithAssets.paths, - assetsMap: installedPkgWithAssets.assetsMap, - archiveIterator: createArchiveIteratorFromMap(installedPkgWithAssets.assetsMap), - }, - }); + await installKibanaAssetsAndReferences({ + savedObjectsClient, + logger, + pkgName, + pkgTitle: packageInfo.title, + installAsAdditionalSpace: true, + spaceId, + assetTags: installedPkgWithAssets.packageInfo?.asset_tags, + installedPkg: installation, + packageInstallContext: { + packageInfo, + paths: installedPkgWithAssets.paths, + assetsMap: installedPkgWithAssets.assetsMap, + archiveIterator: createArchiveIteratorFromMap(installedPkgWithAssets.assetsMap), + }, + }); - return response.ok({ body: { success: true } }); - } catch (error) { - return await defaultFleetErrorHandler({ error, response }); - } + return response.ok({ body: { success: true } }); }; export const deletePackageKibanaAssetsHandler: FleetRequestHandler< TypeOf, undefined > = async (context, request, response) => { - try { - const fleetContext = await context.fleet; - const savedObjectsClient = fleetContext.internalSoClient; - const logger = appContextService.getLogger(); - const spaceId = fleetContext.spaceId; - const { pkgName, pkgVersion } = request.params; + const fleetContext = await context.fleet; + const savedObjectsClient = fleetContext.internalSoClient; + const logger = appContextService.getLogger(); + const spaceId = fleetContext.spaceId; + const { pkgName, pkgVersion } = request.params; - const installation = await getInstallationObject({ - pkgName, - savedObjectsClient, - }); + const installation = await getInstallationObject({ + pkgName, + savedObjectsClient, + }); - if (!installation || installation.attributes.version !== pkgVersion) { - throw new FleetNotFoundError('Version is not installed'); - } + if (!installation || installation.attributes.version !== pkgVersion) { + throw new FleetNotFoundError('Version is not installed'); + } - await deleteKibanaAssetsAndReferencesForSpace({ - savedObjectsClient, - logger, - pkgName, - spaceId, - installedPkg: installation, - }); + await deleteKibanaAssetsAndReferencesForSpace({ + savedObjectsClient, + logger, + pkgName, + spaceId, + installedPkg: installation, + }); - return response.ok({ body: { success: true } }); - } catch (error) { - return await defaultFleetErrorHandler({ error, response }); - } + return response.ok({ body: { success: true } }); }; diff --git a/x-pack/plugins/fleet/server/routes/fleet_proxies/handler.ts b/x-pack/plugins/fleet/server/routes/fleet_proxies/handler.ts index a5905f406a6e7..889b7bca75473 100644 --- a/x-pack/plugins/fleet/server/routes/fleet_proxies/handler.ts +++ b/x-pack/plugins/fleet/server/routes/fleet_proxies/handler.ts @@ -22,7 +22,6 @@ import { updateFleetProxy, getFleetProxyRelatedSavedObjects, } from '../../services/fleet_proxies'; -import { defaultFleetErrorHandler } from '../../errors'; import type { GetOneFleetProxyRequestSchema, PostFleetProxyRequestSchema, @@ -80,18 +79,14 @@ export const postFleetProxyHandler: RequestHandler< > = async (context, request, response) => { const coreContext = await context.core; const soClient = coreContext.savedObjects.client; - try { - const { id, ...data } = request.body; - const proxy = await createFleetProxy(soClient, { ...data, is_preconfigured: false }, { id }); + const { id, ...data } = request.body; + const proxy = await createFleetProxy(soClient, { ...data, is_preconfigured: false }, { id }); - const body = { - item: proxy, - }; + const body = { + item: proxy, + }; - return response.ok({ body }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } + return response.ok({ body }); }; export const putFleetProxyHandler: RequestHandler< @@ -125,26 +120,22 @@ export const putFleetProxyHandler: RequestHandler< }); } - return defaultFleetErrorHandler({ error, response }); + throw error; } }; export const getAllFleetProxyHandler: RequestHandler = async (context, request, response) => { const soClient = (await context.core).savedObjects.client; - try { - const res = await listFleetProxies(soClient); - const body = { - items: res.items, - page: res.page, - perPage: res.perPage, - total: res.total, - }; + const res = await listFleetProxies(soClient); + const body = { + items: res.items, + page: res.page, + perPage: res.perPage, + total: res.total, + }; - return response.ok({ body }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } + return response.ok({ body }); }; export const deleteFleetProxyHandler: RequestHandler< @@ -177,7 +168,7 @@ export const deleteFleetProxyHandler: RequestHandler< }); } - return defaultFleetErrorHandler({ error, response }); + throw error; } }; @@ -199,6 +190,6 @@ export const getFleetProxyHandler: RequestHandler< }); } - return defaultFleetErrorHandler({ error, response }); + throw error; } }; diff --git a/x-pack/plugins/fleet/server/routes/fleet_server_hosts/handler.test.ts b/x-pack/plugins/fleet/server/routes/fleet_server_hosts/handler.test.ts index bfcb91351af11..4ddcd47ad9ab4 100644 --- a/x-pack/plugins/fleet/server/routes/fleet_server_hosts/handler.test.ts +++ b/x-pack/plugins/fleet/server/routes/fleet_server_hosts/handler.test.ts @@ -8,9 +8,14 @@ import { SERVERLESS_DEFAULT_FLEET_SERVER_HOST_ID } from '../../constants'; import { agentPolicyService, appContextService } from '../../services'; import * as fleetServerService from '../../services/fleet_server_host'; +import { withDefaultErrorHandler } from '../../services/security/fleet_router'; import { postFleetServerHost, putFleetServerHostHandler } from './handler'; +const postFleetServerHostWithErrorHandler = withDefaultErrorHandler(postFleetServerHost); +const putFleetServerHostHandlerWithErrorHandler = + withDefaultErrorHandler(putFleetServerHostHandler); + describe('fleet server hosts handler', () => { const mockContext = { core: Promise.resolve({ @@ -45,7 +50,7 @@ describe('fleet server hosts handler', () => { it('should return error on post in serverless if host url is different from default', async () => { jest.spyOn(appContextService, 'getCloud').mockReturnValue({ isServerlessEnabled: true } as any); - const res = await postFleetServerHost( + const res = await postFleetServerHostWithErrorHandler( mockContext, { body: { id: 'host1', host_urls: ['http://localhost:8080'] } } as any, mockResponse as any @@ -62,7 +67,7 @@ describe('fleet server hosts handler', () => { it('should return ok on post in serverless if host url is same as default', async () => { jest.spyOn(appContextService, 'getCloud').mockReturnValue({ isServerlessEnabled: true } as any); - const res = await postFleetServerHost( + const res = await postFleetServerHostWithErrorHandler( mockContext, { body: { id: 'host1', host_urls: ['http://elasticsearch:9200'] } } as any, mockResponse as any @@ -76,7 +81,7 @@ describe('fleet server hosts handler', () => { .spyOn(appContextService, 'getCloud') .mockReturnValue({ isServerlessEnabled: false } as any); - const res = await postFleetServerHost( + const res = await postFleetServerHostWithErrorHandler( mockContext, { body: { id: 'host1', host_urls: ['http://localhost:8080'] } } as any, mockResponse as any @@ -88,7 +93,7 @@ describe('fleet server hosts handler', () => { it('should return error on put in serverless if host url is different from default', async () => { jest.spyOn(appContextService, 'getCloud').mockReturnValue({ isServerlessEnabled: true } as any); - const res = await putFleetServerHostHandler( + const res = await putFleetServerHostHandlerWithErrorHandler( mockContext, { body: { host_urls: ['http://localhost:8080'] }, params: { outputId: 'host1' } } as any, mockResponse as any @@ -105,7 +110,7 @@ describe('fleet server hosts handler', () => { it('should return ok on put in serverless if host url is same as default', async () => { jest.spyOn(appContextService, 'getCloud').mockReturnValue({ isServerlessEnabled: true } as any); - const res = await putFleetServerHostHandler( + const res = await putFleetServerHostHandlerWithErrorHandler( mockContext, { body: { host_urls: ['http://elasticsearch:9200'] }, params: { outputId: 'host1' } } as any, mockResponse as any @@ -117,7 +122,7 @@ describe('fleet server hosts handler', () => { it('should return ok on put in serverless if host urls are not passed', async () => { jest.spyOn(appContextService, 'getCloud').mockReturnValue({ isServerlessEnabled: true } as any); - const res = await putFleetServerHostHandler( + const res = await putFleetServerHostHandlerWithErrorHandler( mockContext, { body: { name: ['Renamed'] }, params: { outputId: 'host1' } } as any, mockResponse as any @@ -131,7 +136,7 @@ describe('fleet server hosts handler', () => { .spyOn(appContextService, 'getCloud') .mockReturnValue({ isServerlessEnabled: false } as any); - const res = await putFleetServerHostHandler( + const res = await putFleetServerHostHandlerWithErrorHandler( mockContext, { body: { host_urls: ['http://localhost:8080'] }, params: { outputId: 'host1' } } as any, mockResponse as any diff --git a/x-pack/plugins/fleet/server/routes/fleet_server_hosts/handler.ts b/x-pack/plugins/fleet/server/routes/fleet_server_hosts/handler.ts index f7159e6b5f498..56913c7b97e14 100644 --- a/x-pack/plugins/fleet/server/routes/fleet_server_hosts/handler.ts +++ b/x-pack/plugins/fleet/server/routes/fleet_server_hosts/handler.ts @@ -12,7 +12,7 @@ import { isEqual } from 'lodash'; import { SERVERLESS_DEFAULT_FLEET_SERVER_HOST_ID } from '../../constants'; -import { defaultFleetErrorHandler, FleetServerHostUnauthorizedError } from '../../errors'; +import { FleetServerHostUnauthorizedError } from '../../errors'; import { agentPolicyService, appContextService } from '../../services'; import { @@ -58,28 +58,24 @@ export const postFleetServerHost: RequestHandler< const soClient = coreContext.savedObjects.client; const esClient = coreContext.elasticsearch.client.asInternalUser; - try { - // In serverless, allow create fleet server host if host url is same as default. - await checkFleetServerHostsWriteAPIsAllowed(soClient, request.body.host_urls); - - const { id, ...data } = request.body; - const FleetServerHost = await createFleetServerHost( - soClient, - { ...data, is_preconfigured: false }, - { id } - ); - if (FleetServerHost.is_default) { - await agentPolicyService.bumpAllAgentPolicies(esClient); - } - - const body = { - item: FleetServerHost, - }; + // In serverless, allow create fleet server host if host url is same as default. + await checkFleetServerHostsWriteAPIsAllowed(soClient, request.body.host_urls); - return response.ok({ body }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); + const { id, ...data } = request.body; + const FleetServerHost = await createFleetServerHost( + soClient, + { ...data, is_preconfigured: false }, + { id } + ); + if (FleetServerHost.is_default) { + await agentPolicyService.bumpAllAgentPolicies(esClient); } + + const body = { + item: FleetServerHost, + }; + + return response.ok({ body }); }; export const getFleetServerHostHandler: RequestHandler< @@ -100,7 +96,7 @@ export const getFleetServerHostHandler: RequestHandler< }); } - return defaultFleetErrorHandler({ error, response }); + throw error; } }; @@ -125,7 +121,7 @@ export const deleteFleetServerHostHandler: RequestHandler< }); } - return defaultFleetErrorHandler({ error, response }); + throw error; } }; @@ -163,23 +159,19 @@ export const putFleetServerHostHandler: RequestHandler< }); } - return defaultFleetErrorHandler({ error, response }); + throw error; } }; export const getAllFleetServerHostsHandler: RequestHandler = async (context, request, response) => { const soClient = (await context.core).savedObjects.client; - try { - const res = await listFleetServerHosts(soClient); - const body = { - items: res.items, - page: res.page, - perPage: res.perPage, - total: res.total, - }; - - return response.ok({ body }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } + const res = await listFleetServerHosts(soClient); + const body = { + items: res.items, + page: res.page, + perPage: res.perPage, + total: res.total, + }; + + return response.ok({ body }); }; diff --git a/x-pack/plugins/fleet/server/routes/health_check/handler.test.ts b/x-pack/plugins/fleet/server/routes/health_check/handler.test.ts index d418b0d7c560b..a93c650017b9e 100644 --- a/x-pack/plugins/fleet/server/routes/health_check/handler.test.ts +++ b/x-pack/plugins/fleet/server/routes/health_check/handler.test.ts @@ -9,6 +9,9 @@ import fetch from 'node-fetch'; import * as fleetServerService from '../../services/fleet_server_host'; import { postHealthCheckHandler } from '.'; +import { withDefaultErrorHandler } from '../../services/security/fleet_router'; + +const postHealthCheckHandlerWithErrorHandler = withDefaultErrorHandler(postHealthCheckHandler); jest.mock('node-fetch'); @@ -47,7 +50,7 @@ describe('Fleet server health_check handler', () => { host_urls: [], } as any); - const res = await postHealthCheckHandler( + const res = await postHealthCheckHandlerWithErrorHandler( mockContext, { body: { id: 'default-fleet-server' } } as any, mockResponse as any @@ -62,7 +65,7 @@ describe('Fleet server health_check handler', () => { }); it('should return a bad request error if body contains deprecated parameter `host`', async () => { - const res = await postHealthCheckHandler( + const res = await postHealthCheckHandlerWithErrorHandler( mockContext, { body: { host: 'https://localhost:8220' } } as any, mockResponse as any @@ -96,7 +99,7 @@ describe('Fleet server health_check handler', () => { ok: true, } as any); - const res = await postHealthCheckHandler( + const res = await postHealthCheckHandlerWithErrorHandler( mockContext, { body: { id: 'default-fleet-server' } } as any, mockResponse as any @@ -118,7 +121,7 @@ describe('Fleet server health_check handler', () => { .spyOn(fleetServerService, 'getFleetServerHost') .mockRejectedValue({ output: { statusCode: 404 }, isBoom: true }); - const res = await postHealthCheckHandler( + const res = await postHealthCheckHandlerWithErrorHandler( mockContext, { body: { id: 'non-existent' } } as any, mockResponse as any @@ -141,7 +144,7 @@ describe('Fleet server health_check handler', () => { } as any); mockedFetch.mockRejectedValue({ message: 'user aborted', name: 'AbortError' }); - const res = await postHealthCheckHandler( + const res = await postHealthCheckHandlerWithErrorHandler( mockContext, { body: { id: 'default-fleet-server' } } as any, mockResponse as any diff --git a/x-pack/plugins/fleet/server/routes/health_check/index.ts b/x-pack/plugins/fleet/server/routes/health_check/index.ts index 051cad347e043..f1ce9271379f8 100644 --- a/x-pack/plugins/fleet/server/routes/health_check/index.ts +++ b/x-pack/plugins/fleet/server/routes/health_check/index.ts @@ -16,7 +16,6 @@ import type { FleetAuthzRouter } from '../../services/security'; import { APP_API_ROUTES } from '../../constants'; import type { FleetRequestHandler } from '../../types'; -import { defaultFleetErrorHandler } from '../../errors'; import { PostHealthCheckRequestSchema } from '../../types'; export const registerRoutes = (router: FleetAuthzRouter) => { @@ -106,6 +105,6 @@ export const postHealthCheckHandler: FleetRequestHandler< body: { status: `OFFLINE`, host_id: request.body.id }, }); } - return defaultFleetErrorHandler({ error, response }); + throw error; } }; diff --git a/x-pack/plugins/fleet/server/routes/message_signing_service/handlers.test.ts b/x-pack/plugins/fleet/server/routes/message_signing_service/handlers.test.ts index d9987f325338b..11b47a626f8d2 100644 --- a/x-pack/plugins/fleet/server/routes/message_signing_service/handlers.test.ts +++ b/x-pack/plugins/fleet/server/routes/message_signing_service/handlers.test.ts @@ -12,9 +12,12 @@ import type { KibanaRequest } from '@kbn/core/server'; import { createAppContextStartContractMock, xpackMocks } from '../../mocks'; import { appContextService } from '../../services'; import type { FleetRequestHandlerContext } from '../../types'; +import { withDefaultErrorHandler } from '../../services/security/fleet_router'; import { rotateKeyPairHandler } from './handlers'; +const rotateKeyPairHandlerWithErrorHandler = withDefaultErrorHandler(rotateKeyPairHandler); + describe('FleetMessageSigningServiceHandler', () => { let context: AwaitedProperties>; let response: ReturnType; @@ -51,7 +54,7 @@ describe('FleetMessageSigningServiceHandler', () => { messageSigningService: undefined, }); - await rotateKeyPairHandler( + await rotateKeyPairHandlerWithErrorHandler( coreMock.createCustomRequestHandlerContext(context), request, response @@ -65,7 +68,7 @@ describe('FleetMessageSigningServiceHandler', () => { }); it('POST /message_signing_service/rotate_key_pair?acknowledge=true succeeds with `acknowledge=true`', async () => { - await rotateKeyPairHandler( + await rotateKeyPairHandlerWithErrorHandler( coreMock.createCustomRequestHandlerContext(context), request, response @@ -89,7 +92,7 @@ describe('FleetMessageSigningServiceHandler', () => { Error(error) ); - await rotateKeyPairHandler( + await rotateKeyPairHandlerWithErrorHandler( coreMock.createCustomRequestHandlerContext(context), request, response diff --git a/x-pack/plugins/fleet/server/routes/message_signing_service/handlers.ts b/x-pack/plugins/fleet/server/routes/message_signing_service/handlers.ts index 7e2acdb5171a8..8bc21e18fbd78 100644 --- a/x-pack/plugins/fleet/server/routes/message_signing_service/handlers.ts +++ b/x-pack/plugins/fleet/server/routes/message_signing_service/handlers.ts @@ -9,7 +9,6 @@ import type { TypeOf } from '@kbn/config-schema'; import type { FleetRequestHandler } from '../../types'; -import { defaultFleetErrorHandler } from '../../errors'; import { appContextService } from '../../services'; import type { RotateKeyPairSchema } from '../../types/rest_spec/message_signing_service'; @@ -41,6 +40,6 @@ export const rotateKeyPairHandler: FleetRequestHandler< }); } catch (error) { logger.error(error); - return defaultFleetErrorHandler({ error: new Error('Failed to rotate key pair!'), response }); + throw new Error('Failed to rotate key pair!'); } }; diff --git a/x-pack/plugins/fleet/server/routes/output/handler.test.ts b/x-pack/plugins/fleet/server/routes/output/handler.test.ts index 26dd6099e474f..665cb9059654e 100644 --- a/x-pack/plugins/fleet/server/routes/output/handler.test.ts +++ b/x-pack/plugins/fleet/server/routes/output/handler.test.ts @@ -7,9 +7,13 @@ import { SERVERLESS_DEFAULT_OUTPUT_ID } from '../../constants'; import { agentPolicyService, appContextService, outputService } from '../../services'; +import { withDefaultErrorHandler } from '../../services/security/fleet_router'; import { postOutputHandler, putOutputHandler } from './handler'; +const putOutputHandlerWithErrorHandler = withDefaultErrorHandler(putOutputHandler); +const postOutputHandlerWithErrorHandler = withDefaultErrorHandler(postOutputHandler); + describe('output handler', () => { const mockContext = { core: Promise.resolve({ @@ -41,7 +45,7 @@ describe('output handler', () => { it('should return error on post output using remote_elasticsearch in serverless', async () => { jest.spyOn(appContextService, 'getCloud').mockReturnValue({ isServerlessEnabled: true } as any); - const res = await postOutputHandler( + const res = await postOutputHandlerWithErrorHandler( mockContext, { body: { id: 'output1', type: 'remote_elasticsearch' } } as any, mockResponse as any @@ -58,7 +62,7 @@ describe('output handler', () => { .spyOn(appContextService, 'getCloud') .mockReturnValue({ isServerlessEnabled: false } as any); - const res = await postOutputHandler( + const res = await postOutputHandlerWithErrorHandler( mockContext, { body: { type: 'remote_elasticsearch' } } as any, mockResponse as any @@ -70,7 +74,7 @@ describe('output handler', () => { it('should return error on put output using remote_elasticsearch in serverless', async () => { jest.spyOn(appContextService, 'getCloud').mockReturnValue({ isServerlessEnabled: true } as any); - const res = await putOutputHandler( + const res = await putOutputHandlerWithErrorHandler( mockContext, { body: { type: 'remote_elasticsearch' }, params: { outputId: 'output1' } } as any, mockResponse as any @@ -87,7 +91,7 @@ describe('output handler', () => { .spyOn(appContextService, 'getCloud') .mockReturnValue({ isServerlessEnabled: false } as any); - const res = await putOutputHandler( + const res = await putOutputHandlerWithErrorHandler( mockContext, { body: { type: 'remote_elasticsearch' }, params: { outputId: 'output1' } } as any, mockResponse as any @@ -99,7 +103,7 @@ describe('output handler', () => { it('should return error on post elasticsearch output in serverless if host url is different from default', async () => { jest.spyOn(appContextService, 'getCloud').mockReturnValue({ isServerlessEnabled: true } as any); - const res = await postOutputHandler( + const res = await postOutputHandlerWithErrorHandler( mockContext, { body: { id: 'output1', type: 'elasticsearch', hosts: ['http://localhost:8080'] } } as any, mockResponse as any @@ -117,7 +121,7 @@ describe('output handler', () => { it('should return ok on post elasticsearch output in serverless if host url is same as default', async () => { jest.spyOn(appContextService, 'getCloud').mockReturnValue({ isServerlessEnabled: true } as any); - const res = await postOutputHandler( + const res = await postOutputHandlerWithErrorHandler( mockContext, { body: { id: 'output1', type: 'elasticsearch', hosts: ['http://elasticsearch:9200'] }, @@ -133,7 +137,7 @@ describe('output handler', () => { .spyOn(appContextService, 'getCloud') .mockReturnValue({ isServerlessEnabled: false } as any); - const res = await postOutputHandler( + const res = await postOutputHandlerWithErrorHandler( mockContext, { body: { id: 'output1', type: 'elasticsearch', hosts: ['http://localhost:8080'] } } as any, mockResponse as any @@ -153,7 +157,7 @@ describe('output handler', () => { } }); - const res = await putOutputHandler( + const res = await putOutputHandlerWithErrorHandler( mockContext, { body: { hosts: ['http://localhost:8080'] }, @@ -174,7 +178,7 @@ describe('output handler', () => { it('should return ok on put elasticsearch output in serverless if host url is same as default', async () => { jest.spyOn(appContextService, 'getCloud').mockReturnValue({ isServerlessEnabled: true } as any); - const res = await putOutputHandler( + const res = await putOutputHandlerWithErrorHandler( mockContext, { body: { hosts: ['http://elasticsearch:9200'] }, @@ -189,7 +193,7 @@ describe('output handler', () => { it('should return ok on put elasticsearch output in serverless if host url is not passed', async () => { jest.spyOn(appContextService, 'getCloud').mockReturnValue({ isServerlessEnabled: true } as any); - const res = await putOutputHandler( + const res = await putOutputHandlerWithErrorHandler( mockContext, { body: { name: 'Renamed output' }, @@ -206,7 +210,7 @@ describe('output handler', () => { .spyOn(appContextService, 'getCloud') .mockReturnValue({ isServerlessEnabled: false } as any); - const res = await putOutputHandler( + const res = await putOutputHandlerWithErrorHandler( mockContext, { body: { hosts: ['http://localhost:8080'] }, @@ -223,7 +227,7 @@ describe('output handler', () => { .spyOn(appContextService, 'getCloud') .mockReturnValue({ isServerlessEnabled: false } as any); - const res = await postOutputHandler( + const res = await postOutputHandlerWithErrorHandler( mockContext, { body: { @@ -246,7 +250,7 @@ describe('output handler', () => { .spyOn(appContextService, 'getCloud') .mockReturnValue({ isServerlessEnabled: false } as any); - const res = await postOutputHandler( + const res = await postOutputHandlerWithErrorHandler( mockContext, { body: { type: 'remote_elasticsearch', secrets: { service_token: 'token2' } } } as any, mockResponse as any diff --git a/x-pack/plugins/fleet/server/routes/output/handler.ts b/x-pack/plugins/fleet/server/routes/output/handler.ts index b35158637d151..0b33dd15e73fe 100644 --- a/x-pack/plugins/fleet/server/routes/output/handler.ts +++ b/x-pack/plugins/fleet/server/routes/output/handler.ts @@ -29,7 +29,7 @@ import type { PostLogstashApiKeyResponse, } from '../../../common/types'; import { outputService } from '../../services/output'; -import { defaultFleetErrorHandler, FleetUnauthorizedError } from '../../errors'; +import { FleetUnauthorizedError } from '../../errors'; import { agentPolicyService, appContextService } from '../../services'; import { generateLogstashApiKey, canCreateLogstashApiKey } from '../../services/api_keys'; @@ -55,20 +55,16 @@ function ensureNoDuplicateSecrets(output: Partial) { export const getOutputsHandler: RequestHandler = async (context, request, response) => { const soClient = (await context.core).savedObjects.client; - try { - const outputs = await outputService.list(soClient); + const outputs = await outputService.list(soClient); - const body: GetOutputsResponse = { - items: outputs.items, - page: outputs.page, - perPage: outputs.perPage, - total: outputs.total, - }; + const body: GetOutputsResponse = { + items: outputs.items, + page: outputs.page, + perPage: outputs.perPage, + total: outputs.total, + }; - return response.ok({ body }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } + return response.ok({ body }); }; export const getOneOuputHandler: RequestHandler< @@ -90,7 +86,7 @@ export const getOneOuputHandler: RequestHandler< }); } - return defaultFleetErrorHandler({ error, response }); + throw error; } }; @@ -126,7 +122,7 @@ export const putOutputHandler: RequestHandler< }); } - return defaultFleetErrorHandler({ error, response }); + throw error; } }; @@ -138,23 +134,19 @@ export const postOutputHandler: RequestHandler< const coreContext = await context.core; const soClient = coreContext.savedObjects.client; const esClient = coreContext.elasticsearch.client.asInternalUser; - try { - const { id, ...newOutput } = request.body; - await validateOutputServerless(newOutput, soClient); - ensureNoDuplicateSecrets(newOutput); - const output = await outputService.create(soClient, esClient, newOutput, { id }); - if (output.is_default || output.is_default_monitoring) { - await agentPolicyService.bumpAllAgentPolicies(esClient); - } + const { id, ...newOutput } = request.body; + await validateOutputServerless(newOutput, soClient); + ensureNoDuplicateSecrets(newOutput); + const output = await outputService.create(soClient, esClient, newOutput, { id }); + if (output.is_default || output.is_default_monitoring) { + await agentPolicyService.bumpAllAgentPolicies(esClient); + } - const body: GetOneOutputResponse = { - item: output, - }; + const body: GetOneOutputResponse = { + item: output, + }; - return response.ok({ body }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } + return response.ok({ body }); }; async function validateOutputServerless( @@ -206,42 +198,31 @@ export const deleteOutputHandler: RequestHandler< }); } - return defaultFleetErrorHandler({ error, response }); + throw error; } }; export const postLogstashApiKeyHandler: RequestHandler = async (context, request, response) => { const esClient = (await context.core).elasticsearch.client.asCurrentUser; - try { - const hasCreatePrivileges = await canCreateLogstashApiKey(esClient); - if (!hasCreatePrivileges) { - throw new FleetUnauthorizedError('Missing permissions to create logstash API key'); - } + const hasCreatePrivileges = await canCreateLogstashApiKey(esClient); + if (!hasCreatePrivileges) { + throw new FleetUnauthorizedError('Missing permissions to create logstash API key'); + } - const apiKey = await generateLogstashApiKey(esClient); + const apiKey = await generateLogstashApiKey(esClient); - const body: PostLogstashApiKeyResponse = { - // Logstash expect the key to be formatted like this id:key - api_key: `${apiKey.id}:${apiKey.api_key}`, - }; + const body: PostLogstashApiKeyResponse = { + // Logstash expect the key to be formatted like this id:key + api_key: `${apiKey.id}:${apiKey.api_key}`, + }; - return response.ok({ body }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } + return response.ok({ body }); }; export const getLatestOutputHealth: RequestHandler< TypeOf > = async (context, request, response) => { const esClient = (await context.core).elasticsearch.client.asInternalUser; - try { - const outputHealth = await outputService.getLatestOutputHealth( - esClient, - request.params.outputId - ); - return response.ok({ body: outputHealth }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } + const outputHealth = await outputService.getLatestOutputHealth(esClient, request.params.outputId); + return response.ok({ body: outputHealth }); }; diff --git a/x-pack/plugins/fleet/server/routes/package_policy/handlers.test.ts b/x-pack/plugins/fleet/server/routes/package_policy/handlers.test.ts index fbcf1ee80f206..ffe3aef824421 100644 --- a/x-pack/plugins/fleet/server/routes/package_policy/handlers.test.ts +++ b/x-pack/plugins/fleet/server/routes/package_policy/handlers.test.ts @@ -326,13 +326,10 @@ describe('When calling package policy', () => { .spyOn(appContextService, 'getExperimentalFeatures') .mockReturnValue({ enableReusableIntegrationPolicies: true } as any); const request = getUpdateKibanaRequest({ policy_ids: ['1', '2'] } as any); - await routeHandler(context, request, response); - expect(response.customError).toHaveBeenCalledWith({ - statusCode: 400, - body: { - message: 'Cannot change agent policies of an agentless integration', - }, - }); + + await expect(() => routeHandler(context, request, response)).rejects.toThrow( + /Cannot change agent policies of an agentless integration/ + ); }); it('should rename the agentless agent policy to sync with the package policy name if agentless is enabled', async () => { diff --git a/x-pack/plugins/fleet/server/routes/package_policy/handlers.ts b/x-pack/plugins/fleet/server/routes/package_policy/handlers.ts index 3bc2f79d6afd7..6fe1e897a9772 100644 --- a/x-pack/plugins/fleet/server/routes/package_policy/handlers.ts +++ b/x-pack/plugins/fleet/server/routes/package_policy/handlers.ts @@ -43,11 +43,7 @@ import type { UpgradePackagePolicyResponse, } from '../../../common/types'; import { installationStatuses, inputsFormat } from '../../../common/constants'; -import { - defaultFleetErrorHandler, - PackagePolicyNotFoundError, - PackagePolicyRequestError, -} from '../../errors'; +import { PackagePolicyNotFoundError, PackagePolicyRequestError } from '../../errors'; import { getInstallation, getInstallations, @@ -80,33 +76,26 @@ export const getPackagePoliciesHandler: FleetRequestHandler< const soClient = fleetContext.internalSoClient; const limitedToPackages = fleetContext.limitedToPackages; - try { - const { items, total, page, perPage } = await packagePolicyService.list( - soClient, - request.query - ); - - checkAllowedPackages(items, limitedToPackages, 'package.name'); + const { items, total, page, perPage } = await packagePolicyService.list(soClient, request.query); - if (request.query.withAgentCount) { - await populatePackagePolicyAssignedAgentsCount(esClient, items); - } + checkAllowedPackages(items, limitedToPackages, 'package.name'); - // agnostic to package-level RBAC - return response.ok({ - body: { - items: - request.query.format === inputsFormat.Simplified - ? items.map((item) => packagePolicyToSimplifiedPackagePolicy(item)) - : items, - total, - page, - perPage, - }, - }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); + if (request.query.withAgentCount) { + await populatePackagePolicyAssignedAgentsCount(esClient, items); } + + // agnostic to package-level RBAC + return response.ok({ + body: { + items: + request.query.format === inputsFormat.Simplified + ? items.map((item) => packagePolicyToSimplifiedPackagePolicy(item)) + : items, + total, + page, + perPage, + }, + }); }; export const bulkGetPackagePoliciesHandler: FleetRequestHandler< @@ -142,7 +131,7 @@ export const bulkGetPackagePoliciesHandler: FleetRequestHandler< }); } - return defaultFleetErrorHandler({ error, response }); + throw error; } }; @@ -177,9 +166,8 @@ export const getOnePackagePolicyHandler: FleetRequestHandler< } catch (error) { if (SavedObjectsErrorHelpers.isNotFoundError(error)) { return notFoundResponse(); - } else { - return defaultFleetErrorHandler({ error, response }); } + throw error; } }; @@ -189,42 +177,39 @@ export const getOrphanedPackagePolicies: RequestHandler = response ) => { const soClient = (await context.core).savedObjects.client; - try { - const installedPackages = await getInstallations(soClient, { - perPage: SO_SEARCH_LIMIT, - filter: ` + + const installedPackages = await getInstallations(soClient, { + perPage: SO_SEARCH_LIMIT, + filter: ` ${PACKAGES_SAVED_OBJECT_TYPE}.attributes.install_status:${installationStatuses.Installed} `, + }); + const orphanedPackagePolicies: PackagePolicy[] = []; + const packagePolicies = await packagePolicyService.list(soClient, { + perPage: SO_SEARCH_LIMIT, + }); + const packagePoliciesByPackage = groupBy(packagePolicies.items, 'package.name'); + const agentPolicies = await agentPolicyService.list(soClient, { + perPage: SO_SEARCH_LIMIT, + }); + const agentPoliciesById = keyBy(agentPolicies.items, 'id'); + const usedPackages = installedPackages.saved_objects.filter( + ({ attributes: { name } }) => !!packagePoliciesByPackage[name] + ); + usedPackages.forEach(({ attributes: { name } }) => { + packagePoliciesByPackage[name].forEach((packagePolicy) => { + if (packagePolicy.policy_ids.every((policyId) => !agentPoliciesById[policyId])) { + orphanedPackagePolicies.push(packagePolicy); + } }); - const orphanedPackagePolicies: PackagePolicy[] = []; - const packagePolicies = await packagePolicyService.list(soClient, { - perPage: SO_SEARCH_LIMIT, - }); - const packagePoliciesByPackage = groupBy(packagePolicies.items, 'package.name'); - const agentPolicies = await agentPolicyService.list(soClient, { - perPage: SO_SEARCH_LIMIT, - }); - const agentPoliciesById = keyBy(agentPolicies.items, 'id'); - const usedPackages = installedPackages.saved_objects.filter( - ({ attributes: { name } }) => !!packagePoliciesByPackage[name] - ); - usedPackages.forEach(({ attributes: { name } }) => { - packagePoliciesByPackage[name].forEach((packagePolicy) => { - if (packagePolicy.policy_ids.every((policyId) => !agentPoliciesById[policyId])) { - orphanedPackagePolicies.push(packagePolicy); - } - }); - }); - - return response.ok({ - body: { - items: orphanedPackagePolicies, - total: orphanedPackagePolicies.length, - }, - }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } + }); + + return response.ok({ + body: { + items: orphanedPackagePolicies, + total: orphanedPackagePolicies.length, + }, + }); }; export const createPackagePolicyHandler: FleetRequestHandler< @@ -323,7 +308,7 @@ export const createPackagePolicyHandler: FleetRequestHandler< body: { message: error.message }, }); } - return defaultFleetErrorHandler({ error, response }); + throw error; } }; @@ -441,7 +426,7 @@ export const updatePackagePolicyHandler: FleetRequestHandler< body: { message: error.message }, }); } - return defaultFleetErrorHandler({ error, response }); + throw error; } }; @@ -455,22 +440,18 @@ export const deletePackagePolicyHandler: RequestHandler< const esClient = coreContext.elasticsearch.client.asInternalUser; const user = appContextService.getSecurityCore().authc.getCurrentUser(request) || undefined; - try { - const body: PostDeletePackagePoliciesResponse = await packagePolicyService.delete( - soClient, - esClient, - request.body.packagePolicyIds, - { user, force: request.body.force, skipUnassignFromAgentPolicies: request.body.force }, - context, - request - ); - - return response.ok({ - body, - }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } + const body: PostDeletePackagePoliciesResponse = await packagePolicyService.delete( + soClient, + esClient, + request.body.packagePolicyIds, + { user, force: request.body.force, skipUnassignFromAgentPolicies: request.body.force }, + context, + request + ); + + return response.ok({ + body, + }); }; export const deleteOnePackagePolicyHandler: RequestHandler< @@ -483,33 +464,29 @@ export const deleteOnePackagePolicyHandler: RequestHandler< const esClient = coreContext.elasticsearch.client.asInternalUser; const user = appContextService.getSecurityCore().authc.getCurrentUser(request) || undefined; - try { - const res = await packagePolicyService.delete( - soClient, - esClient, - [request.params.packagePolicyId], - { user, force: request.query.force, skipUnassignFromAgentPolicies: request.query.force }, - context, - request - ); - - if ( - res[0] && - res[0].success === false && - res[0].statusCode !== 404 // ignore 404 to allow that call to be idempotent - ) { - return response.customError({ - statusCode: res[0].statusCode ?? 500, - body: res[0].body, - }); - } - - return response.ok({ - body: { id: request.params.packagePolicyId }, + const res = await packagePolicyService.delete( + soClient, + esClient, + [request.params.packagePolicyId], + { user, force: request.query.force, skipUnassignFromAgentPolicies: request.query.force }, + context, + request + ); + + if ( + res[0] && + res[0].success === false && + res[0].statusCode !== 404 // ignore 404 to allow that call to be idempotent + ) { + return response.customError({ + statusCode: res[0].statusCode ?? 500, + body: res[0].body, }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); } + + return response.ok({ + body: { id: request.params.packagePolicyId }, + }); }; export const upgradePackagePolicyHandler: RequestHandler< @@ -521,28 +498,24 @@ export const upgradePackagePolicyHandler: RequestHandler< const soClient = coreContext.savedObjects.client; const esClient = coreContext.elasticsearch.client.asInternalUser; const user = appContextService.getSecurityCore().authc.getCurrentUser(request) || undefined; - try { - const body: UpgradePackagePolicyResponse = await packagePolicyService.upgrade( - soClient, - esClient, - request.body.packagePolicyIds, - { user } - ); - - const firstFatalError = body.find((item) => item.statusCode && item.statusCode !== 200); - - if (firstFatalError) { - return response.customError({ - statusCode: firstFatalError.statusCode!, - body: { message: firstFatalError.body!.message }, - }); - } - return response.ok({ - body, + const body: UpgradePackagePolicyResponse = await packagePolicyService.upgrade( + soClient, + esClient, + request.body.packagePolicyIds, + { user } + ); + + const firstFatalError = body.find((item) => item.statusCode && item.statusCode !== 200); + + if (firstFatalError) { + return response.customError({ + statusCode: firstFatalError.statusCode!, + body: { message: firstFatalError.body!.message }, }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); } + return response.ok({ + body, + }); }; export const dryRunUpgradePackagePolicyHandler: RequestHandler< @@ -551,28 +524,25 @@ export const dryRunUpgradePackagePolicyHandler: RequestHandler< TypeOf > = async (context, request, response) => { const soClient = (await context.core).savedObjects.client; - try { - const body: UpgradePackagePolicyDryRunResponse = []; - const { packagePolicyIds } = request.body; - for (const id of packagePolicyIds) { - const result = await packagePolicyService.getUpgradeDryRunDiff(soClient, id); - body.push(result); - } + const body: UpgradePackagePolicyDryRunResponse = []; + const { packagePolicyIds } = request.body; - const firstFatalError = body.find((item) => item.statusCode && item.statusCode !== 200); + for (const id of packagePolicyIds) { + const result = await packagePolicyService.getUpgradeDryRunDiff(soClient, id); + body.push(result); + } - if (firstFatalError) { - return response.customError({ - statusCode: firstFatalError.statusCode!, - body: { message: firstFatalError.body!.message }, - }); - } + const firstFatalError = body.find((item) => item.statusCode && item.statusCode !== 200); - return response.ok({ - body, + if (firstFatalError) { + return response.customError({ + statusCode: firstFatalError.statusCode!, + body: { message: firstFatalError.body!.message }, }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); } + + return response.ok({ + body, + }); }; diff --git a/x-pack/plugins/fleet/server/routes/preconfiguration/handler.ts b/x-pack/plugins/fleet/server/routes/preconfiguration/handler.ts index 7b2956b7ec460..ad4f3ddf17721 100644 --- a/x-pack/plugins/fleet/server/routes/preconfiguration/handler.ts +++ b/x-pack/plugins/fleet/server/routes/preconfiguration/handler.ts @@ -9,7 +9,6 @@ import type { TypeOf } from '@kbn/config-schema'; import type { FleetRequestHandler } from '../../types'; import type { PostResetOnePreconfiguredAgentPoliciesSchema } from '../../types'; -import { defaultFleetErrorHandler } from '../../errors'; import { resetPreconfiguredAgentPolicies } from '../../services/preconfiguration/reset_agent_policies'; export const resetOnePreconfigurationHandler: FleetRequestHandler< @@ -21,12 +20,8 @@ export const resetOnePreconfigurationHandler: FleetRequestHandler< const soClient = coreContext.savedObjects.client; const esClient = coreContext.elasticsearch.client.asInternalUser; - try { - await resetPreconfiguredAgentPolicies(soClient, esClient, request.params.agentPolicyId); - return response.ok({}); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } + await resetPreconfiguredAgentPolicies(soClient, esClient, request.params.agentPolicyId); + return response.ok({}); }; export const resetPreconfigurationHandler: FleetRequestHandler< @@ -38,10 +33,6 @@ export const resetPreconfigurationHandler: FleetRequestHandler< const soClient = coreContext.savedObjects.client; const esClient = coreContext.elasticsearch.client.asInternalUser; - try { - await resetPreconfiguredAgentPolicies(soClient, esClient); - return response.ok({}); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } + await resetPreconfiguredAgentPolicies(soClient, esClient); + return response.ok({}); }; diff --git a/x-pack/plugins/fleet/server/routes/settings/enrollment_settings_handler.ts b/x-pack/plugins/fleet/server/routes/settings/enrollment_settings_handler.ts index 55a3d383433cc..cf6fa4a185efd 100644 --- a/x-pack/plugins/fleet/server/routes/settings/enrollment_settings_handler.ts +++ b/x-pack/plugins/fleet/server/routes/settings/enrollment_settings_handler.ts @@ -17,7 +17,6 @@ import type { EnrollmentSettingsFleetServerPolicy, } from '../../../common/types'; import type { FleetRequestHandler, GetEnrollmentSettingsRequestSchema } from '../../types'; -import { defaultFleetErrorHandler } from '../../errors'; import { agentPolicyService, appContextService, downloadSourceService } from '../../services'; import { getFleetServerHostsForAgentPolicy } from '../../services/fleet_server_host'; import { getFleetProxy } from '../../services/fleet_proxies'; @@ -38,100 +37,96 @@ export const getEnrollmentSettingsHandler: FleetRequestHandler< const coreContext = await context.core; const esClient = coreContext.elasticsearch.client.asInternalUser; const soClient = coreContext.savedObjects.client; + // Get all possible fleet server or scoped normal agent policies + const { fleetServerPolicies, scopedAgentPolicy: scopedAgentPolicyResponse } = + await getFleetServerOrAgentPolicies(soClient, agentPolicyId); + const scopedAgentPolicy = scopedAgentPolicyResponse || { + id: undefined, + name: undefined, + fleet_server_host_id: undefined, + download_source_id: undefined, + data_output_id: undefined, + }; + + // Check if there is any active fleet server enrolled into the fleet server policies policies + if (fleetServerPolicies) { + settingsResponse.fleet_server.policies = fleetServerPolicies; + settingsResponse.fleet_server.has_active = await hasFleetServersForPolicies( + esClient, + appContextService.getInternalUserSOClientWithoutSpaceExtension(), + fleetServerPolicies, + true + ); + } + + // Get download source + // ignore errors if the download source is not found try { - // Get all possible fleet server or scoped normal agent policies - const { fleetServerPolicies, scopedAgentPolicy: scopedAgentPolicyResponse } = - await getFleetServerOrAgentPolicies(soClient, agentPolicyId); - const scopedAgentPolicy = scopedAgentPolicyResponse || { - id: undefined, - name: undefined, - fleet_server_host_id: undefined, - download_source_id: undefined, - data_output_id: undefined, - }; - - // Check if there is any active fleet server enrolled into the fleet server policies policies - if (fleetServerPolicies) { - settingsResponse.fleet_server.policies = fleetServerPolicies; - settingsResponse.fleet_server.has_active = await hasFleetServersForPolicies( - esClient, - appContextService.getInternalUserSOClientWithoutSpaceExtension(), - fleetServerPolicies, - true - ); - } + settingsResponse.download_source = await getDownloadSource( + soClient, + scopedAgentPolicy.download_source_id ?? undefined + ); + } catch (e) { + settingsResponse.download_source = undefined; + } - // Get download source - // ignore errors if the download source is not found - try { - settingsResponse.download_source = await getDownloadSource( + // Get download source proxy + // ignore errors if the download source proxy is not found + try { + if (settingsResponse.download_source?.proxy_id) { + settingsResponse.download_source_proxy = await getFleetProxy( soClient, - scopedAgentPolicy.download_source_id ?? undefined + settingsResponse.download_source.proxy_id ); - } catch (e) { - settingsResponse.download_source = undefined; } + } catch (e) { + settingsResponse.download_source_proxy = undefined; + } - // Get download source proxy - // ignore errors if the download source proxy is not found - try { - if (settingsResponse.download_source?.proxy_id) { - settingsResponse.download_source_proxy = await getFleetProxy( - soClient, - settingsResponse.download_source.proxy_id - ); - } - } catch (e) { - settingsResponse.download_source_proxy = undefined; - } + // Get associated fleet server host, or default one if it doesn't exist + // `getFleetServerHostsForAgentPolicy` errors if there is no default, so catch it + try { + settingsResponse.fleet_server.host = await getFleetServerHostsForAgentPolicy( + soClient, + scopedAgentPolicy + ); + } catch (e) { + settingsResponse.fleet_server.host = undefined; + } - // Get associated fleet server host, or default one if it doesn't exist - // `getFleetServerHostsForAgentPolicy` errors if there is no default, so catch it - try { - settingsResponse.fleet_server.host = await getFleetServerHostsForAgentPolicy( + // If a fleet server host was found, get associated fleet server host proxy if any + // ignore errors if the proxy is not found + try { + if (settingsResponse.fleet_server.host?.proxy_id) { + settingsResponse.fleet_server.host_proxy = await getFleetProxy( soClient, - scopedAgentPolicy + settingsResponse.fleet_server.host.proxy_id ); - } catch (e) { - settingsResponse.fleet_server.host = undefined; - } - - // If a fleet server host was found, get associated fleet server host proxy if any - // ignore errors if the proxy is not found - try { - if (settingsResponse.fleet_server.host?.proxy_id) { - settingsResponse.fleet_server.host_proxy = await getFleetProxy( - soClient, - settingsResponse.fleet_server.host.proxy_id - ); - } - } catch (e) { - settingsResponse.fleet_server.host_proxy = undefined; } + } catch (e) { + settingsResponse.fleet_server.host_proxy = undefined; + } - // Get associated output and proxy (if any) to use for Fleet Server enrollment - try { - if (settingsResponse.fleet_server.policies.length > 0) { - const dataOutput = await getDataOutputForAgentPolicy(soClient, scopedAgentPolicy); - if (dataOutput.type === 'elasticsearch' && dataOutput.hosts?.[0]) { - settingsResponse.fleet_server.es_output = dataOutput; - if (dataOutput.proxy_id) { - settingsResponse.fleet_server.es_output_proxy = await getFleetProxy( - soClient, - dataOutput.proxy_id - ); - } + // Get associated output and proxy (if any) to use for Fleet Server enrollment + try { + if (settingsResponse.fleet_server.policies.length > 0) { + const dataOutput = await getDataOutputForAgentPolicy(soClient, scopedAgentPolicy); + if (dataOutput.type === 'elasticsearch' && dataOutput.hosts?.[0]) { + settingsResponse.fleet_server.es_output = dataOutput; + if (dataOutput.proxy_id) { + settingsResponse.fleet_server.es_output_proxy = await getFleetProxy( + soClient, + dataOutput.proxy_id + ); } } - } catch (e) { - settingsResponse.fleet_server.es_output = undefined; - settingsResponse.fleet_server.es_output_proxy = undefined; } - - return response.ok({ body: settingsResponse }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); + } catch (e) { + settingsResponse.fleet_server.es_output = undefined; + settingsResponse.fleet_server.es_output_proxy = undefined; } + + return response.ok({ body: settingsResponse }); }; export const getFleetServerOrAgentPolicies = async ( diff --git a/x-pack/plugins/fleet/server/routes/settings/settings_handler.ts b/x-pack/plugins/fleet/server/routes/settings/settings_handler.ts index 4123c2ea37e68..96bc4e0cd789e 100644 --- a/x-pack/plugins/fleet/server/routes/settings/settings_handler.ts +++ b/x-pack/plugins/fleet/server/routes/settings/settings_handler.ts @@ -12,21 +12,16 @@ import type { PutSettingsRequestSchema, PutSpaceSettingsRequestSchema, } from '../../types'; -import { defaultFleetErrorHandler } from '../../errors'; import { settingsService, agentPolicyService, appContextService } from '../../services'; import { getSpaceSettings, saveSpaceSettings } from '../../services/spaces/space_settings'; export const getSpaceSettingsHandler: FleetRequestHandler = async (context, request, response) => { - try { - const soClient = (await context.fleet).internalSoClient; - const settings = await getSpaceSettings(soClient.getCurrentNamespace()); - const body = { - item: settings, - }; - return response.ok({ body }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } + const soClient = (await context.fleet).internalSoClient; + const settings = await getSpaceSettings(soClient.getCurrentNamespace()); + const body = { + item: settings, + }; + return response.ok({ body }); }; export const putSpaceSettingsHandler: FleetRequestHandler< @@ -34,22 +29,18 @@ export const putSpaceSettingsHandler: FleetRequestHandler< undefined, TypeOf > = async (context, request, response) => { - try { - const soClient = (await context.fleet).internalSoClient; - await saveSpaceSettings({ - settings: { - allowed_namespace_prefixes: request.body.allowed_namespace_prefixes, - }, - spaceId: soClient.getCurrentNamespace(), - }); - const settings = await getSpaceSettings(soClient.getCurrentNamespace()); - const body = { - item: settings, - }; - return response.ok({ body }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } + const soClient = (await context.fleet).internalSoClient; + await saveSpaceSettings({ + settings: { + allowed_namespace_prefixes: request.body.allowed_namespace_prefixes, + }, + spaceId: soClient.getCurrentNamespace(), + }); + const settings = await getSpaceSettings(soClient.getCurrentNamespace()); + const body = { + item: settings, + }; + return response.ok({ body }); }; export const getSettingsHandler: FleetRequestHandler = async (context, request, response) => { @@ -68,7 +59,7 @@ export const getSettingsHandler: FleetRequestHandler = async (context, request, }); } - return defaultFleetErrorHandler({ error, response }); + throw error; } }; @@ -95,6 +86,6 @@ export const putSettingsHandler: FleetRequestHandler< }); } - return defaultFleetErrorHandler({ error, response }); + throw error; } }; diff --git a/x-pack/plugins/fleet/server/routes/setup/handlers.test.ts b/x-pack/plugins/fleet/server/routes/setup/handlers.test.ts index 6923d00c18222..307829eda020c 100644 --- a/x-pack/plugins/fleet/server/routes/setup/handlers.test.ts +++ b/x-pack/plugins/fleet/server/routes/setup/handlers.test.ts @@ -23,9 +23,13 @@ import { setupFleet } from '../../services/setup'; import type { FleetRequestHandlerContext } from '../../types'; import { hasFleetServers } from '../../services/fleet_server'; import { createFleetAuthzMock } from '../../../common/mocks'; +import { withDefaultErrorHandler } from '../../services/security/fleet_router'; import { fleetSetupHandler, getFleetStatusHandler } from './handlers'; +const fleetSetupWithErrorHandler = withDefaultErrorHandler(fleetSetupHandler); +const getFleetStatusWithErrorHandler = withDefaultErrorHandler(getFleetStatusHandler); + jest.mock('../../services/setup', () => { return { ...jest.requireActual('../../services/setup'), @@ -86,7 +90,11 @@ describe('FleetSetupHandler', () => { nonFatalErrors: [], }) ); - await fleetSetupHandler(coreMock.createCustomRequestHandlerContext(context), request, response); + await fleetSetupWithErrorHandler( + coreMock.createCustomRequestHandlerContext(context), + request, + response + ); const expectedBody: PostFleetSetupResponse = { isInitialized: true, @@ -98,7 +106,11 @@ describe('FleetSetupHandler', () => { it('POST /setup fails w/500 on custom error', async () => { mockSetupFleet.mockImplementation(() => Promise.reject(new Error('SO method mocked to throw'))); - await fleetSetupHandler(coreMock.createCustomRequestHandlerContext(context), request, response); + await fleetSetupWithErrorHandler( + coreMock.createCustomRequestHandlerContext(context), + request, + response + ); expect(response.customError).toHaveBeenCalledTimes(1); expect(response.customError).toHaveBeenCalledWith({ @@ -114,7 +126,11 @@ describe('FleetSetupHandler', () => { Promise.reject(new RegistryError('Registry method mocked to throw')) ); - await fleetSetupHandler(coreMock.createCustomRequestHandlerContext(context), request, response); + await fleetSetupWithErrorHandler( + coreMock.createCustomRequestHandlerContext(context), + request, + response + ); expect(response.customError).toHaveBeenCalledTimes(1); expect(response.customError).toHaveBeenCalledWith({ statusCode: 502, @@ -172,7 +188,7 @@ describe('FleetStatusHandler', () => { .mocked(appContextService.getSecurity().authc.apiKeys.areAPIKeysEnabled) .mockResolvedValue(true); jest.mocked(hasFleetServers).mockResolvedValue(true); - await getFleetStatusHandler( + await getFleetStatusWithErrorHandler( coreMock.createCustomRequestHandlerContext(context), request, response @@ -194,7 +210,7 @@ describe('FleetStatusHandler', () => { .mocked(appContextService.getSecurity().authc.apiKeys.areAPIKeysEnabled) .mockResolvedValue(false); jest.mocked(hasFleetServers).mockResolvedValue(false); - await getFleetStatusHandler( + await getFleetStatusWithErrorHandler( coreMock.createCustomRequestHandlerContext(context), request, response @@ -223,7 +239,7 @@ describe('FleetStatusHandler', () => { jest .mocked(appContextService.getSecurity().authc.apiKeys.areAPIKeysEnabled) .mockResolvedValue(true); - await getFleetStatusHandler( + await getFleetStatusWithErrorHandler( coreMock.createCustomRequestHandlerContext(context), request, response diff --git a/x-pack/plugins/fleet/server/routes/setup/handlers.ts b/x-pack/plugins/fleet/server/routes/setup/handlers.ts index 05ee55320d445..afe53ba0ed58c 100644 --- a/x-pack/plugins/fleet/server/routes/setup/handlers.ts +++ b/x-pack/plugins/fleet/server/routes/setup/handlers.ts @@ -9,7 +9,6 @@ import { appContextService } from '../../services'; import type { GetFleetStatusResponse, PostFleetSetupResponse } from '../../../common/types'; import { formatNonFatalErrors, setupFleet } from '../../services/setup'; import { hasFleetServers } from '../../services/fleet_server'; -import { defaultFleetErrorHandler } from '../../errors'; import type { FleetRequestHandler } from '../../types'; import { getGpgKeyIdOrUndefined } from '../../services/epm/packages/package_verification'; import { isSecretStorageEnabled } from '../../services/secrets'; @@ -21,69 +20,59 @@ export const getFleetStatusHandler: FleetRequestHandler = async (context, reques const esClient = coreContext.elasticsearch.client.asInternalUser; const soClient = appContextService.getInternalUserSOClientWithoutSpaceExtension(); - try { - const isApiKeysEnabled = await appContextService - .getSecurity() - .authc.apiKeys.areAPIKeysEnabled(); + const isApiKeysEnabled = await appContextService.getSecurity().authc.apiKeys.areAPIKeysEnabled(); - const [hasFleetServersRes, useSecretsStorage, isSpaceAwarenessEnabledRes] = await Promise.all([ - hasFleetServers(esClient, soClient), - isSecretStorageEnabled(esClient, soClient), - isSpaceAwarenessEnabled(), - ]); + const [hasFleetServersRes, useSecretsStorage, isSpaceAwarenessEnabledRes] = await Promise.all([ + hasFleetServers(esClient, soClient), + isSecretStorageEnabled(esClient, soClient), + isSpaceAwarenessEnabled(), + ]); - const isFleetServerMissing = !hasFleetServersRes; + const isFleetServerMissing = !hasFleetServersRes; - const isFleetServerStandalone = - appContextService.getConfig()?.internal?.fleetServerStandalone ?? false; - const missingRequirements: GetFleetStatusResponse['missing_requirements'] = []; - const missingOptionalFeatures: GetFleetStatusResponse['missing_optional_features'] = []; + const isFleetServerStandalone = + appContextService.getConfig()?.internal?.fleetServerStandalone ?? false; + const missingRequirements: GetFleetStatusResponse['missing_requirements'] = []; + const missingOptionalFeatures: GetFleetStatusResponse['missing_optional_features'] = []; - if (!isApiKeysEnabled) { - missingRequirements.push('api_keys'); - } - - if (!isFleetServerStandalone && isFleetServerMissing) { - missingRequirements.push('fleet_server'); - } + if (!isApiKeysEnabled) { + missingRequirements.push('api_keys'); + } - if (!appContextService.getEncryptedSavedObjectsSetup()?.canEncrypt) { - missingOptionalFeatures.push('encrypted_saved_object_encryption_key_required'); - } + if (!isFleetServerStandalone && isFleetServerMissing) { + missingRequirements.push('fleet_server'); + } - const body: GetFleetStatusResponse = { - isReady: missingRequirements.length === 0, - missing_requirements: missingRequirements, - missing_optional_features: missingOptionalFeatures, - is_secrets_storage_enabled: useSecretsStorage, - is_space_awareness_enabled: isSpaceAwarenessEnabledRes, - }; + if (!appContextService.getEncryptedSavedObjectsSetup()?.canEncrypt) { + missingOptionalFeatures.push('encrypted_saved_object_encryption_key_required'); + } - const packageVerificationKeyId = await getGpgKeyIdOrUndefined(); + const body: GetFleetStatusResponse = { + isReady: missingRequirements.length === 0, + missing_requirements: missingRequirements, + missing_optional_features: missingOptionalFeatures, + is_secrets_storage_enabled: useSecretsStorage, + is_space_awareness_enabled: isSpaceAwarenessEnabledRes, + }; - if (packageVerificationKeyId) { - body.package_verification_key_id = packageVerificationKeyId; - } + const packageVerificationKeyId = await getGpgKeyIdOrUndefined(); - return response.ok({ - body, - }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); + if (packageVerificationKeyId) { + body.package_verification_key_id = packageVerificationKeyId; } + + return response.ok({ + body, + }); }; export const fleetSetupHandler: FleetRequestHandler = async (context, request, response) => { - try { - const soClient = (await context.fleet).internalSoClient; - const esClient = (await context.core).elasticsearch.client.asInternalUser; - const setupStatus = await setupFleet(soClient, esClient); - const body: PostFleetSetupResponse = { - ...setupStatus, - nonFatalErrors: formatNonFatalErrors(setupStatus.nonFatalErrors), - }; - return response.ok({ body }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } + const soClient = (await context.fleet).internalSoClient; + const esClient = (await context.core).elasticsearch.client.asInternalUser; + const setupStatus = await setupFleet(soClient, esClient); + const body: PostFleetSetupResponse = { + ...setupStatus, + nonFatalErrors: formatNonFatalErrors(setupStatus.nonFatalErrors), + }; + return response.ok({ body }); }; diff --git a/x-pack/plugins/fleet/server/routes/standalone_agent_api_key/handler.ts b/x-pack/plugins/fleet/server/routes/standalone_agent_api_key/handler.ts index 60e0c40c03f40..787cafcb9ac0d 100644 --- a/x-pack/plugins/fleet/server/routes/standalone_agent_api_key/handler.ts +++ b/x-pack/plugins/fleet/server/routes/standalone_agent_api_key/handler.ts @@ -13,34 +13,30 @@ import { INDEX_PRIVILEGES, canCreateStandaloneAgentApiKey, } from '../../services/api_keys/create_standalone_agent_api_key'; -import { FleetUnauthorizedError, defaultFleetErrorHandler } from '../../errors'; +import { FleetUnauthorizedError } from '../../errors'; export const createStandaloneAgentApiKeyHandler: FleetRequestHandler< undefined, undefined, TypeOf > = async (context, request, response) => { - try { - const coreContext = await context.core; - const esClient = coreContext.elasticsearch.client.asCurrentUser; - const canCreate = await canCreateStandaloneAgentApiKey(esClient); + const coreContext = await context.core; + const esClient = coreContext.elasticsearch.client.asCurrentUser; + const canCreate = await canCreateStandaloneAgentApiKey(esClient); - if (!canCreate) { - throw new FleetUnauthorizedError( - `Missing permissions to create standalone API key, You need ${INDEX_PRIVILEGES.privileges.join( - ', ' - )} for indices ${INDEX_PRIVILEGES.names.join(', ')}` - ); - } + if (!canCreate) { + throw new FleetUnauthorizedError( + `Missing permissions to create standalone API key, You need ${INDEX_PRIVILEGES.privileges.join( + ', ' + )} for indices ${INDEX_PRIVILEGES.names.join(', ')}` + ); + } - const key = await createStandaloneAgentApiKey(esClient, request.body.name); + const key = await createStandaloneAgentApiKey(esClient, request.body.name); - return response.ok({ - body: { - item: key, - }, - }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } + return response.ok({ + body: { + item: key, + }, + }); }; diff --git a/x-pack/plugins/fleet/server/routes/uninstall_token/handlers.test.ts b/x-pack/plugins/fleet/server/routes/uninstall_token/handlers.test.ts index b51f19291c58a..09d132e320b9b 100644 --- a/x-pack/plugins/fleet/server/routes/uninstall_token/handlers.test.ts +++ b/x-pack/plugins/fleet/server/routes/uninstall_token/handlers.test.ts @@ -10,7 +10,10 @@ import type { KibanaRequest, VersionedRouter } from '@kbn/core-http-server'; import { httpServerMock, coreMock, loggingSystemMock } from '@kbn/core/server/mocks'; import type { RequestHandler } from '@kbn/core/server'; -import { makeRouterWithFleetAuthz } from '../../services/security/fleet_router'; +import { + makeRouterWithFleetAuthz, + withDefaultErrorHandler, +} from '../../services/security/fleet_router'; import type { FleetAuthzRouter } from '../../services/security/types'; import type { @@ -39,6 +42,11 @@ import { registerRoutes } from '.'; import { getUninstallTokenHandler, getUninstallTokensMetadataHandler } from './handlers'; +const getUninstallTokenHandlerWithErrorHandler = withDefaultErrorHandler(getUninstallTokenHandler); +const getUninstallTokensMetadataHandlerWithErrorHandler = withDefaultErrorHandler( + getUninstallTokensMetadataHandler +); + jest.mock('../../services/agent_policy'); describe('uninstall token handlers', () => { @@ -111,7 +119,7 @@ describe('uninstall token handlers', () => { it('should return uninstall tokens for all policies', async () => { getTokenMetadataMock.mockResolvedValue(uninstallTokensResponseFixture); - await getUninstallTokensMetadataHandler(context, request, response); + await getUninstallTokensMetadataHandlerWithErrorHandler(context, request, response); expect(response.ok).toHaveBeenCalledWith({ body: uninstallTokensResponseFixture, @@ -121,7 +129,7 @@ describe('uninstall token handlers', () => { it('should return internal error when uninstallTokenService throws error', async () => { getTokenMetadataMock.mockRejectedValue(Error('something happened')); - await getUninstallTokensMetadataHandler(context, request, response); + await getUninstallTokensMetadataHandlerWithErrorHandler(context, request, response); expect(response.customError).toHaveBeenCalledWith({ statusCode: 500, @@ -157,7 +165,7 @@ describe('uninstall token handlers', () => { it('should return requested uninstall token', async () => { getTokenMock.mockResolvedValue(uninstallTokenFixture); - await getUninstallTokenHandler(context, request, response); + await getUninstallTokenHandlerWithErrorHandler(context, request, response); expect(getTokenMock).toHaveBeenCalledWith(uninstallTokenFixture.id); expect(response.ok).toHaveBeenCalledWith({ @@ -170,7 +178,7 @@ describe('uninstall token handlers', () => { it('should return internal error when uninstallTokenService throws error', async () => { getTokenMock.mockRejectedValue(Error('something happened')); - await getUninstallTokenHandler(context, request, response); + await getUninstallTokenHandlerWithErrorHandler(context, request, response); expect(response.customError).toHaveBeenCalledWith({ statusCode: 500, diff --git a/x-pack/plugins/fleet/server/routes/uninstall_token/handlers.ts b/x-pack/plugins/fleet/server/routes/uninstall_token/handlers.ts index 24d85b8d14250..6c12a1f0c70c9 100644 --- a/x-pack/plugins/fleet/server/routes/uninstall_token/handlers.ts +++ b/x-pack/plugins/fleet/server/routes/uninstall_token/handlers.ts @@ -13,7 +13,6 @@ import type { GetUninstallTokensMetadataRequestSchema, GetUninstallTokenRequestSchema, } from '../../types/rest_spec/uninstall_token'; -import { defaultFleetErrorHandler } from '../../errors'; import type { GetUninstallTokenResponse } from '../../../common/types/rest_spec/uninstall_token'; import { LEGACY_AGENT_POLICY_SAVED_OBJECT_TYPE, SO_SEARCH_LIMIT } from '../../constants'; @@ -21,51 +20,47 @@ export const getUninstallTokensMetadataHandler: FleetRequestHandler< unknown, TypeOf > = async (context, request, response) => { - try { - const [fleetContext, coreContext] = await Promise.all([context.fleet, context.core]); - const uninstallTokenService = fleetContext.uninstallTokenService.asCurrentUser; - - const { page = 1, perPage = 20, policyId, search } = request.query; - - if (policyId && search) { - return response.badRequest({ - body: { - message: 'Query parameters `policyId` and `search` cannot be used at the same time.', - }, - }); - } - - const soClient = coreContext.savedObjects.client; - - const { items: managedPolicies } = await agentPolicyService.list(soClient, { - fields: ['id'], - perPage: SO_SEARCH_LIMIT, - kuery: `${LEGACY_AGENT_POLICY_SAVED_OBJECT_TYPE}.is_managed:true`, + const [fleetContext, coreContext] = await Promise.all([context.fleet, context.core]); + const uninstallTokenService = fleetContext.uninstallTokenService.asCurrentUser; + + const { page = 1, perPage = 20, policyId, search } = request.query; + + if (policyId && search) { + return response.badRequest({ + body: { + message: 'Query parameters `policyId` and `search` cannot be used at the same time.', + }, }); + } + + const soClient = coreContext.savedObjects.client; + + const { items: managedPolicies } = await agentPolicyService.list(soClient, { + fields: ['id'], + perPage: SO_SEARCH_LIMIT, + kuery: `${LEGACY_AGENT_POLICY_SAVED_OBJECT_TYPE}.is_managed:true`, + }); - const managedPolicyIds = managedPolicies.map((policy) => policy.id); - - let policyIdSearchTerm: string | undefined; - let policyNameSearchTerm: string | undefined; - if (search) { - policyIdSearchTerm = search.trim(); - policyNameSearchTerm = search.trim(); - } else if (policyId) { - policyIdSearchTerm = policyId.trim(); - } - - const body = await uninstallTokenService.getTokenMetadata( - policyIdSearchTerm, - policyNameSearchTerm, - page, - perPage, - managedPolicyIds.length > 0 ? managedPolicyIds : undefined - ); - - return response.ok({ body }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); + const managedPolicyIds = managedPolicies.map((policy) => policy.id); + + let policyIdSearchTerm: string | undefined; + let policyNameSearchTerm: string | undefined; + if (search) { + policyIdSearchTerm = search.trim(); + policyNameSearchTerm = search.trim(); + } else if (policyId) { + policyIdSearchTerm = policyId.trim(); } + + const body = await uninstallTokenService.getTokenMetadata( + policyIdSearchTerm, + policyNameSearchTerm, + page, + perPage, + managedPolicyIds.length > 0 ? managedPolicyIds : undefined + ); + + return response.ok({ body }); }; export const getUninstallTokenHandler: FleetRequestHandler< @@ -74,23 +69,19 @@ export const getUninstallTokenHandler: FleetRequestHandler< const [fleetContext] = await Promise.all([context.fleet, context.core]); const uninstallTokenService = fleetContext.uninstallTokenService.asCurrentUser; - try { - const { uninstallTokenId } = request.params; + const { uninstallTokenId } = request.params; - const token = await uninstallTokenService.getToken(uninstallTokenId); + const token = await uninstallTokenService.getToken(uninstallTokenId); - if (token === null) { - return response.notFound({ - body: { message: `Uninstall Token not found with id ${uninstallTokenId}` }, - }); - } + if (token === null) { + return response.notFound({ + body: { message: `Uninstall Token not found with id ${uninstallTokenId}` }, + }); + } - const body: GetUninstallTokenResponse = { - item: token, - }; + const body: GetUninstallTokenResponse = { + item: token, + }; - return response.ok({ body }); - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } + return response.ok({ body }); }; diff --git a/x-pack/plugins/fleet/server/services/security/fleet_router.ts b/x-pack/plugins/fleet/server/services/security/fleet_router.ts index 11a4b084d4807..775fe7e4765e5 100644 --- a/x-pack/plugins/fleet/server/services/security/fleet_router.ts +++ b/x-pack/plugins/fleet/server/services/security/fleet_router.ts @@ -19,6 +19,7 @@ import type { VersionedRouteConfig } from '@kbn/core-http-server'; import { PUBLIC_API_ACCESS } from '../../../common/constants'; import type { FleetRequestHandlerContext } from '../..'; import { getRequestStore } from '../request_store'; +import { defaultFleetErrorHandler } from '../../errors'; import type { FleetVersionedRouteConfig } from './types'; @@ -47,6 +48,26 @@ function withDefaultPublicAccess( } } +export function withDefaultErrorHandler< + TContext extends FleetRequestHandlerContext, + R extends RouteMethod +>( + wrappedHandler: RequestHandler +): RequestHandler { + return async function defaultErrorHandlerWrapper(context, request, response) { + try { + return await wrappedHandler(context, request, response); + } catch (error: any) { + return defaultFleetErrorHandler({ + error, + response, + context, + request, + }); + } + }; +} + export function makeRouterWithFleetAuthz( router: IRouter, logger: Logger @@ -115,14 +136,15 @@ export function makeRouterWithFleetAuthz + handler: withDefaultErrorHandler((handlerContext, handlerRequest, handlerResponse) => routerAuthzWrapper({ context: handlerContext, request: handlerRequest, response: handlerResponse, handler, hasRequiredAuthz, - }), + }) + ), }); };