mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
Reduce security plugin page load bundle (#98819)
This commit is contained in:
parent
bab9065f3d
commit
a5e80567e4
4 changed files with 90 additions and 37 deletions
|
@ -67,12 +67,12 @@ pageLoadAssetSize:
|
|||
savedObjectsTagging: 59482
|
||||
savedObjectsTaggingOss: 20590
|
||||
searchprofiler: 67080
|
||||
security: 189428
|
||||
security: 95864
|
||||
securityOss: 30806
|
||||
securitySolution: 187863
|
||||
share: 99061
|
||||
snapshotRestore: 79032
|
||||
spaces: 387915
|
||||
spaces: 57868
|
||||
telemetry: 51957
|
||||
telemetryManagementSection: 38586
|
||||
tileMap: 65337
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
import type { ApplicationSetup, FatalErrorsSetup, HttpSetup } from 'src/core/public';
|
||||
|
||||
import { AUTH_URL_HASH_QUERY_STRING_PARAMETER } from '../../../common/constants';
|
||||
import { parseNext } from '../../../common/parse_next';
|
||||
|
||||
interface CreateDeps {
|
||||
application: ApplicationSetup;
|
||||
|
@ -46,6 +45,9 @@ export const captureURLApp = Object.freeze({
|
|||
appRoute: '/internal/security/capture-url',
|
||||
async mount() {
|
||||
try {
|
||||
// This is an async import because it requires `url`, which is a sizable dependency.
|
||||
// Otherwise this becomes part of the "page load bundle".
|
||||
const { parseNext } = await import('../../../common/parse_next');
|
||||
const url = new URL(
|
||||
parseNext(window.location.href, http.basePath.serverBasePath),
|
||||
window.location.origin
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import BroadcastChannel from 'broadcast-channel';
|
||||
|
||||
import { mountWithIntl } from '@kbn/test/jest';
|
||||
import { mountWithIntl, nextTick } from '@kbn/test/jest';
|
||||
import { coreMock } from 'src/core/public/mocks';
|
||||
|
||||
import { createSessionExpiredMock } from './session_expired.mock';
|
||||
|
@ -112,6 +112,7 @@ describe('Session Timeout', () => {
|
|||
|
||||
afterEach(async () => {
|
||||
jest.clearAllMocks();
|
||||
jest.unmock('broadcast-channel');
|
||||
sessionTimeout.stop();
|
||||
});
|
||||
|
||||
|
@ -122,22 +123,42 @@ describe('Session Timeout', () => {
|
|||
|
||||
describe('Lifecycle', () => {
|
||||
test(`starts and initializes on a non-anonymous path`, async () => {
|
||||
await sessionTimeout.start();
|
||||
sessionTimeout.start();
|
||||
await nextTick();
|
||||
// eslint-disable-next-line dot-notation
|
||||
expect(sessionTimeout['channel']).not.toBeUndefined();
|
||||
expect(http.fetch).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test(`starts and initializes if the broadcast channel fails to load`, async () => {
|
||||
jest.mock('broadcast-channel', () => {
|
||||
throw new Error('Unable to load broadcast channel!');
|
||||
});
|
||||
const consoleSpy = jest.spyOn(console, 'warn');
|
||||
|
||||
sessionTimeout.start();
|
||||
await nextTick();
|
||||
// eslint-disable-next-line dot-notation
|
||||
expect(sessionTimeout['channel']).toBeUndefined();
|
||||
expect(http.fetch).toHaveBeenCalledTimes(1);
|
||||
expect(consoleSpy).toHaveBeenCalledTimes(1);
|
||||
expect(consoleSpy.mock.calls[0][0]).toMatchInlineSnapshot(
|
||||
`"Failed to load broadcast channel. Session management will not be kept in sync when multiple tabs are loaded."`
|
||||
);
|
||||
});
|
||||
|
||||
test(`starts and does not initialize on an anonymous path`, async () => {
|
||||
http.anonymousPaths.isAnonymous.mockReturnValue(true);
|
||||
await sessionTimeout.start();
|
||||
sessionTimeout.start();
|
||||
await nextTick();
|
||||
// eslint-disable-next-line dot-notation
|
||||
expect(sessionTimeout['channel']).toBeUndefined();
|
||||
expect(http.fetch).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test(`stops`, async () => {
|
||||
await sessionTimeout.start();
|
||||
sessionTimeout.start();
|
||||
await nextTick();
|
||||
// eslint-disable-next-line dot-notation
|
||||
const close = jest.fn(sessionTimeout['channel']!.close);
|
||||
// eslint-disable-next-line dot-notation
|
||||
|
@ -157,7 +178,8 @@ describe('Session Timeout', () => {
|
|||
...defaultSessionInfo,
|
||||
idleTimeoutExpiration: now + 5_000_000_000,
|
||||
});
|
||||
await sessionTimeout.start();
|
||||
sessionTimeout.start();
|
||||
await nextTick();
|
||||
|
||||
// Advance timers far enough to call intermediate `setTimeout` multiple times, but before any
|
||||
// of the timers is supposed to be triggered.
|
||||
|
@ -184,7 +206,8 @@ describe('Session Timeout', () => {
|
|||
});
|
||||
|
||||
test(`handles success`, async () => {
|
||||
await sessionTimeout.start();
|
||||
sessionTimeout.start();
|
||||
await nextTick();
|
||||
|
||||
expect(http.fetch).toHaveBeenCalledTimes(1);
|
||||
// eslint-disable-next-line dot-notation
|
||||
|
@ -195,7 +218,8 @@ describe('Session Timeout', () => {
|
|||
test(`handles error`, async () => {
|
||||
const mockErrorResponse = new Error('some-error');
|
||||
http.fetch.mockRejectedValue(mockErrorResponse);
|
||||
await sessionTimeout.start();
|
||||
sessionTimeout.start();
|
||||
await nextTick();
|
||||
|
||||
expect(http.fetch).toHaveBeenCalledTimes(1);
|
||||
// eslint-disable-next-line dot-notation
|
||||
|
@ -206,7 +230,8 @@ describe('Session Timeout', () => {
|
|||
|
||||
describe('warning toast', () => {
|
||||
test(`shows idle timeout warning toast`, async () => {
|
||||
await sessionTimeout.start();
|
||||
sessionTimeout.start();
|
||||
await nextTick();
|
||||
|
||||
// we display the warning a minute before we expire the the session, which is 5 seconds before it actually expires
|
||||
jest.advanceTimersByTime(55 * 1000);
|
||||
|
@ -218,7 +243,8 @@ describe('Session Timeout', () => {
|
|||
...defaultSessionInfo,
|
||||
idleTimeoutExpiration: now + 5_000_000_000,
|
||||
});
|
||||
await sessionTimeout.start();
|
||||
sessionTimeout.start();
|
||||
await nextTick();
|
||||
|
||||
// we display the warning a minute before we expire the the session, which is 5 seconds before it actually expires
|
||||
jest.advanceTimersByTime(5_000_000_000 - 66 * 1000);
|
||||
|
@ -236,7 +262,8 @@ describe('Session Timeout', () => {
|
|||
provider: { type: 'basic', name: 'basic1' },
|
||||
};
|
||||
http.fetch.mockResolvedValue(sessionInfo);
|
||||
await sessionTimeout.start();
|
||||
sessionTimeout.start();
|
||||
await nextTick();
|
||||
|
||||
// we display the warning a minute before we expire the the session, which is 5 seconds before it actually expires
|
||||
jest.advanceTimersByTime(55 * 1000);
|
||||
|
@ -250,7 +277,8 @@ describe('Session Timeout', () => {
|
|||
lifespanExpiration: now + 5_000_000_000,
|
||||
};
|
||||
http.fetch.mockResolvedValue(sessionInfo);
|
||||
await sessionTimeout.start();
|
||||
sessionTimeout.start();
|
||||
await nextTick();
|
||||
|
||||
// we display the warning a minute before we expire the the session, which is 5 seconds before it actually expires
|
||||
jest.advanceTimersByTime(5_000_000_000 - 66 * 1000);
|
||||
|
@ -261,7 +289,8 @@ describe('Session Timeout', () => {
|
|||
});
|
||||
|
||||
test(`extend only results in an HTTP call if a warning is shown`, async () => {
|
||||
await sessionTimeout.start();
|
||||
sessionTimeout.start();
|
||||
await nextTick();
|
||||
expect(http.fetch).toHaveBeenCalledTimes(1);
|
||||
|
||||
await sessionTimeout.extend('/foo');
|
||||
|
@ -287,7 +316,8 @@ describe('Session Timeout', () => {
|
|||
provider: { type: 'basic', name: 'basic1' },
|
||||
};
|
||||
http.fetch.mockResolvedValue(sessionInfo);
|
||||
await sessionTimeout.start();
|
||||
sessionTimeout.start();
|
||||
await nextTick();
|
||||
|
||||
// we display the warning a minute before we expire the the session, which is 5 seconds before it actually expires
|
||||
jest.advanceTimersByTime(55 * 1000);
|
||||
|
@ -299,7 +329,8 @@ describe('Session Timeout', () => {
|
|||
});
|
||||
|
||||
test(`extend hides displayed warning toast`, async () => {
|
||||
await sessionTimeout.start();
|
||||
sessionTimeout.start();
|
||||
await nextTick();
|
||||
expect(http.fetch).toHaveBeenCalledTimes(1);
|
||||
|
||||
// we display the warning a minute before we expire the the session, which is 5 seconds before it actually expires
|
||||
|
@ -319,7 +350,8 @@ describe('Session Timeout', () => {
|
|||
});
|
||||
|
||||
test(`extend does nothing for session-related routes`, async () => {
|
||||
await sessionTimeout.start();
|
||||
sessionTimeout.start();
|
||||
await nextTick();
|
||||
expect(http.fetch).toHaveBeenCalledTimes(1);
|
||||
|
||||
// we display the warning a minute before we expire the the session, which is 5 seconds before it actually expires
|
||||
|
@ -333,7 +365,8 @@ describe('Session Timeout', () => {
|
|||
});
|
||||
|
||||
test(`checks for updated session info before the warning displays`, async () => {
|
||||
await sessionTimeout.start();
|
||||
sessionTimeout.start();
|
||||
await nextTick();
|
||||
expect(http.fetch).toHaveBeenCalledTimes(1);
|
||||
|
||||
// we check for updated session info 1 second before the warning is shown
|
||||
|
@ -343,7 +376,8 @@ describe('Session Timeout', () => {
|
|||
});
|
||||
|
||||
test('clicking "extend" causes a new HTTP request (which implicitly extends the session)', async () => {
|
||||
await sessionTimeout.start();
|
||||
sessionTimeout.start();
|
||||
await nextTick();
|
||||
expect(http.fetch).toHaveBeenCalledTimes(1);
|
||||
|
||||
// we display the warning a minute before we expire the the session, which is 5 seconds before it actually expires
|
||||
|
@ -366,7 +400,8 @@ describe('Session Timeout', () => {
|
|||
lifespanExpiration: null,
|
||||
provider: { type: 'basic', name: 'basic1' },
|
||||
});
|
||||
await sessionTimeout.start();
|
||||
sessionTimeout.start();
|
||||
await nextTick();
|
||||
expect(http.fetch).toHaveBeenCalled();
|
||||
|
||||
jest.advanceTimersByTime(0);
|
||||
|
@ -376,7 +411,8 @@ describe('Session Timeout', () => {
|
|||
|
||||
describe('session expiration', () => {
|
||||
test(`expires the session 5 seconds before it really expires`, async () => {
|
||||
await sessionTimeout.start();
|
||||
sessionTimeout.start();
|
||||
await nextTick();
|
||||
|
||||
jest.advanceTimersByTime(114 * 1000);
|
||||
expect(sessionExpired.logout).not.toHaveBeenCalled();
|
||||
|
@ -391,7 +427,8 @@ describe('Session Timeout', () => {
|
|||
idleTimeoutExpiration: now + 5_000_000_000,
|
||||
});
|
||||
|
||||
await sessionTimeout.start();
|
||||
sessionTimeout.start();
|
||||
await nextTick();
|
||||
|
||||
jest.advanceTimersByTime(5_000_000_000 - 6000);
|
||||
expect(sessionExpired.logout).not.toHaveBeenCalled();
|
||||
|
@ -401,7 +438,8 @@ describe('Session Timeout', () => {
|
|||
});
|
||||
|
||||
test(`extend delays the expiration`, async () => {
|
||||
await sessionTimeout.start();
|
||||
sessionTimeout.start();
|
||||
await nextTick();
|
||||
expect(http.fetch).toHaveBeenCalledTimes(1);
|
||||
|
||||
const elapsed = 114 * 1000;
|
||||
|
@ -438,7 +476,8 @@ describe('Session Timeout', () => {
|
|||
lifespanExpiration: null,
|
||||
provider: { type: 'basic', name: 'basic1' },
|
||||
});
|
||||
await sessionTimeout.start();
|
||||
sessionTimeout.start();
|
||||
await nextTick();
|
||||
|
||||
jest.advanceTimersByTime(0);
|
||||
expect(sessionExpired.logout).toHaveBeenCalled();
|
||||
|
@ -446,7 +485,8 @@ describe('Session Timeout', () => {
|
|||
|
||||
test(`'null' sessionTimeout never logs you out`, async () => {
|
||||
http.fetch.mockResolvedValue({ now, idleTimeoutExpiration: null, lifespanExpiration: null });
|
||||
await sessionTimeout.start();
|
||||
sessionTimeout.start();
|
||||
await nextTick();
|
||||
|
||||
jest.advanceTimersByTime(Number.MAX_VALUE);
|
||||
expect(sessionExpired.logout).not.toHaveBeenCalled();
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { BroadcastChannel } from 'broadcast-channel';
|
||||
import type { BroadcastChannel as BroadcastChannelType } from 'broadcast-channel';
|
||||
|
||||
import type { HttpSetup, NotificationsSetup, Toast, ToastInput } from 'src/core/public';
|
||||
|
||||
|
@ -45,7 +45,7 @@ export interface ISessionTimeout {
|
|||
}
|
||||
|
||||
export class SessionTimeout implements ISessionTimeout {
|
||||
private channel?: BroadcastChannel<SessionInfo>;
|
||||
private channel?: BroadcastChannelType<SessionInfo>;
|
||||
private sessionInfo?: SessionInfo;
|
||||
private fetchTimer?: number;
|
||||
private warningTimer?: number;
|
||||
|
@ -64,15 +64,26 @@ export class SessionTimeout implements ISessionTimeout {
|
|||
return;
|
||||
}
|
||||
|
||||
// subscribe to a broadcast channel for session timeout messages
|
||||
// this allows us to synchronize the UX across tabs and avoid repetitive API calls
|
||||
const name = `${this.tenant}/session_timeout`;
|
||||
this.channel = new BroadcastChannel(name, { webWorkerSupport: false });
|
||||
this.channel.onmessage = this.handleSessionInfoAndResetTimers;
|
||||
|
||||
// Triggers an initial call to the endpoint to get session info;
|
||||
// when that returns, it will set the timeout
|
||||
return this.fetchSessionInfoAndResetTimers();
|
||||
import('broadcast-channel')
|
||||
.then(({ BroadcastChannel }) => {
|
||||
// subscribe to a broadcast channel for session timeout messages
|
||||
// this allows us to synchronize the UX across tabs and avoid repetitive API calls
|
||||
const name = `${this.tenant}/session_timeout`;
|
||||
this.channel = new BroadcastChannel(name, { webWorkerSupport: false });
|
||||
this.channel.onmessage = this.handleSessionInfoAndResetTimers;
|
||||
})
|
||||
.catch((e) => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(
|
||||
`Failed to load broadcast channel. Session management will not be kept in sync when multiple tabs are loaded.`,
|
||||
e
|
||||
);
|
||||
})
|
||||
.finally(() => {
|
||||
// Triggers an initial call to the endpoint to get session info;
|
||||
// when that returns, it will set the timeout
|
||||
return this.fetchSessionInfoAndResetTimers();
|
||||
});
|
||||
}
|
||||
|
||||
stop() {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue