import { first } from 'rxjs/operators' ; import { schema } from '@kbn/config-schema' ; import { ElasticsearchConnectionStatus , ERROR_COMPATIBILITY_FAILURE , ERROR_ELASTICSEARCH_CONNECTION_CONFIGURED , ERROR_ENROLL_FAILURE , ERROR_KIBANA_CONFIG_FAILURE , ERROR_KIBANA_CONFIG_NOT_WRITABLE , ERROR_OUTSIDE_PREBOOT_STAGE , } from '../../common' ; import { CompatibilityError } from '../compatibility_error' ; import { ElasticsearchService } from '../elasticsearch_service' ; import type { EnrollResult } from '../elasticsearch_service' ; import type { WriteConfigParameters } from '../kibana_config_writer' ; import type { RouteDefinitionParams } from './' ; /**
 * Defines routes to deal with Elasticsearch `enroll_kibana` APIs.
 */ export function defineEnrollRoutes ( { router , logger , kibanaConfigWriter , elasticsearch , verificationCode , preboot , } ) { router . post ( { path : '/internal/interactive_setup/enroll' , validate : { body : schema . object ( { hosts : schema . arrayOf ( schema . uri ( { scheme : 'https' } ) , { minSize : 1 , } ) , apiKey : schema . string ( { minLength : 1 } ) , caFingerprint : schema . string ( { maxLength : 64 , minLength : 64 } ) , code : schema . maybe ( schema . string ( ) ) , } ) , } , options : { authRequired : false } , } , async ( context , request , response ) => { if ( ! verificationCode . verify ( request . body . code ) ) { return response . forbidden ( ) ; } if ( ! preboot . isSetupOnHold ( ) ) { logger . error ( `Invalid request to [path=${ request . url . pathname }] outside of preboot stage` ) ; return response . badRequest ( { body : { message : 'Cannot process request outside of preboot stage.' , attributes : { type : ERROR_OUTSIDE_PREBOOT_STAGE } , } , } ) ; } const connectionStatus = await elasticsearch . connectionStatus$ . pipe ( first ( ) ) . toPromise ( ) ; if ( connectionStatus === ElasticsearchConnectionStatus . Configured ) { logger . error ( `Invalid request to [path=${ request . url . pathname }], Elasticsearch connection is already configured.` ) ; return response . badRequest ( { body : { message : 'Elasticsearch connection is already configured.' , attributes : { type : ERROR_ELASTICSEARCH_CONNECTION_CONFIGURED } , } , } ) ; } const isConfigWritable = await kibanaConfigWriter . isConfigWritable ( ) ; if ( ! isConfigWritable ) { logger . error ( 'Kibana process does not have enough permissions to write to config file' ) ; return response . customError ( { statusCode : 500 , body : { message : 'Kibana process does not have enough permissions to write to config file.' , attributes : { type : ERROR_KIBANA_CONFIG_NOT_WRITABLE } , } , } ) ; } let configToWrite ; try { configToWrite = await elasticsearch . enroll ( { apiKey : request . body . apiKey , hosts : request . body . hosts , caFingerprint : ElasticsearchService . formatFingerprint ( request . body . caFingerprint ) , } ) ; } catch ( error ) { if ( error instanceof CompatibilityError ) { return response . badRequest ( { body : { message : 'Failed to enroll due to version incompatibility.' , attributes : { type : ERROR_COMPATIBILITY_FAILURE , elasticsearchVersion : error . elasticsearchVersion , kibanaVersion : error . kibanaVersion , } , } , } ) ; } return response . customError ( { statusCode : 500 , body : { message : 'Failed to enroll.' , attributes : { type : ERROR_ENROLL_FAILURE } } , } ) ; } try { await kibanaConfigWriter . writeConfig ( configToWrite ) ; } catch { return response . customError ( { statusCode : 500 , body : { message : 'Failed to save configuration.' , attributes : { type : ERROR_KIBANA_CONFIG_FAILURE } , } , } ) ; } preboot . completeSetup ( { shouldReloadConfig : true } ) ; return response . noContent ( ) ; } ) ; }