Skip to content

Commit

Permalink
[7.x] Upgrade to hapi version 18 (#80468) (#82265)
Browse files Browse the repository at this point in the history
  • Loading branch information
watson authored Nov 2, 2020
1 parent dee1841 commit df4f66b
Show file tree
Hide file tree
Showing 248 changed files with 790 additions and 739 deletions.
34 changes: 20 additions & 14 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@
"url": "https://github.com/elastic/kibana.git"
},
"resolutions": {
"**/@hapi/iron": "^5.1.4",
"**/@types/hapi__boom": "^7.4.1",
"**/@types/hapi__hapi": "^18.2.6",
"**/@types/hapi__mimos": "4.1.0",
"**/@types/node": ">=10.17.17 <10.20.0",
"**/cross-fetch/node-fetch": "^2.6.1",
"**/deepmerge": "^4.2.2",
Expand Down Expand Up @@ -121,7 +125,16 @@
"@elastic/numeral": "^2.5.0",
"@elastic/request-crypto": "1.1.4",
"@elastic/safer-lodash-set": "0.0.0",
"@hapi/boom": "^7.4.11",
"@hapi/cookie": "^10.1.2",
"@hapi/good-squeeze": "5.2.1",
"@hapi/h2o2": "^8.3.2",
"@hapi/hapi": "^18.4.1",
"@hapi/hoek": "^8.5.1",
"@hapi/inert": "^5.2.2",
"@hapi/podium": "^3.4.3",
"@hapi/statehood": "^6.1.2",
"@hapi/vision": "^5.5.4",
"@hapi/wreck": "^15.0.2",
"@kbn/ace": "1.0.0",
"@kbn/analytics": "1.0.0",
Expand All @@ -144,7 +157,6 @@
"angular-elastic": "^2.5.1",
"angular-sanitize": "^1.8.0",
"bluebird": "3.5.5",
"boom": "^7.2.0",
"chalk": "^4.1.0",
"check-disk-space": "^2.1.0",
"chokidar": "^3.4.2",
Expand All @@ -164,15 +176,10 @@
"glob": "^7.1.2",
"glob-all": "^3.2.1",
"globby": "^8.0.1",
"h2o2": "^8.1.2",
"handlebars": "4.7.6",
"hapi": "^17.5.3",
"hapi-auth-cookie": "^9.0.0",
"hjson": "3.2.1",
"hoek": "^5.0.4",
"http-proxy-agent": "^2.1.0",
"https-proxy-agent": "^5.0.0",
"inert": "^5.1.0",
"inline-style": "^2.0.0",
"joi": "^13.5.2",
"js-yaml": "^3.14.0",
Expand Down Expand Up @@ -216,7 +223,6 @@
"tslib": "^2.0.0",
"type-detect": "^4.0.8",
"uuid": "3.3.2",
"vision": "^5.3.3",
"whatwg-fetch": "^3.0.0",
"yauzl": "^2.10.0"
},
Expand Down Expand Up @@ -270,7 +276,6 @@
"@types/archiver": "^3.1.0",
"@types/babel__core": "^7.1.10",
"@types/bluebird": "^3.1.1",
"@types/boom": "^7.2.0",
"@types/chance": "^1.0.0",
"@types/cheerio": "^0.22.10",
"@types/chromedriver": "^81.0.0",
Expand All @@ -290,14 +295,16 @@
"@types/glob": "^7.1.2",
"@types/globby": "^8.0.0",
"@types/graphql": "^0.13.2",
"@types/h2o2": "^8.1.1",
"@types/hapi": "^17.0.18",
"@types/hapi-auth-cookie": "^9.1.0",
"@types/hapi__boom": "^7.4.1",
"@types/hapi__cookie": "^10.1.1",
"@types/hapi__h2o2": "8.3.0",
"@types/hapi__hapi": "^18.2.6",
"@types/hapi__hoek": "^6.2.0",
"@types/hapi__inert": "^5.2.1",
"@types/hapi__podium": "^3.4.1",
"@types/has-ansi": "^3.0.0",
"@types/history": "^4.7.3",
"@types/hjson": "^2.4.2",
"@types/hoek": "^4.1.3",
"@types/inert": "^5.1.2",
"@types/jest": "^26.0.14",
"@types/jest-when": "^2.7.1",
"@types/joi": "^13.4.2",
Expand All @@ -322,7 +329,6 @@
"@types/opn": "^5.1.0",
"@types/pegjs": "^0.10.1",
"@types/pngjs": "^3.4.0",
"@types/podium": "^1.0.0",
"@types/prop-types": "^15.7.3",
"@types/reach__router": "^1.2.6",
"@types/react": "^16.9.36",
Expand Down
2 changes: 1 addition & 1 deletion src/core/public/public.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import { Action } from 'history';
import { ApiResponse } from '@elastic/elasticsearch/lib/Transport';
import Boom from 'boom';
import Boom from '@hapi/boom';
import { ConfigPath } from '@kbn/config';
import { EnvironmentMode } from '@kbn/config';
import { EuiBreadcrumb } from '@elastic/eui';
Expand Down
2 changes: 1 addition & 1 deletion src/core/server/elasticsearch/legacy/errors.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
* under the License.
*/

