"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.updatePackageHandler = exports.reauthorizeTransformsHandler = exports.installPackageFromRegistryHandler = exports.installPackageByUploadHandler = exports.getVerificationKeyIdHandler = exports.getStatsHandler = exports.getListHandler = exports.getLimitedListHandler = exports.getInstalledListHandler = exports.getInfoHandler = exports.getFileHandler = exports.getDataStreamsHandler = exports.getCategoriesHandler = exports.getBulkAssetsHandler = exports.deletePackageHandler = exports.createCustomIntegrationHandler = exports.bulkInstallPackagesFromRegistryHandler = void 0; var _path = _interopRequireDefault(require("path")); var _mimeTypes = _interopRequireDefault(require("mime-types")); var _valid = _interopRequireDefault(require("semver/functions/valid")); var _lodash = require("lodash"); var _http_authorization_header = require("../../../common/http_authorization_header"); var _transform_api_keys = require("../../services/api_keys/transform_api_keys"); var _reauthorize = require("../../services/epm/elasticsearch/transform/reauthorize"); var _packages = require("../../services/epm/packages"); var _errors = require("../../errors"); var _services = require("../../services"); var _cache = require("../../services/epm/archive/cache"); var _storage = require("../../services/epm/archive/storage"); var _get = require("../../services/epm/packages/get"); var _update = require("../../services/epm/packages/update"); var _package_verification = require("../../services/epm/packages/package_verification"); var _data_streams = require("../../services/epm/data_streams"); var _check_naming_collision = require("../../services/epm/packages/custom_integrations/validation/check_naming_collision"); /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ const CACHE_CONTROL_10_MINUTES_HEADER = { 'cache-control': 'max-age=600' }; const getCategoriesHandler = async (context, request, response) => { try { const res = await (0, _packages.getCategories)({ ...request.query }); const body = { items: res, response: res }; return response.ok({ body, headers: { ...CACHE_CONTROL_10_MINUTES_HEADER } }); } catch (error) { return (0, _errors.defaultFleetErrorHandler)({ error, response }); } }; exports.getCategoriesHandler = getCategoriesHandler; const getListHandler = async (context, request, response) => { try { const savedObjectsClient = (await context.fleet).internalSoClient; const res = await (0, _packages.getPackages)({ savedObjectsClient, ...request.query }); const flattenedRes = res.map(pkg => soToInstallationInfo(pkg)); const body = { 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 (0, _errors.defaultFleetErrorHandler)({ error, response }); } }; exports.getListHandler = getListHandler; const getInstalledListHandler = async (context, request, response) => { try { const savedObjectsClient = (await context.fleet).internalSoClient; const res = await (0, _packages.getInstalledPackages)({ savedObjectsClient, ...request.query }); const body = { ...res }; return response.ok({ body }); } catch (error) { return (0, _errors.defaultFleetErrorHandler)({ error, response }); } }; exports.getInstalledListHandler = getInstalledListHandler; const getDataStreamsHandler = 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 (0, _data_streams.getDataStreams)({ esClient, ...request.query }); const body = { ...res }; return response.ok({ body }); } catch (error) { return (0, _errors.defaultFleetErrorHandler)({ error, response }); } }; exports.getDataStreamsHandler = getDataStreamsHandler; const getLimitedListHandler = async (context, request, response) => { try { const savedObjectsClient = (await context.fleet).internalSoClient; const res = await (0, _packages.getLimitedPackages)({ savedObjectsClient, prerelease: request.query.prerelease }); const body = { items: res, response: res }; return response.ok({ body }); } catch (error) { return (0, _errors.defaultFleetErrorHandler)({ error, response }); } }; exports.getLimitedListHandler = getLimitedListHandler; const getFileHandler = async (context, request, response) => { try { const { pkgName, pkgVersion, filePath } = request.params; const savedObjectsClient = (await context.fleet).internalSoClient; const installation = await (0, _packages.getInstallation)({ savedObjectsClient, pkgName }); const useLocalFile = pkgVersion === (installation === null || installation === void 0 ? void 0 : installation.version); if (useLocalFile) { const assetPath = `${pkgName}-${pkgVersion}/${filePath}`; const fileBuffer = (0, _cache.getArchiveEntry)(assetPath); // only pull local installation if we don't have it cached const storedAsset = !fileBuffer && (await (0, _storage.getAsset)({ savedObjectsClient, path: assetPath })); // error, if neither is available if (!fileBuffer && !storedAsset) { return response.custom({ body: `installed 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 } = storedAsset ? { contentType: storedAsset.media_type, buffer: storedAsset.data_utf8 ? Buffer.from(storedAsset.data_utf8, 'utf8') : Buffer.from(storedAsset.data_base64, 'base64') } : { contentType: _mimeTypes.default.contentType(_path.default.extname(assetPath)), buffer: fileBuffer }; if (!contentType) { return response.custom({ body: `unknown content type for file: ${filePath}`, statusCode: 400 }); } return response.custom({ body: buffer, statusCode: 200, headers: { ...CACHE_CONTROL_10_MINUTES_HEADER, 'content-type': contentType } }); } else { const registryResponse = await (0, _packages.getFile)(pkgName, pkgVersion, filePath); const headersToProxy = ['content-type']; const proxiedHeaders = headersToProxy.reduce((headers, knownHeader) => { const value = registryResponse.headers.get(knownHeader); if (value !== null) { headers[knownHeader] = value; } return headers; }, {}); return response.custom({ body: registryResponse.body, statusCode: registryResponse.status, headers: { ...CACHE_CONTROL_10_MINUTES_HEADER, ...proxiedHeaders } }); } } catch (error) { return (0, _errors.defaultFleetErrorHandler)({ error, response }); } }; exports.getFileHandler = getFileHandler; const getInfoHandler = async (context, request, response) => { try { const savedObjectsClient = (await context.fleet).internalSoClient; const { limitedToPackages } = await context.fleet; const { pkgName, pkgVersion } = request.params; (0, _services.checkAllowedPackages)([pkgName], limitedToPackages); const { ignoreUnverified = false, full = false, prerelease } = request.query; if (pkgVersion && !(0, _valid.default)(pkgVersion)) { throw new _errors.FleetError('Package version is not a valid semver'); } const res = await (0, _packages.getPackageInfo)({ savedObjectsClient, pkgName, pkgVersion: pkgVersion || '', skipArchive: !full, ignoreUnverified, prerelease }); const flattenedRes = soToInstallationInfo(res); const body = { item: flattenedRes }; return response.ok({ body }); } catch (error) { return (0, _errors.defaultFleetErrorHandler)({ error, response }); } }; exports.getInfoHandler = getInfoHandler; const getBulkAssetsHandler = async (context, request, response) => { try { const { assetIds } = request.body; const savedObjectsClient = (await context.fleet).internalSoClient; const assets = await (0, _packages.getBulkAssets)(savedObjectsClient, assetIds); const body = { items: assets }; return response.ok({ body }); } catch (error) { return (0, _errors.defaultFleetErrorHandler)({ error, response }); } }; exports.getBulkAssetsHandler = getBulkAssetsHandler; const updatePackageHandler = async (context, request, response) => { try { const savedObjectsClient = (await context.fleet).internalSoClient; const { pkgName } = request.params; const res = await (0, _update.updatePackage)({ savedObjectsClient, pkgName, ...request.body }); const body = { item: res }; return response.ok({ body }); } catch (error) { return (0, _errors.defaultFleetErrorHandler)({ error, response }); } }; exports.updatePackageHandler = updatePackageHandler; const getStatsHandler = async (context, request, response) => { try { const { pkgName } = request.params; const savedObjectsClient = (await context.fleet).internalSoClient; const body = { response: await (0, _get.getPackageUsageStats)({ savedObjectsClient, pkgName }) }; return response.ok({ body }); } catch (error) { return (0, _errors.defaultFleetErrorHandler)({ error, response }); } }; exports.getStatsHandler = getStatsHandler; const installPackageFromRegistryHandler = async (context, request, response) => { var _appContextService$ge, _request$body, _request$body2, _request$query; const coreContext = await context.core; const fleetContext = await context.fleet; const savedObjectsClient = fleetContext.internalSoClient; const esClient = coreContext.elasticsearch.client.asInternalUser; const user = (await ((_appContextService$ge = _services.appContextService.getSecurity()) === null || _appContextService$ge === void 0 ? void 0 : _appContextService$ge.authc.getCurrentUser(request))) || undefined; const { pkgName, pkgVersion } = request.params; const authorizationHeader = _http_authorization_header.HTTPAuthorizationHeader.parseFromRequest(request, user === null || user === void 0 ? void 0 : user.username); const spaceId = fleetContext.spaceId; const res = await (0, _packages.installPackage)({ installSource: 'registry', savedObjectsClient, pkgkey: pkgVersion ? `${pkgName}-${pkgVersion}` : pkgName, esClient, spaceId, force: (_request$body = request.body) === null || _request$body === void 0 ? void 0 : _request$body.force, ignoreConstraints: (_request$body2 = request.body) === null || _request$body2 === void 0 ? void 0 : _request$body2.ignore_constraints, prerelease: (_request$query = request.query) === null || _request$query === void 0 ? void 0 : _request$query.prerelease, authorizationHeader }); if (!res.error) { const body = { items: res.assets || [], _meta: { install_source: res.installSource } }; return response.ok({ body }); } else { return await (0, _errors.defaultFleetErrorHandler)({ error: res.error, response }); } }; exports.installPackageFromRegistryHandler = installPackageFromRegistryHandler; const createCustomIntegrationHandler = async (context, request, response) => { var _appContextService$ge2; const coreContext = await context.core; const fleetContext = await context.fleet; const savedObjectsClient = fleetContext.internalSoClient; const esClient = coreContext.elasticsearch.client.asInternalUser; const user = (await ((_appContextService$ge2 = _services.appContextService.getSecurity()) === null || _appContextService$ge2 === void 0 ? void 0 : _appContextService$ge2.authc.getCurrentUser(request))) || undefined; const kibanaVersion = _services.appContextService.getKibanaVersion(); const authorizationHeader = _http_authorization_header.HTTPAuthorizationHeader.parseFromRequest(request, user === null || user === void 0 ? void 0 : user.username); const spaceId = fleetContext.spaceId; const { integrationName, force, datasets } = request.body; try { const res = await (0, _packages.installPackage)({ installSource: 'custom', savedObjectsClient, pkgName: integrationName, datasets, esClient, spaceId, force, authorizationHeader, kibanaVersion }); if (!res.error) { const body = { items: res.assets || [], _meta: { install_source: res.installSource } }; return response.ok({ body }); } else { return await (0, _errors.defaultFleetErrorHandler)({ error: res.error, response }); } } catch (error) { if (error instanceof _check_naming_collision.NamingCollisionError) { return response.customError({ statusCode: 409, body: { message: error.message } }); } return await (0, _errors.defaultFleetErrorHandler)({ error, response }); } }; exports.createCustomIntegrationHandler = createCustomIntegrationHandler; const bulkInstallServiceResponseToHttpEntry = result => { if ((0, _packages.isBulkInstallError)(result)) { const { statusCode, body } = (0, _errors.fleetErrorToResponseOptions)(result.error); return { name: result.name, statusCode, error: body.message }; } else { return result; } }; const bulkInstallPackagesFromRegistryHandler = async (context, request, response) => { const coreContext = await context.core; const fleetContext = await context.fleet; const savedObjectsClient = fleetContext.internalSoClient; const esClient = coreContext.elasticsearch.client.asInternalUser; const spaceId = fleetContext.spaceId; const bulkInstalledResponses = await (0, _packages.bulkInstallPackages)({ savedObjectsClient, esClient, packagesToInstall: request.body.packages, spaceId, prerelease: request.query.prerelease, force: request.body.force }); const payload = bulkInstalledResponses.map(bulkInstallServiceResponseToHttpEntry); const body = { items: payload, response: payload }; return response.ok({ body }); }; exports.bulkInstallPackagesFromRegistryHandler = bulkInstallPackagesFromRegistryHandler; const installPackageByUploadHandler = async (context, request, response) => { var _appContextService$ge3; const coreContext = await context.core; const fleetContext = await context.fleet; const savedObjectsClient = fleetContext.internalSoClient; const esClient = coreContext.elasticsearch.client.asInternalUser; const contentType = request.headers['content-type']; // from types it could also be string[] or undefined but this is checked later const archiveBuffer = Buffer.from(request.body); const spaceId = fleetContext.spaceId; const user = (await ((_appContextService$ge3 = _services.appContextService.getSecurity()) === null || _appContextService$ge3 === void 0 ? void 0 : _appContextService$ge3.authc.getCurrentUser(request))) || undefined; const authorizationHeader = _http_authorization_header.HTTPAuthorizationHeader.parseFromRequest(request, user === null || user === void 0 ? void 0 : user.username); const res = await (0, _packages.installPackage)({ installSource: 'upload', savedObjectsClient, esClient, archiveBuffer, spaceId, contentType, authorizationHeader }); if (!res.error) { const body = { items: res.assets || [], response: res.assets || [], _meta: { install_source: res.installSource } }; return response.ok({ body }); } else { return (0, _errors.defaultFleetErrorHandler)({ error: res.error, response }); } }; exports.installPackageByUploadHandler = installPackageByUploadHandler; const deletePackageHandler = async (context, request, response) => { try { var _request$query2; 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 (0, _packages.removeInstallation)({ savedObjectsClient, pkgName, pkgVersion, esClient, force: (_request$query2 = request.query) === null || _request$query2 === void 0 ? void 0 : _request$query2.force }); const body = { items: res }; return response.ok({ body }); } catch (error) { return (0, _errors.defaultFleetErrorHandler)({ error, response }); } }; exports.deletePackageHandler = deletePackageHandler; const getVerificationKeyIdHandler = async (context, request, response) => { try { const packageVerificationKeyId = await (0, _package_verification.getGpgKeyIdOrUndefined)(); const body = { id: packageVerificationKeyId || null }; return response.ok({ body }); } catch (error) { return (0, _errors.defaultFleetErrorHandler)({ error, response }); } }; /** * Create transform and optionally start transform * Note that we want to add the current user's roles/permissions to the es-secondary-auth with a API Key. * If API Key has insufficient permissions, it should still create the transforms but not start it * Instead of failing, we need to allow package to continue installing other assets * and prompt for users to authorize the transforms with the appropriate permissions after package is done installing */ exports.getVerificationKeyIdHandler = getVerificationKeyIdHandler; const reauthorizeTransformsHandler = async (context, request, response) => { const coreContext = await context.core; const savedObjectsClient = (await context.fleet).internalSoClient; const esClient = coreContext.elasticsearch.client.asInternalUser; const { pkgName, pkgVersion } = request.params; const { transforms } = request.body; let username; try { var _appContextService$ge4; const user = await ((_appContextService$ge4 = _services.appContextService.getSecurity()) === null || _appContextService$ge4 === void 0 ? void 0 : _appContextService$ge4.authc.getCurrentUser(request)); if (user) { username = user.username; } } catch (e) { // User might not have permission to get username, or security is not enabled, and that's okay. } try { const logger = _services.appContextService.getLogger(); const authorizationHeader = _http_authorization_header.HTTPAuthorizationHeader.parseFromRequest(request, username); const secondaryAuth = await (0, _transform_api_keys.generateTransformSecondaryAuthHeaders)({ authorizationHeader, logger, username, pkgName, pkgVersion }); const resp = await (0, _reauthorize.handleTransformReauthorizeAndStart)({ esClient, savedObjectsClient, logger, pkgName, pkgVersion, transforms, secondaryAuth, username }); return response.ok({ body: resp }); } catch (error) { return (0, _errors.defaultFleetErrorHandler)({ error, response }); } }; // Don't expose the whole SO in the API response, only selected fields exports.reauthorizeTransformsHandler = reauthorizeTransformsHandler; const soToInstallationInfo = pkg => { var _pkg$savedObject; if ('savedObject' in pkg && (_pkg$savedObject = pkg.savedObject) !== null && _pkg$savedObject !== void 0 && _pkg$savedObject.attributes) { const { attributes } = pkg.savedObject; const installationInfo = { ...(0, _lodash.pick)(pkg.savedObject, ['created_at', 'updated_at', 'namespaces', 'type']), installed_kibana: attributes.installed_kibana, installed_kibana_space_id: attributes.installed_kibana_space_id, installed_es: attributes.installed_es, install_status: attributes.install_status, install_source: attributes.install_source, name: attributes.name, version: attributes.version, verification_status: attributes.verification_status, verification_key_id: attributes.verification_key_id, experimental_data_stream_features: attributes.experimental_data_stream_features }; return { // When savedObject gets removed, replace `pkg` with `...omit(pkg, 'savedObject')` ...pkg, installationInfo }; } return pkg; };