Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NP licensing plugin improvements #51818

Merged
merged 26 commits into from
Dec 9, 2019
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
6eb8572
add onPreResponse interceptor
mshustov Nov 27, 2019
c861c8b
use onPreResponse interceptor to add license sign
mshustov Nov 27, 2019
58a31d2
expose registerPreResponse to plugins
mshustov Nov 27, 2019
d536a92
refresh for license update get the most fresh license
mshustov Nov 27, 2019
c194e37
license plugin injects own header for signature: 'kbn-license-sig'
mshustov Nov 27, 2019
674062e
add integration tests for license type and license header
mshustov Nov 27, 2019
5c30f9f
Merge branch 'master' into np-licensing
mshustov Nov 28, 2019
b5e409a
Merge branch 'master' into np-licensing
mshustov Nov 28, 2019
58c92b6
switch config to duration
mshustov Nov 29, 2019
61d8a72
don't run interceptor on anon paths. add tests
mshustov Nov 29, 2019
2166b3d
add functional tests for licensing plugin
mshustov Nov 29, 2019
268cc45
regen docs
mshustov Nov 29, 2019
a3a890a
fix test in security due to updated mocks;
mshustov Nov 29, 2019
b1b79a8
Merge branch 'master' into np-licensing
mshustov Dec 2, 2019
6f0397d
update snapshots accoring to new mock implementation
mshustov Dec 2, 2019
d4cb636
Merge branch 'master' into np-licensing
mshustov Dec 3, 2019
5992878
migrate license expired banner to NP
mshustov Dec 3, 2019
10a91eb
Merge branch 'master' into np-licensing
mshustov Dec 4, 2019
4b45708
add readme for the licensing plugin
mshustov Dec 4, 2019
6b9c7bf
remove outdated import. licensing has separate functional tests
mshustov Dec 4, 2019
b9fbe71
add tag for test to run on CI
mshustov Dec 4, 2019
c9d6259
Merge branch 'master' into np-licensing
mshustov Dec 6, 2019
7eec72a
regen docs
mshustov Dec 6, 2019
404582d
Update x-pack/plugins/licensing/README.md
mshustov Dec 7, 2019
cf83cb9
Merge branch 'master' into np-licensing
mshustov Dec 9, 2019
5168054
update tests
mshustov Dec 9, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export interface HttpServiceSetup
| [registerAuth](./kibana-plugin-server.httpservicesetup.registerauth.md) | <code>(handler: AuthenticationHandler) =&gt; void</code> | To define custom authentication and/or authorization mechanism for incoming requests. |
| [registerOnPostAuth](./kibana-plugin-server.httpservicesetup.registeronpostauth.md) | <code>(handler: OnPostAuthHandler) =&gt; void</code> | To define custom logic to perform for incoming requests. |
| [registerOnPreAuth](./kibana-plugin-server.httpservicesetup.registeronpreauth.md) | <code>(handler: OnPreAuthHandler) =&gt; void</code> | To define custom logic to perform for incoming requests. |
| [registerOnPreResponse](./kibana-plugin-server.httpservicesetup.registeronpreresponse.md) | <code>(handler: OnPreResponseHandler) =&gt; void</code> | To define custom logic to perform for the server response. |
| [registerRouteHandlerContext](./kibana-plugin-server.httpservicesetup.registerroutehandlercontext.md) | <code>&lt;T extends keyof RequestHandlerContext&gt;(contextName: T, provider: RequestHandlerContextProvider&lt;T&gt;) =&gt; RequestHandlerContextContainer</code> | Register a context provider for a route handler. |

## Example
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-server](./kibana-plugin-server.md) &gt; [HttpServiceSetup](./kibana-plugin-server.httpservicesetup.md) &gt; [registerOnPreResponse](./kibana-plugin-server.httpservicesetup.registeronpreresponse.md)

## HttpServiceSetup.registerOnPreResponse property

To define custom logic to perform for the server response.

<b>Signature:</b>

```typescript
registerOnPreResponse: (handler: OnPreResponseHandler) => void;
```

## Remarks

Doesn't provide the whole response object. Allows to extend response with custom headers. See [OnPreResponseHandler](./kibana-plugin-server.onpreresponsehandler.md)<!-- -->.

4 changes: 4 additions & 0 deletions docs/development/core/server/kibana-plugin-server.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ The plugin integrates with the core system via lifecycle events: `setup`<!-- -->
| [LogMeta](./kibana-plugin-server.logmeta.md) | Contextual metadata |
| [OnPostAuthToolkit](./kibana-plugin-server.onpostauthtoolkit.md) | A tool set defining an outcome of OnPostAuth interceptor for incoming request. |
| [OnPreAuthToolkit](./kibana-plugin-server.onpreauthtoolkit.md) | A tool set defining an outcome of OnPreAuth interceptor for incoming request. |
| [OnPreResponseToolkit](./kibana-plugin-server.onpreresponsetoolkit.md) | A tool set defining an outcome of OnPreAuth interceptor for incoming request. |
| [PackageInfo](./kibana-plugin-server.packageinfo.md) | |
| [Plugin](./kibana-plugin-server.plugin.md) | The interface that should be returned by a <code>PluginInitializer</code>. |
| [PluginConfigDescriptor](./kibana-plugin-server.pluginconfigdescriptor.md) | Describes a plugin configuration schema and capabilities. |
Expand All @@ -82,6 +83,8 @@ The plugin integrates with the core system via lifecycle events: `setup`<!-- -->
| [PluginsServiceSetup](./kibana-plugin-server.pluginsservicesetup.md) | |
| [PluginsServiceStart](./kibana-plugin-server.pluginsservicestart.md) | |
| [RequestHandlerContext](./kibana-plugin-server.requesthandlercontext.md) | Plugin specific context passed to a route handler.<!-- -->Provides the following clients: - [savedObjects.client](./kibana-plugin-server.savedobjectsclient.md) - Saved Objects client which uses the credentials of the incoming request - [elasticsearch.dataClient](./kibana-plugin-server.scopedclusterclient.md) - Elasticsearch data client which uses the credentials of the incoming request - [elasticsearch.adminClient](./kibana-plugin-server.scopedclusterclient.md) - Elasticsearch admin client which uses the credentials of the incoming request |
| [ResponseExtensions](./kibana-plugin-server.responseextensions.md) | Additional data to extend a response. |
| [ResponseInfo](./kibana-plugin-server.responseinfo.md) | Response status code. |
| [RouteConfig](./kibana-plugin-server.routeconfig.md) | Route specific configuration. |
| [RouteConfigOptions](./kibana-plugin-server.routeconfigoptions.md) | Additional route options. |
| [RouteConfigOptionsBody](./kibana-plugin-server.routeconfigoptionsbody.md) | Additional body options for a route |
Expand Down Expand Up @@ -168,6 +171,7 @@ The plugin integrates with the core system via lifecycle events: `setup`<!-- -->
| [MutatingOperationRefreshSetting](./kibana-plugin-server.mutatingoperationrefreshsetting.md) | Elasticsearch Refresh setting for mutating operation |
| [OnPostAuthHandler](./kibana-plugin-server.onpostauthhandler.md) | See [OnPostAuthToolkit](./kibana-plugin-server.onpostauthtoolkit.md)<!-- -->. |
| [OnPreAuthHandler](./kibana-plugin-server.onpreauthhandler.md) | See [OnPreAuthToolkit](./kibana-plugin-server.onpreauthtoolkit.md)<!-- -->. |
| [OnPreResponseHandler](./kibana-plugin-server.onpreresponsehandler.md) | See [OnPreAuthToolkit](./kibana-plugin-server.onpreauthtoolkit.md)<!-- -->. |
| [PluginConfigSchema](./kibana-plugin-server.pluginconfigschema.md) | Dedicated type for plugin configuration schema. |
| [PluginInitializer](./kibana-plugin-server.plugininitializer.md) | The <code>plugin</code> export at the root of a plugin's <code>server</code> directory should conform to this interface. |
| [PluginName](./kibana-plugin-server.pluginname.md) | Dedicated type for plugin name/id that is supposed to make Map/Set/Arrays that use it as a key or value more obvious. |
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-server](./kibana-plugin-server.md) &gt; [OnPreResponseHandler](./kibana-plugin-server.onpreresponsehandler.md)

## OnPreResponseHandler type

See [OnPreAuthToolkit](./kibana-plugin-server.onpreauthtoolkit.md)<!-- -->.

<b>Signature:</b>

```typescript
export declare type OnPreResponseHandler = (request: KibanaRequest, response: ResponseInfo, toolkit: OnPreResponseToolkit) => OnPreResponseResult | Promise<OnPreResponseResult>;
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-server](./kibana-plugin-server.md) &gt; [OnPreResponseToolkit](./kibana-plugin-server.onpreresponsetoolkit.md)

## OnPreResponseToolkit interface

A tool set defining an outcome of OnPreAuth interceptor for incoming request.

<b>Signature:</b>

```typescript
export interface OnPreResponseToolkit
```

## Properties

| Property | Type | Description |
| --- | --- | --- |
| [next](./kibana-plugin-server.onpreresponsetoolkit.next.md) | <code>(responseExtensions?: ResponseExtensions) =&gt; OnPreResponseResult</code> | To pass request to the next handler |

Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-server](./kibana-plugin-server.md) &gt; [OnPreResponseToolkit](./kibana-plugin-server.onpreresponsetoolkit.md) &gt; [next](./kibana-plugin-server.onpreresponsetoolkit.next.md)

## OnPreResponseToolkit.next property

To pass request to the next handler

<b>Signature:</b>

```typescript
next: (responseExtensions?: ResponseExtensions) => OnPreResponseResult;
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-server](./kibana-plugin-server.md) &gt; [ResponseExtensions](./kibana-plugin-server.responseextensions.md) &gt; [headers](./kibana-plugin-server.responseextensions.headers.md)

## ResponseExtensions.headers property

<b>Signature:</b>

