Skip to content

Commit

Permalink
[KP] Separate onPreAuth & onPreRouting http interceptors (#70775)
Browse files Browse the repository at this point in the history
Co-authored-by: Aleh Zasypkin <aleh.zasypkin@gmail.com>
Co-authored-by: Josh Dover <me@joshdover.com>
  • Loading branch information
3 people authored Jul 13, 2020
1 parent e51b92d commit 0ea414c
Show file tree
Hide file tree
Showing 29 changed files with 605 additions and 97 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,9 @@ async (context, request, response) => {
| [csp](./kibana-plugin-core-server.httpservicesetup.csp.md) | <code>ICspConfig</code> | The CSP config used for Kibana. |
| [getServerInfo](./kibana-plugin-core-server.httpservicesetup.getserverinfo.md) | <code>() =&gt; HttpServerInfo</code> | Provides common [information](./kibana-plugin-core-server.httpserverinfo.md) about the running http server. |
| [registerAuth](./kibana-plugin-core-server.httpservicesetup.registerauth.md) | <code>(handler: AuthenticationHandler) =&gt; void</code> | To define custom authentication and/or authorization mechanism for incoming requests. |
| [registerOnPostAuth](./kibana-plugin-core-server.httpservicesetup.registeronpostauth.md) | <code>(handler: OnPostAuthHandler) =&gt; void</code> | To define custom logic to perform for incoming requests. |
| [registerOnPreAuth](./kibana-plugin-core-server.httpservicesetup.registeronpreauth.md) | <code>(handler: OnPreAuthHandler) =&gt; void</code> | To define custom logic to perform for incoming requests. |
| [registerOnPostAuth](./kibana-plugin-core-server.httpservicesetup.registeronpostauth.md) | <code>(handler: OnPostAuthHandler) =&gt; void</code> | To define custom logic after Auth interceptor did make sure a user has access to the requested resource. |
| [registerOnPreAuth](./kibana-plugin-core-server.httpservicesetup.registeronpreauth.md) | <code>(handler: OnPreAuthHandler) =&gt; void</code> | To define custom logic to perform for incoming requests before the Auth interceptor performs a check that user has access to requested resources. |
| [registerOnPreResponse](./kibana-plugin-core-server.httpservicesetup.registeronpreresponse.md) | <code>(handler: OnPreResponseHandler) =&gt; void</code> | To define custom logic to perform for the server response. |
| [registerOnPreRouting](./kibana-plugin-core-server.httpservicesetup.registeronprerouting.md) | <code>(handler: OnPreRoutingHandler) =&gt; void</code> | To define custom logic to perform for incoming requests before server performs a route lookup. |
| [registerRouteHandlerContext](./kibana-plugin-core-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. |

Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

## HttpServiceSetup.registerOnPostAuth property

To define custom logic to perform for incoming requests.
To define custom logic after Auth interceptor did make sure a user has access to the requested resource.

<b>Signature:</b>

Expand All @@ -14,5 +14,5 @@ registerOnPostAuth: (handler: OnPostAuthHandler) => void;

## Remarks

Runs the handler after Auth interceptor did make sure a user has access to the requested resource. The auth state is available at stage via http.auth.get(..) Can register any number of registerOnPreAuth, which are called in sequence (from the first registered to the last). See [OnPostAuthHandler](./kibana-plugin-core-server.onpostauthhandler.md)<!-- -->.
The auth state is available at stage via http.auth.get(..) Can register any number of registerOnPreRouting, which are called in sequence (from the first registered to the last). See [OnPostAuthHandler](./kibana-plugin-core-server.onpostauthhandler.md)<!-- -->.

Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

## HttpServiceSetup.registerOnPreAuth property

To define custom logic to perform for incoming requests.
To define custom logic to perform for incoming requests before the Auth interceptor performs a check that user has access to requested resources.

<b>Signature:</b>

Expand All @@ -14,5 +14,5 @@ registerOnPreAuth: (handler: OnPreAuthHandler) => void;

## Remarks

Runs the handler before Auth interceptor performs a check that user has access to requested resources, so it's the only place when you can forward a request to another URL right on the server. Can register any number of registerOnPostAuth, which are called in sequence (from the first registered to the last). See [OnPreAuthHandler](./kibana-plugin-core-server.onpreauthhandler.md)<!-- -->.
Can register any number of registerOnPostAuth, which are called in sequence (from the first registered to the last). See [OnPreRoutingHandler](./kibana-plugin-core-server.onpreroutinghandler.md)<!-- -->.

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-core-server](./kibana-plugin-core-server.md) &gt; [HttpServiceSetup](./kibana-plugin-core-server.httpservicesetup.md) &gt; [registerOnPreRouting](./kibana-plugin-core-server.httpservicesetup.registeronprerouting.md)

## HttpServiceSetup.registerOnPreRouting property

To define custom logic to perform for incoming requests before server performs a route lookup.

<b>Signature:</b>

```typescript
registerOnPreRouting: (handler: OnPreRoutingHandler) => void;
```

## 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 [OnPreRoutingHandler](./kibana-plugin-core-server.onpreroutinghandler.md)<!-- -->.

6 changes: 4 additions & 2 deletions docs/development/core/server/kibana-plugin-core-server.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,8 @@ The plugin integrates with the core system via lifecycle events: `setup`<!-- -->
| [OnPreAuthToolkit](./kibana-plugin-core-server.onpreauthtoolkit.md) | A tool set defining an outcome of OnPreAuth interceptor for incoming request. |
| [OnPreResponseExtensions](./kibana-plugin-core-server.onpreresponseextensions.md) | Additional data to extend a response. |
| [OnPreResponseInfo](./kibana-plugin-core-server.onpreresponseinfo.md) | Response status code. |
| [OnPreResponseToolkit](./kibana-plugin-core-server.onpreresponsetoolkit.md) | A tool set defining an outcome of OnPreAuth interceptor for incoming request. |
| [OnPreResponseToolkit](./kibana-plugin-core-server.onpreresponsetoolkit.md) | A tool set defining an outcome of OnPreRouting interceptor for incoming request. |
| [OnPreRoutingToolkit](./kibana-plugin-core-server.onpreroutingtoolkit.md) | A tool set defining an outcome of OnPreRouting interceptor for incoming request. |
| [OpsMetrics](./kibana-plugin-core-server.opsmetrics.md) | Regroups metrics gathered by all the collectors. This contains metrics about the os/runtime, the kibana process and the http server. |
| [OpsOsMetrics](./kibana-plugin-core-server.opsosmetrics.md) | OS related metrics |
| [OpsProcessMetrics](./kibana-plugin-core-server.opsprocessmetrics.md) | Process related metrics |
Expand Down Expand Up @@ -256,7 +257,8 @@ The plugin integrates with the core system via lifecycle events: `setup`<!-- -->
| [MutatingOperationRefreshSetting](./kibana-plugin-core-server.mutatingoperationrefreshsetting.md) | Elasticsearch Refresh setting for mutating operation |
| [OnPostAuthHandler](./kibana-plugin-core-server.onpostauthhandler.md) | See [OnPostAuthToolkit](./kibana-plugin-core-server.onpostauthtoolkit.md)<!-- -->. |
| [OnPreAuthHandler](./kibana-plugin-core-server.onpreauthhandler.md) | See [OnPreAuthToolkit](./kibana-plugin-core-server.onpreauthtoolkit.md)<!-- -->. |
| [OnPreResponseHandler](./kibana-plugin-core-server.onpreresponsehandler.md) | See [OnPreAuthToolkit](./kibana-plugin-core-server.onpreauthtoolkit.md)<!-- -->. |
| [OnPreResponseHandler](./kibana-plugin-core-server.onpreresponsehandler.md) | See [OnPreRoutingToolkit](./kibana-plugin-core-server.onpreroutingtoolkit.md)<!-- -->. |
| [OnPreRoutingHandler](./kibana-plugin-core-server.onpreroutinghandler.md) | See [OnPreRoutingToolkit](./kibana-plugin-core-server.onpreroutingtoolkit.md)<!-- -->. |
| [PluginConfigSchema](./kibana-plugin-core-server.pluginconfigschema.md) | Dedicated type for plugin configuration schema. |
| [PluginInitializer](./kibana-plugin-core-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-core-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
Expand Up @@ -17,5 +17,4 @@ export interface OnPreAuthToolkit
| Property | Type | Description |
| --- | --- | --- |
| [next](./kibana-plugin-core-server.onpreauthtoolkit.next.md) | <code>() =&gt; OnPreAuthResult</code> | To pass request to the next handler |
| [rewriteUrl](./kibana-plugin-core-server.onpreauthtoolkit.rewriteurl.md) | <code>(url: string) =&gt; OnPreAuthResult</code> | Rewrite requested resources url before is was authenticated and routed to a handler |

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

## OnPreResponseHandler type

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

<b>Signature:</b>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

## OnPreResponseToolkit interface

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

<b>Signature:</b>

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-core-server](./kibana-plugin-core-server.md) &gt; [OnPreRoutingHandler](./kibana-plugin-core-server.onpreroutinghandler.md)

## OnPreRoutingHandler type

See [OnPreRoutingToolkit](./kibana-plugin-core-server.onpreroutingtoolkit.md)<!-- -->.

<b>Signature:</b>

```typescript
export declare type OnPreRoutingHandler = (request: KibanaRequest, response: LifecycleResponseFactory, toolkit: OnPreRoutingToolkit) => OnPreRoutingResult | KibanaResponse | Promise<OnPreRoutingResult | KibanaResponse>;
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [OnPreRoutingToolkit](./kibana-plugin-core-server.onpreroutingtoolkit.md)

## OnPreRoutingToolkit interface

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

<b>Signature:</b>

```typescript
export interface OnPreRoutingToolkit
```

## Properties

| Property | Type | Description |
| --- | --- | --- |
| [next](./kibana-plugin-core-server.onpreroutingtoolkit.next.md) | <code>() =&gt; OnPreRoutingResult</code> | To pass request to the next handler |
| [rewriteUrl](./kibana-plugin-core-server.onpreroutingtoolkit.rewriteurl.md) | <code>(url: string) =&gt; OnPreRoutingResult</code> | Rewrite requested resources url before is was authenticated and routed to a 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-core-server](./kibana-plugin-core-server.md) &gt; [OnPreRoutingToolkit](./kibana-plugin-core-server.onpreroutingtoolkit.md) &gt; [next](./kibana-plugin-core-server.onpreroutingtoolkit.next.md)

## OnPreRoutingToolkit.next property

To pass request to the next handler

<b>Signature:</b>

```typescript
next: () => OnPreRoutingResult;
```
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-core-server](./kibana-plugin-core-server.md) &gt; [OnPreRoutingToolkit](./kibana-plugin-core-server.onpreroutingtoolkit.md) &gt; [rewriteUrl](./kibana-plugin-core-server.onpreroutingtoolkit.rewriteurl.md)

## OnPreRoutingToolkit.rewriteUrl property

Rewrite requested resources url before is was authenticated and routed to a handler

<b>Signature:</b>

```typescript
rewriteUrl: (url: string) => OnPreRoutingResult;
```
4 changes: 2 additions & 2 deletions src/core/server/http/http_server.mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import {
} from './router';
import { OnPreResponseToolkit } from './lifecycle/on_pre_response';
import { OnPostAuthToolkit } from './lifecycle/on_post_auth';
import { OnPreAuthToolkit } from './lifecycle/on_pre_auth';
import { OnPreRoutingToolkit } from './lifecycle/on_pre_routing';

interface RequestFixtureOptions<P = any, Q = any, B = any> {
auth?: { isAuthenticated: boolean };
Expand Down Expand Up @@ -161,7 +161,7 @@ const createLifecycleResponseFactoryMock = (): jest.Mocked<LifecycleResponseFact
customError: jest.fn(),
});

type ToolkitMock = jest.Mocked<OnPreResponseToolkit & OnPostAuthToolkit & OnPreAuthToolkit>;
type ToolkitMock = jest.Mocked<OnPreResponseToolkit & OnPostAuthToolkit & OnPreRoutingToolkit>;

const createToolkitMock = (): ToolkitMock => {
return {
Expand Down
10 changes: 10 additions & 0 deletions src/core/server/http/http_server.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1089,6 +1089,16 @@ describe('setup contract', () => {
});
});

describe('#registerOnPreRouting', () => {
test('does not throw if called after stop', async () => {
const { registerOnPreRouting } = await server.setup(config);
await server.stop();
expect(() => {
registerOnPreRouting((req, res) => res.unauthorized());
}).not.toThrow();
});
});

describe('#registerOnPreAuth', () => {
test('does not throw if called after stop', async () => {
const { registerOnPreAuth } = await server.setup(config);
Expand Down
34 changes: 26 additions & 8 deletions src/core/server/http/http_server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@ import { Logger, LoggerFactory } from '../logging';
import { HttpConfig } from './http_config';
import { createServer, getListenerOptions, getServerOptions } from './http_tools';
import { adoptToHapiAuthFormat, AuthenticationHandler } from './lifecycle/auth';
import { adoptToHapiOnPreAuth, OnPreAuthHandler } from './lifecycle/on_pre_auth';
import { adoptToHapiOnPostAuthFormat, OnPostAuthHandler } from './lifecycle/on_post_auth';
import { adoptToHapiOnPreAuthFormat, OnPreAuthHandler } from './lifecycle/on_pre_auth';
import { adoptToHapiOnRequest, OnPreRoutingHandler } from './lifecycle/on_pre_routing';
import { adoptToHapiOnPreResponseFormat, OnPreResponseHandler } from './lifecycle/on_pre_response';
import { IRouter, RouteConfigOptions, KibanaRouteState, isSafeMethod } from './router';
import {
Expand All @@ -49,8 +50,9 @@ export interface HttpServerSetup {
basePath: HttpServiceSetup['basePath'];
csp: HttpServiceSetup['csp'];
createCookieSessionStorageFactory: HttpServiceSetup['createCookieSessionStorageFactory'];
registerAuth: HttpServiceSetup['registerAuth'];
registerOnPreRouting: HttpServiceSetup['registerOnPreRouting'];
registerOnPreAuth: HttpServiceSetup['registerOnPreAuth'];
registerAuth: HttpServiceSetup['registerAuth'];
registerOnPostAuth: HttpServiceSetup['registerOnPostAuth'];
registerOnPreResponse: HttpServiceSetup['registerOnPreResponse'];
getAuthHeaders: GetAuthHeaders;
Expand All @@ -64,7 +66,11 @@ export interface HttpServerSetup {
/** @internal */
export type LifecycleRegistrar = Pick<
HttpServerSetup,
'registerAuth' | 'registerOnPreAuth' | 'registerOnPostAuth' | 'registerOnPreResponse'
| 'registerOnPreRouting'
| 'registerOnPreAuth'
| 'registerAuth'
| 'registerOnPostAuth'
| 'registerOnPreResponse'
>;

export class HttpServer {
Expand Down Expand Up @@ -113,12 +119,13 @@ export class HttpServer {
return {
registerRouter: this.registerRouter.bind(this),
registerStaticDir: this.registerStaticDir.bind(this),
registerOnPreRouting: this.registerOnPreRouting.bind(this),
registerOnPreAuth: this.registerOnPreAuth.bind(this),
registerAuth: this.registerAuth.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),
basePath: basePathService,
csp: config.csp,
auth: {
Expand Down Expand Up @@ -222,7 +229,7 @@ export class HttpServer {
return;
}

this.registerOnPreAuth((request, response, toolkit) => {
this.registerOnPreRouting((request, response, toolkit) => {
const oldUrl = request.url.href!;
const newURL = basePathService.remove(oldUrl);
const shouldRedirect = newURL !== oldUrl;
Expand Down Expand Up @@ -263,6 +270,17 @@ export class HttpServer {
}
}

private registerOnPreAuth(fn: OnPreAuthHandler) {
if (this.server === undefined) {
throw new Error('Server is not created yet');
}
if (this.stopped) {
this.log.warn(`registerOnPreAuth called after stop`);
}

this.server.ext('onPreAuth', adoptToHapiOnPreAuth(fn, this.log));
}

private registerOnPostAuth(fn: OnPostAuthHandler) {
if (this.server === undefined) {
throw new Error('Server is not created yet');
Expand All @@ -274,15 +292,15 @@ export class HttpServer {
this.server.ext('onPostAuth', adoptToHapiOnPostAuthFormat(fn, this.log));
}

private registerOnPreAuth(fn: OnPreAuthHandler) {
private registerOnPreRouting(fn: OnPreRoutingHandler) {
if (this.server === undefined) {
throw new Error('Server is not created yet');
}
if (this.stopped) {
this.log.warn(`registerOnPreAuth called after stop`);
this.log.warn(`registerOnPreRouting called after stop`);
}

this.server.ext('onRequest', adoptToHapiOnPreAuthFormat(fn, this.log));
this.server.ext('onRequest', adoptToHapiOnRequest(fn, this.log));
}

private registerOnPreResponse(fn: OnPreResponseHandler) {
Expand Down
8 changes: 5 additions & 3 deletions src/core/server/http/http_service.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import {
} from './types';
import { HttpService } from './http_service';
import { AuthStatus } from './auth_state_storage';
import { OnPreAuthToolkit } from './lifecycle/on_pre_auth';
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';
Expand Down Expand Up @@ -87,6 +87,7 @@ const createInternalSetupContractMock = () => {
config: jest.fn().mockReturnValue(configMock.create()),
} as unknown) as jest.MockedClass<Server>,
createCookieSessionStorageFactory: jest.fn(),
registerOnPreRouting: jest.fn(),
registerOnPreAuth: jest.fn(),
registerAuth: jest.fn(),
registerOnPostAuth: jest.fn(),
Expand Down Expand Up @@ -117,7 +118,8 @@ const createSetupContractMock = () => {

const mock: HttpServiceSetupMock = {
createCookieSessionStorageFactory: internalMock.createCookieSessionStorageFactory,
registerOnPreAuth: internalMock.registerOnPreAuth,
registerOnPreRouting: internalMock.registerOnPreRouting,
registerOnPreAuth: jest.fn(),
registerAuth: internalMock.registerAuth,
registerOnPostAuth: internalMock.registerOnPostAuth,
registerOnPreResponse: internalMock.registerOnPreResponse,
Expand Down Expand Up @@ -173,7 +175,7 @@ const createHttpServiceMock = () => {
return mocked;
};

const createOnPreAuthToolkitMock = (): jest.Mocked<OnPreAuthToolkit> => ({
const createOnPreAuthToolkitMock = (): jest.Mocked<OnPreRoutingToolkit> => ({
next: jest.fn(),
rewriteUrl: jest.fn(),
});
Expand Down
3 changes: 2 additions & 1 deletion src/core/server/http/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export {
SafeRouteMethod,
} from './router';
export { BasePathProxyServer } from './base_path_proxy_server';
export { OnPreAuthHandler, OnPreAuthToolkit } from './lifecycle/on_pre_auth';
export { OnPreRoutingHandler, OnPreRoutingToolkit } from './lifecycle/on_pre_routing';
export {
AuthenticationHandler,
AuthHeaders,
Expand All @@ -78,6 +78,7 @@ export {
AuthResultType,
} from './lifecycle/auth';
export { OnPostAuthHandler, OnPostAuthToolkit } from './lifecycle/on_post_auth';
export { OnPreAuthHandler, OnPreAuthToolkit } from './lifecycle/on_pre_auth';
export {
OnPreResponseHandler,
OnPreResponseToolkit,
Expand Down
Loading

0 comments on commit 0ea414c

Please sign in to comment.