import Boom from 'boom';
import Boom from '@hapi/boom';

import { LegacyElasticsearchErrorHelpers } from './errors';

Expand Down
2 changes: 1 addition & 1 deletion src/core/server/elasticsearch/legacy/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
* under the License.
*/

import Boom from 'boom';
import Boom from '@hapi/boom';
import { get } from 'lodash';

const code = Symbol('ElasticsearchError');
Expand Down
4 changes: 2 additions & 2 deletions src/core/server/http/base_path_proxy_server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ import { Agent as HttpsAgent, ServerOptions as TlsOptions } from 'https';

import apm from 'elastic-apm-node';
import { ByteSizeValue } from '@kbn/config-schema';
import { Server, Request } from 'hapi';
import HapiProxy from 'h2o2';
import { Server, Request } from '@hapi/hapi';
import HapiProxy from '@hapi/h2o2';
import { sampleSize } from 'lodash';
import * as Rx from 'rxjs';
import { take } from 'rxjs/operators';
Expand Down
26 changes: 14 additions & 12 deletions src/core/server/http/cookie_session_storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@
* under the License.
*/

import { Request, Server } from 'hapi';
import hapiAuthCookie from 'hapi-auth-cookie';
import { Request, Server } from '@hapi/hapi';
import hapiAuthCookie from '@hapi/cookie';
// @ts-expect-error no TS definitions
import Statehood from 'statehood';
import Statehood from '@hapi/statehood';

import { KibanaRequest, ensureRawRequest } from './router';
import { SessionStorageFactory, SessionStorage } from './session_storage';
Expand Down Expand Up @@ -80,7 +80,7 @@ class ScopedCookieSessionStorage<T extends Record<string, any>> implements Sessi
const session = await this.server.auth.test('security-cookie', this.request);
// A browser can send several cookies, if it's not an array, just return the session value
if (!Array.isArray(session)) {
return session as T;
return session.credentials as T;
}

// If we have an array with one value, we're good also
Expand Down Expand Up @@ -141,20 +141,22 @@ export async function createCookieSessionStorageFactory<T>(
await server.register({ plugin: hapiAuthCookie });

server.auth.strategy('security-cookie', 'cookie', {
cookie: cookieOptions.name,
password: cookieOptions.encryptionKey,
validateFunc: async (req, session: T | T[]) => {
cookie: {
name: cookieOptions.name,
password: cookieOptions.encryptionKey,
isSecure: cookieOptions.isSecure,
path: basePath === undefined ? '/' : basePath,
clearInvalid: false,
isHttpOnly: true,
isSameSite: cookieOptions.sameSite === 'None' ? false : cookieOptions.sameSite ?? false,
},
validateFunc: async (req: Request, session: T | T[]) => {
const result = cookieOptions.validate(session);
if (!result.isValid) {
clearInvalidCookie(req, result.path);
}
return { valid: result.isValid };
},
isSecure: cookieOptions.isSecure,
path: basePath,
clearInvalid: false,
isHttpOnly: true,
isSameSite: cookieOptions.sameSite === 'None' ? false : cookieOptions.sameSite ?? false,
});

// A hack to support SameSite: 'None'.
Expand Down
21 changes: 10 additions & 11 deletions src/core/server/http/http_server.mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
* specific language governing permissions and limitations
* under the License.
*/
import { parse as parseUrl } from 'url';
import { Request } from 'hapi';
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';
Expand Down Expand Up @@ -73,7 +73,7 @@ function createKibanaRequestMock<P = any, Q = any, B = any>({
auth = { isAuthenticated: true },
}: RequestFixtureOptions<P, Q, B> = {}) {
const queryString = stringify(query, { sort: false });
const url = parseUrl(`${path}${queryString ? `?${queryString}` : ''}`);
const url = new URL(`${path}${queryString ? `?${queryString}` : ''}`, 'http://localhost');

return KibanaRequest.from<P, Q, B>(
createRawRequestMock({
Expand All @@ -87,6 +87,9 @@ function createKibanaRequestMock<P = any, Q = any, B = any>({
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: {
Expand Down Expand Up @@ -120,9 +123,11 @@ 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 = Object.assign({ pathname, path, href: path }, customization.url);
const url = new URL(
formatUrl(Object.assign({ pathname, path, href: path }, customization.url)),
'http://localhost'
);

// @ts-expect-error _core isn't supposed to be accessed - remove once we upgrade to hapi v18
return merge(
{},
{
Expand All @@ -140,12 +145,6 @@ function createRawRequestMock(customization: DeepPartial<Request> = {}) {
socket: {},
},
},
// TODO: Remove once we upgrade to hapi v18
_core: {
info: {
uri: 'http://localhost',
},
},
},
customization
) as Request;
Expand Down
4 changes: 2 additions & 2 deletions src/core/server/http/http_server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
* specific language governing permissions and limitations
* under the License.
*/
import { Server } from 'hapi';
import HapiStaticFiles from 'inert';
import { Server } from '@hapi/hapi';
import HapiStaticFiles from '@hapi/inert';
import url from 'url';
import uuid from 'uuid';

Expand Down
3 changes: 2 additions & 1 deletion src/core/server/http/http_service.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
* under the License.
*/

import { Server } from 'hapi';
import { Server } from '@hapi/hapi';
import type { PublicMethodsOf } from '@kbn/utility-types';

import { CspConfig } from '../csp';
Expand Down Expand Up @@ -88,6 +88,7 @@ const createInternalSetupContractMock = () => {
start: jest.fn(),
stop: jest.fn(),
config: jest.fn().mockReturnValue(configMock.create()),
// @ts-expect-error somehow it thinks that `Server` isn't a `Construtable`
} as unknown) as jest.MockedClass<Server>,
createCookieSessionStorageFactory: jest.fn(),
registerOnPreRouting: jest.fn(),
Expand Down
2 changes: 1 addition & 1 deletion src/core/server/http/http_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

import { Observable, Subscription, combineLatest } from 'rxjs';
import { first, map } from 'rxjs/operators';
import { Server } from 'hapi';
import { Server } from '@hapi/hapi';
import { pick } from '@kbn/std';

import { CoreService } from '../../types';
Expand Down
2 changes: 1 addition & 1 deletion src/core/server/http/http_tools.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jest.mock('uuid', () => ({
}));

import supertest from 'supertest';
import { Request, ResponseToolkit } from 'hapi';
import { Request, ResponseToolkit } from '@hapi/hapi';
import Joi from 'joi';

import {
Expand Down
4 changes: 2 additions & 2 deletions src/core/server/http/http_tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
* under the License.
*/

import { Lifecycle, Request, ResponseToolkit, Server, ServerOptions, Util } from 'hapi';
import Hoek from 'hoek';
import { Lifecycle, Request, ResponseToolkit, Server, ServerOptions, Util } from '@hapi/hapi';
import Hoek from '@hapi/hoek';
import { ServerOptions as TLSOptions } from 'https';
import { ValidationError } from 'joi';
import uuid from 'uuid';
Expand Down
2 changes: 1 addition & 1 deletion src/core/server/http/https_redirect_server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
* under the License.
*/

import { Request, ResponseToolkit, Server } from 'hapi';
import { Request, ResponseToolkit, Server } from '@hapi/hapi';
import { format as formatUrl } from 'url';

import { Logger } from '../logging';
Expand Down
4 changes: 2 additions & 2 deletions src/core/server/http/integration_tests/core_services.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ import {
legacyClusterClientInstanceMock,
} from './core_service.test.mocks';

import Boom from 'boom';
import { Request } from 'hapi';
import Boom from '@hapi/boom';
import { Request } from '@hapi/hapi';
import { errors as esErrors } from 'elasticsearch';
import { LegacyElasticsearchErrorHelpers } from '../../elasticsearch/legacy';

Expand Down
48 changes: 48 additions & 0 deletions src/core/server/http/integration_tests/request.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,54 @@ describe('KibanaRequest', () => {
});
});
});

describe('route options', () => {
describe('authRequired', () => {
it('returns false if a route configured with "authRequired": false', async () => {
const { server: innerServer, createRouter, registerAuth } = await server.setup(setupDeps);
registerAuth((req, res, t) => t.authenticated());
const router = createRouter('/');
router.get(
{ path: '/', validate: false, options: { authRequired: false } },
(context, req, res) => res.ok({ body: { authRequired: req.route.options.authRequired } })
);
await server.start();

await supertest(innerServer.listener).get('/').expect(200, {
authRequired: false,
});
});
it('returns "optional" if a route configured with "authRequired": optional', async () => {
const { server: innerServer, createRouter, registerAuth } = await server.setup(setupDeps);
registerAuth((req, res, t) => t.authenticated());
const router = createRouter('/');
router.get(
{ path: '/', validate: false, options: { authRequired: 'optional' } },
(context, req, res) => res.ok({ body: { authRequired: req.route.options.authRequired } })
);
await server.start();

await supertest(innerServer.listener).get('/').expect(200, {
authRequired: 'optional',
});
});
it('returns true if a route configured with "authRequired": true', async () => {
const { server: innerServer, createRouter, registerAuth } = await server.setup(setupDeps);
registerAuth((req, res, t) => t.authenticated());
const router = createRouter('/');
router.get(
{ path: '/', validate: false, options: { authRequired: true } },
(context, req, res) => res.ok({ body: { authRequired: req.route.options.authRequired } })
);
await server.start();

await supertest(innerServer.listener).get('/').expect(200, {
authRequired: true,
});
});
});
});

describe('events', () => {
describe('aborted$', () => {
it('emits once and completes when request aborted', async (done) => {
Expand Down
Loading

0 comments on commit df4f66b

Please sign in to comment.