```typescript
headers?: ResponseHeaders;
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-server](./kibana-plugin-server.md) &gt; [ResponseExtensions](./kibana-plugin-server.responseextensions.md)

## ResponseExtensions interface

Additional data to extend a response.

<b>Signature:</b>

```typescript
export interface ResponseExtensions
```

## Properties

| Property | Type | Description |
| --- | --- | --- |
| [headers](./kibana-plugin-server.responseextensions.headers.md) | <code>ResponseHeaders</code> | |

20 changes: 20 additions & 0 deletions docs/development/core/server/kibana-plugin-server.responseinfo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-server](./kibana-plugin-server.md) &gt; [ResponseInfo](./kibana-plugin-server.responseinfo.md)

## ResponseInfo interface

Response status code.

<b>Signature:</b>

```typescript
export interface ResponseInfo
```

## Properties

| Property | Type | Description |
| --- | --- | --- |
| [statusCode](./kibana-plugin-server.responseinfo.statuscode.md) | <code>number</code> | |

Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-server](./kibana-plugin-server.md) &gt; [ResponseInfo](./kibana-plugin-server.responseinfo.md) &gt; [statusCode](./kibana-plugin-server.responseinfo.statuscode.md)

## ResponseInfo.statusCode property
mshustov marked this conversation as resolved.
Show resolved Hide resolved

<b>Signature:</b>

```typescript
statusCode: number;
```
7 changes: 5 additions & 2 deletions src/core/public/http/http_service.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ import { HttpService } from './http_service';
import { HttpSetup } from './types';
import { BehaviorSubject } from 'rxjs';
import { BasePath } from './base_path_service';
import { AnonymousPaths } from './anonymous_paths';

type ServiceSetupMockType = jest.Mocked<HttpSetup> & {
basePath: BasePath;
anonymousPaths: jest.Mocked<HttpSetup['anonymousPaths']>;
};

const createServiceMock = ({ basePath = '' } = {}): ServiceSetupMockType => ({
Expand All @@ -37,7 +37,10 @@ const createServiceMock = ({ basePath = '' } = {}): ServiceSetupMockType => ({
delete: jest.fn(),
options: jest.fn(),
basePath: new BasePath(basePath),
anonymousPaths: new AnonymousPaths(new BasePath(basePath)),
anonymousPaths: {
register: jest.fn(),
isAnonymous: jest.fn(),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

plugin authors doesn't have to read AnonymousPaths implementation to be able to use mocks. WDYT?

},
addLoadingCount: jest.fn(),
getLoadingCount$: jest.fn().mockReturnValue(new BehaviorSubject(0)),
stop: jest.fn(),
Expand Down
6 changes: 3 additions & 3 deletions src/core/public/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
*/
import { applicationServiceMock } from './application/application_service.mock';
import { chromeServiceMock } from './chrome/chrome_service.mock';
import { CoreContext, CoreSetup, CoreStart, PluginInitializerContext, NotificationsSetup } from '.';
import { CoreContext, PluginInitializerContext } from '.';
import { docLinksServiceMock } from './doc_links/doc_links_service.mock';
import { fatalErrorsServiceMock } from './fatal_errors/fatal_errors_service.mock';
import { httpServiceMock } from './http/http_service.mock';
Expand All @@ -42,7 +42,7 @@ export { overlayServiceMock } from './overlays/overlay_service.mock';
export { uiSettingsServiceMock } from './ui_settings/ui_settings_service.mock';

function createCoreSetupMock({ basePath = '' } = {}) {
const mock: MockedKeys<CoreSetup> & { notifications: MockedKeys<NotificationsSetup> } = {
const mock = {
application: applicationServiceMock.createSetupContract(),
context: contextServiceMock.createSetupContract(),
fatalErrors: fatalErrorsServiceMock.createSetupContract(),
Expand All @@ -58,7 +58,7 @@ function createCoreSetupMock({ basePath = '' } = {}) {
}

function createCoreStartMock({ basePath = '' } = {}) {
const mock: MockedKeys<CoreStart> & { notifications: MockedKeys<NotificationsSetup> } = {
const mock = {
application: applicationServiceMock.createStartContract(),
chrome: chromeServiceMock.createStartContract(),
docLinks: docLinksServiceMock.createStartContract(),
Expand Down
49 changes: 15 additions & 34 deletions src/core/server/http/http_server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
* under the License.
*/

import { Request, Server } from 'hapi';
import { Server } from 'hapi';
import url from 'url';

import { Logger, LoggerFactory } from '../logging';
Expand All @@ -26,8 +26,9 @@ import { createServer, getListenerOptions, getServerOptions } from './http_tools
import { adoptToHapiAuthFormat, AuthenticationHandler } from './lifecycle/auth';
import { adoptToHapiOnPostAuthFormat, OnPostAuthHandler } from './lifecycle/on_post_auth';
import { adoptToHapiOnPreAuthFormat, OnPreAuthHandler } from './lifecycle/on_pre_auth';
import { adoptToHapiOnPreResponseFormat, OnPreResponseHandler } from './lifecycle/on_pre_response';

import { ResponseHeaders, IRouter } from './router';
import { IRouter } from './router';
import {
SessionStorageCookieOptions,
createCookieSessionStorageFactory,
Expand All @@ -50,6 +51,7 @@ export interface HttpServerSetup {
registerAuth: HttpServiceSetup['registerAuth'];
registerOnPreAuth: HttpServiceSetup['registerOnPreAuth'];
registerOnPostAuth: HttpServiceSetup['registerOnPostAuth'];
registerOnPreResponse: HttpServiceSetup['registerOnPreResponse'];
isTlsEnabled: HttpServiceSetup['isTlsEnabled'];
auth: {
get: GetAuthState;
Expand Down Expand Up @@ -103,6 +105,7 @@ export class HttpServer {
registerRouter: this.registerRouter.bind(this),
registerOnPreAuth: this.registerOnPreAuth.bind(this),
registerOnPostAuth: this.registerOnPostAuth.bind(this),
registerOnPreResponse: this.registerOnPreResponse.bind(this),
createCookieSessionStorageFactory: <T>(cookieOptions: SessionStorageCookieOptions<T>) =>
this.createCookieSessionStorageFactory(cookieOptions, config.basePath),
registerAuth: this.registerAuth.bind(this),
Expand Down Expand Up @@ -232,6 +235,14 @@ export class HttpServer {
this.server.ext('onRequest', adoptToHapiOnPreAuthFormat(fn, this.log));
}

private registerOnPreResponse(fn: OnPreResponseHandler) {
if (this.server === undefined) {
throw new Error('Server is not created yet');
}

this.server.ext('onPreResponse', adoptToHapiOnPreResponseFormat(fn, this.log));
}

private async createCookieSessionStorageFactory<T>(
cookieOptions: SessionStorageCookieOptions<T>,
basePath?: string
Expand Down Expand Up @@ -289,39 +300,9 @@ export class HttpServer {
// https://github.com/hapijs/hapi/blob/master/API.md#-serverauthdefaultoptions
this.server.auth.default('session');

this.server.ext('onPreResponse', (request, t) => {
this.registerOnPreResponse((request, responseInfo, t) => {
const authResponseHeaders = this.authResponseHeaders.get(request);
this.extendResponseWithHeaders(request, authResponseHeaders);
return t.continue;
});
}

private extendResponseWithHeaders(request: Request, headers?: ResponseHeaders) {
const response = request.response;
if (!headers || !response) return;

if (response instanceof Error) {
this.findHeadersIntersection(response.output.headers, headers);
// hapi wraps all error response in Boom object internally
response.output.headers = {
...response.output.headers,
...(headers as any), // hapi types don't specify string[] as valid value
};
} else {
for (const [headerName, headerValue] of Object.entries(headers)) {
this.findHeadersIntersection(response.headers, headers);
response.header(headerName, headerValue as any); // hapi types don't specify string[] as valid value
}
}
}

// NOTE: responseHeaders contains not a full list of response headers, but only explicitly set on a response object.
// any headers added by hapi internally, like `content-type`, `content-length`, etc. do not present here.
private findHeadersIntersection(responseHeaders: ResponseHeaders, headers: ResponseHeaders) {
Object.keys(headers).forEach(headerName => {
if (responseHeaders[headerName] !== undefined) {
this.log.warn(`Server rewrites a response header [${headerName}].`);
}
return t.next({ headers: authResponseHeaders });
});
}
}
7 changes: 7 additions & 0 deletions src/core/server/http/http_service.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { AuthToolkit } from './lifecycle/auth';
import { sessionStorageMock } from './cookie_session_storage.mocks';
import { IRouter } from './router';
import { OnPostAuthToolkit } from './lifecycle/on_post_auth';
import { OnPreResponseToolkit } from './lifecycle/on_pre_response';

type ServiceSetupMockType = jest.Mocked<InternalHttpServiceSetup> & {
basePath: jest.Mocked<InternalHttpServiceSetup['basePath']>;
Expand Down Expand Up @@ -62,6 +63,7 @@ const createSetupContractMock = () => {
registerAuth: jest.fn(),
registerOnPostAuth: jest.fn(),
registerRouteHandlerContext: jest.fn(),
registerOnPreResponse: jest.fn(),
createRouter: jest.fn(),
basePath: createBasePathMock(),
auth: {
Expand Down Expand Up @@ -103,12 +105,17 @@ const createAuthToolkitMock = (): jest.Mocked<AuthToolkit> => ({
authenticated: jest.fn(),
});

const createOnPreResponseToolkitMock = (): jest.Mocked<OnPreResponseToolkit> => ({
next: jest.fn(),
});

export const httpServiceMock = {
create: createHttpServiceMock,
createBasePath: createBasePathMock,
createSetupContract: createSetupContractMock,
createOnPreAuthToolkit: createOnPreAuthToolkitMock,
createOnPostAuthToolkit: createOnPostAuthToolkitMock,
createOnPreResponseToolkit: createOnPreResponseToolkitMock,
createAuthToolkit: createAuthToolkitMock,
createRouter: createRouterMock,
};
6 changes: 6 additions & 0 deletions src/core/server/http/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@ export {
AuthResultType,
} from './lifecycle/auth';
export { OnPostAuthHandler, OnPostAuthToolkit } from './lifecycle/on_post_auth';
export {
OnPreResponseHandler,
OnPreResponseToolkit,
ResponseInfo,
ResponseExtensions,
} from './lifecycle/on_pre_response';
export { SessionStorageFactory, SessionStorage } from './session_storage';
export {
SessionStorageCookieOptions,
Expand Down
Loading