mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
Migrate server-side http
types to @kbn/core-http-server
(#135808)
* create empty packages * create more packages * start moving most types to `@kbn/core-http-server` * export moved types * add bazel dependencies for `@kbn/core-http-server` * create explicit responseFactory types * start adapting imports * adapt imports in http/router * continue adapting imports * revert creation of other packages * adapt lifecycle types * move lifecycle types * move missing types * adapt more usages * fix entrypoint exports * fix internal request handler contexts * ok let's wait for CI now * ok just a last one * gotcha * clean some tsdoc * start fixing violations * move router types to sub folder * fix more violations * lint * more test violations * lint 2 * fix violations external to core * move ICspConfig to package * move external url types * move IBasePath to package * move more types * start fixing violations due to latest moves * fix server/types * move auth_header types * move context container type * move contract types * tsdoc * fix violations due to latests moves * fix import in reporting * fix type in canvas * move context container out of nested folder * update README * self-review * remove duplicate entry from codeowners file * create the @kbn/hapi-mocks package * move router mocks to correct package
This commit is contained in:
parent
7ba2e6358a
commit
885e80a1cd
146 changed files with 3536 additions and 2697 deletions
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
|
@ -349,7 +349,6 @@
|
|||
/x-pack/test/security_functional/ @elastic/kibana-security
|
||||
/x-pack/test/spaces_api_integration/ @elastic/kibana-security
|
||||
/x-pack/test/saved_object_api_integration/ @elastic/kibana-security
|
||||
/src/core/server/csp/ @elastic/kibana-security @elastic/kibana-core
|
||||
/examples/preboot_example/ @elastic/kibana-security @elastic/kibana-core
|
||||
#CC# /x-pack/plugins/security/ @elastic/kibana-security
|
||||
|
||||
|
|
|
@ -176,6 +176,7 @@
|
|||
"@kbn/core-fatal-errors-browser": "link:bazel-bin/packages/core/fatal-errors/core-fatal-errors-browser",
|
||||
"@kbn/core-fatal-errors-browser-internal": "link:bazel-bin/packages/core/fatal-errors/core-fatal-errors-browser-internal",
|
||||
"@kbn/core-fatal-errors-browser-mocks": "link:bazel-bin/packages/core/fatal-errors/core-fatal-errors-browser-mocks",
|
||||
"@kbn/core-http-server": "link:bazel-bin/packages/core/http/core-http-server",
|
||||
"@kbn/core-i18n-browser": "link:bazel-bin/packages/core/i18n/core-i18n-browser",
|
||||
"@kbn/core-i18n-browser-internal": "link:bazel-bin/packages/core/i18n/core-i18n-browser-internal",
|
||||
"@kbn/core-i18n-browser-mocks": "link:bazel-bin/packages/core/i18n/core-i18n-browser-mocks",
|
||||
|
@ -204,6 +205,7 @@
|
|||
"@kbn/field-types": "link:bazel-bin/packages/kbn-field-types",
|
||||
"@kbn/flot-charts": "link:bazel-bin/packages/kbn-flot-charts",
|
||||
"@kbn/handlebars": "link:bazel-bin/packages/kbn-handlebars",
|
||||
"@kbn/hapi-mocks": "link:bazel-bin/packages/kbn-hapi-mocks",
|
||||
"@kbn/home-sample-data-cards": "link:bazel-bin/packages/home/sample_data_cards",
|
||||
"@kbn/i18n": "link:bazel-bin/packages/kbn-i18n",
|
||||
"@kbn/i18n-react": "link:bazel-bin/packages/kbn-i18n-react",
|
||||
|
@ -734,6 +736,7 @@
|
|||
"@types/kbn__core-fatal-errors-browser": "link:bazel-bin/packages/core/fatal-errors/core-fatal-errors-browser/npm_module_types",
|
||||
"@types/kbn__core-fatal-errors-browser-internal": "link:bazel-bin/packages/core/fatal-errors/core-fatal-errors-browser-internal/npm_module_types",
|
||||
"@types/kbn__core-fatal-errors-browser-mocks": "link:bazel-bin/packages/core/fatal-errors/core-fatal-errors-browser-mocks/npm_module_types",
|
||||
"@types/kbn__core-http-server": "link:bazel-bin/packages/core/http/core-http-server/npm_module_types",
|
||||
"@types/kbn__core-i18n-browser": "link:bazel-bin/packages/core/i18n/core-i18n-browser/npm_module_types",
|
||||
"@types/kbn__core-i18n-browser-internal": "link:bazel-bin/packages/core/i18n/core-i18n-browser-internal/npm_module_types",
|
||||
"@types/kbn__core-i18n-browser-mocks": "link:bazel-bin/packages/core/i18n/core-i18n-browser-mocks/npm_module_types",
|
||||
|
@ -771,6 +774,7 @@
|
|||
"@types/kbn__find-used-node-modules": "link:bazel-bin/packages/kbn-find-used-node-modules/npm_module_types",
|
||||
"@types/kbn__generate": "link:bazel-bin/packages/kbn-generate/npm_module_types",
|
||||
"@types/kbn__handlebars": "link:bazel-bin/packages/kbn-handlebars/npm_module_types",
|
||||
"@types/kbn__hapi-mocks": "link:bazel-bin/packages/kbn-hapi-mocks/npm_module_types",
|
||||
"@types/kbn__home-sample-data-cards": "link:bazel-bin/packages/home/sample_data_cards/npm_module_types",
|
||||
"@types/kbn__i18n": "link:bazel-bin/packages/kbn-i18n/npm_module_types",
|
||||
"@types/kbn__i18n-react": "link:bazel-bin/packages/kbn-i18n-react/npm_module_types",
|
||||
|
|
|
@ -45,6 +45,7 @@ filegroup(
|
|||
"//packages/core/fatal-errors/core-fatal-errors-browser-internal:build",
|
||||
"//packages/core/fatal-errors/core-fatal-errors-browser-mocks:build",
|
||||
"//packages/core/fatal-errors/core-fatal-errors-browser:build",
|
||||
"//packages/core/http/core-http-server:build",
|
||||
"//packages/core/i18n/core-i18n-browser-internal:build",
|
||||
"//packages/core/i18n/core-i18n-browser-mocks:build",
|
||||
"//packages/core/i18n/core-i18n-browser:build",
|
||||
|
@ -108,6 +109,7 @@ filegroup(
|
|||
"//packages/kbn-flot-charts:build",
|
||||
"//packages/kbn-generate:build",
|
||||
"//packages/kbn-handlebars:build",
|
||||
"//packages/kbn-hapi-mocks:build",
|
||||
"//packages/kbn-i18n-react:build",
|
||||
"//packages/kbn-i18n:build",
|
||||
"//packages/kbn-import-resolver:build",
|
||||
|
@ -228,6 +230,7 @@ filegroup(
|
|||
"//packages/core/fatal-errors/core-fatal-errors-browser-internal:build_types",
|
||||
"//packages/core/fatal-errors/core-fatal-errors-browser-mocks:build_types",
|
||||
"//packages/core/fatal-errors/core-fatal-errors-browser:build_types",
|
||||
"//packages/core/http/core-http-server:build_types",
|
||||
"//packages/core/i18n/core-i18n-browser-internal:build_types",
|
||||
"//packages/core/i18n/core-i18n-browser-mocks:build_types",
|
||||
"//packages/core/i18n/core-i18n-browser:build_types",
|
||||
|
@ -282,6 +285,7 @@ filegroup(
|
|||
"//packages/kbn-find-used-node-modules:build_types",
|
||||
"//packages/kbn-generate:build_types",
|
||||
"//packages/kbn-handlebars:build_types",
|
||||
"//packages/kbn-hapi-mocks:build_types",
|
||||
"//packages/kbn-i18n-react:build_types",
|
||||
"//packages/kbn-i18n:build_types",
|
||||
"//packages/kbn-import-resolver:build_types",
|
||||
|
|
104
packages/core/http/core-http-server/BUILD.bazel
Normal file
104
packages/core/http/core-http-server/BUILD.bazel
Normal file
|
@ -0,0 +1,104 @@
|
|||
load("@npm//@bazel/typescript:index.bzl", "ts_config")
|
||||
load("@build_bazel_rules_nodejs//:index.bzl", "js_library")
|
||||
load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", "ts_project")
|
||||
|
||||
PKG_DIRNAME = "core-http-server"
|
||||
PKG_REQUIRE_NAME = "@kbn/core-http-server"
|
||||
|
||||
SOURCE_FILES = glob(
|
||||
[
|
||||
"src/**/*.ts",
|
||||
],
|
||||
exclude = [
|
||||
"**/*.test.*",
|
||||
"**/*.stories.*",
|
||||
],
|
||||
)
|
||||
|
||||
SRCS = SOURCE_FILES
|
||||
|
||||
filegroup(
|
||||
name = "srcs",
|
||||
srcs = SRCS,
|
||||
)
|
||||
|
||||
NPM_MODULE_EXTRA_FILES = [
|
||||
"package.json",
|
||||
]
|
||||
|
||||
RUNTIME_DEPS = [
|
||||
"//packages/kbn-config-schema",
|
||||
]
|
||||
|
||||
TYPES_DEPS = [
|
||||
"@npm//@types/node",
|
||||
"@npm//@types/jest",
|
||||
"@npm//rxjs",
|
||||
"@npm//@hapi/hapi",
|
||||
"@npm//@types/hapi__hapi",
|
||||
"@npm//@hapi/boom",
|
||||
"//packages/kbn-config-schema:npm_module_types",
|
||||
"//packages/kbn-utility-types:npm_module_types",
|
||||
"//packages/core/base/core-base-common:npm_module_types"
|
||||
]
|
||||
|
||||
jsts_transpiler(
|
||||
name = "target_node",
|
||||
srcs = SRCS,
|
||||
build_pkg_name = package_name(),
|
||||
)
|
||||
|
||||
ts_config(
|
||||
name = "tsconfig",
|
||||
src = "tsconfig.json",
|
||||
deps = [
|
||||
"//:tsconfig.base.json",
|
||||
"//:tsconfig.bazel.json",
|
||||
],
|
||||
)
|
||||
|
||||
ts_project(
|
||||
name = "tsc_types",
|
||||
args = ['--pretty'],
|
||||
srcs = SRCS,
|
||||
deps = TYPES_DEPS,
|
||||
declaration = True,
|
||||
emit_declaration_only = True,
|
||||
out_dir = "target_types",
|
||||
root_dir = "src",
|
||||
tsconfig = ":tsconfig",
|
||||
)
|
||||
|
||||
js_library(
|
||||
name = PKG_DIRNAME,
|
||||
srcs = NPM_MODULE_EXTRA_FILES,
|
||||
deps = RUNTIME_DEPS + [":target_node"],
|
||||
package_name = PKG_REQUIRE_NAME,
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
pkg_npm(
|
||||
name = "npm_module",
|
||||
deps = [":" + PKG_DIRNAME],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "build",
|
||||
srcs = [":npm_module"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
pkg_npm_types(
|
||||
name = "npm_module_types",
|
||||
srcs = SRCS,
|
||||
deps = [":tsc_types"],
|
||||
package_name = PKG_REQUIRE_NAME,
|
||||
tsconfig = ":tsconfig",
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "build_types",
|
||||
srcs = [":npm_module_types"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
3
packages/core/http/core-http-server/README.md
Normal file
3
packages/core/http/core-http-server/README.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
# @kbn/core-http-server
|
||||
|
||||
This package contains the public types for Core's server-side http service.
|
|
@ -6,13 +6,8 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export { RouteValidator } from './validator';
|
||||
export type {
|
||||
RouteValidatorConfig,
|
||||
RouteValidationSpec,
|
||||
RouteValidationFunction,
|
||||
RouteValidatorOptions,
|
||||
RouteValidatorFullConfig,
|
||||
RouteValidationResultFactory,
|
||||
} from './validator';
|
||||
export { RouteValidationError } from './validator_error';
|
||||
module.exports = {
|
||||
preset: '@kbn/test/jest_node',
|
||||
rootDir: '../../../..',
|
||||
roots: ['<rootDir>/packages/core/http/core-http-server'],
|
||||
};
|
7
packages/core/http/core-http-server/package.json
Normal file
7
packages/core/http/core-http-server/package.json
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"name": "@kbn/core-http-server",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"main": "./target_node/index.js",
|
||||
"license": "SSPL-1.0 OR Elastic License 2.0"
|
||||
}
|
27
packages/core/http/core-http-server/src/auth_headers.ts
Normal file
27
packages/core/http/core-http-server/src/auth_headers.ts
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { KibanaRequest } from './router';
|
||||
import type { AuthHeaders } from './lifecycle';
|
||||
|
||||
/**
|
||||
* Get headers to authenticate a user against Elasticsearch.
|
||||
* @param request {@link KibanaRequest} - an incoming request.
|
||||
* @return authentication headers {@link AuthHeaders} for - an incoming request.
|
||||
* @public
|
||||
* */
|
||||
export type GetAuthHeaders = (request: KibanaRequest) => AuthHeaders | undefined;
|
||||
|
||||
/** @public */
|
||||
export type SetAuthHeaders = (request: KibanaRequest, headers: AuthHeaders) => void;
|
||||
|
||||
/** @public */
|
||||
export interface IAuthHeadersStorage {
|
||||
set: SetAuthHeaders;
|
||||
get: GetAuthHeaders;
|
||||
}
|
44
packages/core/http/core-http-server/src/auth_state.ts
Normal file
44
packages/core/http/core-http-server/src/auth_state.ts
Normal file
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { KibanaRequest } from './router';
|
||||
|
||||
/**
|
||||
* Status indicating an outcome of the authentication.
|
||||
* @public
|
||||
*/
|
||||
export enum AuthStatus {
|
||||
/**
|
||||
* `auth` interceptor successfully authenticated a user
|
||||
*/
|
||||
authenticated = 'authenticated',
|
||||
/**
|
||||
* `auth` interceptor failed user authentication
|
||||
*/
|
||||
unauthenticated = 'unauthenticated',
|
||||
/**
|
||||
* `auth` interceptor has not been registered
|
||||
*/
|
||||
unknown = 'unknown',
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets authentication state for a request. Returned by `auth` interceptor.
|
||||
* @param request {@link KibanaRequest} - an incoming request.
|
||||
* @public
|
||||
*/
|
||||
export type GetAuthState = <T = unknown>(
|
||||
request: KibanaRequest
|
||||
) => { status: AuthStatus; state: T };
|
||||
|
||||
/**
|
||||
* Returns authentication status for a request.
|
||||
* @param request {@link KibanaRequest} - an incoming request.
|
||||
* @public
|
||||
*/
|
||||
export type IsAuthenticated = (request: KibanaRequest) => boolean;
|
52
packages/core/http/core-http-server/src/base_path.ts
Normal file
52
packages/core/http/core-http-server/src/base_path.ts
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { KibanaRequest } from './router';
|
||||
|
||||
/**
|
||||
* Access or manipulate the Kibana base path
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface IBasePath {
|
||||
/**
|
||||
* returns the server's basePath.
|
||||
*
|
||||
* See {@link IBasePath.get} for getting the basePath value for a specific request
|
||||
*/
|
||||
readonly serverBasePath: string;
|
||||
|
||||
/**
|
||||
* The server's publicly exposed base URL, if configured. Includes protocol, host, port (optional) and the
|
||||
* {@link IBasePath.serverBasePath}.
|
||||
*
|
||||
* @remarks
|
||||
* Should be used for generating external URL links back to this Kibana instance.
|
||||
*/
|
||||
readonly publicBaseUrl?: string;
|
||||
|
||||
/**
|
||||
* returns `basePath` value, specific for an incoming request.
|
||||
*/
|
||||
get(request: KibanaRequest): string;
|
||||
|
||||
/**
|
||||
* sets `basePath` value, specific for an incoming request.
|
||||
*/
|
||||
set(request: KibanaRequest, requestSpecificBasePath: string): void;
|
||||
|
||||
/**
|
||||
* Prepends `path` with the basePath.
|
||||
*/
|
||||
prepend(path: string): string;
|
||||
|
||||
/**
|
||||
* Removes the prepended basePath from the `path`.
|
||||
*/
|
||||
remove(path: string): string;
|
||||
}
|
36
packages/core/http/core-http-server/src/csp.ts
Normal file
36
packages/core/http/core-http-server/src/csp.ts
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
/**
|
||||
* CSP configuration for use in Kibana.
|
||||
* @public
|
||||
*/
|
||||
export interface ICspConfig {
|
||||
/**
|
||||
* Specify whether browsers that do not support CSP should be
|
||||
* able to use Kibana. Use `true` to block and `false` to allow.
|
||||
*/
|
||||
readonly strict: boolean;
|
||||
|
||||
/**
|
||||
* Specify whether users with legacy browsers should be warned
|
||||
* about their lack of Kibana security compliance.
|
||||
*/
|
||||
readonly warnLegacyBrowsers: boolean;
|
||||
|
||||
/**
|
||||
* Whether or not embedding (using iframes) should be allowed by the CSP. If embedding is disabled, a restrictive 'frame-ancestors' rule will be added to the default CSP rules.
|
||||
*/
|
||||
readonly disableEmbedding: boolean;
|
||||
|
||||
/**
|
||||
* The CSP rules in a formatted directives string for use
|
||||
* in a `Content-Security-Policy` header.
|
||||
*/
|
||||
readonly header: string;
|
||||
}
|
55
packages/core/http/core-http-server/src/external_url.ts
Normal file
55
packages/core/http/core-http-server/src/external_url.ts
Normal file
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
/**
|
||||
* External Url configuration for use in Kibana.
|
||||
* @public
|
||||
*/
|
||||
export interface IExternalUrlConfig {
|
||||
/**
|
||||
* A set of policies describing which external urls are allowed.
|
||||
*/
|
||||
readonly policy: IExternalUrlPolicy[];
|
||||
}
|
||||
|
||||
/**
|
||||
* A policy describing whether access to an external destination is allowed.
|
||||
* @public
|
||||
*/
|
||||
export interface IExternalUrlPolicy {
|
||||
/**
|
||||
* Indicates if this policy allows or denies access to the described destination.
|
||||
*/
|
||||
allow: boolean;
|
||||
|
||||
/**
|
||||
* Optional host describing the external destination.
|
||||
* May be combined with `protocol`.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* // allows access to all of google.com, using any protocol.
|
||||
* allow: true,
|
||||
* host: 'google.com'
|
||||
* ```
|
||||
*/
|
||||
host?: string;
|
||||
|
||||
/**
|
||||
* Optional protocol describing the external destination.
|
||||
* May be combined with `host`.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* // allows access to all destinations over the `https` protocol.
|
||||
* allow: true,
|
||||
* protocol: 'https'
|
||||
* ```
|
||||
*/
|
||||
protocol?: string;
|
||||
}
|
391
packages/core/http/core-http-server/src/http_contract.ts
Normal file
391
packages/core/http/core-http-server/src/http_contract.ts
Normal file
|
@ -0,0 +1,391 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type {
|
||||
IContextContainer,
|
||||
IContextProvider,
|
||||
IRouter,
|
||||
RequestHandlerContextBase,
|
||||
} from './router';
|
||||
import type {
|
||||
AuthenticationHandler,
|
||||
OnPostAuthHandler,
|
||||
OnPreAuthHandler,
|
||||
OnPreResponseHandler,
|
||||
OnPreRoutingHandler,
|
||||
} from './lifecycle';
|
||||
import type { IBasePath } from './base_path';
|
||||
import type { ICspConfig } from './csp';
|
||||
import type { GetAuthState, IsAuthenticated } from './auth_state';
|
||||
import type { SessionStorageCookieOptions, SessionStorageFactory } from './session_storage';
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface HttpAuth {
|
||||
/**
|
||||
* Gets authentication state for a request. Returned by `auth` interceptor.
|
||||
* {@link GetAuthState}
|
||||
*/
|
||||
get: GetAuthState;
|
||||
/**
|
||||
* Returns authentication status for a request.
|
||||
* {@link IsAuthenticated}
|
||||
*/
|
||||
isAuthenticated: IsAuthenticated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Kibana HTTP Service provides an abstraction to work with the HTTP stack at the `preboot` stage. This functionality
|
||||
* allows Kibana to serve user requests even before Kibana becomes fully operational. Only Core and `preboot` plugins
|
||||
* can define HTTP routes at this stage.
|
||||
*
|
||||
* @example
|
||||
* To handle an incoming request in your preboot plugin you should:
|
||||
* - Use `@kbn/config-schema` package to create a schema to validate the request `params`, `query`, and `body`. Every incoming request will be validated against the created schema. If validation failed, the request is rejected with `400` status and `Bad request` error without calling the route's handler.
|
||||
* To opt out of validating the request, specify `false`.
|
||||
* ```ts
|
||||
* import { schema, TypeOf } from '@kbn/config-schema';
|
||||
* const validate = {
|
||||
* params: schema.object({
|
||||
* id: schema.string(),
|
||||
* }),
|
||||
* };
|
||||
* ```
|
||||
*
|
||||
* - Declare a function to respond to incoming request.
|
||||
* The function will receive `request` object containing request details: url, headers, matched route, as well as validated `params`, `query`, `body`.
|
||||
* And `response` object instructing HTTP server to create HTTP response with information sent back to the client as the response body, headers, and HTTP status.
|
||||
* Any exception raised during the handler call will generate `500 Server error` response and log error details for further investigation. See below for returning custom error responses.
|
||||
* ```ts
|
||||
* const handler = async (context: RequestHandlerContext, request: KibanaRequest, response: ResponseFactory) => {
|
||||
* const data = await findObject(request.params.id);
|
||||
* // creates a command to respond with 'not found' error
|
||||
* if (!data) {
|
||||
* return response.notFound();
|
||||
* }
|
||||
* // creates a command to send found data to the client and set response headers
|
||||
* return response.ok({
|
||||
* body: data,
|
||||
* headers: { 'content-type': 'application/json' }
|
||||
* });
|
||||
* }
|
||||
* ```
|
||||
* * - Acquire `preboot` {@link IRouter} instance and register route handler for GET request to 'path/{id}' path.
|
||||
* ```ts
|
||||
* import { schema, TypeOf } from '@kbn/config-schema';
|
||||
*
|
||||
* const validate = {
|
||||
* params: schema.object({
|
||||
* id: schema.string(),
|
||||
* }),
|
||||
* };
|
||||
*
|
||||
* httpPreboot.registerRoutes('my-plugin', (router) => {
|
||||
* router.get({ path: 'path/{id}', validate }, async (context, request, response) => {
|
||||
* const data = await findObject(request.params.id);
|
||||
* if (!data) {
|
||||
* return response.notFound();
|
||||
* }
|
||||
* return response.ok({
|
||||
* body: data,
|
||||
* headers: { 'content-type': 'application/json' }
|
||||
* });
|
||||
* });
|
||||
* });
|
||||
* ```
|
||||
* @public
|
||||
*/
|
||||
export interface HttpServicePreboot<
|
||||
DefaultRequestHandlerType extends RequestHandlerContextBase = RequestHandlerContextBase
|
||||
> {
|
||||
/**
|
||||
* Provides ability to acquire `preboot` {@link IRouter} instance for a particular top-level path and register handler
|
||||
* functions for any number of nested routes.
|
||||
*
|
||||
* @remarks
|
||||
* Each route can have only one handler function, which is executed when the route is matched.
|
||||
* See the {@link IRouter} documentation for more information.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* registerRoutes('my-plugin', (router) => {
|
||||
* // handler is called when '/my-plugin/path' resource is requested with `GET` method
|
||||
* router.get({ path: '/path', validate: false }, (context, req, res) => res.ok({ content: 'ok' }));
|
||||
* });
|
||||
* ```
|
||||
* @public
|
||||
*/
|
||||
registerRoutes<ContextType extends DefaultRequestHandlerType = DefaultRequestHandlerType>(
|
||||
path: string,
|
||||
callback: (router: IRouter<ContextType>) => void
|
||||
): void;
|
||||
|
||||
/**
|
||||
* Access or manipulate the Kibana base path
|
||||
* See {@link IBasePath}.
|
||||
*/
|
||||
basePath: IBasePath;
|
||||
|
||||
/**
|
||||
* Provides common {@link HttpServerInfo | information} about the running preboot http server.
|
||||
*/
|
||||
getServerInfo: () => HttpServerInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Kibana HTTP Service provides own abstraction for work with HTTP stack.
|
||||
* Plugins don't have direct access to `hapi` server and its primitives anymore. Moreover,
|
||||
* plugins shouldn't rely on the fact that HTTP Service uses one or another library under the hood.
|
||||
* This gives the platform flexibility to upgrade or changing our internal HTTP stack without breaking plugins.
|
||||
* If the HTTP Service lacks functionality you need, we are happy to discuss and support your needs.
|
||||
*
|
||||
* @example
|
||||
* To handle an incoming request in your plugin you should:
|
||||
* - Create a `Router` instance.
|
||||
* ```ts
|
||||
* const router = httpSetup.createRouter();
|
||||
* ```
|
||||
*
|
||||
* - Use `@kbn/config-schema` package to create a schema to validate the request `params`, `query`, and `body`. Every incoming request will be validated against the created schema. If validation failed, the request is rejected with `400` status and `Bad request` error without calling the route's handler.
|
||||
* To opt out of validating the request, specify `false`.
|
||||
* ```ts
|
||||
* import { schema, TypeOf } from '@kbn/config-schema';
|
||||
* const validate = {
|
||||
* params: schema.object({
|
||||
* id: schema.string(),
|
||||
* }),
|
||||
* };
|
||||
* ```
|
||||
*
|
||||
* - Declare a function to respond to incoming request.
|
||||
* The function will receive `request` object containing request details: url, headers, matched route, as well as validated `params`, `query`, `body`.
|
||||
* And `response` object instructing HTTP server to create HTTP response with information sent back to the client as the response body, headers, and HTTP status.
|
||||
* Unlike, `hapi` route handler in the Legacy platform, any exception raised during the handler call will generate `500 Server error` response and log error details for further investigation. See below for returning custom error responses.
|
||||
* ```ts
|
||||
* const handler = async (context: RequestHandlerContext, request: KibanaRequest, response: ResponseFactory) => {
|
||||
* const data = await findObject(request.params.id);
|
||||
* // creates a command to respond with 'not found' error
|
||||
* if (!data) return response.notFound();
|
||||
* // creates a command to send found data to the client and set response headers
|
||||
* return response.ok({
|
||||
* body: data,
|
||||
* headers: {
|
||||
* 'content-type': 'application/json'
|
||||
* }
|
||||
* });
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* - Register route handler for GET request to 'path/{id}' path
|
||||
* ```ts
|
||||
* import { schema, TypeOf } from '@kbn/config-schema';
|
||||
* const router = httpSetup.createRouter();
|
||||
*
|
||||
* const validate = {
|
||||
* params: schema.object({
|
||||
* id: schema.string(),
|
||||
* }),
|
||||
* };
|
||||
*
|
||||
* router.get({
|
||||
* path: 'path/{id}',
|
||||
* validate
|
||||
* },
|
||||
* async (context, request, response) => {
|
||||
* const data = await findObject(request.params.id);
|
||||
* if (!data) return response.notFound();
|
||||
* return response.ok({
|
||||
* body: data,
|
||||
* headers: {
|
||||
* 'content-type': 'application/json'
|
||||
* }
|
||||
* });
|
||||
* });
|
||||
* ```
|
||||
* @public
|
||||
*/
|
||||
export interface HttpServiceSetup<
|
||||
DefaultRequestHandlerType extends RequestHandlerContextBase = RequestHandlerContextBase
|
||||
> {
|
||||
/**
|
||||
* Creates cookie based session storage factory {@link SessionStorageFactory}
|
||||
* @param cookieOptions {@link SessionStorageCookieOptions} - options to configure created cookie session storage.
|
||||
*/
|
||||
createCookieSessionStorageFactory: <T>(
|
||||
cookieOptions: SessionStorageCookieOptions<T>
|
||||
) => Promise<SessionStorageFactory<T>>;
|
||||
|
||||
/**
|
||||
* To define custom logic to perform for incoming requests before server performs a route lookup.
|
||||
*
|
||||
* @remarks
|
||||
* It's the only place when you can forward a request to another URL right on the server.
|
||||
* Can register any number of registerOnPreRouting, which are called in sequence
|
||||
* (from the first registered to the last). See {@link OnPreRoutingHandler}.
|
||||
*
|
||||
* @param handler {@link OnPreRoutingHandler} - function to call.
|
||||
*/
|
||||
registerOnPreRouting: (handler: OnPreRoutingHandler) => void;
|
||||
|
||||
/**
|
||||
* To define custom logic to perform for incoming requests before
|
||||
* the Auth interceptor performs a check that user has access to requested resources.
|
||||
*
|
||||
* @remarks
|
||||
* Can register any number of registerOnPreAuth, which are called in sequence
|
||||
* (from the first registered to the last). See {@link OnPreAuthHandler}.
|
||||
*
|
||||
* @param handler {@link OnPreRoutingHandler} - function to call.
|
||||
*/
|
||||
registerOnPreAuth: (handler: OnPreAuthHandler) => void;
|
||||
|
||||
/**
|
||||
* To define custom authentication and/or authorization mechanism for incoming requests.
|
||||
*
|
||||
* @remarks
|
||||
* A handler should return a state to associate with the incoming request.
|
||||
* The state can be retrieved later via http.auth.get(..)
|
||||
* Only one AuthenticationHandler can be registered. See {@link AuthenticationHandler}.
|
||||
*
|
||||
* @param handler {@link AuthenticationHandler} - function to perform authentication.
|
||||
*/
|
||||
registerAuth: (handler: AuthenticationHandler) => void;
|
||||
|
||||
/**
|
||||
* To define custom logic after Auth interceptor did make sure a user has access to the requested resource.
|
||||
*
|
||||
* @remarks
|
||||
* The auth state is available at stage via http.auth.get(..)
|
||||
* Can register any number of registerOnPostAuth, which are called in sequence
|
||||
* (from the first registered to the last). See {@link OnPostAuthHandler}.
|
||||
*
|
||||
* @param handler {@link OnPostAuthHandler} - function to call.
|
||||
*/
|
||||
registerOnPostAuth: (handler: OnPostAuthHandler) => void;
|
||||
|
||||
/**
|
||||
* To define custom logic to perform for the server response.
|
||||
*
|
||||
* @remarks
|
||||
* Doesn't provide the whole response object.
|
||||
* Supports extending response with custom headers.
|
||||
* See {@link OnPreResponseHandler}.
|
||||
*
|
||||
* @param handler {@link OnPreResponseHandler} - function to call.
|
||||
*/
|
||||
registerOnPreResponse: (handler: OnPreResponseHandler) => void;
|
||||
|
||||
/**
|
||||
* Access or manipulate the Kibana base path
|
||||
* See {@link IBasePath}.
|
||||
*/
|
||||
basePath: IBasePath;
|
||||
|
||||
/**
|
||||
* The CSP config used for Kibana.
|
||||
*/
|
||||
csp: ICspConfig;
|
||||
|
||||
/**
|
||||
* Provides ability to declare a handler function for a particular path and HTTP request method.
|
||||
*
|
||||
* @remarks
|
||||
* Each route can have only one handler function, which is executed when the route is matched.
|
||||
* See the {@link IRouter} documentation for more information.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const router = createRouter();
|
||||
* // handler is called when '/path' resource is requested with `GET` method
|
||||
* router.get({ path: '/path', validate: false }, (context, req, res) => res.ok({ content: 'ok' }));
|
||||
* ```
|
||||
* @public
|
||||
*/
|
||||
createRouter: <
|
||||
Context extends DefaultRequestHandlerType = DefaultRequestHandlerType
|
||||
>() => IRouter<Context>;
|
||||
|
||||
/**
|
||||
* Register a context provider for a route handler.
|
||||
* @example
|
||||
* ```ts
|
||||
* // my-plugin.ts
|
||||
* interface MyRequestHandlerContext extends RequestHandlerContext {
|
||||
* myApp: { search(id: string): Promise<Result> };
|
||||
* }
|
||||
* deps.http.registerRouteHandlerContext<MyRequestHandlerContext, 'myApp'>(
|
||||
* 'myApp',
|
||||
* (context, req) => {
|
||||
* async function search (id: string) {
|
||||
* return await context.elasticsearch.client.asCurrentUser.find(id);
|
||||
* }
|
||||
* return { search };
|
||||
* }
|
||||
* );
|
||||
*
|
||||
* // my-route-handler.ts
|
||||
* import type { MyRequestHandlerContext } from './my-plugin.ts';
|
||||
* const router = createRouter<MyRequestHandlerContext>();
|
||||
* router.get({ path: '/', validate: false }, async (context, req, res) => {
|
||||
* const response = await context.myApp.search(...);
|
||||
* return res.ok(response);
|
||||
* });
|
||||
* ```
|
||||
* @public
|
||||
*/
|
||||
registerRouteHandlerContext: <
|
||||
Context extends DefaultRequestHandlerType,
|
||||
ContextName extends keyof Omit<Context, 'resolve'>
|
||||
>(
|
||||
contextName: ContextName,
|
||||
provider: IContextProvider<Context, ContextName>
|
||||
) => IContextContainer;
|
||||
|
||||
/**
|
||||
* Provides common {@link HttpServerInfo | information} about the running http server.
|
||||
*/
|
||||
getServerInfo: () => HttpServerInfo;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export interface HttpServiceStart {
|
||||
/**
|
||||
* Access or manipulate the Kibana base path
|
||||
* See {@link IBasePath}.
|
||||
*/
|
||||
basePath: IBasePath;
|
||||
|
||||
/**
|
||||
* Auth status.
|
||||
* See {@link HttpAuth}
|
||||
*/
|
||||
auth: HttpAuth;
|
||||
|
||||
/**
|
||||
* Provides common {@link HttpServerInfo | information} about the running http server.
|
||||
*/
|
||||
getServerInfo: () => HttpServerInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Information about what hostname, port, and protocol the server process is
|
||||
* running on. Note that this may not match the URL that end-users access
|
||||
* Kibana at. For the public URL, see {@link BasePath.publicBaseUrl}.
|
||||
* @public
|
||||
*/
|
||||
export interface HttpServerInfo {
|
||||
/** The name of the Kibana server */
|
||||
name: string;
|
||||
/** The hostname of the server */
|
||||
hostname: string;
|
||||
/** The port the server is listening on */
|
||||
port: number;
|
||||
/** The protocol used by the server */
|
||||
protocol: 'http' | 'https' | 'socket';
|
||||
}
|
127
packages/core/http/core-http-server/src/index.ts
Normal file
127
packages/core/http/core-http-server/src/index.ts
Normal file
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export {
|
||||
OnPreRoutingResultType,
|
||||
AuthResultType,
|
||||
OnPostAuthResultType,
|
||||
OnPreResponseResultType,
|
||||
OnPreAuthResultType,
|
||||
} from './lifecycle';
|
||||
export type {
|
||||
OnPreAuthToolkit,
|
||||
AuthenticationHandler,
|
||||
AuthHeaders,
|
||||
AuthRedirectedParams,
|
||||
AuthResult,
|
||||
AuthResultAuthenticated,
|
||||
AuthResultNotHandled,
|
||||
AuthResultParams,
|
||||
AuthResultRedirected,
|
||||
AuthToolkit,
|
||||
OnPostAuthHandler,
|
||||
OnPostAuthNextResult,
|
||||
OnPostAuthToolkit,
|
||||
OnPostAuthResult,
|
||||
OnPreAuthHandler,
|
||||
OnPreAuthNextResult,
|
||||
OnPreAuthResult,
|
||||
OnPreResponseExtensions,
|
||||
OnPreResponseHandler,
|
||||
OnPreResponseInfo,
|
||||
OnPreResponseRender,
|
||||
OnPreResponseResult,
|
||||
OnPreResponseResultNext,
|
||||
OnPreResponseResultRender,
|
||||
OnPreResponseToolkit,
|
||||
OnPreRoutingHandler,
|
||||
OnPreRoutingResult,
|
||||
OnPreRoutingResultNext,
|
||||
OnPreRoutingResultRewriteUrl,
|
||||
OnPreRoutingToolkit,
|
||||
} from './lifecycle';
|
||||
|
||||
export type {
|
||||
IContextProvider,
|
||||
IContextContainer,
|
||||
HandlerContextType,
|
||||
HandlerFunction,
|
||||
HandlerParameters,
|
||||
Headers,
|
||||
KnownHeaders,
|
||||
KnownKeys,
|
||||
ResponseHeaders,
|
||||
StringKeysAsVals,
|
||||
KibanaRequest,
|
||||
KibanaRequestAuth,
|
||||
KibanaRequestEvents,
|
||||
KibanaRequestRoute,
|
||||
KibanaRequestRouteOptions,
|
||||
KibanaRequestState,
|
||||
KibanaRouteOptions,
|
||||
RequestHandlerWrapper,
|
||||
RequestHandler,
|
||||
RequestHandlerContextBase,
|
||||
ResponseError,
|
||||
CustomHttpResponseOptions,
|
||||
HttpResponseOptions,
|
||||
HttpResponsePayload,
|
||||
IKibanaResponse,
|
||||
RedirectResponseOptions,
|
||||
ResponseErrorAttributes,
|
||||
ErrorHttpResponseOptions,
|
||||
RouteConfigOptions,
|
||||
RouteMethod,
|
||||
DestructiveRouteMethod,
|
||||
RouteConfig,
|
||||
RouteConfigOptionsBody,
|
||||
RouteContentType,
|
||||
SafeRouteMethod,
|
||||
RouteValidationFunction,
|
||||
RouteValidationResultFactory,
|
||||
RouteValidationSpec,
|
||||
RouteValidatorConfig,
|
||||
RouteValidatorFullConfig,
|
||||
RouteValidatorOptions,
|
||||
IRouter,
|
||||
RouteRegistrar,
|
||||
RouterRoute,
|
||||
IKibanaSocket,
|
||||
KibanaErrorResponseFactory,
|
||||
KibanaRedirectionResponseFactory,
|
||||
KibanaSuccessResponseFactory,
|
||||
KibanaResponseFactory,
|
||||
LifecycleResponseFactory,
|
||||
} from './router';
|
||||
export { validBodyOutput, RouteValidationError } from './router';
|
||||
|
||||
export type { ICspConfig } from './csp';
|
||||
|
||||
export type { IExternalUrlPolicy, IExternalUrlConfig } from './external_url';
|
||||
|
||||
export type { IBasePath } from './base_path';
|
||||
|
||||
export type {
|
||||
SessionStorage,
|
||||
SessionStorageFactory,
|
||||
SessionCookieValidationResult,
|
||||
SessionStorageCookieOptions,
|
||||
} from './session_storage';
|
||||
|
||||
export type { GetAuthState, IsAuthenticated } from './auth_state';
|
||||
export { AuthStatus } from './auth_state';
|
||||
|
||||
export type { IAuthHeadersStorage, SetAuthHeaders, GetAuthHeaders } from './auth_headers';
|
||||
|
||||
export type {
|
||||
HttpAuth,
|
||||
HttpServerInfo,
|
||||
HttpServicePreboot,
|
||||
HttpServiceSetup,
|
||||
HttpServiceStart,
|
||||
} from './http_contract';
|
105
packages/core/http/core-http-server/src/lifecycle/auth.ts
Normal file
105
packages/core/http/core-http-server/src/lifecycle/auth.ts
Normal file
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type {
|
||||
ResponseHeaders,
|
||||
IKibanaResponse,
|
||||
KibanaRequest,
|
||||
LifecycleResponseFactory,
|
||||
} from '../router';
|
||||
|
||||
/** @public */
|
||||
export enum AuthResultType {
|
||||
authenticated = 'authenticated',
|
||||
notHandled = 'notHandled',
|
||||
redirected = 'redirected',
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export interface AuthResultAuthenticated extends AuthResultParams {
|
||||
type: AuthResultType.authenticated;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export interface AuthResultNotHandled {
|
||||
type: AuthResultType.notHandled;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export interface AuthResultRedirected extends AuthRedirectedParams {
|
||||
type: AuthResultType.redirected;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export type AuthResult = AuthResultAuthenticated | AuthResultNotHandled | AuthResultRedirected;
|
||||
|
||||
/** @public */
|
||||
export type AuthHeaders = Record<string, string | string[]>;
|
||||
|
||||
/**
|
||||
* Result of successful authentication.
|
||||
* @public
|
||||
*/
|
||||
export interface AuthResultParams {
|
||||
/**
|
||||
* Data to associate with an incoming request. Any downstream plugin may get access to the data.
|
||||
*/
|
||||
state?: Record<string, any>;
|
||||
/**
|
||||
* Auth specific headers to attach to a request object.
|
||||
* Used to perform a request to Elasticsearch on behalf of an authenticated user.
|
||||
*/
|
||||
requestHeaders?: AuthHeaders;
|
||||
/**
|
||||
* Auth specific headers to attach to a response object.
|
||||
* Used to send back authentication mechanism related headers to a client when needed.
|
||||
*/
|
||||
responseHeaders?: AuthHeaders;
|
||||
}
|
||||
|
||||
/**
|
||||
* Result of auth redirection.
|
||||
* @public
|
||||
*/
|
||||
export interface AuthRedirectedParams {
|
||||
/**
|
||||
* Headers to attach for auth redirect.
|
||||
* Must include "location" header
|
||||
*/
|
||||
headers: { location: string } & ResponseHeaders;
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
* A tool set defining an outcome of Auth interceptor for incoming request.
|
||||
*/
|
||||
export interface AuthToolkit {
|
||||
/** Authentication is successful with given credentials, allow request to pass through */
|
||||
authenticated: (data?: AuthResultParams) => AuthResult;
|
||||
/**
|
||||
* User has no credentials.
|
||||
* Allows user to access a resource when authRequired is 'optional'
|
||||
* Rejects a request when authRequired: true
|
||||
* */
|
||||
notHandled: () => AuthResult;
|
||||
/**
|
||||
* Redirects user to another location to complete authentication when authRequired: true
|
||||
* Allows user to access a resource without redirection when authRequired: 'optional'
|
||||
* */
|
||||
redirected: (headers: { location: string } & ResponseHeaders) => AuthResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link AuthToolkit}.
|
||||
* @public
|
||||
*/
|
||||
export type AuthenticationHandler = (
|
||||
request: KibanaRequest,
|
||||
response: LifecycleResponseFactory,
|
||||
toolkit: AuthToolkit
|
||||
) => AuthResult | IKibanaResponse | Promise<AuthResult | IKibanaResponse>;
|
57
packages/core/http/core-http-server/src/lifecycle/index.ts
Normal file
57
packages/core/http/core-http-server/src/lifecycle/index.ts
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export type {
|
||||
AuthHeaders,
|
||||
AuthRedirectedParams,
|
||||
AuthResult,
|
||||
AuthResultAuthenticated,
|
||||
AuthResultNotHandled,
|
||||
AuthResultParams,
|
||||
AuthResultRedirected,
|
||||
AuthToolkit,
|
||||
AuthenticationHandler,
|
||||
} from './auth';
|
||||
export { AuthResultType } from './auth';
|
||||
|
||||
export type {
|
||||
OnPostAuthHandler,
|
||||
OnPostAuthNextResult,
|
||||
OnPostAuthToolkit,
|
||||
OnPostAuthResult,
|
||||
} from './on_post_auth';
|
||||
export { OnPostAuthResultType } from './on_post_auth';
|
||||
|
||||
export type {
|
||||
OnPreAuthHandler,
|
||||
OnPreAuthNextResult,
|
||||
OnPreAuthResult,
|
||||
OnPreAuthToolkit,
|
||||
} from './on_pre_auth';
|
||||
export { OnPreAuthResultType } from './on_pre_auth';
|
||||
|
||||
export type {
|
||||
OnPreResponseExtensions,
|
||||
OnPreResponseHandler,
|
||||
OnPreResponseInfo,
|
||||
OnPreResponseRender,
|
||||
OnPreResponseResult,
|
||||
OnPreResponseResultNext,
|
||||
OnPreResponseResultRender,
|
||||
OnPreResponseToolkit,
|
||||
} from './on_pre_response';
|
||||
export { OnPreResponseResultType } from './on_pre_response';
|
||||
|
||||
export type {
|
||||
OnPreRoutingHandler,
|
||||
OnPreRoutingResult,
|
||||
OnPreRoutingResultNext,
|
||||
OnPreRoutingResultRewriteUrl,
|
||||
OnPreRoutingToolkit,
|
||||
} from './on_pre_routing';
|
||||
export { OnPreRoutingResultType } from './on_pre_routing';
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { IKibanaResponse, KibanaRequest, LifecycleResponseFactory } from '../router';
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export enum OnPostAuthResultType {
|
||||
next = 'next',
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface OnPostAuthNextResult {
|
||||
type: OnPostAuthResultType.next;
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type OnPostAuthResult = OnPostAuthNextResult;
|
||||
|
||||
/**
|
||||
* @public
|
||||
* A tool set defining an outcome of OnPostAuth interceptor for incoming request.
|
||||
*/
|
||||
export interface OnPostAuthToolkit {
|
||||
/** To pass request to the next handler */
|
||||
next: () => OnPostAuthResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link OnPostAuthToolkit}.
|
||||
* @public
|
||||
*/
|
||||
export type OnPostAuthHandler = (
|
||||
request: KibanaRequest,
|
||||
response: LifecycleResponseFactory,
|
||||
toolkit: OnPostAuthToolkit
|
||||
) => OnPostAuthResult | IKibanaResponse | Promise<OnPostAuthResult | IKibanaResponse>;
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { IKibanaResponse, KibanaRequest, LifecycleResponseFactory } from '../router';
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export enum OnPreAuthResultType {
|
||||
next = 'next',
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface OnPreAuthNextResult {
|
||||
type: OnPreAuthResultType.next;
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type OnPreAuthResult = OnPreAuthNextResult;
|
||||
|
||||
/**
|
||||
* @public
|
||||
* A tool set defining an outcome of OnPreAuth interceptor for incoming request.
|
||||
*/
|
||||
export interface OnPreAuthToolkit {
|
||||
/** To pass request to the next handler */
|
||||
next: () => OnPreAuthResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link OnPreAuthToolkit}.
|
||||
* @public
|
||||
*/
|
||||
export type OnPreAuthHandler = (
|
||||
request: KibanaRequest,
|
||||
response: LifecycleResponseFactory,
|
||||
toolkit: OnPreAuthToolkit
|
||||
) => OnPreAuthResult | IKibanaResponse | Promise<OnPreAuthResult | IKibanaResponse>;
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { ResponseHeaders, KibanaRequest } from '../router';
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export enum OnPreResponseResultType {
|
||||
render = 'render',
|
||||
next = 'next',
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface OnPreResponseResultRender {
|
||||
type: OnPreResponseResultType.render;
|
||||
body: string;
|
||||
headers?: ResponseHeaders;
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface OnPreResponseResultNext {
|
||||
type: OnPreResponseResultType.next;
|
||||
headers?: ResponseHeaders;
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type OnPreResponseResult = OnPreResponseResultRender | OnPreResponseResultNext;
|
||||
|
||||
/**
|
||||
* Additional data to extend a response when rendering a new body
|
||||
* @public
|
||||
*/
|
||||
export interface OnPreResponseRender {
|
||||
/** additional headers to attach to the response */
|
||||
headers?: ResponseHeaders;
|
||||
/** the body to use in the response */
|
||||
body: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Additional data to extend a response.
|
||||
* @public
|
||||
*/
|
||||
export interface OnPreResponseExtensions {
|
||||
/** additional headers to attach to the response */
|
||||
headers?: ResponseHeaders;
|
||||
}
|
||||
|
||||
/**
|
||||
* Response status code.
|
||||
* @public
|
||||
*/
|
||||
export interface OnPreResponseInfo {
|
||||
statusCode: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* A tool set defining an outcome of OnPreResponse interceptor for incoming request.
|
||||
* @public
|
||||
*/
|
||||
export interface OnPreResponseToolkit {
|
||||
/** To override the response with a different body */
|
||||
render: (responseRender: OnPreResponseRender) => OnPreResponseResult;
|
||||
/** To pass request to the next handler */
|
||||
next: (responseExtensions?: OnPreResponseExtensions) => OnPreResponseResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link OnPreResponseToolkit}.
|
||||
* @public
|
||||
*/
|
||||
export type OnPreResponseHandler = (
|
||||
request: KibanaRequest,
|
||||
preResponse: OnPreResponseInfo,
|
||||
toolkit: OnPreResponseToolkit
|
||||
) => OnPreResponseResult | Promise<OnPreResponseResult>;
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { IKibanaResponse, KibanaRequest, LifecycleResponseFactory } from '../router';
|
||||
|
||||
export enum OnPreRoutingResultType {
|
||||
next = 'next',
|
||||
rewriteUrl = 'rewriteUrl',
|
||||
}
|
||||
|
||||
export interface OnPreRoutingResultNext {
|
||||
type: OnPreRoutingResultType.next;
|
||||
}
|
||||
|
||||
export interface OnPreRoutingResultRewriteUrl {
|
||||
type: OnPreRoutingResultType.rewriteUrl;
|
||||
url: string;
|
||||
}
|
||||
|
||||
export type OnPreRoutingResult = OnPreRoutingResultNext | OnPreRoutingResultRewriteUrl;
|
||||
|
||||
/**
|
||||
* @public
|
||||
* A tool set defining an outcome of OnPreRouting interceptor for incoming request.
|
||||
*/
|
||||
export interface OnPreRoutingToolkit {
|
||||
/** To pass request to the next handler */
|
||||
next: () => OnPreRoutingResult;
|
||||
/** Rewrite requested resources url before is was authenticated and routed to a handler */
|
||||
rewriteUrl: (url: string) => OnPreRoutingResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link OnPreRoutingToolkit}.
|
||||
* @public
|
||||
*/
|
||||
export type OnPreRoutingHandler = (
|
||||
request: KibanaRequest,
|
||||
response: LifecycleResponseFactory,
|
||||
toolkit: OnPreRoutingToolkit
|
||||
) => OnPreRoutingResult | IKibanaResponse | Promise<OnPreRoutingResult | IKibanaResponse>;
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { PluginOpaqueId } from '@kbn/core-base-common';
|
||||
import type { ShallowPromise } from '@kbn/utility-types';
|
||||
import type { HandlerParameters, IContextProvider } from './context_provider';
|
||||
import type { RequestHandler } from './request_handler';
|
||||
import type { RequestHandlerContextBase } from './request_handler_context';
|
||||
|
||||
/**
|
||||
* An object that handles registration of context providers and configuring handlers with context.
|
||||
*
|
||||
* @remarks
|
||||
* A {@link IContextContainer} can be used by any Core service or plugin (known as the "service owner") which wishes to
|
||||
* expose APIs in a handler function. The container object will manage registering context providers and configuring a
|
||||
* handler with all of the contexts that should be exposed to the handler's plugin. This is dependent on the
|
||||
* dependencies that the handler's plugin declares.
|
||||
*
|
||||
* Contexts providers are executed in the order they were registered. Each provider gets access to context values
|
||||
* provided by any plugins that it depends on.
|
||||
*
|
||||
* In order to configure a handler with context, you must call the {@link IContextContainer.createHandler} function and
|
||||
* use the returned handler which will automatically build a context object when called.
|
||||
*
|
||||
* When registering context or creating handlers, the _calling plugin's opaque id_ must be provided. This id is passed
|
||||
* in via the plugin's initializer and can be accessed from the {@link PluginInitializerContext.opaqueId} Note this
|
||||
* should NOT be the context service owner's id, but the plugin that is actually registering the context or handler.
|
||||
*
|
||||
* ```ts
|
||||
* // Correct
|
||||
* class MyPlugin {
|
||||
* private readonly handlers = new Map();
|
||||
*
|
||||
* setup(core) {
|
||||
* this.contextContainer = core.context.createContextContainer();
|
||||
* return {
|
||||
* registerContext(pluginOpaqueId, contextName, provider) {
|
||||
* this.contextContainer.registerContext(pluginOpaqueId, contextName, provider);
|
||||
* },
|
||||
* registerRoute(pluginOpaqueId, path, handler) {
|
||||
* this.handlers.set(
|
||||
* path,
|
||||
* this.contextContainer.createHandler(pluginOpaqueId, handler)
|
||||
* );
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* // Incorrect
|
||||
* class MyPlugin {
|
||||
* private readonly handlers = new Map();
|
||||
*
|
||||
* constructor(private readonly initContext: PluginInitializerContext) {}
|
||||
*
|
||||
* setup(core) {
|
||||
* this.contextContainer = core.context.createContextContainer();
|
||||
* return {
|
||||
* registerContext(contextName, provider) {
|
||||
* // BUG!
|
||||
* // This would leak this context to all handlers rather that only plugins that depend on the calling plugin.
|
||||
* this.contextContainer.registerContext(this.initContext.opaqueId, contextName, provider);
|
||||
* },
|
||||
* registerRoute(path, handler) {
|
||||
* this.handlers.set(
|
||||
* path,
|
||||
* // BUG!
|
||||
* // This handler will not receive any contexts provided by other dependencies of the calling plugin.
|
||||
* this.contextContainer.createHandler(this.initContext.opaqueId, handler)
|
||||
* );
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface IContextContainer {
|
||||
/**
|
||||
* Register a new context provider.
|
||||
*
|
||||
* @remarks
|
||||
* The value (or resolved Promise value) returned by the `provider` function will be attached to the context object
|
||||
* on the key specified by `contextName`.
|
||||
*
|
||||
* Throws an exception if more than one provider is registered for the same `contextName`.
|
||||
*
|
||||
* @param pluginOpaqueId - The plugin opaque ID for the plugin that registers this context.
|
||||
* @param contextName - The key of the `TContext` object this provider supplies the value for.
|
||||
* @param provider - A {@link IContextProvider} to be called each time a new context is created.
|
||||
* @returns The {@link IContextContainer} for method chaining.
|
||||
*/
|
||||
registerContext<Context extends RequestHandlerContextBase, ContextName extends keyof Context>(
|
||||
pluginOpaqueId: PluginOpaqueId,
|
||||
contextName: ContextName,
|
||||
provider: IContextProvider<Context, ContextName>
|
||||
): this;
|
||||
|
||||
/**
|
||||
* Create a new handler function pre-wired to context for the plugin.
|
||||
*
|
||||
* @param pluginOpaqueId - The plugin opaque ID for the plugin that registers this handler.
|
||||
* @param handler - Handler function to pass context object to.
|
||||
* @returns A function that takes `RequestHandler` parameters, calls `handler` with a new context, and returns a Promise of
|
||||
* the `handler` return value.
|
||||
*/
|
||||
createHandler(
|
||||
pluginOpaqueId: PluginOpaqueId,
|
||||
handler: RequestHandler
|
||||
): (...rest: HandlerParameters<RequestHandler>) => ShallowPromise<ReturnType<RequestHandler>>;
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { MaybePromise } from '@kbn/utility-types';
|
||||
import type { RequestHandler } from './request_handler';
|
||||
import type { RequestHandlerContextBase } from './request_handler_context';
|
||||
|
||||
/**
|
||||
* A function that returns a context value for a specific key of given context type.
|
||||
*
|
||||
* @remarks
|
||||
* This function will be called each time a new context is built for a handler invocation.
|
||||
*
|
||||
* @param context - A partial context object containing only the keys for values provided by plugin dependencies
|
||||
* @param rest - Additional parameters provided by the service owner of this context
|
||||
* @returns The context value associated with this key. May also return a Promise which will be resolved before
|
||||
* attaching to the context object.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type IContextProvider<
|
||||
Context extends RequestHandlerContextBase,
|
||||
ContextName extends keyof Context
|
||||
> = (
|
||||
// context.core will always be available, but plugin contexts are typed as optional
|
||||
context: Omit<Context, ContextName>,
|
||||
...rest: HandlerParameters<RequestHandler>
|
||||
) => MaybePromise<Awaited<Context[ContextName]>>;
|
||||
|
||||
/**
|
||||
* A function that accepts a context object and an optional number of additional arguments. Used for the generic types
|
||||
* in {@link IContextContainer}
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type HandlerFunction<T extends object> = (context: T, ...args: any[]) => any;
|
||||
|
||||
/**
|
||||
* Extracts the type of the first argument of a {@link HandlerFunction} to represent the type of the context.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type HandlerContextType<T extends HandlerFunction<any>> = T extends HandlerFunction<infer U>
|
||||
? U
|
||||
: never;
|
||||
|
||||
/**
|
||||
* Extracts the types of the additional arguments of a {@link HandlerFunction}, excluding the
|
||||
* {@link HandlerContextType}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type HandlerParameters<T extends HandlerFunction<any>> = T extends (
|
||||
context: any,
|
||||
...args: infer U
|
||||
) => any
|
||||
? U
|
||||
: never;
|
59
packages/core/http/core-http-server/src/router/headers.ts
Normal file
59
packages/core/http/core-http-server/src/router/headers.ts
Normal file
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { IncomingHttpHeaders } from 'http';
|
||||
|
||||
/**
|
||||
* Converts an object type to a new object type where each string
|
||||
* key is copied to the values of the object, and non string keys are
|
||||
* given a `never` value. This allows us to map over the values and
|
||||
* get the list of all string keys on a type in `KnownKeys<T>`
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type StringKeysAsVals<T> = {
|
||||
[K in keyof T]: string extends K ? never : number extends K ? never : K;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a Union type of all known keys of a given interface.
|
||||
* @example
|
||||
* ```ts
|
||||
* interface Person {
|
||||
* name: string;
|
||||
* age: number;
|
||||
* [attributes: string]: string | number;
|
||||
* }
|
||||
* type PersonKnownKeys = KnownKeys<Person>; // "age" | "name"
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type KnownKeys<T> = StringKeysAsVals<T> extends { [_ in keyof T]: infer U } ? U : never;
|
||||
|
||||
/**
|
||||
* Set of well-known HTTP headers.
|
||||
* @public
|
||||
*/
|
||||
export type KnownHeaders = KnownKeys<IncomingHttpHeaders>;
|
||||
|
||||
/**
|
||||
* Http request headers to read.
|
||||
* @public
|
||||
*/
|
||||
export type Headers = { [header in KnownHeaders]?: string | string[] | undefined } & {
|
||||
[header: string]: string | string[] | undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
* Http response headers to set.
|
||||
* @public
|
||||
*/
|
||||
export type ResponseHeaders =
|
||||
| Record<KnownHeaders, string | string[]>
|
||||
| Record<string, string | string[]>;
|
71
packages/core/http/core-http-server/src/router/index.ts
Normal file
71
packages/core/http/core-http-server/src/router/index.ts
Normal file
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export type {
|
||||
IContextProvider,
|
||||
HandlerContextType,
|
||||
HandlerFunction,
|
||||
HandlerParameters,
|
||||
} from './context_provider';
|
||||
export type { IContextContainer } from './context_container';
|
||||
export type {
|
||||
Headers,
|
||||
KnownHeaders,
|
||||
KnownKeys,
|
||||
ResponseHeaders,
|
||||
StringKeysAsVals,
|
||||
} from './headers';
|
||||
export type {
|
||||
KibanaRequest,
|
||||
KibanaRequestAuth,
|
||||
KibanaRequestEvents,
|
||||
KibanaRequestRoute,
|
||||
KibanaRequestRouteOptions,
|
||||
KibanaRequestState,
|
||||
KibanaRouteOptions,
|
||||
} from './request';
|
||||
export type { RequestHandlerWrapper, RequestHandler } from './request_handler';
|
||||
export type { RequestHandlerContextBase } from './request_handler_context';
|
||||
export type {
|
||||
ResponseError,
|
||||
CustomHttpResponseOptions,
|
||||
HttpResponseOptions,
|
||||
HttpResponsePayload,
|
||||
IKibanaResponse,
|
||||
RedirectResponseOptions,
|
||||
ResponseErrorAttributes,
|
||||
ErrorHttpResponseOptions,
|
||||
} from './response';
|
||||
export type {
|
||||
RouteConfigOptions,
|
||||
RouteMethod,
|
||||
DestructiveRouteMethod,
|
||||
RouteConfig,
|
||||
RouteConfigOptionsBody,
|
||||
RouteContentType,
|
||||
SafeRouteMethod,
|
||||
} from './route';
|
||||
export { validBodyOutput } from './route';
|
||||
export type {
|
||||
RouteValidationFunction,
|
||||
RouteValidationResultFactory,
|
||||
RouteValidationSpec,
|
||||
RouteValidatorConfig,
|
||||
RouteValidatorFullConfig,
|
||||
RouteValidatorOptions,
|
||||
} from './route_validator';
|
||||
export { RouteValidationError } from './route_validator';
|
||||
export type { IRouter, RouteRegistrar, RouterRoute } from './router';
|
||||
export type { IKibanaSocket } from './socket';
|
||||
export type {
|
||||
KibanaErrorResponseFactory,
|
||||
KibanaRedirectionResponseFactory,
|
||||
KibanaSuccessResponseFactory,
|
||||
KibanaResponseFactory,
|
||||
LifecycleResponseFactory,
|
||||
} from './response_factory';
|
167
packages/core/http/core-http-server/src/router/request.ts
Normal file
167
packages/core/http/core-http-server/src/router/request.ts
Normal file
|
@ -0,0 +1,167 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { URL } from 'url';
|
||||
import type { RequestApplicationState, RouteOptionsApp } from '@hapi/hapi';
|
||||
import type { Observable } from 'rxjs';
|
||||
import type { RecursiveReadonly } from '@kbn/utility-types';
|
||||
import type { IKibanaSocket } from './socket';
|
||||
import type { RouteMethod, RouteConfigOptions } from './route';
|
||||
import type { Headers } from './headers';
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface KibanaRouteOptions extends RouteOptionsApp {
|
||||
xsrfRequired: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface KibanaRequestState extends RequestApplicationState {
|
||||
requestId: string;
|
||||
requestUuid: string;
|
||||
rewrittenUrl?: URL;
|
||||
traceId?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Route options: If 'GET' or 'OPTIONS' method, body options won't be returned.
|
||||
* @public
|
||||
*/
|
||||
export type KibanaRequestRouteOptions<Method extends RouteMethod> = Method extends 'get' | 'options'
|
||||
? Required<Omit<RouteConfigOptions<Method>, 'body'>>
|
||||
: Required<RouteConfigOptions<Method>>;
|
||||
|
||||
/**
|
||||
* Request specific route information exposed to a handler.
|
||||
* @public
|
||||
* */
|
||||
export interface KibanaRequestRoute<Method extends RouteMethod> {
|
||||
path: string;
|
||||
method: Method;
|
||||
options: KibanaRequestRouteOptions<Method>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Request events.
|
||||
* @public
|
||||
* */
|
||||
export interface KibanaRequestEvents {
|
||||
/**
|
||||
* Observable that emits once if and when the request has been aborted.
|
||||
*/
|
||||
aborted$: Observable<void>;
|
||||
|
||||
/**
|
||||
* Observable that emits once if and when the request has been completely handled.
|
||||
*
|
||||
* @remarks
|
||||
* The request may be considered completed if:
|
||||
* - A response has been sent to the client; or
|
||||
* - The request was aborted.
|
||||
*/
|
||||
completed$: Observable<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Auth status for this request.
|
||||
* @public
|
||||
*/
|
||||
export interface KibanaRequestAuth {
|
||||
/** true if the request has been successfully authenticated, false otherwise. */
|
||||
isAuthenticated: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Kibana specific abstraction for an incoming request.
|
||||
* @public
|
||||
*/
|
||||
export interface KibanaRequest<
|
||||
Params = unknown,
|
||||
Query = unknown,
|
||||
Body = unknown,
|
||||
Method extends RouteMethod = any
|
||||
> {
|
||||
/**
|
||||
* A identifier to identify this request.
|
||||
*
|
||||
* @remarks
|
||||
* Depending on the user's configuration, this value may be sourced from the
|
||||
* incoming request's `X-Opaque-Id` header which is not guaranteed to be unique
|
||||
* per request.
|
||||
*/
|
||||
readonly id: string;
|
||||
|
||||
/**
|
||||
* A UUID to identify this request.
|
||||
*
|
||||
* @remarks
|
||||
* This value is NOT sourced from the incoming request's `X-Opaque-Id` header. it
|
||||
* is always a UUID uniquely identifying the request.
|
||||
*/
|
||||
readonly uuid: string;
|
||||
|
||||
/** a WHATWG URL standard object. */
|
||||
readonly url: URL;
|
||||
|
||||
/** matched route details */
|
||||
readonly route: RecursiveReadonly<KibanaRequestRoute<Method>>;
|
||||
|
||||
/**
|
||||
* Readonly copy of incoming request headers.
|
||||
* @remarks
|
||||
* This property will contain a `filtered` copy of request headers.
|
||||
*/
|
||||
readonly headers: Headers;
|
||||
|
||||
/**
|
||||
* Whether or not the request is a "system request" rather than an application-level request.
|
||||
* Can be set on the client using the `HttpFetchOptions#asSystemRequest` option.
|
||||
*/
|
||||
readonly isSystemRequest: boolean;
|
||||
|
||||
/**
|
||||
* The socket associated with this request.
|
||||
* See {@link IKibanaSocket}.
|
||||
*/
|
||||
readonly socket: IKibanaSocket;
|
||||
|
||||
/**
|
||||
* Allow to listen to events bound to this request.
|
||||
* See {@link KibanaRequestEvents}.
|
||||
*/
|
||||
readonly events: KibanaRequestEvents;
|
||||
|
||||
/**
|
||||
* The auth status of this request.
|
||||
* See {@link KibanaRequestAuth}.
|
||||
*/
|
||||
readonly auth: KibanaRequestAuth;
|
||||
|
||||
/**
|
||||
* URL rewritten in onPreRouting request interceptor.
|
||||
*/
|
||||
readonly rewrittenUrl?: URL;
|
||||
|
||||
/**
|
||||
* The path parameter of this request.
|
||||
*/
|
||||
readonly params: Params;
|
||||
|
||||
/**
|
||||
* The query parameter of this request.
|
||||
*/
|
||||
readonly query: Query;
|
||||
|
||||
/**
|
||||
* The body payload of this request.
|
||||
*/
|
||||
readonly body: Body;
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { RouteMethod } from './route';
|
||||
import type { KibanaRequest } from './request';
|
||||
import type { RequestHandlerContextBase } from './request_handler_context';
|
||||
import type { IKibanaResponse } from './response';
|
||||
import type { KibanaResponseFactory } from './response_factory';
|
||||
|
||||
/**
|
||||
* A function executed when route path matched requested resource path.
|
||||
* Request handler is expected to return a result of one of {@link KibanaResponseFactory} functions.
|
||||
* If anything else is returned, or an error is thrown, the HTTP service will automatically log the error
|
||||
* and respond `500 - Internal Server Error`.
|
||||
* @param context {@link RequestHandlerContext} - the core context exposed for this request.
|
||||
* @param request {@link KibanaRequest} - object containing information about requested resource,
|
||||
* such as path, method, headers, parameters, query, body, etc.
|
||||
* @param response {@link KibanaResponseFactory} - a set of helper functions used to respond to a request.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const router = httpSetup.createRouter();
|
||||
* // creates a route handler for GET request on 'my-app/path/{id}' path
|
||||
* router.get(
|
||||
* {
|
||||
* path: 'path/{id}',
|
||||
* // defines a validation schema for a named segment of the route path
|
||||
* validate: {
|
||||
* params: schema.object({
|
||||
* id: schema.string(),
|
||||
* }),
|
||||
* },
|
||||
* },
|
||||
* // function to execute to create a responses
|
||||
* async (context, request, response) => {
|
||||
* const data = await context.findObject(request.params.id);
|
||||
* // creates a command to respond with 'not found' error
|
||||
* if (!data) return response.notFound();
|
||||
* // creates a command to send found data to the client
|
||||
* return response.ok(data);
|
||||
* }
|
||||
* );
|
||||
* ```
|
||||
* @public
|
||||
*/
|
||||
export type RequestHandler<
|
||||
P = unknown,
|
||||
Q = unknown,
|
||||
B = unknown,
|
||||
Context extends RequestHandlerContextBase = RequestHandlerContextBase,
|
||||
Method extends RouteMethod = any,
|
||||
ResponseFactory extends KibanaResponseFactory = KibanaResponseFactory
|
||||
> = (
|
||||
context: Context,
|
||||
request: KibanaRequest<P, Q, B, Method>,
|
||||
response: ResponseFactory
|
||||
) => IKibanaResponse<any> | Promise<IKibanaResponse<any>>;
|
||||
|
||||
/**
|
||||
* Type-safe wrapper for {@link RequestHandler} function.
|
||||
* @example
|
||||
* ```typescript
|
||||
* export const wrapper: RequestHandlerWrapper = handler => {
|
||||
* return async (context, request, response) => {
|
||||
* // do some logic
|
||||
* ...
|
||||
* };
|
||||
* }
|
||||
* ```
|
||||
* @public
|
||||
*/
|
||||
export type RequestHandlerWrapper = <
|
||||
P,
|
||||
Q,
|
||||
B,
|
||||
Context extends RequestHandlerContextBase = RequestHandlerContextBase,
|
||||
Method extends RouteMethod = any,
|
||||
ResponseFactory extends KibanaResponseFactory = KibanaResponseFactory
|
||||
>(
|
||||
handler: RequestHandler<P, Q, B, Context, Method, ResponseFactory>
|
||||
) => RequestHandler<P, Q, B, Context, Method, ResponseFactory>;
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { AwaitedProperties } from '@kbn/utility-types';
|
||||
|
||||
/**
|
||||
* Base, abstract type for request handler contexts.
|
||||
* @public
|
||||
**/
|
||||
export interface RequestHandlerContextBase {
|
||||
/**
|
||||
* Await all the specified context parts and return them.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const resolved = await context.resolve(['core', 'pluginA']);
|
||||
* const esClient = resolved.core.elasticsearch.client;
|
||||
* const pluginAService = resolved.pluginA.someService;
|
||||
* ```
|
||||
*/
|
||||
resolve: <T extends keyof Omit<this, 'resolve'>>(
|
||||
parts: T[]
|
||||
) => Promise<AwaitedProperties<Pick<this, T>>>;
|
||||
}
|
91
packages/core/http/core-http-server/src/router/response.ts
Normal file
91
packages/core/http/core-http-server/src/router/response.ts
Normal file
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { Stream } from 'stream';
|
||||
import type { ResponseHeaders } from './headers';
|
||||
|
||||
/**
|
||||
* HTTP response parameters
|
||||
* @public
|
||||
*/
|
||||
export interface HttpResponseOptions {
|
||||
/** HTTP message to send to the client */
|
||||
body?: HttpResponsePayload;
|
||||
/** HTTP Headers with additional information about response */
|
||||
headers?: ResponseHeaders;
|
||||
/** Bypass the default error formatting */
|
||||
bypassErrorFormat?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Data send to the client as a response payload.
|
||||
* @public
|
||||
*/
|
||||
export type HttpResponsePayload = undefined | string | Record<string, any> | Buffer | Stream;
|
||||
|
||||
/**
|
||||
* Additional data to provide error details.
|
||||
* @public
|
||||
*/
|
||||
export type ResponseErrorAttributes = Record<string, any>;
|
||||
/**
|
||||
* Error message and optional data send to the client in case of error.
|
||||
* @public
|
||||
*/
|
||||
export type ResponseError =
|
||||
| string
|
||||
| Error
|
||||
| {
|
||||
message: string | Error;
|
||||
attributes?: ResponseErrorAttributes;
|
||||
};
|
||||
|
||||
/**
|
||||
* A response data object, expected to returned as a result of {@link RequestHandler} execution
|
||||
* @public
|
||||
*/
|
||||
export interface IKibanaResponse<T extends HttpResponsePayload | ResponseError = any> {
|
||||
readonly status: number;
|
||||
readonly payload?: T;
|
||||
readonly options: HttpResponseOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* HTTP response parameters for a response with adjustable status code.
|
||||
* @public
|
||||
*/
|
||||
export interface CustomHttpResponseOptions<T extends HttpResponsePayload | ResponseError> {
|
||||
/** HTTP message to send to the client */
|
||||
body?: T;
|
||||
/** HTTP Headers with additional information about response */
|
||||
headers?: ResponseHeaders;
|
||||
/** Bypass the default error formatting */
|
||||
bypassErrorFormat?: boolean;
|
||||
statusCode: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* HTTP response parameters for redirection response
|
||||
* @public
|
||||
*/
|
||||
export type RedirectResponseOptions = HttpResponseOptions & {
|
||||
headers: {
|
||||
location: string;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* HTTP response parameters
|
||||
* @public
|
||||
*/
|
||||
export interface ErrorHttpResponseOptions {
|
||||
/** HTTP message to send to the client */
|
||||
body?: ResponseError;
|
||||
/** HTTP Headers with additional information about response */
|
||||
headers?: ResponseHeaders;
|
||||
}
|
|
@ -0,0 +1,213 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { Stream } from 'stream';
|
||||
import type {
|
||||
CustomHttpResponseOptions,
|
||||
HttpResponseOptions,
|
||||
HttpResponsePayload,
|
||||
IKibanaResponse,
|
||||
RedirectResponseOptions,
|
||||
ResponseError,
|
||||
ErrorHttpResponseOptions,
|
||||
} from './response';
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface KibanaSuccessResponseFactory {
|
||||
/**
|
||||
* The request has succeeded.
|
||||
* Status code: `200`.
|
||||
* @param options - {@link HttpResponseOptions} configures HTTP response body & headers.
|
||||
*/
|
||||
ok(options?: HttpResponseOptions): IKibanaResponse;
|
||||
|
||||
/**
|
||||
* The request has been accepted for processing.
|
||||
* Status code: `202`.
|
||||
* @param options - {@link HttpResponseOptions} configures HTTP response body & headers.
|
||||
*/
|
||||
accepted(options?: HttpResponseOptions): IKibanaResponse;
|
||||
|
||||
/**
|
||||
* The server has successfully fulfilled the request and that there is no additional content to send in the response payload body.
|
||||
* Status code: `204`.
|
||||
* @param options - {@link HttpResponseOptions} configures HTTP response body & headers.
|
||||
*/
|
||||
noContent(options?: HttpResponseOptions): IKibanaResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface KibanaRedirectionResponseFactory {
|
||||
/**
|
||||
* Redirect to a different URI.
|
||||
* Status code: `302`.
|
||||
* @param options - {@link RedirectResponseOptions} configures HTTP response body & headers.
|
||||
* Expects `location` header to be set.
|
||||
*/
|
||||
redirected(options: RedirectResponseOptions): IKibanaResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface KibanaErrorResponseFactory {
|
||||
/**
|
||||
* The server cannot process the request due to something that is perceived to be a client error.
|
||||
* Status code: `400`.
|
||||
* @param options - {@link HttpResponseOptions} configures HTTP response headers, error message and other error details to pass to the client
|
||||
*/
|
||||
badRequest(options?: ErrorHttpResponseOptions): IKibanaResponse;
|
||||
|
||||
/**
|
||||
* The request cannot be applied because it lacks valid authentication credentials for the target resource.
|
||||
* Status code: `401`.
|
||||
* @param options - {@link HttpResponseOptions} configures HTTP response headers, error message and other error details to pass to the client
|
||||
*/
|
||||
unauthorized(options?: ErrorHttpResponseOptions): IKibanaResponse;
|
||||
|
||||
/**
|
||||
* Server cannot grant access to a resource.
|
||||
* Status code: `403`.
|
||||
* @param options - {@link HttpResponseOptions} configures HTTP response headers, error message and other error details to pass to the client
|
||||
*/
|
||||
forbidden(options?: ErrorHttpResponseOptions): IKibanaResponse;
|
||||
|
||||
/**
|
||||
* Server cannot find a current representation for the target resource.
|
||||
* Status code: `404`.
|
||||
* @param options - {@link HttpResponseOptions} configures HTTP response headers, error message and other error details to pass to the client
|
||||
*/
|
||||
notFound(options?: ErrorHttpResponseOptions): IKibanaResponse;
|
||||
|
||||
/**
|
||||
* The request could not be completed due to a conflict with the current state of the target resource.
|
||||
* Status code: `409`.
|
||||
* @param options - {@link HttpResponseOptions} configures HTTP response headers, error message and other error details to pass to the client
|
||||
*/
|
||||
conflict(options?: ErrorHttpResponseOptions): IKibanaResponse;
|
||||
|
||||
/**
|
||||
* Creates an error response with defined status code and payload.
|
||||
* @param options - {@link CustomHttpResponseOptions} configures HTTP response headers, error message and other error details to pass to the client
|
||||
*/
|
||||
customError(options: CustomHttpResponseOptions<ResponseError | Buffer | Stream>): IKibanaResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set of helpers used to create `KibanaResponse` to form HTTP response on an incoming request.
|
||||
* Should be returned as a result of {@link RequestHandler} execution.
|
||||
*
|
||||
* @example
|
||||
* 1. Successful response. Supported types of response body are:
|
||||
* - `undefined`, no content to send.
|
||||
* - `string`, send text
|
||||
* - `JSON`, send JSON object, HTTP server will throw if given object is not valid (has circular references, for example)
|
||||
* - `Stream` send data stream
|
||||
* - `Buffer` send binary stream
|
||||
* ```js
|
||||
* return response.ok();
|
||||
* return response.ok({ body: 'ack' });
|
||||
* return response.ok({ body: { id: '1' } });
|
||||
* return response.ok({ body: Buffer.from(...) });
|
||||
*
|
||||
* const stream = new Stream.PassThrough();
|
||||
* fs.createReadStream('./file').pipe(stream);
|
||||
* return res.ok({ body: stream });
|
||||
* ```
|
||||
* HTTP headers are configurable via response factory parameter `options` {@link HttpResponseOptions}.
|
||||
*
|
||||
* ```js
|
||||
* return response.ok({
|
||||
* body: { id: '1' },
|
||||
* headers: {
|
||||
* 'content-type': 'application/json'
|
||||
* }
|
||||
* });
|
||||
* ```
|
||||
* 2. Redirection response. Redirection URL is configures via 'Location' header.
|
||||
* ```js
|
||||
* return response.redirected({
|
||||
* body: 'The document has moved',
|
||||
* headers: {
|
||||
* location: '/new-url',
|
||||
* },
|
||||
* });
|
||||
* ```
|
||||
* 3. Error response. You may pass an error message to the client, where error message can be:
|
||||
* - `string` send message text
|
||||
* - `Error` send the message text of given Error object.
|
||||
* - `{ message: string | Error, attributes: {data: Record<string, any>, ...} }` - send message text and attach additional error data.
|
||||
* ```js
|
||||
* return response.unauthorized({
|
||||
* body: 'User has no access to the requested resource.',
|
||||
* headers: {
|
||||
* 'WWW-Authenticate': 'challenge',
|
||||
* }
|
||||
* })
|
||||
* return response.badRequest();
|
||||
* return response.badRequest({ body: 'validation error' });
|
||||
*
|
||||
* try {
|
||||
* // ...
|
||||
* } catch(error){
|
||||
* return response.badRequest({ body: error });
|
||||
* }
|
||||
*
|
||||
* return response.badRequest({
|
||||
* body:{
|
||||
* message: 'validation error',
|
||||
* attributes: {
|
||||
* requestBody: request.body,
|
||||
* failedFields: validationResult
|
||||
* }
|
||||
* }
|
||||
* });
|
||||
*
|
||||
* try {
|
||||
* // ...
|
||||
* } catch(error) {
|
||||
* return response.badRequest({
|
||||
* body: error
|
||||
* });
|
||||
* }
|
||||
*
|
||||
* ```
|
||||
* 4. Custom response. `ResponseFactory` may not cover your use case, so you can use the `custom` function to customize the response.
|
||||
* ```js
|
||||
* return response.custom({
|
||||
* body: 'ok',
|
||||
* statusCode: 201,
|
||||
* headers: {
|
||||
* location: '/created-url'
|
||||
* }
|
||||
* })
|
||||
* ```
|
||||
* @public
|
||||
*/
|
||||
export type KibanaResponseFactory = KibanaSuccessResponseFactory &
|
||||
KibanaRedirectionResponseFactory &
|
||||
KibanaErrorResponseFactory & {
|
||||
/**
|
||||
* Creates a response with defined status code and payload.
|
||||
* @param options - {@link CustomHttpResponseOptions} configures HTTP response parameters.
|
||||
*/
|
||||
custom<T extends HttpResponsePayload | ResponseError>(
|
||||
options: CustomHttpResponseOptions<T>
|
||||
): IKibanaResponse;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates an object containing redirection or error response with error details, HTTP headers, and other data transmitted to the client.
|
||||
* @public
|
||||
*/
|
||||
export type LifecycleResponseFactory = KibanaRedirectionResponseFactory &
|
||||
KibanaErrorResponseFactory;
|
231
packages/core/http/core-http-server/src/router/route.ts
Normal file
231
packages/core/http/core-http-server/src/router/route.ts
Normal file
|
@ -0,0 +1,231 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { RouteValidatorFullConfig } from './route_validator';
|
||||
|
||||
/**
|
||||
* The set of valid body.output
|
||||
* @public
|
||||
*/
|
||||
export const validBodyOutput = ['data', 'stream'] as const;
|
||||
|
||||
/**
|
||||
* Set of HTTP methods changing the state of the server.
|
||||
* @public
|
||||
*/
|
||||
export type DestructiveRouteMethod = 'post' | 'put' | 'delete' | 'patch';
|
||||
|
||||
/**
|
||||
* Set of HTTP methods not changing the state of the server.
|
||||
* @public
|
||||
*/
|
||||
export type SafeRouteMethod = 'get' | 'options';
|
||||
|
||||
/**
|
||||
* The set of common HTTP methods supported by Kibana routing.
|
||||
* @public
|
||||
*/
|
||||
export type RouteMethod = SafeRouteMethod | DestructiveRouteMethod;
|
||||
|
||||
/**
|
||||
* The set of supported parseable Content-Types
|
||||
* @public
|
||||
*/
|
||||
export type RouteContentType =
|
||||
| 'application/json'
|
||||
| 'application/*+json'
|
||||
| 'application/octet-stream'
|
||||
| 'application/x-www-form-urlencoded'
|
||||
| 'multipart/form-data'
|
||||
| 'text/*';
|
||||
|
||||
/**
|
||||
* Additional body options for a route
|
||||
* @public
|
||||
*/
|
||||
export interface RouteConfigOptionsBody {
|
||||
/**
|
||||
* A string or an array of strings with the allowed mime types for the endpoint. Use this settings to limit the set of allowed mime types. Note that allowing additional mime types not listed
|
||||
* above will not enable them to be parsed, and if parse is true, the request will result in an error response.
|
||||
*
|
||||
* Default value: allows parsing of the following mime types:
|
||||
* * application/json
|
||||
* * application/*+json
|
||||
* * application/octet-stream
|
||||
* * application/x-www-form-urlencoded
|
||||
* * multipart/form-data
|
||||
* * text/*
|
||||
*/
|
||||
accepts?: RouteContentType | RouteContentType[] | string | string[];
|
||||
|
||||
/**
|
||||
* Limits the size of incoming payloads to the specified byte count. Allowing very large payloads may cause the server to run out of memory.
|
||||
*
|
||||
* Default value: The one set in the kibana.yml config file under the parameter `server.maxPayload`.
|
||||
*/
|
||||
maxBytes?: number;
|
||||
|
||||
/**
|
||||
* The processed payload format. The value must be one of:
|
||||
* * 'data' - the incoming payload is read fully into memory. If parse is true, the payload is parsed (JSON, form-decoded, multipart) based on the 'Content-Type' header. If parse is false, a raw
|
||||
* Buffer is returned.
|
||||
* * 'stream' - the incoming payload is made available via a Stream.Readable interface. If the payload is 'multipart/form-data' and parse is true, field values are presented as text while files
|
||||
* are provided as streams. File streams from a 'multipart/form-data' upload will also have a hapi property containing the filename and headers properties. Note that payload streams for multipart
|
||||
* payloads are a synthetic interface created on top of the entire multipart content loaded into memory. To avoid loading large multipart payloads into memory, set parse to false and handle the
|
||||
* multipart payload in the handler using a streaming parser (e.g. pez).
|
||||
*
|
||||
* Default value: 'data', unless no validation.body is provided in the route definition. In that case the default is 'stream' to alleviate memory pressure.
|
||||
*/
|
||||
output?: typeof validBodyOutput[number];
|
||||
|
||||
/**
|
||||
* Determines if the incoming payload is processed or presented raw. Available values:
|
||||
* * true - if the request 'Content-Type' matches the allowed mime types set by allow (for the whole payload as well as parts), the payload is converted into an object when possible. If the
|
||||
* format is unknown, a Bad Request (400) error response is sent. Any known content encoding is decoded.
|
||||
* * false - the raw payload is returned unmodified.
|
||||
* * 'gunzip' - the raw payload is returned unmodified after any known content encoding is decoded.
|
||||
*
|
||||
* Default value: true, unless no validation.body is provided in the route definition. In that case the default is false to alleviate memory pressure.
|
||||
*/
|
||||
parse?: boolean | 'gunzip';
|
||||
}
|
||||
|
||||
/**
|
||||
* Additional route options.
|
||||
* @public
|
||||
*/
|
||||
export interface RouteConfigOptions<Method extends RouteMethod> {
|
||||
/**
|
||||
* Defines authentication mode for a route:
|
||||
* - true. A user has to have valid credentials to access a resource
|
||||
* - false. A user can access a resource without any credentials.
|
||||
* - 'optional'. A user can access a resource, and will be authenticated if provided credentials are valid.
|
||||
* Can be useful when we grant access to a resource but want to identify a user if possible.
|
||||
*
|
||||
* Defaults to `true` if an auth mechanism is registered.
|
||||
*/
|
||||
authRequired?: boolean | 'optional';
|
||||
|
||||
/**
|
||||
* Defines xsrf protection requirements for a route:
|
||||
* - true. Requires an incoming POST/PUT/DELETE request to contain `kbn-xsrf` header.
|
||||
* - false. Disables xsrf protection.
|
||||
*
|
||||
* Set to true by default
|
||||
*/
|
||||
xsrfRequired?: Method extends 'get' ? never : boolean;
|
||||
|
||||
/**
|
||||
* Additional metadata tag strings to attach to the route.
|
||||
*/
|
||||
tags?: readonly string[];
|
||||
|
||||
/**
|
||||
* Additional body options {@link RouteConfigOptionsBody}.
|
||||
*/
|
||||
body?: Method extends 'get' | 'options' ? undefined : RouteConfigOptionsBody;
|
||||
|
||||
/**
|
||||
* Defines per-route timeouts.
|
||||
*/
|
||||
timeout?: {
|
||||
/**
|
||||
* Milliseconds to receive the payload
|
||||
*/
|
||||
payload?: Method extends 'get' | 'options' ? undefined : number;
|
||||
|
||||
/**
|
||||
* Milliseconds the socket can be idle before it's closed
|
||||
*/
|
||||
idleSocket?: number;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Route specific configuration.
|
||||
* @public
|
||||
*/
|
||||
export interface RouteConfig<P, Q, B, Method extends RouteMethod> {
|
||||
/**
|
||||
* The endpoint _within_ the router path to register the route.
|
||||
*
|
||||
* @remarks
|
||||
* E.g. if the router is registered at `/elasticsearch` and the route path is
|
||||
* `/search`, the full path for the route is `/elasticsearch/search`.
|
||||
* Supports:
|
||||
* - named path segments `path/{name}`.
|
||||
* - optional path segments `path/{position?}`.
|
||||
* - multi-segments `path/{coordinates*2}`.
|
||||
* Segments are accessible within a handler function as `params` property of {@link KibanaRequest} object.
|
||||
* To have read access to `params` you *must* specify validation schema with {@link RouteConfig.validate}.
|
||||
*/
|
||||
path: string;
|
||||
|
||||
/**
|
||||
* A schema created with `@kbn/config-schema` that every request will be validated against.
|
||||
*
|
||||
* @remarks
|
||||
* You *must* specify a validation schema to be able to read:
|
||||
* - url path segments
|
||||
* - request query
|
||||
* - request body
|
||||
* To opt out of validating the request, specify `validate: false`. In this case
|
||||
* request params, query, and body will be **empty** objects and have no
|
||||
* access to raw values.
|
||||
* In some cases you may want to use another validation library. To do this, you need to
|
||||
* instruct the `@kbn/config-schema` library to output **non-validated values** with
|
||||
* setting schema as `schema.object({}, { unknowns: 'allow' })`;
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* import { schema } from '@kbn/config-schema';
|
||||
* router.get({
|
||||
* path: 'path/{id}',
|
||||
* validate: {
|
||||
* params: schema.object({
|
||||
* id: schema.string(),
|
||||
* }),
|
||||
* query: schema.object({...}),
|
||||
* body: schema.object({...}),
|
||||
* },
|
||||
* },
|
||||
* (context, req, res,) {
|
||||
* req.params; // type Readonly<{id: string}>
|
||||
* console.log(req.params.id); // value
|
||||
* });
|
||||
*
|
||||
* router.get({
|
||||
* path: 'path/{id}',
|
||||
* validate: false, // handler has no access to params, query, body values.
|
||||
* },
|
||||
* (context, req, res,) {
|
||||
* req.params; // type Readonly<{}>;
|
||||
* console.log(req.params.id); // undefined
|
||||
* });
|
||||
*
|
||||
* router.get({
|
||||
* path: 'path/{id}',
|
||||
* validate: {
|
||||
* // handler has access to raw non-validated params in runtime
|
||||
* params: schema.object({}, { unknowns: 'allow' })
|
||||
* },
|
||||
* },
|
||||
* (context, req, res,) {
|
||||
* req.params; // type Readonly<{}>;
|
||||
* console.log(req.params.id); // value
|
||||
* myValidationLibrary.validate({ params: req.params });
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
validate: RouteValidatorFullConfig<P, Q, B> | false;
|
||||
|
||||
/**
|
||||
* Additional route options {@link RouteConfigOptions}.
|
||||
*/
|
||||
options?: RouteConfigOptions<Method>;
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { ObjectType, SchemaTypeError, Type } from '@kbn/config-schema';
|
||||
|
||||
/**
|
||||
* Error to return when the validation is not successful.
|
||||
* @public
|
||||
*/
|
||||
export class RouteValidationError extends SchemaTypeError {
|
||||
constructor(error: Error | string, path: string[] = []) {
|
||||
super(error, path);
|
||||
|
||||
// Set the prototype explicitly, see:
|
||||
// https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work
|
||||
Object.setPrototypeOf(this, RouteValidationError.prototype);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validation result factory to be used in the custom validation function to return the valid data or validation errors
|
||||
*
|
||||
* See {@link RouteValidationFunction}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface RouteValidationResultFactory {
|
||||
ok: <T>(value: T) => { value: T };
|
||||
badRequest: (error: Error | string, path?: string[]) => { error: RouteValidationError };
|
||||
}
|
||||
|
||||
/**
|
||||
* The custom validation function if @kbn/config-schema is not a valid solution for your specific plugin requirements.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* The validation should look something like:
|
||||
* ```typescript
|
||||
* interface MyExpectedBody {
|
||||
* bar: string;
|
||||
* baz: number;
|
||||
* }
|
||||
*
|
||||
* const myBodyValidation: RouteValidationFunction<MyExpectedBody> = (data, validationResult) => {
|
||||
* const { ok, badRequest } = validationResult;
|
||||
* const { bar, baz } = data || {};
|
||||
* if (typeof bar === 'string' && typeof baz === 'number') {
|
||||
* return ok({ bar, baz });
|
||||
* } else {
|
||||
* return badRequest('Wrong payload', ['body']);
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type RouteValidationFunction<T> = (
|
||||
data: any,
|
||||
validationResult: RouteValidationResultFactory
|
||||
) =>
|
||||
| {
|
||||
value: T;
|
||||
error?: never;
|
||||
}
|
||||
| {
|
||||
value?: never;
|
||||
error: RouteValidationError;
|
||||
};
|
||||
|
||||
/**
|
||||
* Allowed property validation options: either @kbn/config-schema validations or custom validation functions
|
||||
*
|
||||
* See {@link RouteValidationFunction} for custom validation.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type RouteValidationSpec<T> = ObjectType | Type<T> | RouteValidationFunction<T>;
|
||||
|
||||
/**
|
||||
* The configuration object to the RouteValidator class.
|
||||
* Set `params`, `query` and/or `body` to specify the validation logic to follow for that property.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface RouteValidatorConfig<P, Q, B> {
|
||||
/**
|
||||
* Validation logic for the URL params
|
||||
* @public
|
||||
*/
|
||||
params?: RouteValidationSpec<P>;
|
||||
/**
|
||||
* Validation logic for the Query params
|
||||
* @public
|
||||
*/
|
||||
query?: RouteValidationSpec<Q>;
|
||||
/**
|
||||
* Validation logic for the body payload
|
||||
* @public
|
||||
*/
|
||||
body?: RouteValidationSpec<B>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Additional options for the RouteValidator class to modify its default behaviour.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface RouteValidatorOptions {
|
||||
/**
|
||||
* Set the `unsafe` config to avoid running some additional internal *safe* validations on top of your custom validation
|
||||
* @public
|
||||
*/
|
||||
unsafe?: {
|
||||
params?: boolean;
|
||||
query?: boolean;
|
||||
body?: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Route validations config and options merged into one object
|
||||
* @public
|
||||
*/
|
||||
export type RouteValidatorFullConfig<P, Q, B> = RouteValidatorConfig<P, Q, B> &
|
||||
RouteValidatorOptions;
|
99
packages/core/http/core-http-server/src/router/router.ts
Normal file
99
packages/core/http/core-http-server/src/router/router.ts
Normal file
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { Request, ResponseObject, ResponseToolkit } from '@hapi/hapi';
|
||||
import type Boom from '@hapi/boom';
|
||||
import type { RouteConfig, RouteMethod } from './route';
|
||||
import type { RequestHandler, RequestHandlerWrapper } from './request_handler';
|
||||
import type { RequestHandlerContextBase } from './request_handler_context';
|
||||
import type { RouteConfigOptions } from './route';
|
||||
|
||||
/**
|
||||
* Route handler common definition
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type RouteRegistrar<
|
||||
Method extends RouteMethod,
|
||||
Context extends RequestHandlerContextBase = RequestHandlerContextBase
|
||||
> = <P, Q, B>(
|
||||
route: RouteConfig<P, Q, B, Method>,
|
||||
handler: RequestHandler<P, Q, B, Context, Method>
|
||||
) => void;
|
||||
|
||||
/**
|
||||
* Registers route handlers for specified resource path and method.
|
||||
* See {@link RouteConfig} and {@link RequestHandler} for more information about arguments to route registrations.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface IRouter<Context extends RequestHandlerContextBase = RequestHandlerContextBase> {
|
||||
/**
|
||||
* Resulted path
|
||||
*/
|
||||
routerPath: string;
|
||||
|
||||
/**
|
||||
* Register a route handler for `GET` request.
|
||||
* @param route {@link RouteConfig} - a route configuration.
|
||||
* @param handler {@link RequestHandler} - a function to call to respond to an incoming request
|
||||
*/
|
||||
get: RouteRegistrar<'get', Context>;
|
||||
|
||||
/**
|
||||
* Register a route handler for `POST` request.
|
||||
* @param route {@link RouteConfig} - a route configuration.
|
||||
* @param handler {@link RequestHandler} - a function to call to respond to an incoming request
|
||||
*/
|
||||
post: RouteRegistrar<'post', Context>;
|
||||
|
||||
/**
|
||||
* Register a route handler for `PUT` request.
|
||||
* @param route {@link RouteConfig} - a route configuration.
|
||||
* @param handler {@link RequestHandler} - a function to call to respond to an incoming request
|
||||
*/
|
||||
put: RouteRegistrar<'put', Context>;
|
||||
|
||||
/**
|
||||
* Register a route handler for `PATCH` request.
|
||||
* @param route {@link RouteConfig} - a route configuration.
|
||||
* @param handler {@link RequestHandler} - a function to call to respond to an incoming request
|
||||
*/
|
||||
patch: RouteRegistrar<'patch', Context>;
|
||||
|
||||
/**
|
||||
* Register a route handler for `DELETE` request.
|
||||
* @param route {@link RouteConfig} - a route configuration.
|
||||
* @param handler {@link RequestHandler} - a function to call to respond to an incoming request
|
||||
*/
|
||||
delete: RouteRegistrar<'delete', Context>;
|
||||
|
||||
/**
|
||||
* Wrap a router handler to catch and converts legacy boom errors to proper custom errors.
|
||||
* @param handler {@link RequestHandler} - a route handler to wrap
|
||||
*/
|
||||
handleLegacyErrors: RequestHandlerWrapper;
|
||||
|
||||
/**
|
||||
* Returns all routes registered with this router.
|
||||
* @returns List of registered routes.
|
||||
* @internal
|
||||
*/
|
||||
getRoutes: () => RouterRoute[];
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export interface RouterRoute {
|
||||
method: RouteMethod;
|
||||
path: string;
|
||||
options: RouteConfigOptions<RouteMethod>;
|
||||
handler: (
|
||||
req: Request,
|
||||
responseToolkit: ResponseToolkit
|
||||
) => Promise<ResponseObject | Boom.Boom<any>>;
|
||||
}
|
54
packages/core/http/core-http-server/src/router/socket.ts
Normal file
54
packages/core/http/core-http-server/src/router/socket.ts
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { DetailedPeerCertificate, PeerCertificate } from 'tls';
|
||||
|
||||
/**
|
||||
* A tiny abstraction for TCP socket.
|
||||
* @public
|
||||
*/
|
||||
export interface IKibanaSocket {
|
||||
getPeerCertificate(detailed: true): DetailedPeerCertificate | null;
|
||||
getPeerCertificate(detailed: false): PeerCertificate | null;
|
||||
/**
|
||||
* Returns an object representing the peer's certificate.
|
||||
* The returned object has some properties corresponding to the field of the certificate.
|
||||
* If detailed argument is true the full chain with issuer property will be returned,
|
||||
* if false only the top certificate without issuer property.
|
||||
* If the peer does not provide a certificate, it returns null.
|
||||
* @param detailed - If true; the full chain with issuer property will be returned.
|
||||
* @returns An object representing the peer's certificate.
|
||||
*/
|
||||
getPeerCertificate(detailed?: boolean): PeerCertificate | DetailedPeerCertificate | null;
|
||||
|
||||
/**
|
||||
* Returns a string containing the negotiated SSL/TLS protocol version of the current connection. The value 'unknown' will be returned for
|
||||
* connected sockets that have not completed the handshaking process. The value null will be returned for server sockets or disconnected
|
||||
* client sockets. See https://www.openssl.org/docs/man1.0.2/ssl/SSL_get_version.html for more information.
|
||||
*/
|
||||
getProtocol(): string | null;
|
||||
|
||||
/**
|
||||
* Renegotiates a connection to obtain the peer's certificate. This cannot be used when the protocol version is TLSv1.3.
|
||||
* @param options - The options may contain the following fields: rejectUnauthorized, requestCert (See tls.createServer() for details).
|
||||
* @returns A Promise that will be resolved if renegotiation succeeded, or will be rejected if renegotiation failed.
|
||||
*/
|
||||
renegotiate(options: { rejectUnauthorized?: boolean; requestCert?: boolean }): Promise<void>;
|
||||
|
||||
/**
|
||||
* Indicates whether or not the peer certificate was signed by one of the specified CAs. When TLS
|
||||
* isn't used the value is `undefined`.
|
||||
*/
|
||||
readonly authorized?: boolean;
|
||||
|
||||
/**
|
||||
* The reason why the peer's certificate has not been verified. This property becomes available
|
||||
* only when `authorized` is `false`.
|
||||
*/
|
||||
readonly authorizationError?: Error;
|
||||
}
|
81
packages/core/http/core-http-server/src/session_storage.ts
Normal file
81
packages/core/http/core-http-server/src/session_storage.ts
Normal file
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { KibanaRequest } from './router';
|
||||
|
||||
/**
|
||||
* Provides an interface to store and retrieve data across requests.
|
||||
* @public
|
||||
*/
|
||||
export interface SessionStorage<T> {
|
||||
/**
|
||||
* Retrieves session value from the session storage.
|
||||
*/
|
||||
get(): Promise<T | null>;
|
||||
|
||||
/**
|
||||
* Puts current session value into the session storage.
|
||||
* @param sessionValue - value to put
|
||||
*/
|
||||
set(sessionValue: T): void;
|
||||
|
||||
/**
|
||||
* Clears current session.
|
||||
*/
|
||||
clear(): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* SessionStorage factory to bind one to an incoming request
|
||||
* @public */
|
||||
export interface SessionStorageFactory<T> {
|
||||
asScoped: (request: KibanaRequest) => SessionStorage<T>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration used to create HTTP session storage based on top of cookie mechanism.
|
||||
* @public
|
||||
*/
|
||||
export interface SessionStorageCookieOptions<T> {
|
||||
/**
|
||||
* Name of the session cookie.
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* A key used to encrypt a cookie's value. Should be at least 32 characters long.
|
||||
*/
|
||||
encryptionKey: string;
|
||||
/**
|
||||
* Function called to validate a cookie's decrypted value.
|
||||
*/
|
||||
validate: (sessionValue: T | T[]) => SessionCookieValidationResult;
|
||||
/**
|
||||
* Flag indicating whether the cookie should be sent only via a secure connection.
|
||||
*/
|
||||
isSecure: boolean;
|
||||
/**
|
||||
* Defines SameSite attribute of the Set-Cookie Header.
|
||||
* https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite
|
||||
*/
|
||||
sameSite?: 'Strict' | 'Lax' | 'None';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return type from a function to validate cookie contents.
|
||||
* @public
|
||||
*/
|
||||
export interface SessionCookieValidationResult {
|
||||
/**
|
||||
* Whether the cookie is valid or not.
|
||||
*/
|
||||
isValid: boolean;
|
||||
/**
|
||||
* The "Path" attribute of the cookie; if the cookie is invalid, this is used to clear it.
|
||||
*/
|
||||
path?: string;
|
||||
}
|
17
packages/core/http/core-http-server/tsconfig.json
Normal file
17
packages/core/http/core-http-server/tsconfig.json
Normal file
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"extends": "../../../../tsconfig.bazel.json",
|
||||
"compilerOptions": {
|
||||
"declaration": true,
|
||||
"emitDeclarationOnly": true,
|
||||
"outDir": "target_types",
|
||||
"rootDir": "src",
|
||||
"stripInternal": false,
|
||||
"types": [
|
||||
"jest",
|
||||
"node"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
]
|
||||
}
|
102
packages/kbn-hapi-mocks/BUILD.bazel
Normal file
102
packages/kbn-hapi-mocks/BUILD.bazel
Normal file
|
@ -0,0 +1,102 @@
|
|||
load("@npm//@bazel/typescript:index.bzl", "ts_config")
|
||||
load("@build_bazel_rules_nodejs//:index.bzl", "js_library")
|
||||
load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", "ts_project")
|
||||
|
||||
PKG_DIRNAME = "kbn-hapi-mocks"
|
||||
PKG_REQUIRE_NAME = "@kbn/hapi-mocks"
|
||||
|
||||
SOURCE_FILES = glob(
|
||||
[
|
||||
"src/**/*.ts",
|
||||
],
|
||||
exclude = [
|
||||
"**/*.test.*",
|
||||
"**/*.stories.*",
|
||||
],
|
||||
)
|
||||
|
||||
SRCS = SOURCE_FILES
|
||||
|
||||
filegroup(
|
||||
name = "srcs",
|
||||
srcs = SRCS,
|
||||
)
|
||||
|
||||
NPM_MODULE_EXTRA_FILES = [
|
||||
"package.json",
|
||||
]
|
||||
|
||||
RUNTIME_DEPS = [
|
||||
"@npm//lodash",
|
||||
]
|
||||
|
||||
TYPES_DEPS = [
|
||||
"@npm//@types/node",
|
||||
"@npm//@types/jest",
|
||||
"@npm//@types/lodash",
|
||||
"@npm//@hapi/hapi",
|
||||
"@npm//@types/hapi__hapi",
|
||||
"//packages/kbn-utility-types:npm_module_types",
|
||||
]
|
||||
|
||||
jsts_transpiler(
|
||||
name = "target_node",
|
||||
srcs = SRCS,
|
||||
build_pkg_name = package_name(),
|
||||
)
|
||||
|
||||
ts_config(
|
||||
name = "tsconfig",
|
||||
src = "tsconfig.json",
|
||||
deps = [
|
||||
"//:tsconfig.base.json",
|
||||
"//:tsconfig.bazel.json",
|
||||
],
|
||||
)
|
||||
|
||||
ts_project(
|
||||
name = "tsc_types",
|
||||
args = ['--pretty'],
|
||||
srcs = SRCS,
|
||||
deps = TYPES_DEPS,
|
||||
declaration = True,
|
||||
declaration_map = True,
|
||||
emit_declaration_only = True,
|
||||
out_dir = "target_types",
|
||||
root_dir = "src",
|
||||
tsconfig = ":tsconfig",
|
||||
)
|
||||
|
||||
js_library(
|
||||
name = PKG_DIRNAME,
|
||||
srcs = NPM_MODULE_EXTRA_FILES,
|
||||
deps = RUNTIME_DEPS + [":target_node"],
|
||||
package_name = PKG_REQUIRE_NAME,
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
pkg_npm(
|
||||
name = "npm_module",
|
||||
deps = [":" + PKG_DIRNAME],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "build",
|
||||
srcs = [":npm_module"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
pkg_npm_types(
|
||||
name = "npm_module_types",
|
||||
srcs = SRCS,
|
||||
deps = [":tsc_types"],
|
||||
package_name = PKG_REQUIRE_NAME,
|
||||
tsconfig = ":tsconfig",
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "build_types",
|
||||
srcs = [":npm_module_types"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
3
packages/kbn-hapi-mocks/README.md
Normal file
3
packages/kbn-hapi-mocks/README.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
# @kbn/hapi-mocks
|
||||
|
||||
This package contains mocks for `@hapi` types
|
13
packages/kbn-hapi-mocks/jest.config.js
Normal file
13
packages/kbn-hapi-mocks/jest.config.js
Normal file
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
preset: '@kbn/test/jest_node',
|
||||
rootDir: '../..',
|
||||
roots: ['<rootDir>/packages/kbn-hapi-mocks'],
|
||||
};
|
7
packages/kbn-hapi-mocks/package.json
Normal file
7
packages/kbn-hapi-mocks/package.json
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"name": "@kbn/hapi-mocks",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"main": "./target_node/index.js",
|
||||
"license": "SSPL-1.0 OR Elastic License 2.0"
|
||||
}
|
|
@ -6,4 +6,8 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export * from './context';
|
||||
import { createRequestMock } from './request';
|
||||
|
||||
export const hapiMocks = {
|
||||
createRequest: createRequestMock,
|
||||
};
|
42
packages/kbn-hapi-mocks/src/request.ts
Normal file
42
packages/kbn-hapi-mocks/src/request.ts
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { Request } from '@hapi/hapi';
|
||||
import { format as formatUrl, URL } from 'url';
|
||||
import { merge } from 'lodash';
|
||||
import type { DeepPartial } from '@kbn/utility-types';
|
||||
|
||||
export const createRequestMock = (customization: DeepPartial<Request> = {}): Request => {
|
||||
const pathname = customization.url?.pathname || '/';
|
||||
const path = `${pathname}${customization.url?.search || ''}`;
|
||||
const url = new URL(
|
||||
formatUrl(Object.assign({ pathname, path, href: path }, customization.url)),
|
||||
'http://localhost'
|
||||
);
|
||||
|
||||
return merge(
|
||||
{},
|
||||
{
|
||||
app: { xsrfRequired: true } as any,
|
||||
auth: {
|
||||
isAuthenticated: true,
|
||||
},
|
||||
headers: {},
|
||||
path,
|
||||
route: { settings: {} },
|
||||
url,
|
||||
raw: {
|
||||
req: {
|
||||
url: path,
|
||||
socket: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
customization
|
||||
) as Request;
|
||||
};
|
18
packages/kbn-hapi-mocks/tsconfig.json
Normal file
18
packages/kbn-hapi-mocks/tsconfig.json
Normal file
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"extends": "../../tsconfig.bazel.json",
|
||||
"compilerOptions": {
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"emitDeclarationOnly": true,
|
||||
"outDir": "target_types",
|
||||
"rootDir": "src",
|
||||
"stripInternal": false,
|
||||
"types": [
|
||||
"jest",
|
||||
"node"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
]
|
||||
}
|
|
@ -122,3 +122,17 @@ export type Writable<T> = {
|
|||
*/
|
||||
export type OneOf<T, K extends keyof T> = Omit<T, K> &
|
||||
{ [k in K]: Pick<Required<T>, k> & { [k1 in Exclude<K, k>]?: never } }[K];
|
||||
|
||||
/**
|
||||
* Deep partial version of a type.
|
||||
*/
|
||||
export type DeepPartial<T> = T extends any[]
|
||||
? DeepPartialArray<T[number]>
|
||||
: T extends object
|
||||
? DeepPartialObject<T>
|
||||
: T;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
export interface DeepPartialArray<T> extends Array<DeepPartial<T>> {}
|
||||
|
||||
export type DeepPartialObject<T> = { [P in keyof T]+?: DeepPartial<T[P]> };
|
||||
|
|
|
@ -7,9 +7,10 @@
|
|||
*/
|
||||
|
||||
import type { CoreContext } from '@kbn/core-base-server-internal';
|
||||
import { Logger } from '@kbn/logging';
|
||||
import type { Logger } from '@kbn/logging';
|
||||
import type { KibanaRequest } from '@kbn/core-http-server';
|
||||
import { Capabilities, CapabilitiesProvider, CapabilitiesSwitcher } from './types';
|
||||
import { InternalHttpServicePreboot, InternalHttpServiceSetup, KibanaRequest } from '../http';
|
||||
import { InternalHttpServicePreboot, InternalHttpServiceSetup } from '../http';
|
||||
import { mergeCapabilities } from './merge_capabilities';
|
||||
import { getCapabilitiesResolver, CapabilitiesResolver } from './resolve_capabilities';
|
||||
import { registerRoutes } from './routes';
|
||||
|
|
|
@ -6,9 +6,9 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { KibanaRequest } from '@kbn/core-http-server';
|
||||
import { Capabilities } from './types';
|
||||
import { resolveCapabilities } from './resolve_capabilities';
|
||||
import { KibanaRequest } from '../http';
|
||||
import { httpServerMock } from '../http/http_server.mocks';
|
||||
|
||||
describe('resolveCapabilities', () => {
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
*/
|
||||
|
||||
import { cloneDeep } from 'lodash';
|
||||
import type { KibanaRequest } from '@kbn/core-http-server';
|
||||
import { Capabilities, CapabilitiesSwitcher } from './types';
|
||||
import { KibanaRequest } from '../http';
|
||||
|
||||
export type CapabilitiesResolver = (
|
||||
request: KibanaRequest,
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { IRouter } from '@kbn/core-http-server';
|
||||
import { CapabilitiesResolver } from '../resolve_capabilities';
|
||||
import { IRouter } from '../../http';
|
||||
import { registerCapabilitiesRoutes } from './resolve_capabilities';
|
||||
|
||||
export function registerRoutes(router: IRouter, resolver: CapabilitiesResolver) {
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
*/
|
||||
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { IRouter } from '../../http';
|
||||
import type { IRouter } from '@kbn/core-http-server';
|
||||
import { CapabilitiesResolver } from '../resolve_capabilities';
|
||||
|
||||
const applicationIdRegexp = /^[a-zA-Z0-9_:-]+$/;
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { KibanaRequest } from '@kbn/core-http-server';
|
||||
import { Capabilities } from '../../types/capabilities';
|
||||
import { KibanaRequest } from '../http';
|
||||
|
||||
export type { Capabilities };
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { IContextContainer } from './context';
|
||||
import type { IContextContainer } from '@kbn/core-http-server';
|
||||
|
||||
export type ContextContainerMock = jest.Mocked<IContextContainer>;
|
||||
|
|
@ -6,9 +6,10 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { ContextContainer } from './context';
|
||||
import type { PluginOpaqueId, RequestHandlerContextBase } from '../..';
|
||||
import { httpServerMock } from '../../http/http_server.mocks';
|
||||
import type { PluginOpaqueId } from '@kbn/core-base-common';
|
||||
import type { RequestHandlerContextBase } from '@kbn/core-http-server';
|
||||
import { ContextContainer } from './context_container';
|
||||
import { httpServerMock } from '../http/http_server.mocks';
|
||||
|
||||
const pluginA = Symbol('pluginA');
|
||||
const pluginB = Symbol('pluginB');
|
|
@ -7,166 +7,17 @@
|
|||
*/
|
||||
|
||||
import { flatten } from 'lodash';
|
||||
import { ShallowPromise, MaybePromise } from '@kbn/utility-types';
|
||||
import { ShallowPromise } from '@kbn/utility-types';
|
||||
import type { PluginOpaqueId } from '@kbn/core-base-common';
|
||||
import type { CoreId } from '@kbn/core-base-common-internal';
|
||||
import type { RequestHandler, RequestHandlerContextBase } from '../..';
|
||||
|
||||
/**
|
||||
* A function that returns a context value for a specific key of given context type.
|
||||
*
|
||||
* @remarks
|
||||
* This function will be called each time a new context is built for a handler invocation.
|
||||
*
|
||||
* @param context - A partial context object containing only the keys for values provided by plugin dependencies
|
||||
* @param rest - Additional parameters provided by the service owner of this context
|
||||
* @returns The context value associated with this key. May also return a Promise which will be resolved before
|
||||
* attaching to the context object.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type IContextProvider<
|
||||
Context extends RequestHandlerContextBase,
|
||||
ContextName extends keyof Context
|
||||
> = (
|
||||
// context.core will always be available, but plugin contexts are typed as optional
|
||||
context: Omit<Context, ContextName>,
|
||||
...rest: HandlerParameters<RequestHandler>
|
||||
) => MaybePromise<Awaited<Context[ContextName]>>;
|
||||
|
||||
/**
|
||||
* A function that accepts a context object and an optional number of additional arguments. Used for the generic types
|
||||
* in {@link IContextContainer}
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type HandlerFunction<T extends object> = (context: T, ...args: any[]) => any;
|
||||
|
||||
/**
|
||||
* Extracts the type of the first argument of a {@link HandlerFunction} to represent the type of the context.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type HandlerContextType<T extends HandlerFunction<any>> = T extends HandlerFunction<infer U>
|
||||
? U
|
||||
: never;
|
||||
|
||||
/**
|
||||
* Extracts the types of the additional arguments of a {@link HandlerFunction}, excluding the
|
||||
* {@link HandlerContextType}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type HandlerParameters<T extends HandlerFunction<any>> = T extends (
|
||||
context: any,
|
||||
...args: infer U
|
||||
) => any
|
||||
? U
|
||||
: never;
|
||||
|
||||
/**
|
||||
* An object that handles registration of context providers and configuring handlers with context.
|
||||
*
|
||||
* @remarks
|
||||
* A {@link IContextContainer} can be used by any Core service or plugin (known as the "service owner") which wishes to
|
||||
* expose APIs in a handler function. The container object will manage registering context providers and configuring a
|
||||
* handler with all of the contexts that should be exposed to the handler's plugin. This is dependent on the
|
||||
* dependencies that the handler's plugin declares.
|
||||
*
|
||||
* Contexts providers are executed in the order they were registered. Each provider gets access to context values
|
||||
* provided by any plugins that it depends on.
|
||||
*
|
||||
* In order to configure a handler with context, you must call the {@link IContextContainer.createHandler} function and
|
||||
* use the returned handler which will automatically build a context object when called.
|
||||
*
|
||||
* When registering context or creating handlers, the _calling plugin's opaque id_ must be provided. This id is passed
|
||||
* in via the plugin's initializer and can be accessed from the {@link PluginInitializerContext.opaqueId} Note this
|
||||
* should NOT be the context service owner's id, but the plugin that is actually registering the context or handler.
|
||||
*
|
||||
* ```ts
|
||||
* // Correct
|
||||
* class MyPlugin {
|
||||
* private readonly handlers = new Map();
|
||||
*
|
||||
* setup(core) {
|
||||
* this.contextContainer = core.context.createContextContainer();
|
||||
* return {
|
||||
* registerContext(pluginOpaqueId, contextName, provider) {
|
||||
* this.contextContainer.registerContext(pluginOpaqueId, contextName, provider);
|
||||
* },
|
||||
* registerRoute(pluginOpaqueId, path, handler) {
|
||||
* this.handlers.set(
|
||||
* path,
|
||||
* this.contextContainer.createHandler(pluginOpaqueId, handler)
|
||||
* );
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* // Incorrect
|
||||
* class MyPlugin {
|
||||
* private readonly handlers = new Map();
|
||||
*
|
||||
* constructor(private readonly initContext: PluginInitializerContext) {}
|
||||
*
|
||||
* setup(core) {
|
||||
* this.contextContainer = core.context.createContextContainer();
|
||||
* return {
|
||||
* registerContext(contextName, provider) {
|
||||
* // BUG!
|
||||
* // This would leak this context to all handlers rather that only plugins that depend on the calling plugin.
|
||||
* this.contextContainer.registerContext(this.initContext.opaqueId, contextName, provider);
|
||||
* },
|
||||
* registerRoute(path, handler) {
|
||||
* this.handlers.set(
|
||||
* path,
|
||||
* // BUG!
|
||||
* // This handler will not receive any contexts provided by other dependencies of the calling plugin.
|
||||
* this.contextContainer.createHandler(this.initContext.opaqueId, handler)
|
||||
* );
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface IContextContainer {
|
||||
/**
|
||||
* Register a new context provider.
|
||||
*
|
||||
* @remarks
|
||||
* The value (or resolved Promise value) returned by the `provider` function will be attached to the context object
|
||||
* on the key specified by `contextName`.
|
||||
*
|
||||
* Throws an exception if more than one provider is registered for the same `contextName`.
|
||||
*
|
||||
* @param pluginOpaqueId - The plugin opaque ID for the plugin that registers this context.
|
||||
* @param contextName - The key of the `TContext` object this provider supplies the value for.
|
||||
* @param provider - A {@link IContextProvider} to be called each time a new context is created.
|
||||
* @returns The {@link IContextContainer} for method chaining.
|
||||
*/
|
||||
registerContext<Context extends RequestHandlerContextBase, ContextName extends keyof Context>(
|
||||
pluginOpaqueId: PluginOpaqueId,
|
||||
contextName: ContextName,
|
||||
provider: IContextProvider<Context, ContextName>
|
||||
): this;
|
||||
|
||||
/**
|
||||
* Create a new handler function pre-wired to context for the plugin.
|
||||
*
|
||||
* @param pluginOpaqueId - The plugin opaque ID for the plugin that registers this handler.
|
||||
* @param handler - Handler function to pass context object to.
|
||||
* @returns A function that takes `RequestHandler` parameters, calls `handler` with a new context, and returns a Promise of
|
||||
* the `handler` return value.
|
||||
*/
|
||||
createHandler(
|
||||
pluginOpaqueId: PluginOpaqueId,
|
||||
handler: RequestHandler
|
||||
): (...rest: HandlerParameters<RequestHandler>) => ShallowPromise<ReturnType<RequestHandler>>;
|
||||
}
|
||||
import type {
|
||||
RequestHandler,
|
||||
RequestHandlerContextBase,
|
||||
IContextProvider,
|
||||
IContextContainer,
|
||||
HandlerParameters,
|
||||
HandlerContextType,
|
||||
} from '@kbn/core-http-server';
|
||||
|
||||
/** @internal */
|
||||
export class ContextContainer implements IContextContainer {
|
|
@ -9,7 +9,7 @@
|
|||
import type { PublicMethodsOf } from '@kbn/utility-types';
|
||||
|
||||
import { ContextService, InternalContextSetup, InternalContextPreboot } from './context_service';
|
||||
import { contextMock } from './container/context.mock';
|
||||
import { contextMock } from './context_container.mock';
|
||||
|
||||
const createPrebootContractMock = (mockContext = {}) => {
|
||||
const prebootContract: jest.Mocked<InternalContextPreboot> = {
|
||||
|
|
|
@ -6,9 +6,9 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { contextMock } from './container/context.mock';
|
||||
import { contextMock } from './context_container.mock';
|
||||
|
||||
export const MockContextConstructor = jest.fn(contextMock.create);
|
||||
jest.doMock('./container/context', () => ({
|
||||
jest.doMock('./context_container', () => ({
|
||||
ContextContainer: MockContextConstructor,
|
||||
}));
|
||||
|
|
|
@ -8,7 +8,8 @@
|
|||
|
||||
import type { PluginOpaqueId } from '@kbn/core-base-common';
|
||||
import type { CoreContext } from '@kbn/core-base-server-internal';
|
||||
import { IContextContainer, ContextContainer } from './container';
|
||||
import type { IContextContainer } from '@kbn/core-http-server';
|
||||
import { ContextContainer } from './context_container';
|
||||
|
||||
type PrebootDeps = SetupDeps;
|
||||
|
||||
|
|
|
@ -8,10 +8,3 @@
|
|||
|
||||
export { ContextService } from './context_service';
|
||||
export type { InternalContextPreboot, InternalContextSetup } from './context_service';
|
||||
export type {
|
||||
IContextContainer,
|
||||
IContextProvider,
|
||||
HandlerFunction,
|
||||
HandlerContextType,
|
||||
HandlerParameters,
|
||||
} from './container';
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
*/
|
||||
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { IRouter } from '../../http';
|
||||
import type { IRouter } from '@kbn/core-http-server';
|
||||
import { createDynamicAssetHandler } from './dynamic_asset_response';
|
||||
import { FileHashCache } from './file_hash_cache';
|
||||
|
||||
|
|
|
@ -11,8 +11,8 @@ import { resolve, extname } from 'path';
|
|||
import mime from 'mime-types';
|
||||
import agent from 'elastic-apm-node';
|
||||
|
||||
import type { RequestHandler } from '@kbn/core-http-server';
|
||||
import { fstat, close } from './fs';
|
||||
import { RequestHandler } from '../../http';
|
||||
import { IFileHashCache } from './file_hash_cache';
|
||||
import { getFileHash } from './file_hash';
|
||||
import { selectCompressedFile } from './select_compressed_file';
|
||||
|
|
|
@ -11,7 +11,7 @@ import { PackageInfo } from '@kbn/config';
|
|||
import { fromRoot } from '@kbn/utils';
|
||||
import UiSharedDepsNpm from '@kbn/ui-shared-deps-npm';
|
||||
import * as UiSharedDepsSrc from '@kbn/ui-shared-deps-src';
|
||||
import { IRouter } from '../../http';
|
||||
import type { IRouter } from '@kbn/core-http-server';
|
||||
import { UiPlugins } from '../../plugins';
|
||||
import { FileHashCache } from './file_hash_cache';
|
||||
import { registerRouteForBundle } from './bundles_route';
|
||||
|
|
|
@ -13,8 +13,13 @@ import { schema } from '@kbn/config-schema';
|
|||
import { fromRoot } from '@kbn/utils';
|
||||
import type { Logger } from '@kbn/logging';
|
||||
import type { CoreContext } from '@kbn/core-base-server-internal';
|
||||
|
||||
import { IRouter, IBasePath, IKibanaResponse, KibanaResponseFactory, KibanaRequest } from '../http';
|
||||
import type {
|
||||
IRouter,
|
||||
IKibanaResponse,
|
||||
KibanaResponseFactory,
|
||||
KibanaRequest,
|
||||
IBasePath,
|
||||
} from '@kbn/core-http-server';
|
||||
import { HttpResources, HttpResourcesServiceToolkit } from '../http_resources';
|
||||
import { InternalCorePreboot, InternalCoreSetup } from '../internal_types';
|
||||
import { registerBundleRoutes } from './bundle_routes';
|
||||
|
|
|
@ -11,8 +11,9 @@ import { readFile } from 'fs/promises';
|
|||
import supertest from 'supertest';
|
||||
import { loggingSystemMock } from '@kbn/core-logging-server-mocks';
|
||||
import { executionContextServiceMock } from '@kbn/core-execution-context-server-mocks';
|
||||
import type { IRouter } from '@kbn/core-http-server';
|
||||
import { contextServiceMock } from '../../context/context_service.mock';
|
||||
import { HttpService, IRouter } from '../../http';
|
||||
import { HttpService } from '../../http';
|
||||
import { createHttpServer } from '../../http/test_utils';
|
||||
import { registerRouteForBundle } from '../bundle_routes/bundles_route';
|
||||
import { FileHashCache } from '../bundle_routes/file_hash_cache';
|
||||
|
|
|
@ -6,8 +6,7 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { RequestHandlerContextBase } from '..';
|
||||
import type { IRouter } from '../http';
|
||||
import type { IRouter, RequestHandlerContextBase } from '@kbn/core-http-server';
|
||||
import type { UiSettingsRequestHandlerContext } from '../ui_settings';
|
||||
|
||||
/**
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { KibanaRequest } from '@kbn/core-http-server';
|
||||
import type { InternalCoreStart } from './internal_types';
|
||||
import type { KibanaRequest } from './http';
|
||||
import {
|
||||
CoreSavedObjectsRouteHandlerContext,
|
||||
SavedObjectsRequestHandlerContext,
|
||||
|
|
|
@ -6,8 +6,7 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { IRouter } from '../http';
|
||||
import type { RequestHandlerContextBase } from '..';
|
||||
import type { IRouter, RequestHandlerContextBase } from '@kbn/core-http-server';
|
||||
import type { DeprecationsRequestHandlerContext } from './deprecations_route_handler_context';
|
||||
|
||||
/**
|
||||
|
|
|
@ -8,7 +8,8 @@
|
|||
|
||||
import type { Client } from '@elastic/elasticsearch';
|
||||
import type { Logger } from '@kbn/logging';
|
||||
import { IAuthHeadersStorage, Headers, isKibanaRequest, isRealRequest } from '../../http';
|
||||
import type { Headers, IAuthHeadersStorage } from '@kbn/core-http-server';
|
||||
import { isKibanaRequest, isRealRequest } from '../../http';
|
||||
import { ensureRawRequest, filterHeaders } from '../../http/router';
|
||||
import { ScopeableRequest } from '../types';
|
||||
import { ElasticsearchClient } from './types';
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
*/
|
||||
|
||||
import type { UnauthorizedError } from '@kbn/es-errors';
|
||||
import type { SetAuthHeaders } from '../../http';
|
||||
import type { SetAuthHeaders } from '@kbn/core-http-server';
|
||||
import { httpServerMock } from '../../http/http_server.mocks';
|
||||
import {
|
||||
createInternalErrorHandler,
|
||||
|
|
|
@ -8,7 +8,8 @@
|
|||
|
||||
import { MaybePromise } from '@kbn/utility-types';
|
||||
import { UnauthorizedError } from '@kbn/es-errors';
|
||||
import { AuthHeaders, KibanaRequest, SetAuthHeaders, isRealRequest } from '../../http';
|
||||
import type { AuthHeaders, KibanaRequest, SetAuthHeaders } from '@kbn/core-http-server';
|
||||
import { isRealRequest } from '../../http';
|
||||
import { ScopeableRequest } from '../types';
|
||||
|
||||
/**
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { KibanaRequest } from '../http';
|
||||
import type { KibanaRequest } from '@kbn/core-http-server';
|
||||
import type { IScopedClusterClient } from './client';
|
||||
import type { InternalElasticsearchServiceStart } from './types';
|
||||
|
||||
|
|
|
@ -16,11 +16,12 @@ import type {
|
|||
InternalExecutionContextSetup,
|
||||
IExecutionContext,
|
||||
} from '@kbn/core-execution-context-server-internal';
|
||||
import type { IAuthHeadersStorage } from '@kbn/core-http-server';
|
||||
|
||||
import { registerAnalyticsContextProvider } from './register_analytics_context_provider';
|
||||
import { ClusterClient, ElasticsearchClientConfig } from './client';
|
||||
import { ElasticsearchConfig, ElasticsearchConfigType } from './elasticsearch_config';
|
||||
import type { InternalHttpServiceSetup, IAuthHeadersStorage } from '../http';
|
||||
import type { InternalHttpServiceSetup } from '../http';
|
||||
import {
|
||||
InternalElasticsearchServicePreboot,
|
||||
InternalElasticsearchServiceSetup,
|
||||
|
|
|
@ -7,8 +7,7 @@
|
|||
*/
|
||||
|
||||
import { Observable } from 'rxjs';
|
||||
import { Headers } from '../http/router';
|
||||
import { KibanaRequest } from '../http';
|
||||
import type { Headers, KibanaRequest } from '@kbn/core-http-server';
|
||||
import { ElasticsearchConfig } from './elasticsearch_config';
|
||||
import { IClusterClient, ICustomClusterClient, ElasticsearchClientConfig } from './client';
|
||||
import { NodesVersionCompatibility } from './version_check/ensure_es_version';
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
import { AuthHeadersStorage } from './auth_headers_storage';
|
||||
import { httpServerMock } from './http_server.mocks';
|
||||
|
||||
describe('AuthHeadersStorage', () => {
|
||||
describe('stores authorization headers', () => {
|
||||
it('retrieves a copy of headers associated with Kibana request', () => {
|
||||
|
|
|
@ -7,25 +7,13 @@
|
|||
*/
|
||||
|
||||
import { Request } from '@hapi/hapi';
|
||||
import { KibanaRequest, ensureRawRequest } from './router';
|
||||
import { AuthHeaders } from './lifecycle/auth';
|
||||
|
||||
/**
|
||||
* Get headers to authenticate a user against Elasticsearch.
|
||||
* @param request {@link KibanaRequest} - an incoming request.
|
||||
* @return authentication headers {@link AuthHeaders} for - an incoming request.
|
||||
* @public
|
||||
* */
|
||||
export type GetAuthHeaders = (request: KibanaRequest) => AuthHeaders | undefined;
|
||||
|
||||
/** @internal */
|
||||
export type SetAuthHeaders = (request: KibanaRequest, headers: AuthHeaders) => void;
|
||||
|
||||
/** @internal */
|
||||
export interface IAuthHeadersStorage {
|
||||
set: SetAuthHeaders;
|
||||
get: GetAuthHeaders;
|
||||
}
|
||||
import type {
|
||||
KibanaRequest,
|
||||
AuthHeaders,
|
||||
IAuthHeadersStorage,
|
||||
GetAuthHeaders,
|
||||
} from '@kbn/core-http-server';
|
||||
import { ensureRawRequest } from './router';
|
||||
|
||||
/** @internal */
|
||||
export class AuthHeadersStorage implements IAuthHeadersStorage {
|
||||
|
|
|
@ -6,50 +6,20 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
import { Request } from '@hapi/hapi';
|
||||
import { ensureRawRequest, KibanaRequest } from './router';
|
||||
|
||||
/**
|
||||
* Status indicating an outcome of the authentication.
|
||||
* @public
|
||||
*/
|
||||
export enum AuthStatus {
|
||||
/**
|
||||
* `auth` interceptor successfully authenticated a user
|
||||
*/
|
||||
authenticated = 'authenticated',
|
||||
/**
|
||||
* `auth` interceptor failed user authentication
|
||||
*/
|
||||
unauthenticated = 'unauthenticated',
|
||||
/**
|
||||
* `auth` interceptor has not been registered
|
||||
*/
|
||||
unknown = 'unknown',
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets authentication state for a request. Returned by `auth` interceptor.
|
||||
* @param request {@link KibanaRequest} - an incoming request.
|
||||
* @public
|
||||
*/
|
||||
export type GetAuthState = <T = unknown>(
|
||||
request: KibanaRequest
|
||||
) => { status: AuthStatus; state: T };
|
||||
|
||||
/**
|
||||
* Returns authentication status for a request.
|
||||
* @param request {@link KibanaRequest} - an incoming request.
|
||||
* @public
|
||||
*/
|
||||
export type IsAuthenticated = (request: KibanaRequest) => boolean;
|
||||
import type { KibanaRequest, IsAuthenticated } from '@kbn/core-http-server';
|
||||
import { AuthStatus } from '@kbn/core-http-server';
|
||||
import { ensureRawRequest } from './router';
|
||||
|
||||
/** @internal */
|
||||
export class AuthStateStorage {
|
||||
private readonly storage = new WeakMap<Request, unknown>();
|
||||
|
||||
constructor(private readonly canBeAuthenticated: () => boolean) {}
|
||||
|
||||
public set = (request: KibanaRequest | Request, state: unknown) => {
|
||||
this.storage.set(ensureRawRequest(request), state);
|
||||
};
|
||||
|
||||
public get = <T = unknown>(request: KibanaRequest | Request) => {
|
||||
const key = ensureRawRequest(request);
|
||||
const state = this.storage.get(key) as T;
|
||||
|
@ -61,6 +31,7 @@ export class AuthStateStorage {
|
|||
|
||||
return { status, state };
|
||||
};
|
||||
|
||||
public isAuthenticated: IsAuthenticated = (request) => {
|
||||
return this.get(request).status === AuthStatus.authenticated;
|
||||
};
|
||||
|
|
|
@ -8,50 +8,30 @@
|
|||
|
||||
import { modifyUrl } from '@kbn/std';
|
||||
import { Request } from '@hapi/hapi';
|
||||
import { ensureRawRequest, KibanaRequest } from './router';
|
||||
import type { KibanaRequest, IBasePath } from '@kbn/core-http-server';
|
||||
import { ensureRawRequest } from './router';
|
||||
|
||||
/**
|
||||
* Access or manipulate the Kibana base path
|
||||
* Core internal implementation of {@link IBasePath}
|
||||
*
|
||||
* @public
|
||||
* @internal
|
||||
*/
|
||||
export class BasePath {
|
||||
export class BasePath implements IBasePath {
|
||||
private readonly basePathCache = new WeakMap<Request, string>();
|
||||
|
||||
/**
|
||||
* returns the server's basePath
|
||||
*
|
||||
* See {@link BasePath.get} for getting the basePath value for a specific request
|
||||
*/
|
||||
public readonly serverBasePath: string;
|
||||
/**
|
||||
* The server's publicly exposed base URL, if configured. Includes protocol, host, port (optional) and the
|
||||
* {@link BasePath.serverBasePath}.
|
||||
*
|
||||
* @remarks
|
||||
* Should be used for generating external URL links back to this Kibana instance.
|
||||
*/
|
||||
public readonly publicBaseUrl?: string;
|
||||
|
||||
/** @internal */
|
||||
constructor(serverBasePath: string = '', publicBaseUrl?: string) {
|
||||
this.serverBasePath = serverBasePath;
|
||||
this.publicBaseUrl = publicBaseUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns `basePath` value, specific for an incoming request.
|
||||
*/
|
||||
public get = (request: KibanaRequest) => {
|
||||
const requestScopePath = this.basePathCache.get(ensureRawRequest(request)) || '';
|
||||
return `${this.serverBasePath}${requestScopePath}`;
|
||||
};
|
||||
|
||||
/**
|
||||
* sets `basePath` value, specific for an incoming request.
|
||||
*
|
||||
* @privateRemarks should work only for KibanaRequest as soon as spaces migrate to NP
|
||||
*/
|
||||
public set = (request: KibanaRequest, requestSpecificBasePath: string) => {
|
||||
const rawRequest = ensureRawRequest(request);
|
||||
|
||||
|
@ -63,9 +43,6 @@ export class BasePath {
|
|||
this.basePathCache.set(rawRequest, requestSpecificBasePath);
|
||||
};
|
||||
|
||||
/**
|
||||
* Prepends `path` with the basePath.
|
||||
*/
|
||||
public prepend = (path: string): string => {
|
||||
if (this.serverBasePath === '') return path;
|
||||
return modifyUrl(path, (parts) => {
|
||||
|
@ -75,9 +52,6 @@ export class BasePath {
|
|||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes the prepended basePath from the `path`.
|
||||
*/
|
||||
public remove = (path: string): string => {
|
||||
if (this.serverBasePath === '') {
|
||||
return path;
|
||||
|
@ -94,11 +68,3 @@ export class BasePath {
|
|||
return path;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Access or manipulate the Kibana base path
|
||||
*
|
||||
* {@link BasePath}
|
||||
* @public
|
||||
*/
|
||||
export type IBasePath = Pick<BasePath, keyof BasePath>;
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { SessionStorageFactory, SessionStorage } from './session_storage';
|
||||
import type { SessionStorageFactory, SessionStorage } from '@kbn/core-http-server';
|
||||
|
||||
const createSessionStorageMock = <T>(): jest.Mocked<SessionStorage<T>> => ({
|
||||
get: jest.fn().mockResolvedValue({}),
|
||||
|
|
|
@ -9,52 +9,14 @@
|
|||
import { Request, Server } from '@hapi/hapi';
|
||||
import hapiAuthCookie from '@hapi/cookie';
|
||||
|
||||
import { KibanaRequest, ensureRawRequest } from './router';
|
||||
import { SessionStorageFactory, SessionStorage } from './session_storage';
|
||||
import { Logger } from '..';
|
||||
|
||||
/**
|
||||
* Configuration used to create HTTP session storage based on top of cookie mechanism.
|
||||
* @public
|
||||
*/
|
||||
export interface SessionStorageCookieOptions<T> {
|
||||
/**
|
||||
* Name of the session cookie.
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* A key used to encrypt a cookie's value. Should be at least 32 characters long.
|
||||
*/
|
||||
encryptionKey: string;
|
||||
/**
|
||||
* Function called to validate a cookie's decrypted value.
|
||||
*/
|
||||
validate: (sessionValue: T | T[]) => SessionCookieValidationResult;
|
||||
/**
|
||||
* Flag indicating whether the cookie should be sent only via a secure connection.
|
||||
*/
|
||||
isSecure: boolean;
|
||||
/**
|
||||
* Defines SameSite attribute of the Set-Cookie Header.
|
||||
* https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite
|
||||
*/
|
||||
sameSite?: 'Strict' | 'Lax' | 'None';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return type from a function to validate cookie contents.
|
||||
* @public
|
||||
*/
|
||||
export interface SessionCookieValidationResult {
|
||||
/**
|
||||
* Whether the cookie is valid or not.
|
||||
*/
|
||||
isValid: boolean;
|
||||
/**
|
||||
* The "Path" attribute of the cookie; if the cookie is invalid, this is used to clear it.
|
||||
*/
|
||||
path?: string;
|
||||
}
|
||||
import type { Logger } from '@kbn/logging';
|
||||
import type {
|
||||
KibanaRequest,
|
||||
SessionStorageFactory,
|
||||
SessionStorage,
|
||||
SessionStorageCookieOptions,
|
||||
} from '@kbn/core-http-server';
|
||||
import { ensureRawRequest } from './router';
|
||||
|
||||
class ScopedCookieSessionStorage<T extends Record<string, any>> implements SessionStorage<T> {
|
||||
constructor(
|
||||
|
@ -62,6 +24,7 @@ class ScopedCookieSessionStorage<T extends Record<string, any>> implements Sessi
|
|||
private readonly server: Server,
|
||||
private readonly request: Request
|
||||
) {}
|
||||
|
||||
public async get(): Promise<T | null> {
|
||||
try {
|
||||
const session = await this.server.auth.test('security-cookie', this.request);
|
||||
|
@ -86,9 +49,11 @@ class ScopedCookieSessionStorage<T extends Record<string, any>> implements Sessi
|
|||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public set(sessionValue: T) {
|
||||
return this.request.cookieAuth.set(sessionValue);
|
||||
}
|
||||
|
||||
public clear() {
|
||||
return this.request.cookieAuth.clear();
|
||||
}
|
||||
|
|
|
@ -6,40 +6,12 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { ICspConfig } from '@kbn/core-http-server';
|
||||
import { cspConfig, CspConfigType } from './config';
|
||||
import { CspDirectives } from './csp_directives';
|
||||
|
||||
const DEFAULT_CONFIG = Object.freeze(cspConfig.schema.validate({}));
|
||||
|
||||
/**
|
||||
* CSP configuration for use in Kibana.
|
||||
* @public
|
||||
*/
|
||||
export interface ICspConfig {
|
||||
/**
|
||||
* Specify whether browsers that do not support CSP should be
|
||||
* able to use Kibana. Use `true` to block and `false` to allow.
|
||||
*/
|
||||
readonly strict: boolean;
|
||||
|
||||
/**
|
||||
* Specify whether users with legacy browsers should be warned
|
||||
* about their lack of Kibana security compliance.
|
||||
*/
|
||||
readonly warnLegacyBrowsers: boolean;
|
||||
|
||||
/**
|
||||
* Whether or not embedding (using iframes) should be allowed by the CSP. If embedding is disabled, a restrictive 'frame-ancestors' rule will be added to the default CSP rules.
|
||||
*/
|
||||
readonly disableEmbedding: boolean;
|
||||
|
||||
/**
|
||||
* The CSP rules in a formatted directives string for use
|
||||
* in a `Content-Security-Policy` header.
|
||||
*/
|
||||
readonly header: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* CSP configuration for use in Kibana.
|
||||
* @public
|
||||
|
|
|
@ -7,6 +7,5 @@
|
|||
*/
|
||||
|
||||
export { CspConfig } from './csp_config';
|
||||
export type { ICspConfig } from './csp_config';
|
||||
export { cspConfig } from './config';
|
||||
export type { CspConfigType } from './config';
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import { TypeOf, schema } from '@kbn/config-schema';
|
||||
import type { ServiceConfigDescriptor } from '@kbn/core-base-server-internal';
|
||||
import { IExternalUrlPolicy } from '.';
|
||||
import type { IExternalUrlPolicy } from '@kbn/core-http-server';
|
||||
|
||||
/**
|
||||
* @internal
|
||||
|
|
|
@ -7,58 +7,11 @@
|
|||
*/
|
||||
|
||||
import { createSHA256Hash } from '@kbn/crypto';
|
||||
import type { IExternalUrlPolicy, IExternalUrlConfig } from '@kbn/core-http-server';
|
||||
import { externalUrlConfig } from './config';
|
||||
|
||||
const DEFAULT_CONFIG = Object.freeze(externalUrlConfig.schema.validate({}));
|
||||
|
||||
/**
|
||||
* External Url configuration for use in Kibana.
|
||||
* @public
|
||||
*/
|
||||
export interface IExternalUrlConfig {
|
||||
/**
|
||||
* A set of policies describing which external urls are allowed.
|
||||
*/
|
||||
readonly policy: IExternalUrlPolicy[];
|
||||
}
|
||||
|
||||
/**
|
||||
* A policy describing whether access to an external destination is allowed.
|
||||
* @public
|
||||
*/
|
||||
export interface IExternalUrlPolicy {
|
||||
/**
|
||||
* Indicates if this policy allows or denies access to the described destination.
|
||||
*/
|
||||
allow: boolean;
|
||||
|
||||
/**
|
||||
* Optional host describing the external destination.
|
||||
* May be combined with `protocol`.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* // allows access to all of google.com, using any protocol.
|
||||
* allow: true,
|
||||
* host: 'google.com'
|
||||
* ```
|
||||
*/
|
||||
host?: string;
|
||||
|
||||
/**
|
||||
* Optional protocol describing the external destination.
|
||||
* May be combined with `host`.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* // allows access to all destinations over the `https` protocol.
|
||||
* allow: true,
|
||||
* protocol: 'https'
|
||||
* ```
|
||||
*/
|
||||
protocol?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* External Url configuration for use in Kibana.
|
||||
* @public
|
||||
|
|
|
@ -7,6 +7,5 @@
|
|||
*/
|
||||
|
||||
export { ExternalUrlConfig } from './external_url_config';
|
||||
export type { IExternalUrlConfig, IExternalUrlPolicy } from './external_url_config';
|
||||
export { externalUrlConfig } from './config';
|
||||
export type { ExternalUrlConfigType } from './config';
|
||||
|
|
|
@ -10,13 +10,14 @@ import { ByteSizeValue, schema, TypeOf } from '@kbn/config-schema';
|
|||
import { IHttpConfig, SslConfig, sslSchema } from '@kbn/server-http-tools';
|
||||
import type { ServiceConfigDescriptor } from '@kbn/core-base-server-internal';
|
||||
import { uuidRegexp } from '@kbn/core-base-server-internal';
|
||||
import type { ICspConfig, IExternalUrlConfig } from '@kbn/core-http-server';
|
||||
|
||||
import { hostname } from 'os';
|
||||
import url from 'url';
|
||||
|
||||
import type { Duration } from 'moment';
|
||||
import { CspConfigType, CspConfig, ICspConfig } from './csp';
|
||||
import { ExternalUrlConfig, IExternalUrlConfig } from './external_url';
|
||||
import { CspConfigType, CspConfig } from './csp';
|
||||
import { ExternalUrlConfig } from './external_url';
|
||||
import {
|
||||
securityResponseHeadersSchema,
|
||||
parseRawSecurityResponseHeadersConfig,
|
||||
|
|
|
@ -6,160 +6,14 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { URL, format as formatUrl } from 'url';
|
||||
import { Request } from '@hapi/hapi';
|
||||
import { merge } from 'lodash';
|
||||
import { Socket } from 'net';
|
||||
import { stringify } from 'query-string';
|
||||
|
||||
import { schema } from '@kbn/config-schema';
|
||||
|
||||
import {
|
||||
CoreKibanaRequest,
|
||||
import { hapiMocks } from '@kbn/hapi-mocks';
|
||||
import type {
|
||||
LifecycleResponseFactory,
|
||||
RouteMethod,
|
||||
KibanaRequest,
|
||||
KibanaResponseFactory,
|
||||
RouteValidationSpec,
|
||||
KibanaRouteOptions,
|
||||
KibanaRequestState,
|
||||
} from './router';
|
||||
import { OnPreResponseToolkit } from './lifecycle/on_pre_response';
|
||||
import { OnPostAuthToolkit } from './lifecycle/on_post_auth';
|
||||
import { OnPreRoutingToolkit } from './lifecycle/on_pre_routing';
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export interface RequestFixtureOptions<P = any, Q = any, B = any> {
|
||||
auth?: { isAuthenticated: boolean };
|
||||
headers?: Record<string, string>;
|
||||
params?: Record<string, any>;
|
||||
body?: Record<string, any>;
|
||||
query?: Record<string, any>;
|
||||
path?: string;
|
||||
method?: RouteMethod;
|
||||
socket?: Socket;
|
||||
routeTags?: string[];
|
||||
kibanaRouteOptions?: KibanaRouteOptions;
|
||||
kibanaRequestState?: KibanaRequestState;
|
||||
routeAuthRequired?: false;
|
||||
validation?: {
|
||||
params?: RouteValidationSpec<P>;
|
||||
query?: RouteValidationSpec<Q>;
|
||||
body?: RouteValidationSpec<B>;
|
||||
};
|
||||
}
|
||||
|
||||
function createKibanaRequestMock<P = any, Q = any, B = any>({
|
||||
path = '/path',
|
||||
headers = { accept: 'something/html' },
|
||||
params = {},
|
||||
body = {},
|
||||
query = {},
|
||||
method = 'get',
|
||||
socket = new Socket(),
|
||||
routeTags,
|
||||
routeAuthRequired,
|
||||
validation = {},
|
||||
kibanaRouteOptions = { xsrfRequired: true },
|
||||
kibanaRequestState = {
|
||||
requestId: '123',
|
||||
requestUuid: '123e4567-e89b-12d3-a456-426614174000',
|
||||
},
|
||||
auth = { isAuthenticated: true },
|
||||
}: RequestFixtureOptions<P, Q, B> = {}): KibanaRequest<P, Q, B> {
|
||||
const queryString = stringify(query, { sort: false });
|
||||
const url = new URL(`${path}${queryString ? `?${queryString}` : ''}`, 'http://localhost');
|
||||
|
||||
return CoreKibanaRequest.from<P, Q, B>(
|
||||
createRawRequestMock({
|
||||
app: kibanaRequestState,
|
||||
auth,
|
||||
headers,
|
||||
params,
|
||||
query,
|
||||
payload: body,
|
||||
path,
|
||||
method,
|
||||
url,
|
||||
route: {
|
||||
// @ts-expect-error According to types/hapi__hapi the following settings-fields have problems:
|
||||
// - `auth` can't be a boolean, but it can according to the @hapi/hapi source (https://github.com/hapijs/hapi/blob/v18.4.2/lib/route.js#L139)
|
||||
// - `app` isn't a valid property, but it is and this was fixed in the types in v19.0.1 (https://github.com/DefinitelyTyped/DefinitelyTyped/pull/41968)
|
||||
settings: { tags: routeTags, auth: routeAuthRequired, app: kibanaRouteOptions },
|
||||
},
|
||||
raw: {
|
||||
req: {
|
||||
socket,
|
||||
// these are needed to avoid an error when consuming KibanaRequest.events
|
||||
on: jest.fn(),
|
||||
off: jest.fn(),
|
||||
},
|
||||
},
|
||||
}),
|
||||
{
|
||||
params: validation.params || schema.any(),
|
||||
body: validation.body || schema.any(),
|
||||
query: validation.query || schema.any(),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
type DeepPartial<T> = T extends any[]
|
||||
? DeepPartialArray<T[number]>
|
||||
: T extends object
|
||||
? DeepPartialObject<T>
|
||||
: T;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
interface DeepPartialArray<T> extends Array<DeepPartial<T>> {}
|
||||
|
||||
type DeepPartialObject<T> = { [P in keyof T]+?: DeepPartial<T[P]> };
|
||||
|
||||
function createRawRequestMock(customization: DeepPartial<Request> = {}) {
|
||||
const pathname = customization.url?.pathname || '/';
|
||||
const path = `${pathname}${customization.url?.search || ''}`;
|
||||
const url = new URL(
|
||||
formatUrl(Object.assign({ pathname, path, href: path }, customization.url)),
|
||||
'http://localhost'
|
||||
);
|
||||
|
||||
return merge(
|
||||
{},
|
||||
{
|
||||
app: { xsrfRequired: true } as any,
|
||||
auth: {
|
||||
isAuthenticated: true,
|
||||
},
|
||||
headers: {},
|
||||
path,
|
||||
route: { settings: {} },
|
||||
url,
|
||||
raw: {
|
||||
req: {
|
||||
url: path,
|
||||
socket: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
customization
|
||||
) as Request;
|
||||
}
|
||||
|
||||
const createResponseFactoryMock = (): jest.Mocked<KibanaResponseFactory> => ({
|
||||
ok: jest.fn(),
|
||||
accepted: jest.fn(),
|
||||
noContent: jest.fn(),
|
||||
custom: jest.fn(),
|
||||
redirected: jest.fn(),
|
||||
badRequest: jest.fn(),
|
||||
unauthorized: jest.fn(),
|
||||
forbidden: jest.fn(),
|
||||
notFound: jest.fn(),
|
||||
conflict: jest.fn(),
|
||||
customError: jest.fn(),
|
||||
});
|
||||
OnPreResponseToolkit,
|
||||
OnPostAuthToolkit,
|
||||
OnPreRoutingToolkit,
|
||||
} from '@kbn/core-http-server';
|
||||
import { mockRouter } from './router/router.mock';
|
||||
|
||||
const createLifecycleResponseFactoryMock = (): jest.Mocked<LifecycleResponseFactory> => ({
|
||||
redirected: jest.fn(),
|
||||
|
@ -182,9 +36,9 @@ const createToolkitMock = (): ToolkitMock => {
|
|||
};
|
||||
|
||||
export const httpServerMock = {
|
||||
createKibanaRequest: createKibanaRequestMock,
|
||||
createRawRequest: createRawRequestMock,
|
||||
createResponseFactory: createResponseFactoryMock,
|
||||
createKibanaRequest: mockRouter.createKibanaRequest,
|
||||
createRawRequest: hapiMocks.createRequest,
|
||||
createResponseFactory: mockRouter.createResponseFactory,
|
||||
createLifecycleResponseFactory: createLifecycleResponseFactoryMock,
|
||||
createToolkit: createToolkitMock,
|
||||
};
|
||||
|
|
|
@ -14,18 +14,18 @@ import { join } from 'path';
|
|||
|
||||
import { ByteSizeValue, schema } from '@kbn/config-schema';
|
||||
import { loggingSystemMock } from '@kbn/core-logging-server-mocks';
|
||||
import { HttpConfig } from './http_config';
|
||||
import {
|
||||
Router,
|
||||
import type {
|
||||
KibanaRequest,
|
||||
KibanaResponseFactory,
|
||||
RequestHandler,
|
||||
RouteValidationResultFactory,
|
||||
RouteValidationFunction,
|
||||
} from './router';
|
||||
RequestHandlerContextBase,
|
||||
} from '@kbn/core-http-server';
|
||||
import { HttpConfig } from './http_config';
|
||||
import { Router } from './router';
|
||||
import { HttpServer } from './http_server';
|
||||
import { Readable } from 'stream';
|
||||
import { RequestHandlerContextBase } from '..';
|
||||
import { KBN_CERT_PATH, KBN_KEY_PATH } from '@kbn/dev-utils';
|
||||
import moment from 'moment';
|
||||
import { of } from 'rxjs';
|
||||
|
|
|
@ -24,29 +24,35 @@ import { take } from 'rxjs/operators';
|
|||
import apm from 'elastic-apm-node';
|
||||
import type { Logger, LoggerFactory } from '@kbn/logging';
|
||||
import type { InternalExecutionContextSetup } from '@kbn/core-execution-context-server-internal';
|
||||
import { HttpConfig } from './http_config';
|
||||
import { adoptToHapiAuthFormat, AuthenticationHandler } from './lifecycle/auth';
|
||||
import { adoptToHapiOnPreAuth, OnPreAuthHandler } from './lifecycle/on_pre_auth';
|
||||
import { adoptToHapiOnPostAuthFormat, OnPostAuthHandler } from './lifecycle/on_post_auth';
|
||||
import { adoptToHapiOnRequest, OnPreRoutingHandler } from './lifecycle/on_pre_routing';
|
||||
import { adoptToHapiOnPreResponseFormat, OnPreResponseHandler } from './lifecycle/on_pre_response';
|
||||
import {
|
||||
import type {
|
||||
IRouter,
|
||||
RouteConfigOptions,
|
||||
KibanaRouteOptions,
|
||||
KibanaRequestState,
|
||||
isSafeMethod,
|
||||
RouterRoute,
|
||||
} from './router';
|
||||
import {
|
||||
AuthenticationHandler,
|
||||
OnPreAuthHandler,
|
||||
OnPostAuthHandler,
|
||||
OnPreRoutingHandler,
|
||||
OnPreResponseHandler,
|
||||
SessionStorageCookieOptions,
|
||||
createCookieSessionStorageFactory,
|
||||
} from './cookie_session_storage';
|
||||
HttpServiceSetup,
|
||||
HttpServerInfo,
|
||||
HttpAuth,
|
||||
IAuthHeadersStorage,
|
||||
} from '@kbn/core-http-server';
|
||||
import { HttpConfig } from './http_config';
|
||||
import { adoptToHapiAuthFormat } from './lifecycle/auth';
|
||||
import { adoptToHapiOnPreAuth } from './lifecycle/on_pre_auth';
|
||||
import { adoptToHapiOnPostAuthFormat } from './lifecycle/on_post_auth';
|
||||
import { adoptToHapiOnRequest } from './lifecycle/on_pre_routing';
|
||||
import { adoptToHapiOnPreResponseFormat } from './lifecycle/on_pre_response';
|
||||
import { isSafeMethod } from './router';
|
||||
import { createCookieSessionStorageFactory } from './cookie_session_storage';
|
||||
import { AuthStateStorage } from './auth_state_storage';
|
||||
import { AuthHeadersStorage, IAuthHeadersStorage } from './auth_headers_storage';
|
||||
import { AuthHeadersStorage } from './auth_headers_storage';
|
||||
import { BasePath } from './base_path_service';
|
||||
import { getEcsResponseLog } from './logging';
|
||||
import { HttpServiceSetup, HttpServerInfo, HttpAuth } from './types';
|
||||
|
||||
/** @internal */
|
||||
export interface HttpServerSetup {
|
||||
|
|
|
@ -9,28 +9,30 @@
|
|||
import { Server } from '@hapi/hapi';
|
||||
import type { PublicMethodsOf } from '@kbn/utility-types';
|
||||
import { configMock } from '@kbn/config-mocks';
|
||||
import type {
|
||||
RequestHandlerContextBase,
|
||||
OnPreRoutingToolkit,
|
||||
AuthToolkit,
|
||||
OnPostAuthToolkit,
|
||||
OnPreAuthToolkit,
|
||||
OnPreResponseToolkit,
|
||||
IAuthHeadersStorage,
|
||||
HttpServicePreboot,
|
||||
HttpServiceSetup,
|
||||
HttpServiceStart,
|
||||
} from '@kbn/core-http-server';
|
||||
import { AuthStatus } from '@kbn/core-http-server';
|
||||
|
||||
import { CspConfig } from './csp';
|
||||
import { mockRouter, RouterMock } from './router/router.mock';
|
||||
import {
|
||||
InternalHttpServicePreboot,
|
||||
HttpServicePreboot,
|
||||
InternalHttpServiceSetup,
|
||||
HttpServiceSetup,
|
||||
HttpServiceStart,
|
||||
InternalHttpServiceStart,
|
||||
} from './types';
|
||||
import { HttpService } from './http_service';
|
||||
import { AuthStatus } from './auth_state_storage';
|
||||
import { OnPreRoutingToolkit } from './lifecycle/on_pre_routing';
|
||||
import { AuthToolkit } from './lifecycle/auth';
|
||||
import { sessionStorageMock } from './cookie_session_storage.mocks';
|
||||
import { OnPostAuthToolkit } from './lifecycle/on_post_auth';
|
||||
import { OnPreAuthToolkit } from './lifecycle/on_pre_auth';
|
||||
import { OnPreResponseToolkit } from './lifecycle/on_pre_response';
|
||||
import { ExternalUrlConfig } from './external_url';
|
||||
import type { IAuthHeadersStorage } from './auth_headers_storage';
|
||||
import type { RequestHandlerContextBase } from '..';
|
||||
|
||||
type BasePathMocked = jest.Mocked<InternalHttpServiceSetup['basePath']>;
|
||||
type AuthMocked = jest.Mocked<InternalHttpServiceSetup['auth']>;
|
||||
|
|
|
@ -11,13 +11,13 @@ import { mockHttpServer } from './http_service.test.mocks';
|
|||
import { noop } from 'lodash';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { REPO_ROOT } from '@kbn/utils';
|
||||
import { hapiMocks } from '@kbn/hapi-mocks';
|
||||
import { ConfigService, Env } from '@kbn/config';
|
||||
import { getEnvOptions } from '@kbn/config-mocks';
|
||||
import { loggingSystemMock } from '@kbn/core-logging-server-mocks';
|
||||
import { executionContextServiceMock } from '@kbn/core-execution-context-server-mocks';
|
||||
import { HttpService } from '.';
|
||||
import { HttpConfigType, config } from './http_config';
|
||||
import { httpServerMock } from './http_server.mocks';
|
||||
import { contextServiceMock } from '../context/context_service.mock';
|
||||
import { cspConfig } from './csp';
|
||||
import { externalUrlConfig, ExternalUrlConfig } from './external_url';
|
||||
|
@ -149,7 +149,7 @@ test('spins up `preboot` server until started if configured with `autoListen:tru
|
|||
};
|
||||
|
||||
const [[{ handler }]] = prebootHapiServer.route.mock.calls;
|
||||
const response503 = await handler(httpServerMock.createRawRequest(), mockResponseToolkit);
|
||||
const response503 = await handler(hapiMocks.createRequest(), mockResponseToolkit);
|
||||
expect(response503).toBe(mockResponse);
|
||||
expect({
|
||||
body: mockResponseToolkit.response.mock.calls,
|
||||
|
|
|
@ -15,19 +15,22 @@ import { Env } from '@kbn/config';
|
|||
import type { CoreContext, CoreService } from '@kbn/core-base-server-internal';
|
||||
import type { PluginOpaqueId } from '@kbn/core-base-common';
|
||||
import type { InternalExecutionContextSetup } from '@kbn/core-execution-context-server-internal';
|
||||
import type {
|
||||
RequestHandlerContextBase,
|
||||
IRouter,
|
||||
IContextContainer,
|
||||
IContextProvider,
|
||||
} from '@kbn/core-http-server';
|
||||
|
||||
import type { RequestHandlerContextBase } from '..';
|
||||
import { InternalContextSetup, InternalContextPreboot } from '../context';
|
||||
import { CspConfigType, cspConfig } from './csp';
|
||||
|
||||
import { Router, IRouter } from './router';
|
||||
import { Router } from './router';
|
||||
import { HttpConfig, HttpConfigType, config as httpConfig } from './http_config';
|
||||
import { HttpServer } from './http_server';
|
||||
import { HttpsRedirectServer } from './https_redirect_server';
|
||||
|
||||
import {
|
||||
RequestHandlerContextContainer,
|
||||
RequestHandlerContextProvider,
|
||||
InternalHttpServicePreboot,
|
||||
InternalHttpServiceSetup,
|
||||
InternalHttpServiceStart,
|
||||
|
@ -60,7 +63,7 @@ export class HttpService
|
|||
private readonly env: Env;
|
||||
private internalPreboot?: InternalHttpServicePreboot;
|
||||
private internalSetup?: InternalHttpServiceSetup;
|
||||
private requestHandlerContext?: RequestHandlerContextContainer;
|
||||
private requestHandlerContext?: IContextContainer;
|
||||
|
||||
constructor(private readonly coreContext: CoreContext) {
|
||||
const { logger, configService, env } = coreContext;
|
||||
|
@ -178,7 +181,7 @@ export class HttpService
|
|||
>(
|
||||
pluginOpaqueId: PluginOpaqueId,
|
||||
contextName: ContextName,
|
||||
provider: RequestHandlerContextProvider<Context, ContextName>
|
||||
provider: IContextProvider<Context, ContextName>
|
||||
) => this.requestHandlerContext!.registerContext(pluginOpaqueId, contextName, provider),
|
||||
|
||||
registerPrebootRoutes: this.internalPreboot!.registerRoutes,
|
||||
|
|
|
@ -9,97 +9,14 @@
|
|||
export { config, HttpConfig } from './http_config';
|
||||
export type { HttpConfigType } from './http_config';
|
||||
export { HttpService } from './http_service';
|
||||
export type { GetAuthHeaders, SetAuthHeaders, IAuthHeadersStorage } from './auth_headers_storage';
|
||||
export type { AuthStatus, GetAuthState, IsAuthenticated } from './auth_state_storage';
|
||||
export {
|
||||
isKibanaRequest,
|
||||
isRealRequest,
|
||||
CoreKibanaRequest,
|
||||
kibanaResponseFactory,
|
||||
validBodyOutput,
|
||||
} from './router';
|
||||
export { isKibanaRequest, isRealRequest, CoreKibanaRequest, kibanaResponseFactory } from './router';
|
||||
export type {
|
||||
CustomHttpResponseOptions,
|
||||
IKibanaSocket,
|
||||
Headers,
|
||||
HttpResponseOptions,
|
||||
HttpResponsePayload,
|
||||
ErrorHttpResponseOptions,
|
||||
KibanaRequest,
|
||||
KibanaRequestEvents,
|
||||
KibanaRequestRoute,
|
||||
KibanaRequestRouteOptions,
|
||||
IKibanaResponse,
|
||||
KnownHeaders,
|
||||
LifecycleResponseFactory,
|
||||
RedirectResponseOptions,
|
||||
RequestHandler,
|
||||
RequestHandlerWrapper,
|
||||
ResponseError,
|
||||
ResponseErrorAttributes,
|
||||
ResponseHeaders,
|
||||
KibanaResponseFactory,
|
||||
RouteConfig,
|
||||
IRouter,
|
||||
RouteMethod,
|
||||
RouteRegistrar,
|
||||
RouteConfigOptions,
|
||||
RouteConfigOptionsBody,
|
||||
RouteContentType,
|
||||
RouteValidatorConfig,
|
||||
RouteValidationSpec,
|
||||
RouteValidationFunction,
|
||||
RouteValidatorOptions,
|
||||
RouteValidationError,
|
||||
RouteValidatorFullConfig,
|
||||
RouteValidationResultFactory,
|
||||
DestructiveRouteMethod,
|
||||
SafeRouteMethod,
|
||||
} from './router';
|
||||
export type { OnPreRoutingHandler, OnPreRoutingToolkit } from './lifecycle/on_pre_routing';
|
||||
export type {
|
||||
AuthenticationHandler,
|
||||
AuthHeaders,
|
||||
AuthResultParams,
|
||||
AuthRedirected,
|
||||
AuthRedirectedParams,
|
||||
AuthToolkit,
|
||||
AuthResult,
|
||||
Authenticated,
|
||||
AuthNotHandled,
|
||||
AuthResultType,
|
||||
} from './lifecycle/auth';
|
||||
export type { OnPostAuthHandler, OnPostAuthToolkit } from './lifecycle/on_post_auth';
|
||||
export type { OnPreAuthHandler, OnPreAuthToolkit } from './lifecycle/on_pre_auth';
|
||||
export type {
|
||||
OnPreResponseHandler,
|
||||
OnPreResponseToolkit,
|
||||
OnPreResponseRender,
|
||||
OnPreResponseExtensions,
|
||||
OnPreResponseInfo,
|
||||
} from './lifecycle/on_pre_response';
|
||||
export type { SessionStorageFactory, SessionStorage } from './session_storage';
|
||||
export type {
|
||||
SessionStorageCookieOptions,
|
||||
SessionCookieValidationResult,
|
||||
} from './cookie_session_storage';
|
||||
export type {
|
||||
RequestHandlerContextContainer,
|
||||
RequestHandlerContextProvider,
|
||||
HttpAuth,
|
||||
HttpServicePreboot,
|
||||
InternalHttpServicePreboot,
|
||||
HttpServiceSetup,
|
||||
InternalHttpServiceSetup,
|
||||
HttpServiceStart,
|
||||
InternalHttpServiceStart,
|
||||
HttpServerInfo,
|
||||
} from './types';
|
||||
export { BasePath } from './base_path_service';
|
||||
export type { IBasePath } from './base_path_service';
|
||||
|
||||
export { cspConfig, CspConfig } from './csp';
|
||||
export type { ICspConfig } from './csp';
|
||||
|
||||
export { externalUrlConfig, ExternalUrlConfig } from './external_url';
|
||||
export type { IExternalUrlConfig, IExternalUrlPolicy } from './external_url';
|
||||
|
|
|
@ -6,9 +6,8 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { IRouter, RouteConfigOptions, HttpAuth } from '@kbn/core-http-server';
|
||||
import * as kbnTestServer from '../../../test_helpers/kbn_server';
|
||||
import { IRouter, RouteConfigOptions } from '../router';
|
||||
import { HttpAuth } from '../types';
|
||||
|
||||
describe('http auth', () => {
|
||||
let root: ReturnType<typeof kbnTestServer.createRoot>;
|
||||
|
|
|
@ -11,11 +11,11 @@ import moment from 'moment';
|
|||
import { BehaviorSubject } from 'rxjs';
|
||||
import { ByteSizeValue } from '@kbn/config-schema';
|
||||
import { configServiceMock } from '@kbn/config-mocks';
|
||||
import type { IRouter, RouteRegistrar } from '@kbn/core-http-server';
|
||||
|
||||
import { createHttpServer } from '../test_utils';
|
||||
import { HttpService } from '../http_service';
|
||||
import { HttpServerSetup } from '../http_server';
|
||||
import { IRouter, RouteRegistrar } from '../router';
|
||||
|
||||
import { contextServiceMock } from '../../context/context_service.mock';
|
||||
import { executionContextServiceMock } from '@kbn/core-execution-context-server-mocks';
|
||||
|
|
|
@ -8,42 +8,24 @@
|
|||
|
||||
import { Lifecycle, Request, ResponseToolkit } from '@hapi/hapi';
|
||||
import type { Logger } from '@kbn/logging';
|
||||
import type {
|
||||
AuthenticationHandler,
|
||||
ResponseHeaders,
|
||||
AuthResultParams,
|
||||
AuthResult,
|
||||
AuthResultAuthenticated,
|
||||
AuthResultNotHandled,
|
||||
AuthResultRedirected,
|
||||
AuthToolkit,
|
||||
} from '@kbn/core-http-server';
|
||||
import { AuthResultType } from '@kbn/core-http-server';
|
||||
import {
|
||||
HapiResponseAdapter,
|
||||
KibanaRequest,
|
||||
CoreKibanaRequest,
|
||||
IKibanaResponse,
|
||||
lifecycleResponseFactory,
|
||||
LifecycleResponseFactory,
|
||||
isKibanaResponse,
|
||||
ResponseHeaders,
|
||||
} from '../router';
|
||||
|
||||
/** @public */
|
||||
export enum AuthResultType {
|
||||
authenticated = 'authenticated',
|
||||
notHandled = 'notHandled',
|
||||
redirected = 'redirected',
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export interface Authenticated extends AuthResultParams {
|
||||
type: AuthResultType.authenticated;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export interface AuthNotHandled {
|
||||
type: AuthResultType.notHandled;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export interface AuthRedirected extends AuthRedirectedParams {
|
||||
type: AuthResultType.redirected;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export type AuthResult = Authenticated | AuthNotHandled | AuthRedirected;
|
||||
|
||||
const authResult = {
|
||||
authenticated(data: AuthResultParams = {}): AuthResult {
|
||||
return {
|
||||
|
@ -64,94 +46,24 @@ const authResult = {
|
|||
headers,
|
||||
};
|
||||
},
|
||||
isAuthenticated(result: AuthResult): result is Authenticated {
|
||||
isAuthenticated(result: AuthResult): result is AuthResultAuthenticated {
|
||||
return result?.type === AuthResultType.authenticated;
|
||||
},
|
||||
isNotHandled(result: AuthResult): result is AuthNotHandled {
|
||||
isNotHandled(result: AuthResult): result is AuthResultNotHandled {
|
||||
return result?.type === AuthResultType.notHandled;
|
||||
},
|
||||
isRedirected(result: AuthResult): result is AuthRedirected {
|
||||
isRedirected(result: AuthResult): result is AuthResultRedirected {
|
||||
return result?.type === AuthResultType.redirected;
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Auth Headers map
|
||||
* @public
|
||||
*/
|
||||
|
||||
export type AuthHeaders = Record<string, string | string[]>;
|
||||
|
||||
/**
|
||||
* Result of successful authentication.
|
||||
* @public
|
||||
*/
|
||||
export interface AuthResultParams {
|
||||
/**
|
||||
* Data to associate with an incoming request. Any downstream plugin may get access to the data.
|
||||
*/
|
||||
state?: Record<string, any>;
|
||||
/**
|
||||
* Auth specific headers to attach to a request object.
|
||||
* Used to perform a request to Elasticsearch on behalf of an authenticated user.
|
||||
*/
|
||||
requestHeaders?: AuthHeaders;
|
||||
/**
|
||||
* Auth specific headers to attach to a response object.
|
||||
* Used to send back authentication mechanism related headers to a client when needed.
|
||||
*/
|
||||
responseHeaders?: AuthHeaders;
|
||||
}
|
||||
|
||||
/**
|
||||
* Result of auth redirection.
|
||||
* @public
|
||||
*/
|
||||
export interface AuthRedirectedParams {
|
||||
/**
|
||||
* Headers to attach for auth redirect.
|
||||
* Must include "location" header
|
||||
*/
|
||||
headers: { location: string } & ResponseHeaders;
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
* A tool set defining an outcome of Auth interceptor for incoming request.
|
||||
*/
|
||||
export interface AuthToolkit {
|
||||
/** Authentication is successful with given credentials, allow request to pass through */
|
||||
authenticated: (data?: AuthResultParams) => AuthResult;
|
||||
/**
|
||||
* User has no credentials.
|
||||
* Allows user to access a resource when authRequired is 'optional'
|
||||
* Rejects a request when authRequired: true
|
||||
* */
|
||||
notHandled: () => AuthResult;
|
||||
/**
|
||||
* Redirects user to another location to complete authentication when authRequired: true
|
||||
* Allows user to access a resource without redirection when authRequired: 'optional'
|
||||
* */
|
||||
redirected: (headers: { location: string } & ResponseHeaders) => AuthResult;
|
||||
}
|
||||
|
||||
const toolkit: AuthToolkit = {
|
||||
authenticated: authResult.authenticated,
|
||||
notHandled: authResult.notHandled,
|
||||
redirected: authResult.redirected,
|
||||
};
|
||||
|
||||
/**
|
||||
* See {@link AuthToolkit}.
|
||||
* @public
|
||||
*/
|
||||
export type AuthenticationHandler = (
|
||||
request: KibanaRequest,
|
||||
response: LifecycleResponseFactory,
|
||||
toolkit: AuthToolkit
|
||||
) => AuthResult | IKibanaResponse | Promise<AuthResult | IKibanaResponse>;
|
||||
|
||||
/** @public */
|
||||
/** @internal */
|
||||
export function adoptToHapiAuthFormat(
|
||||
fn: AuthenticationHandler,
|
||||
log: Logger,
|
||||
|
|
|
@ -8,59 +8,34 @@
|
|||
|
||||
import { Lifecycle, Request, ResponseToolkit as HapiResponseToolkit } from '@hapi/hapi';
|
||||
import type { Logger } from '@kbn/logging';
|
||||
import type {
|
||||
OnPostAuthNextResult,
|
||||
OnPostAuthToolkit,
|
||||
OnPostAuthResult,
|
||||
OnPostAuthHandler,
|
||||
} from '@kbn/core-http-server';
|
||||
import { OnPostAuthResultType } from '@kbn/core-http-server';
|
||||
import {
|
||||
HapiResponseAdapter,
|
||||
CoreKibanaRequest,
|
||||
KibanaRequest,
|
||||
KibanaResponse,
|
||||
lifecycleResponseFactory,
|
||||
LifecycleResponseFactory,
|
||||
isKibanaResponse,
|
||||
} from '../router';
|
||||
|
||||
enum ResultType {
|
||||
next = 'next',
|
||||
}
|
||||
|
||||
interface Next {
|
||||
type: ResultType.next;
|
||||
}
|
||||
|
||||
type OnPostAuthResult = Next;
|
||||
|
||||
const postAuthResult = {
|
||||
next(): OnPostAuthResult {
|
||||
return { type: ResultType.next };
|
||||
return { type: OnPostAuthResultType.next };
|
||||
},
|
||||
isNext(result: OnPostAuthResult): result is Next {
|
||||
return result && result.type === ResultType.next;
|
||||
isNext(result: OnPostAuthResult): result is OnPostAuthNextResult {
|
||||
return result && result.type === OnPostAuthResultType.next;
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* @public
|
||||
* A tool set defining an outcome of OnPostAuth interceptor for incoming request.
|
||||
*/
|
||||
export interface OnPostAuthToolkit {
|
||||
/** To pass request to the next handler */
|
||||
next: () => OnPostAuthResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link OnPostAuthToolkit}.
|
||||
* @public
|
||||
*/
|
||||
export type OnPostAuthHandler = (
|
||||
request: KibanaRequest,
|
||||
response: LifecycleResponseFactory,
|
||||
toolkit: OnPostAuthToolkit
|
||||
) => OnPostAuthResult | KibanaResponse | Promise<OnPostAuthResult | KibanaResponse>;
|
||||
|
||||
const toolkit: OnPostAuthToolkit = {
|
||||
next: postAuthResult.next,
|
||||
};
|
||||
|
||||
/**
|
||||
* @public
|
||||
* Adopt custom request interceptor to Hapi lifecycle system.
|
||||
* @param fn - an extension point allowing to perform custom logic for
|
||||
* incoming HTTP requests.
|
||||
|
@ -73,7 +48,7 @@ export function adoptToHapiOnPostAuthFormat(fn: OnPostAuthHandler, log: Logger)
|
|||
const hapiResponseAdapter = new HapiResponseAdapter(responseToolkit);
|
||||
try {
|
||||
const result = await fn(CoreKibanaRequest.from(request), lifecycleResponseFactory, toolkit);
|
||||
if (result instanceof KibanaResponse) {
|
||||
if (isKibanaResponse(result)) {
|
||||
return hapiResponseAdapter.handle(result);
|
||||
}
|
||||
if (postAuthResult.isNext(result)) {
|
||||
|
|
|
@ -8,59 +8,34 @@
|
|||
|
||||
import { Lifecycle, Request, ResponseToolkit as HapiResponseToolkit } from '@hapi/hapi';
|
||||
import type { Logger } from '@kbn/logging';
|
||||
import type {
|
||||
OnPreAuthResult,
|
||||
OnPreAuthNextResult,
|
||||
OnPreAuthHandler,
|
||||
OnPreAuthToolkit,
|
||||
} from '@kbn/core-http-server';
|
||||
import { OnPreAuthResultType } from '@kbn/core-http-server';
|
||||
import {
|
||||
HapiResponseAdapter,
|
||||
KibanaRequest,
|
||||
CoreKibanaRequest,
|
||||
KibanaResponse,
|
||||
isKibanaResponse,
|
||||
lifecycleResponseFactory,
|
||||
LifecycleResponseFactory,
|
||||
} from '../router';
|
||||
|
||||
enum ResultType {
|
||||
next = 'next',
|
||||
}
|
||||
|
||||
interface Next {
|
||||
type: ResultType.next;
|
||||
}
|
||||
|
||||
type OnPreAuthResult = Next;
|
||||
|
||||
const preAuthResult = {
|
||||
next(): OnPreAuthResult {
|
||||
return { type: ResultType.next };
|
||||
return { type: OnPreAuthResultType.next };
|
||||
},
|
||||
isNext(result: OnPreAuthResult): result is Next {
|
||||
return result && result.type === ResultType.next;
|
||||
isNext(result: OnPreAuthResult): result is OnPreAuthNextResult {
|
||||
return result && result.type === OnPreAuthResultType.next;
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* @public
|
||||
* A tool set defining an outcome of OnPreAuth interceptor for incoming request.
|
||||
*/
|
||||
export interface OnPreAuthToolkit {
|
||||
/** To pass request to the next handler */
|
||||
next: () => OnPreAuthResult;
|
||||
}
|
||||
|
||||
const toolkit: OnPreAuthToolkit = {
|
||||
next: preAuthResult.next,
|
||||
};
|
||||
|
||||
/**
|
||||
* See {@link OnPreAuthToolkit}.
|
||||
* @public
|
||||
*/
|
||||
export type OnPreAuthHandler = (
|
||||
request: KibanaRequest,
|
||||
response: LifecycleResponseFactory,
|
||||
toolkit: OnPreAuthToolkit
|
||||
) => OnPreAuthResult | KibanaResponse | Promise<OnPreAuthResult | KibanaResponse>;
|
||||
|
||||
/**
|
||||
* @public
|
||||
* Adopt custom request interceptor to Hapi lifecycle system.
|
||||
* @param fn - an extension point allowing to perform custom logic for
|
||||
* incoming HTTP requests before a user has been authenticated.
|
||||
|
@ -74,7 +49,7 @@ export function adoptToHapiOnPreAuth(fn: OnPreAuthHandler, log: Logger) {
|
|||
|
||||
try {
|
||||
const result = await fn(CoreKibanaRequest.from(request), lifecycleResponseFactory, toolkit);
|
||||
if (result instanceof KibanaResponse) {
|
||||
if (isKibanaResponse(result)) {
|
||||
return hapiResponseAdapter.handle(result);
|
||||
}
|
||||
|
||||
|
|
|
@ -14,101 +14,44 @@ import {
|
|||
} from '@hapi/hapi';
|
||||
import Boom from '@hapi/boom';
|
||||
import type { Logger } from '@kbn/logging';
|
||||
|
||||
import { HapiResponseAdapter, CoreKibanaRequest, KibanaRequest, ResponseHeaders } from '../router';
|
||||
|
||||
enum ResultType {
|
||||
render = 'render',
|
||||
next = 'next',
|
||||
}
|
||||
|
||||
interface Render {
|
||||
type: ResultType.render;
|
||||
body: string;
|
||||
headers?: ResponseHeaders;
|
||||
}
|
||||
|
||||
interface Next {
|
||||
type: ResultType.next;
|
||||
headers?: ResponseHeaders;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
type OnPreResponseResult = Render | Next;
|
||||
|
||||
/**
|
||||
* Additional data to extend a response when rendering a new body
|
||||
* @public
|
||||
*/
|
||||
export interface OnPreResponseRender {
|
||||
/** additional headers to attach to the response */
|
||||
headers?: ResponseHeaders;
|
||||
/** the body to use in the response */
|
||||
body: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Additional data to extend a response.
|
||||
* @public
|
||||
*/
|
||||
export interface OnPreResponseExtensions {
|
||||
/** additional headers to attach to the response */
|
||||
headers?: ResponseHeaders;
|
||||
}
|
||||
|
||||
/**
|
||||
* Response status code.
|
||||
* @public
|
||||
*/
|
||||
export interface OnPreResponseInfo {
|
||||
statusCode: number;
|
||||
}
|
||||
import type {
|
||||
ResponseHeaders,
|
||||
OnPreResponseRender,
|
||||
OnPreResponseResult,
|
||||
OnPreResponseToolkit,
|
||||
OnPreResponseResultRender,
|
||||
OnPreResponseResultNext,
|
||||
OnPreResponseExtensions,
|
||||
OnPreResponseHandler,
|
||||
} from '@kbn/core-http-server';
|
||||
import { OnPreResponseResultType } from '@kbn/core-http-server';
|
||||
import { HapiResponseAdapter, CoreKibanaRequest } from '../router';
|
||||
|
||||
const preResponseResult = {
|
||||
render(responseRender: OnPreResponseRender): OnPreResponseResult {
|
||||
return { type: ResultType.render, body: responseRender.body, headers: responseRender?.headers };
|
||||
return {
|
||||
type: OnPreResponseResultType.render,
|
||||
body: responseRender.body,
|
||||
headers: responseRender?.headers,
|
||||
};
|
||||
},
|
||||
isRender(result: OnPreResponseResult): result is Render {
|
||||
return result && result.type === ResultType.render;
|
||||
isRender(result: OnPreResponseResult): result is OnPreResponseResultRender {
|
||||
return result && result.type === OnPreResponseResultType.render;
|
||||
},
|
||||
next(responseExtensions?: OnPreResponseExtensions): OnPreResponseResult {
|
||||
return { type: ResultType.next, headers: responseExtensions?.headers };
|
||||
return { type: OnPreResponseResultType.next, headers: responseExtensions?.headers };
|
||||
},
|
||||
isNext(result: OnPreResponseResult): result is Next {
|
||||
return result && result.type === ResultType.next;
|
||||
isNext(result: OnPreResponseResult): result is OnPreResponseResultNext {
|
||||
return result && result.type === OnPreResponseResultType.next;
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* A tool set defining an outcome of OnPreResponse interceptor for incoming request.
|
||||
* @public
|
||||
*/
|
||||
export interface OnPreResponseToolkit {
|
||||
/** To override the response with a different body */
|
||||
render: (responseRender: OnPreResponseRender) => OnPreResponseResult;
|
||||
/** To pass request to the next handler */
|
||||
next: (responseExtensions?: OnPreResponseExtensions) => OnPreResponseResult;
|
||||
}
|
||||
|
||||
const toolkit: OnPreResponseToolkit = {
|
||||
render: preResponseResult.render,
|
||||
next: preResponseResult.next,
|
||||
};
|
||||
|
||||
/**
|
||||
* See {@link OnPreRoutingToolkit}.
|
||||
* @public
|
||||
*/
|
||||
export type OnPreResponseHandler = (
|
||||
request: KibanaRequest,
|
||||
preResponse: OnPreResponseInfo,
|
||||
toolkit: OnPreResponseToolkit
|
||||
) => OnPreResponseResult | Promise<OnPreResponseResult>;
|
||||
|
||||
/**
|
||||
* @public
|
||||
* Adopt custom request interceptor to Hapi lifecycle system.
|
||||
* @param fn - an extension point allowing to perform custom logic for
|
||||
* incoming HTTP requests.
|
||||
|
|
|
@ -8,75 +8,43 @@
|
|||
|
||||
import { Lifecycle, Request, ResponseToolkit as HapiResponseToolkit } from '@hapi/hapi';
|
||||
import type { Logger } from '@kbn/logging';
|
||||
import type {
|
||||
KibanaRequestState,
|
||||
OnPreRoutingToolkit,
|
||||
OnPreRoutingResultRewriteUrl,
|
||||
OnPreRoutingResultNext,
|
||||
OnPreRoutingResult,
|
||||
OnPreRoutingHandler,
|
||||
} from '@kbn/core-http-server';
|
||||
import { OnPreRoutingResultType } from '@kbn/core-http-server';
|
||||
import {
|
||||
HapiResponseAdapter,
|
||||
CoreKibanaRequest,
|
||||
KibanaRequest,
|
||||
KibanaResponse,
|
||||
isKibanaResponse,
|
||||
lifecycleResponseFactory,
|
||||
LifecycleResponseFactory,
|
||||
KibanaRequestState,
|
||||
} from '../router';
|
||||
|
||||
enum ResultType {
|
||||
next = 'next',
|
||||
rewriteUrl = 'rewriteUrl',
|
||||
}
|
||||
|
||||
interface Next {
|
||||
type: ResultType.next;
|
||||
}
|
||||
|
||||
interface RewriteUrl {
|
||||
type: ResultType.rewriteUrl;
|
||||
url: string;
|
||||
}
|
||||
|
||||
type OnPreRoutingResult = Next | RewriteUrl;
|
||||
|
||||
const preRoutingResult = {
|
||||
next(): OnPreRoutingResult {
|
||||
return { type: ResultType.next };
|
||||
return { type: OnPreRoutingResultType.next };
|
||||
},
|
||||
rewriteUrl(url: string): OnPreRoutingResult {
|
||||
return { type: ResultType.rewriteUrl, url };
|
||||
return { type: OnPreRoutingResultType.rewriteUrl, url };
|
||||
},
|
||||
isNext(result: OnPreRoutingResult): result is Next {
|
||||
return result && result.type === ResultType.next;
|
||||
isNext(result: OnPreRoutingResult): result is OnPreRoutingResultNext {
|
||||
return result && result.type === OnPreRoutingResultType.next;
|
||||
},
|
||||
isRewriteUrl(result: OnPreRoutingResult): result is RewriteUrl {
|
||||
return result && result.type === ResultType.rewriteUrl;
|
||||
isRewriteUrl(result: OnPreRoutingResult): result is OnPreRoutingResultRewriteUrl {
|
||||
return result && result.type === OnPreRoutingResultType.rewriteUrl;
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* @public
|
||||
* A tool set defining an outcome of OnPreRouting interceptor for incoming request.
|
||||
*/
|
||||
export interface OnPreRoutingToolkit {
|
||||
/** To pass request to the next handler */
|
||||
next: () => OnPreRoutingResult;
|
||||
/** Rewrite requested resources url before is was authenticated and routed to a handler */
|
||||
rewriteUrl: (url: string) => OnPreRoutingResult;
|
||||
}
|
||||
|
||||
const toolkit: OnPreRoutingToolkit = {
|
||||
next: preRoutingResult.next,
|
||||
rewriteUrl: preRoutingResult.rewriteUrl,
|
||||
};
|
||||
|
||||
/**
|
||||
* See {@link OnPreRoutingToolkit}.
|
||||
* @public
|
||||
*/
|
||||
export type OnPreRoutingHandler = (
|
||||
request: KibanaRequest,
|
||||
response: LifecycleResponseFactory,
|
||||
toolkit: OnPreRoutingToolkit
|
||||
) => OnPreRoutingResult | KibanaResponse | Promise<OnPreRoutingResult | KibanaResponse>;
|
||||
|
||||
/**
|
||||
* @public
|
||||
* Adopt custom request interceptor to Hapi lifecycle system.
|
||||
* @param fn - an extension point allowing to perform custom logic for
|
||||
* incoming HTTP requests.
|
||||
|
@ -90,7 +58,7 @@ export function adoptToHapiOnRequest(fn: OnPreRoutingHandler, log: Logger) {
|
|||
|
||||
try {
|
||||
const result = await fn(CoreKibanaRequest.from(request), lifecycleResponseFactory, toolkit);
|
||||
if (result instanceof KibanaResponse) {
|
||||
if (isKibanaResponse(result)) {
|
||||
return hapiResponseAdapter.handle(result);
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { KibanaRequest, RouteMethod, KibanaRouteOptions } from '@kbn/core-http-server';
|
||||
import {
|
||||
createCustomHeadersPreResponseHandler,
|
||||
createVersionCheckPostAuthHandler,
|
||||
|
@ -13,7 +14,6 @@ import {
|
|||
} from './lifecycle_handlers';
|
||||
import { httpServerMock } from './http_server.mocks';
|
||||
import { HttpConfig } from './http_config';
|
||||
import { KibanaRequest, RouteMethod, KibanaRouteOptions } from './router';
|
||||
|
||||
const createConfig = (partial: Partial<HttpConfig>): HttpConfig => partial as HttpConfig;
|
||||
|
||||
|
|
|
@ -7,8 +7,7 @@
|
|||
*/
|
||||
|
||||
import { Env } from '@kbn/config';
|
||||
import { OnPostAuthHandler } from './lifecycle/on_post_auth';
|
||||
import { OnPreResponseHandler } from './lifecycle/on_pre_response';
|
||||
import type { OnPostAuthHandler, OnPreResponseHandler } from '@kbn/core-http-server';
|
||||
import { HttpConfig } from './http_config';
|
||||
import { isSafeMethod } from './router';
|
||||
import { LifecycleRegistrar } from './http_server';
|
||||
|
|
|
@ -11,8 +11,8 @@ import { isBoom } from '@hapi/boom';
|
|||
import type { Request } from '@hapi/hapi';
|
||||
import numeral from '@elastic/numeral';
|
||||
import type { LogMeta, Logger } from '@kbn/logging';
|
||||
import type { KibanaRequestState } from '@kbn/core-http-server';
|
||||
import { getResponsePayloadBytes } from './get_payload_size';
|
||||
import type { KibanaRequestState } from '../router';
|
||||
|
||||
const FORBIDDEN_HEADERS = ['authorization', 'cookie', 'set-cookie'];
|
||||
const REDACTED_HEADER_TEXT = '[REDACTED]';
|
||||
|
|
|
@ -7,10 +7,14 @@
|
|||
*/
|
||||
|
||||
import Boom from '@hapi/boom';
|
||||
import { KibanaResponse, KibanaResponseFactory, kibanaResponseFactory } from './response';
|
||||
import type {
|
||||
KibanaResponseFactory,
|
||||
RequestHandlerContextBase,
|
||||
KibanaRequest,
|
||||
RequestHandler,
|
||||
} from '@kbn/core-http-server';
|
||||
import { kibanaResponseFactory, KibanaResponse } from './response';
|
||||
import { wrapErrors } from './error_wrapper';
|
||||
import { RequestHandlerContextBase } from '../..';
|
||||
import { KibanaRequest, RequestHandler } from '..';
|
||||
|
||||
const createHandler =
|
||||
(handler: () => any): RequestHandler<any, any, any> =>
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue