export function initStatusApi(server: Server) { server.route({ method: 'GET', path: `${ROOT}/server`, options: { validate: { query: Joi.object().keys({ _debug: Joi.bool() }) } }, handler: req => { const setup = setupRequest(req); return getServerStatus({ setup }).catch(defaultErrorHandler); } }); server.route({ method: 'GET', path: `${ROOT}/agent`, options: { validate: { query: Joi.object().keys({ _debug: Joi.bool() }) } }, handler: req => { const setup = setupRequest(req); return getAgentStatus({ setup }).catch(defaultErrorHandler); } }); }
export const createBeatUpdateRoute = (libs: CMServerLibs) => ({ method: 'PUT', path: '/api/beats/agent/{beatId}', licenseRequired: true, config: { auth: { mode: 'optional', }, validate: { headers: Joi.object({ 'kbn-beats-access-token': Joi.string(), }).options({ allowUnknown: true, }), params: Joi.object({ beatId: Joi.string(), }), payload: Joi.object({ active: Joi.bool(), ephemeral_id: Joi.string(), host_name: Joi.string(), local_configuration_yml: Joi.string(), metadata: Joi.object(), name: Joi.string(), type: Joi.string(), version: Joi.string(), }), }, }, handler: async (request: FrameworkRequest, reply: any) => { const { beatId } = request.params; const accessToken = request.headers['kbn-beats-access-token']; const remoteAddress = request.info.remoteAddress; const userOrToken = accessToken || request.user; if (request.user.kind === 'unauthenticated' && request.payload.active !== undefined) { return reply({ message: 'access-token is not a valid auth type to change beat status' }).code( 401 ); } try { const status = await libs.beats.update(userOrToken, beatId, { ...request.payload, host_ip: remoteAddress, }); switch (status) { case 'beat-not-found': return reply({ message: 'Beat not found', success: false }).code(404); case 'invalid-access-token': return reply({ message: 'Invalid access token', success: false }).code(401); } reply({ success: true }).code(204); } catch (err) { return reply(wrapEsError(err)); } }, });
export const createTagRemovalsRoute = (libs: CMServerLibs) => ({ method: 'POST', path: '/api/beats/agents_tags/removals', licenseRequired: true, config: { validate: { payload: Joi.object({ removals: Joi.array().items( Joi.object({ beatId: Joi.string().required(), tag: Joi.string().required(), }) ), }).required(), }, }, handler: async (request: FrameworkRequest, reply: any) => { const { removals } = request.payload; try { const response = await libs.beats.removeTagsFromBeats(request.user, removals); reply(response); } catch (err) { // TODO move this to kibana route thing in adapter return reply(wrapEsError(err)); } }, });
export const createSetTagRoute = (libs: CMServerLibs) => ({ method: 'PUT', path: '/api/beats/tag/{tagId}', licenseRequired: REQUIRED_LICENSES, requiredRoles: ['beats_admin'], config: { validate: { params: Joi.object({ tagId: Joi.string(), }), payload: Joi.object({ color: Joi.string(), name: Joi.string(), }), }, }, handler: async (request: FrameworkRequest) => { const defaultConfig = { id: request.params.tagId, name: request.params.tagId, color: '#DD0A73', hasConfigurationBlocksTypes: [], }; const config = { ...defaultConfig, ...get(request, 'payload', {}) }; try { const id = await libs.tags.upsertTag(request.user, config); return { success: true, id }; } catch (err) { // TODO move this to kibana route thing in adapter return wrapEsError(err); } }, });
export const registerCapabilitiesRoute = ( server: Server, defaultCapabilities: Capabilities, modifiers: CapabilitiesModifier[] ) => { server.route({ path: '/api/capabilities', method: 'POST', options: { validate: { payload: Joi.object({ capabilities: Joi.object().required(), }).required(), }, }, async handler(request) { const { capabilities } = request.payload as { capabilities: Capabilities }; return { capabilities: await resolveCapabilities( request, modifiers, defaultCapabilities, capabilities ), }; }, }); };
export const createListTagsRoute = (libs: CMServerLibs) => ({ method: 'GET', path: '/api/beats/tags', validate: { headers: Joi.object({ 'kbn-beats-enrollment-token': Joi.string().required(), }).options({ allowUnknown: true, }), query: Joi.object({ ESQuery: Joi.string(), }), }, licenseRequired: true, handler: async (request: any, reply: any) => { let tags: BeatTag[]; try { tags = await libs.tags.getAll( request.user // request.query ? JSON.parse(request.query.ESQuery) : undefined ); } catch (err) { return reply(wrapEsError(err)); } reply(tags); }, });
export const createSetTagRoute = (libs: CMServerLibs) => ({ method: 'PUT', path: '/api/beats/tag/{tagId}', licenseRequired: REQUIRED_LICENSES, requiredRoles: ['beats_admin'], config: { validate: { params: Joi.object({ tagId: Joi.string(), }), payload: Joi.object({ color: Joi.string(), name: Joi.string(), }), }, }, handler: async (request: FrameworkRequest): Promise<ReturnTypeUpsert<BeatTag>> => { const defaultConfig = { id: request.params.tagId, name: request.params.tagId, color: '#DD0A73', hasConfigurationBlocksTypes: [], }; const config = { ...defaultConfig, ...get(request, 'payload', {}) }; const id = await libs.tags.upsertTag(request.user, config); const tag = await libs.tags.getWithIds(request.user, [id]); // TODO the action needs to be surfaced return { success: true, item: tag[0], action: 'created' }; }, });
export const createListTagsRoute = (libs: CMServerLibs) => ({ method: 'GET', path: '/api/beats/tags', requiredRoles: ['beats_admin'], licenseRequired: REQUIRED_LICENSES, validate: { headers: Joi.object({ 'kbn-beats-enrollment-token': Joi.string().required(), }).options({ allowUnknown: true, }), query: Joi.object({ ESQuery: Joi.string(), }), }, handler: async (request: FrameworkRequest): Promise<ReturnTypeList<BeatTag>> => { let tags: BeatTag[]; tags = await libs.tags.getAll( request.user, request.query && request.query.ESQuery ? JSON.parse(request.query.ESQuery) : undefined ); return { list: tags, success: true, page: -1, total: -1 }; }, });
export const createTagAssignmentsRoute = (libs: CMServerLibs) => ({ method: 'POST', path: '/api/beats/agents_tags/assignments', licenseRequired: REQUIRED_LICENSES, requiredRoles: ['beats_admin'], config: { validate: { payload: Joi.object({ assignments: Joi.array().items( Joi.object({ beatId: Joi.string().required(), tag: Joi.string().required(), }) ), }).required(), }, }, handler: async (request: FrameworkRequest) => { const { assignments }: { assignments: BeatsTagAssignment[] } = request.payload; try { const response = await libs.beats.assignTagsToBeats(request.user, assignments); return response; } catch (err) { // TODO move this to kibana route thing in adapter return wrapEsError(err); } }, });
export const createResolveImportErrorsRoute = (prereqs: Prerequisites, server: Hapi.Server) => ({ path: '/api/saved_objects/_resolve_import_errors', method: 'POST', config: { pre: [prereqs.getSavedObjectsClient], payload: { maxBytes: server.config().get('savedObjects.maxImportPayloadBytes'), output: 'stream', allow: 'multipart/form-data', }, validate: { payload: Joi.object({ file: Joi.object().required(), overwrites: Joi.array() .items( Joi.object({ type: Joi.string().required(), id: Joi.string().required(), }) ) .default([]), replaceReferences: Joi.array() .items( Joi.object({ type: Joi.string().required(), from: Joi.string().required(), to: Joi.string().required(), }) ) .default([]), skips: Joi.array() .items( Joi.object({ type: Joi.string().required(), id: Joi.string().required(), }) ) .default([]), }).default(), }, }, async handler(request: ImportRequest) { const { savedObjectsClient } = request.pre; const { filename } = request.payload.file.hapi; const fileExtension = extname(filename).toLowerCase(); if (fileExtension !== '.ndjson') { return Boom.badRequest(`Invalid file extension ${fileExtension}`); } return await resolveImportErrors({ savedObjectsClient, readStream: request.payload.file, objectLimit: request.server.config().get('savedObjects.maxImportExportSize'), skips: request.payload.skips, overwrites: request.payload.overwrites, replaceReferences: request.payload.replaceReferences, }); }, });