mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
* inOneOf --> hasAtLeast. to follow to licensing hierarchical model * adopt licensing tests * add license mock and use it in the tests * adopt security plugin to hasAtLeast and licensing mocks * adopt uptime to hasAtLeast * update readme * add test for unknown license * fix import in js test * fix security plugin merge conflict * Update x-pack/plugins/security/common/licensing/license_service.ts Co-Authored-By: Larry Gregory <lgregorydev@gmail.com> * Update x-pack/plugins/licensing/common/types.ts Co-Authored-By: Josh Dover <me@joshdover.com> * simplify tests * remove unused import
This commit is contained in:
parent
3b0e778c79
commit
e79b62f943
18 changed files with 157 additions and 153 deletions
|
@ -8,7 +8,7 @@ import { ILicense } from '../../../../../../../plugins/licensing/server';
|
|||
import { licenseCheck } from '../license';
|
||||
|
||||
describe('license check', () => {
|
||||
let mockLicense: Pick<ILicense, 'isActive' | 'isOneOf'>;
|
||||
let mockLicense: Pick<ILicense, 'isActive' | 'hasAtLeast'>;
|
||||
|
||||
it('throws for null license', () => {
|
||||
expect(licenseCheck(null)).toMatchSnapshot();
|
||||
|
@ -16,7 +16,7 @@ describe('license check', () => {
|
|||
|
||||
it('throws for unsupported license type', () => {
|
||||
mockLicense = {
|
||||
isOneOf: jest.fn().mockReturnValue(false),
|
||||
hasAtLeast: jest.fn().mockReturnValue(false),
|
||||
isActive: false,
|
||||
};
|
||||
expect(licenseCheck(mockLicense)).toMatchSnapshot();
|
||||
|
@ -24,7 +24,7 @@ describe('license check', () => {
|
|||
|
||||
it('throws for inactive license', () => {
|
||||
mockLicense = {
|
||||
isOneOf: jest.fn().mockReturnValue(true),
|
||||
hasAtLeast: jest.fn().mockReturnValue(true),
|
||||
isActive: false,
|
||||
};
|
||||
expect(licenseCheck(mockLicense)).toMatchSnapshot();
|
||||
|
@ -32,7 +32,7 @@ describe('license check', () => {
|
|||
|
||||
it('returns result for a valid license', () => {
|
||||
mockLicense = {
|
||||
isOneOf: jest.fn().mockReturnValue(true),
|
||||
hasAtLeast: jest.fn().mockReturnValue(true),
|
||||
isActive: true,
|
||||
};
|
||||
expect(licenseCheck(mockLicense)).toMatchSnapshot();
|
||||
|
|
|
@ -11,7 +11,7 @@ export interface UMLicenseStatusResponse {
|
|||
message?: string;
|
||||
}
|
||||
export type UMLicenseCheck = (
|
||||
license: Pick<ILicense, 'isActive' | 'isOneOf'> | null
|
||||
license: Pick<ILicense, 'isActive' | 'hasAtLeast'> | null
|
||||
) => UMLicenseStatusResponse;
|
||||
|
||||
export const licenseCheck: UMLicenseCheck = license => {
|
||||
|
@ -21,7 +21,7 @@ export const licenseCheck: UMLicenseCheck = license => {
|
|||
statusCode: 400,
|
||||
};
|
||||
}
|
||||
if (!license.isOneOf(['basic', 'standard', 'gold', 'platinum', 'enterprise', 'trial'])) {
|
||||
if (!license.hasAtLeast('basic')) {
|
||||
return {
|
||||
message: 'License not supported',
|
||||
statusCode: 401,
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { licensingMock } from '../../../../../plugins/licensing/server/licensing.mocks';
|
||||
import { licensingMock } from '../../../../../plugins/licensing/server/mocks';
|
||||
import { XPackInfoLicense } from './xpack_info_license';
|
||||
|
||||
function getXPackInfoLicense(getRawLicense) {
|
||||
|
|
|
@ -56,7 +56,7 @@ chrome.navLinks.update('myPlugin', {
|
|||
"requiredPlugins": ["licensing"],
|
||||
|
||||
// my_plugin/server/plugin.ts
|
||||
import { LicensingPluginSetup, LICENSE_CHECK_STATE } from '../licensing'
|
||||
import { LicensingPluginSetup, LICENSE_CHECK_STATE } from '../licensing/server'
|
||||
|
||||
interface SetupDeps {
|
||||
licensing: LicensingPluginSetup;
|
||||
|
@ -77,7 +77,8 @@ class MyPlugin {
|
|||
}
|
||||
}
|
||||
|
||||
// my_plugin/client/plugin.ts
|
||||
// my_plugin/public/plugin.ts
|
||||
import { LicensingPluginSetup, LICENSE_CHECK_STATE } from '../licensing/public'
|
||||
class MyPlugin {
|
||||
setup(core: CoreSetup, deps: SetupDeps) {
|
||||
deps.licensing.license$.subscribe(license => {
|
||||
|
|
|
@ -6,13 +6,13 @@
|
|||
|
||||
import { License } from './license';
|
||||
import { LICENSE_CHECK_STATE } from './types';
|
||||
import { licenseMock } from './licensing.mocks';
|
||||
import { licenseMock } from './licensing.mock';
|
||||
|
||||
describe('License', () => {
|
||||
const basicLicense = licenseMock.create();
|
||||
const basicExpiredLicense = licenseMock.create({ license: { status: 'expired' } });
|
||||
const goldLicense = licenseMock.create({ license: { type: 'gold' } });
|
||||
const enterpriseLicense = licenseMock.create({ license: { type: 'enterprise' } });
|
||||
const basicLicense = licenseMock.createLicense();
|
||||
const basicExpiredLicense = licenseMock.createLicense({ license: { status: 'expired' } });
|
||||
const goldLicense = licenseMock.createLicense({ license: { type: 'gold' } });
|
||||
const enterpriseLicense = licenseMock.createLicense({ license: { type: 'enterprise' } });
|
||||
|
||||
const errorMessage = 'unavailable';
|
||||
const errorLicense = new License({ error: errorMessage, signature: '' });
|
||||
|
@ -50,34 +50,23 @@ describe('License', () => {
|
|||
expect(unavailableLicense.isActive).toBe(false);
|
||||
});
|
||||
|
||||
it('isBasic', () => {
|
||||
expect(basicLicense.isBasic).toBe(true);
|
||||
expect(goldLicense.isBasic).toBe(false);
|
||||
expect(enterpriseLicense.isBasic).toBe(false);
|
||||
expect(errorLicense.isBasic).toBe(false);
|
||||
expect(unavailableLicense.isBasic).toBe(false);
|
||||
});
|
||||
it('hasAtLeast', () => {
|
||||
expect(basicLicense.hasAtLeast('platinum')).toBe(false);
|
||||
expect(basicLicense.hasAtLeast('gold')).toBe(false);
|
||||
expect(basicLicense.hasAtLeast('basic')).toBe(true);
|
||||
|
||||
it('isNotBasic', () => {
|
||||
expect(basicLicense.isNotBasic).toBe(false);
|
||||
expect(goldLicense.isNotBasic).toBe(true);
|
||||
expect(enterpriseLicense.isNotBasic).toBe(true);
|
||||
expect(errorLicense.isNotBasic).toBe(false);
|
||||
expect(unavailableLicense.isNotBasic).toBe(false);
|
||||
});
|
||||
expect(errorLicense.hasAtLeast('basic')).toBe(false);
|
||||
|
||||
it('isOneOf', () => {
|
||||
expect(basicLicense.isOneOf('platinum')).toBe(false);
|
||||
expect(basicLicense.isOneOf(['platinum'])).toBe(false);
|
||||
expect(basicLicense.isOneOf(['gold', 'platinum'])).toBe(false);
|
||||
expect(basicLicense.isOneOf(['platinum', 'gold'])).toBe(false);
|
||||
expect(basicLicense.isOneOf(['basic', 'gold'])).toBe(true);
|
||||
expect(basicLicense.isOneOf(['basic'])).toBe(true);
|
||||
expect(basicLicense.isOneOf('basic')).toBe(true);
|
||||
expect(unavailableLicense.hasAtLeast('basic')).toBe(false);
|
||||
|
||||
expect(errorLicense.isOneOf(['basic', 'gold', 'platinum'])).toBe(false);
|
||||
expect(goldLicense.hasAtLeast('basic')).toBe(true);
|
||||
expect(goldLicense.hasAtLeast('gold')).toBe(true);
|
||||
expect(goldLicense.hasAtLeast('platinum')).toBe(false);
|
||||
|
||||
expect(unavailableLicense.isOneOf(['basic', 'gold', 'platinum'])).toBe(false);
|
||||
expect(enterpriseLicense.hasAtLeast('basic')).toBe(true);
|
||||
expect(enterpriseLicense.hasAtLeast('platinum')).toBe(true);
|
||||
expect(enterpriseLicense.hasAtLeast('enterprise')).toBe(true);
|
||||
expect(enterpriseLicense.hasAtLeast('trial')).toBe(false);
|
||||
});
|
||||
|
||||
it('getUnavailableReason', () => {
|
||||
|
@ -115,9 +104,13 @@ describe('License', () => {
|
|||
});
|
||||
|
||||
it('throws in case of unknown license type', () => {
|
||||
expect(
|
||||
() => basicLicense.check('ccr', 'any' as any).state
|
||||
).toThrowErrorMatchingInlineSnapshot(`"\\"any\\" is not a valid license type"`);
|
||||
expect(() => basicLicense.check('ccr', 'any' as any)).toThrowErrorMatchingInlineSnapshot(
|
||||
`"\\"any\\" is not a valid license type"`
|
||||
);
|
||||
|
||||
expect(() => basicLicense.hasAtLeast('any' as any)).toThrowErrorMatchingInlineSnapshot(
|
||||
`"\\"any\\" is not a valid license type"`
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -26,8 +26,6 @@ export class License implements ILicense {
|
|||
public readonly error?: string;
|
||||
public readonly isActive: boolean;
|
||||
public readonly isAvailable: boolean;
|
||||
public readonly isBasic: boolean;
|
||||
public readonly isNotBasic: boolean;
|
||||
|
||||
public readonly uid?: string;
|
||||
public readonly status?: LicenseStatus;
|
||||
|
@ -70,8 +68,6 @@ export class License implements ILicense {
|
|||
}
|
||||
|
||||
this.isActive = this.status === 'active';
|
||||
this.isBasic = this.isActive && this.type === 'basic';
|
||||
this.isNotBasic = this.isActive && this.type !== 'basic';
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
|
@ -89,23 +85,20 @@ export class License implements ILicense {
|
|||
}
|
||||
}
|
||||
|
||||
isOneOf(candidateLicenses: LicenseType | LicenseType[]) {
|
||||
if (!this.type) {
|
||||
hasAtLeast(minimumLicenseRequired: LicenseType) {
|
||||
const type = this.type;
|
||||
if (!type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Array.isArray(candidateLicenses)) {
|
||||
candidateLicenses = [candidateLicenses];
|
||||
}
|
||||
|
||||
return candidateLicenses.includes(this.type);
|
||||
}
|
||||
|
||||
check(pluginName: string, minimumLicenseRequired: LicenseType) {
|
||||
if (!(minimumLicenseRequired in LICENSE_TYPE)) {
|
||||
throw new Error(`"${minimumLicenseRequired}" is not a valid license type`);
|
||||
}
|
||||
|
||||
return LICENSE_TYPE[minimumLicenseRequired] <= LICENSE_TYPE[type];
|
||||
}
|
||||
|
||||
check(pluginName: string, minimumLicenseRequired: LicenseType) {
|
||||
if (!this.isAvailable) {
|
||||
return {
|
||||
state: LICENSE_CHECK_STATE.Unavailable,
|
||||
|
@ -117,26 +110,24 @@ export class License implements ILicense {
|
|||
};
|
||||
}
|
||||
|
||||
const type = this.type!;
|
||||
|
||||
if (!this.isActive) {
|
||||
return {
|
||||
state: LICENSE_CHECK_STATE.Expired,
|
||||
message: i18n.translate('xpack.licensing.check.errorExpiredMessage', {
|
||||
defaultMessage:
|
||||
'You cannot use {pluginName} because your {licenseType} license has expired.',
|
||||
values: { licenseType: type, pluginName },
|
||||
values: { licenseType: this.type!, pluginName },
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
if (LICENSE_TYPE[type] < LICENSE_TYPE[minimumLicenseRequired]) {
|
||||
if (!this.hasAtLeast(minimumLicenseRequired)) {
|
||||
return {
|
||||
state: LICENSE_CHECK_STATE.Invalid,
|
||||
message: i18n.translate('xpack.licensing.check.errorUnsupportedMessage', {
|
||||
defaultMessage:
|
||||
'Your {licenseType} license does not support {pluginName}. Please upgrade your license.',
|
||||
values: { licenseType: type, pluginName },
|
||||
values: { licenseType: this.type!, pluginName },
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -7,22 +7,19 @@
|
|||
import { Subject } from 'rxjs';
|
||||
import { take, toArray } from 'rxjs/operators';
|
||||
|
||||
import { ILicense, LicenseType } from './types';
|
||||
import { ILicense } from './types';
|
||||
import { createLicenseUpdate } from './license_update';
|
||||
import { licenseMock } from './licensing.mocks';
|
||||
import { licenseMock } from './licensing.mock';
|
||||
|
||||
const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
|
||||
const stop$ = new Subject();
|
||||
describe('licensing update', () => {
|
||||
it('loads updates when triggered', async () => {
|
||||
const types: LicenseType[] = ['basic', 'gold'];
|
||||
|
||||
const trigger$ = new Subject();
|
||||
const fetcher = jest
|
||||
.fn()
|
||||
.mockImplementation(() =>
|
||||
Promise.resolve(licenseMock.create({ license: { type: types.shift() } }))
|
||||
);
|
||||
.mockResolvedValueOnce(licenseMock.createLicense({ license: { type: 'basic' } }))
|
||||
.mockResolvedValueOnce(licenseMock.createLicense({ license: { type: 'gold' } }));
|
||||
|
||||
const { license$ } = createLicenseUpdate(trigger$, stop$, fetcher);
|
||||
|
||||
|
@ -38,8 +35,8 @@ describe('licensing update', () => {
|
|||
});
|
||||
|
||||
it('starts with initial value if presents', async () => {
|
||||
const initialLicense = licenseMock.create({ license: { type: 'platinum' } });
|
||||
const fetchedLicense = licenseMock.create({ license: { type: 'gold' } });
|
||||
const initialLicense = licenseMock.createLicense({ license: { type: 'platinum' } });
|
||||
const fetchedLicense = licenseMock.createLicense({ license: { type: 'gold' } });
|
||||
const trigger$ = new Subject();
|
||||
|
||||
const fetcher = jest.fn().mockResolvedValue(fetchedLicense);
|
||||
|
@ -55,14 +52,11 @@ describe('licensing update', () => {
|
|||
it('does not emit if license has not changed', async () => {
|
||||
const trigger$ = new Subject();
|
||||
|
||||
let i = 0;
|
||||
const fetcher = jest
|
||||
.fn()
|
||||
.mockImplementation(() =>
|
||||
Promise.resolve(
|
||||
++i < 3 ? licenseMock.create() : licenseMock.create({ license: { type: 'gold' } })
|
||||
)
|
||||
);
|
||||
.mockResolvedValueOnce(licenseMock.createLicense())
|
||||
.mockResolvedValueOnce(licenseMock.createLicense())
|
||||
.mockResolvedValueOnce(licenseMock.createLicense({ license: { type: 'gold' } }));
|
||||
|
||||
const { license$ } = createLicenseUpdate(trigger$, stop$, fetcher);
|
||||
trigger$.next();
|
||||
|
@ -83,7 +77,7 @@ describe('licensing update', () => {
|
|||
it('new subscriptions does not force re-fetch', async () => {
|
||||
const trigger$ = new Subject();
|
||||
|
||||
const fetcher = jest.fn().mockResolvedValue(licenseMock.create());
|
||||
const fetcher = jest.fn().mockResolvedValue(licenseMock.createLicense());
|
||||
|
||||
const { license$ } = createLicenseUpdate(trigger$, stop$, fetcher);
|
||||
|
||||
|
@ -103,9 +97,9 @@ describe('licensing update', () => {
|
|||
new Promise(resolve => {
|
||||
if (firstCall) {
|
||||
firstCall = false;
|
||||
setTimeout(() => resolve(licenseMock.create()), delayMs);
|
||||
setTimeout(() => resolve(licenseMock.createLicense()), delayMs);
|
||||
} else {
|
||||
resolve(licenseMock.create({ license: { type: 'gold' } }));
|
||||
resolve(licenseMock.createLicense({ license: { type: 'gold' } }));
|
||||
}
|
||||
})
|
||||
);
|
||||
|
@ -126,7 +120,7 @@ describe('licensing update', () => {
|
|||
|
||||
it('completes license$ stream when stop$ is triggered', () => {
|
||||
const trigger$ = new Subject();
|
||||
const fetcher = jest.fn().mockResolvedValue(licenseMock.create());
|
||||
const fetcher = jest.fn().mockResolvedValue(licenseMock.createLicense());
|
||||
|
||||
const { license$ } = createLicenseUpdate(trigger$, stop$, fetcher);
|
||||
let completed = false;
|
||||
|
@ -138,7 +132,7 @@ describe('licensing update', () => {
|
|||
|
||||
it('stops fetching when stop$ is triggered', () => {
|
||||
const trigger$ = new Subject();
|
||||
const fetcher = jest.fn().mockResolvedValue(licenseMock.create());
|
||||
const fetcher = jest.fn().mockResolvedValue(licenseMock.createLicense());
|
||||
|
||||
const { license$ } = createLicenseUpdate(trigger$, stop$, fetcher);
|
||||
const values: ILicense[] = [];
|
||||
|
@ -152,8 +146,8 @@ describe('licensing update', () => {
|
|||
|
||||
it('refreshManually guarantees license fetching', async () => {
|
||||
const trigger$ = new Subject();
|
||||
const firstLicense = licenseMock.create({ license: { uid: 'first', type: 'basic' } });
|
||||
const secondLicense = licenseMock.create({ license: { uid: 'second', type: 'gold' } });
|
||||
const firstLicense = licenseMock.createLicense({ license: { uid: 'first', type: 'basic' } });
|
||||
const secondLicense = licenseMock.createLicense({ license: { uid: 'second', type: 'gold' } });
|
||||
|
||||
const fetcher = jest
|
||||
.fn()
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
import { PublicLicense, PublicFeatures } from './types';
|
||||
import { ILicense, PublicLicense, PublicFeatures, LICENSE_CHECK_STATE } from './types';
|
||||
import { License } from './license';
|
||||
|
||||
function createLicense({
|
||||
|
@ -40,6 +40,22 @@ function createLicense({
|
|||
});
|
||||
}
|
||||
|
||||
export const licenseMock = {
|
||||
create: createLicense,
|
||||
const createLicenseMock = () => {
|
||||
const mock: jest.Mocked<ILicense> = {
|
||||
isActive: true,
|
||||
isAvailable: true,
|
||||
signature: '',
|
||||
toJSON: jest.fn(),
|
||||
getUnavailableReason: jest.fn(),
|
||||
getFeature: jest.fn(),
|
||||
check: jest.fn(),
|
||||
hasAtLeast: jest.fn(),
|
||||
};
|
||||
mock.check.mockReturnValue({ state: LICENSE_CHECK_STATE.Valid });
|
||||
mock.hasAtLeast.mockReturnValue(true);
|
||||
return mock;
|
||||
};
|
||||
export const licenseMock = {
|
||||
createLicense,
|
||||
createLicenseMock,
|
||||
};
|
|
@ -140,16 +140,6 @@ export interface ILicense {
|
|||
*/
|
||||
isAvailable: boolean;
|
||||
|
||||
/**
|
||||
* Determine if the type of the license is basic, and also active.
|
||||
*/
|
||||
isBasic: boolean;
|
||||
|
||||
/**
|
||||
* Determine if the type of the license is not basic, and also active.
|
||||
*/
|
||||
isNotBasic: boolean;
|
||||
|
||||
/**
|
||||
* Returns
|
||||
*/
|
||||
|
@ -166,10 +156,10 @@ export interface ILicense {
|
|||
getUnavailableReason: () => string | undefined;
|
||||
|
||||
/**
|
||||
* Determine if the provided license types match against the license type.
|
||||
* @param candidateLicenses license types to intersect against the license.
|
||||
* Determine if license type >= minimal required license type.
|
||||
* @param minimumLicenseRequired the minimum valid license required for the given feature
|
||||
*/
|
||||
isOneOf(candidateLicenses: LicenseType | LicenseType[]): boolean;
|
||||
hasAtLeast(minimumLicenseRequired: LicenseType): boolean;
|
||||
|
||||
/**
|
||||
* For a given plugin and license type, receive information about the status of the license.
|
||||
|
|
|
@ -5,10 +5,10 @@
|
|||
*/
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { LicensingPluginSetup } from './types';
|
||||
import { licenseMock } from '../common/licensing.mocks';
|
||||
import { licenseMock } from '../common/licensing.mock';
|
||||
|
||||
const createSetupMock = () => {
|
||||
const license = licenseMock.create();
|
||||
const license = licenseMock.createLicense();
|
||||
const mock: jest.Mocked<LicensingPluginSetup> = {
|
||||
license$: new BehaviorSubject(license),
|
||||
refresh: jest.fn(),
|
||||
|
@ -20,5 +20,5 @@ const createSetupMock = () => {
|
|||
|
||||
export const licensingMock = {
|
||||
createSetup: createSetupMock,
|
||||
createLicense: licenseMock.create,
|
||||
...licenseMock,
|
||||
};
|
|
@ -11,7 +11,7 @@ import { LicenseType } from '../common/types';
|
|||
import { LicensingPlugin, licensingSessionStorageKey } from './plugin';
|
||||
|
||||
import { License } from '../common/license';
|
||||
import { licenseMock } from '../common/licensing.mocks';
|
||||
import { licenseMock } from '../common/licensing.mock';
|
||||
import { coreMock } from '../../../../src/core/public/mocks';
|
||||
import { HttpInterceptor } from 'src/core/public';
|
||||
|
||||
|
@ -30,8 +30,12 @@ describe('licensing plugin', () => {
|
|||
plugin = new LicensingPlugin(coreMock.createPluginInitializerContext(), sessionStorage);
|
||||
|
||||
const coreSetup = coreMock.createSetup();
|
||||
const firstLicense = licenseMock.create({ license: { uid: 'first', type: 'basic' } });
|
||||
const secondLicense = licenseMock.create({ license: { uid: 'second', type: 'gold' } });
|
||||
const firstLicense = licenseMock.createLicense({
|
||||
license: { uid: 'first', type: 'basic' },
|
||||
});
|
||||
const secondLicense = licenseMock.createLicense({
|
||||
license: { uid: 'second', type: 'gold' },
|
||||
});
|
||||
coreSetup.http.get.mockResolvedValueOnce(firstLicense).mockResolvedValueOnce(secondLicense);
|
||||
|
||||
const { license$, refresh } = await plugin.setup(coreSetup);
|
||||
|
@ -53,7 +57,7 @@ describe('licensing plugin', () => {
|
|||
plugin = new LicensingPlugin(coreMock.createPluginInitializerContext(), sessionStorage);
|
||||
|
||||
const coreSetup = coreMock.createSetup();
|
||||
const fetchedLicense = licenseMock.create();
|
||||
const fetchedLicense = licenseMock.createLicense();
|
||||
coreSetup.http.get.mockResolvedValue(fetchedLicense);
|
||||
|
||||
const { refresh } = await plugin.setup(coreSetup);
|
||||
|
@ -71,7 +75,7 @@ describe('licensing plugin', () => {
|
|||
describe('#license$', () => {
|
||||
it('starts with license saved in sessionStorage if available', async () => {
|
||||
const sessionStorage = coreMock.createStorage();
|
||||
const savedLicense = licenseMock.create({ license: { uid: 'saved' } });
|
||||
const savedLicense = licenseMock.createLicense({ license: { uid: 'saved' } });
|
||||
sessionStorage.getItem.mockReturnValue(JSON.stringify(savedLicense));
|
||||
plugin = new LicensingPlugin(coreMock.createPluginInitializerContext(), sessionStorage);
|
||||
|
||||
|
@ -90,12 +94,12 @@ describe('licensing plugin', () => {
|
|||
const types: LicenseType[] = ['gold', 'platinum'];
|
||||
|
||||
const sessionStorage = coreMock.createStorage();
|
||||
sessionStorage.getItem.mockReturnValue(JSON.stringify(licenseMock.create()));
|
||||
sessionStorage.getItem.mockReturnValue(JSON.stringify(licenseMock.createLicense()));
|
||||
plugin = new LicensingPlugin(coreMock.createPluginInitializerContext(), sessionStorage);
|
||||
|
||||
const coreSetup = coreMock.createSetup();
|
||||
coreSetup.http.get.mockImplementation(() =>
|
||||
Promise.resolve(licenseMock.create({ license: { type: types.shift() } }))
|
||||
Promise.resolve(licenseMock.createLicense({ license: { type: types.shift() } }))
|
||||
);
|
||||
const { license$, refresh } = await plugin.setup(coreSetup);
|
||||
|
||||
|
@ -123,7 +127,7 @@ describe('licensing plugin', () => {
|
|||
|
||||
const coreSetup = coreMock.createSetup();
|
||||
|
||||
const fetchedLicense = licenseMock.create({ license: { uid: 'fresh' } });
|
||||
const fetchedLicense = licenseMock.createLicense({ license: { uid: 'fresh' } });
|
||||
coreSetup.http.get.mockResolvedValue(fetchedLicense);
|
||||
|
||||
const { license$, refresh } = await plugin.setup(coreSetup);
|
||||
|
@ -196,7 +200,7 @@ describe('licensing plugin', () => {
|
|||
|
||||
const coreSetup = coreMock.createSetup();
|
||||
|
||||
coreSetup.http.get.mockResolvedValue(licenseMock.create({ signature: 'signature-1' }));
|
||||
coreSetup.http.get.mockResolvedValue(licenseMock.createLicense({ signature: 'signature-1' }));
|
||||
|
||||
let registeredInterceptor: HttpInterceptor;
|
||||
coreSetup.http.intercept.mockImplementation((interceptor: HttpInterceptor) => {
|
||||
|
@ -321,7 +325,7 @@ describe('licensing plugin', () => {
|
|||
|
||||
const coreSetup = coreMock.createSetup();
|
||||
coreSetup.http.get.mockResolvedValueOnce(
|
||||
licenseMock.create({ license: { status: 'active', type: 'gold' } })
|
||||
licenseMock.createLicense({ license: { status: 'active', type: 'gold' } })
|
||||
);
|
||||
|
||||
const { refresh } = await plugin.setup(coreSetup);
|
||||
|
@ -338,8 +342,12 @@ describe('licensing plugin', () => {
|
|||
plugin = new LicensingPlugin(coreMock.createPluginInitializerContext(), sessionStorage);
|
||||
|
||||
const coreSetup = coreMock.createSetup();
|
||||
const activeLicense = licenseMock.create({ license: { status: 'active', type: 'gold' } });
|
||||
const expiredLicense = licenseMock.create({ license: { status: 'expired', type: 'gold' } });
|
||||
const activeLicense = licenseMock.createLicense({
|
||||
license: { status: 'active', type: 'gold' },
|
||||
});
|
||||
const expiredLicense = licenseMock.createLicense({
|
||||
license: { status: 'expired', type: 'gold' },
|
||||
});
|
||||
coreSetup.http.get
|
||||
.mockResolvedValueOnce(activeLicense)
|
||||
.mockResolvedValueOnce(expiredLicense)
|
||||
|
|
|
@ -5,10 +5,10 @@
|
|||
*/
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { LicensingPluginSetup } from './types';
|
||||
import { licenseMock } from '../common/licensing.mocks';
|
||||
import { licenseMock } from '../common/licensing.mock';
|
||||
|
||||
const createSetupMock = () => {
|
||||
const license = licenseMock.create();
|
||||
const license = licenseMock.createLicense();
|
||||
const mock: jest.Mocked<LicensingPluginSetup> = {
|
||||
license$: new BehaviorSubject(license),
|
||||
refresh: jest.fn(),
|
||||
|
@ -25,5 +25,5 @@ const createSetupMock = () => {
|
|||
|
||||
export const licensingMock = {
|
||||
createSetup: createSetupMock,
|
||||
createLicense: licenseMock.create,
|
||||
createLicense: licenseMock.createLicense,
|
||||
};
|
||||
|
|
|
@ -5,14 +5,14 @@
|
|||
*/
|
||||
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { licenseMock } from '../common/licensing.mocks';
|
||||
import { licenseMock } from '../common/licensing.mock';
|
||||
|
||||
import { createRouteHandlerContext } from './licensing_route_handler_context';
|
||||
|
||||
describe('createRouteHandlerContext', () => {
|
||||
it('returns a function providing the last license value', async () => {
|
||||
const firstLicense = licenseMock.create();
|
||||
const secondLicense = licenseMock.create();
|
||||
const firstLicense = licenseMock.createLicense();
|
||||
const secondLicense = licenseMock.createLicense();
|
||||
const license$ = new BehaviorSubject(firstLicense);
|
||||
|
||||
const routeHandler = createRouteHandlerContext(license$);
|
||||
|
|
|
@ -3,5 +3,27 @@
|
|||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { LicensingPluginSetup } from './types';
|
||||
import { licenseMock } from '../common/licensing.mock';
|
||||
|
||||
export * from './licensing.mocks';
|
||||
const createSetupMock = () => {
|
||||
const license = licenseMock.createLicense();
|
||||
const mock: jest.Mocked<LicensingPluginSetup> = {
|
||||
license$: new BehaviorSubject(license),
|
||||
refresh: jest.fn(),
|
||||
createLicensePoller: jest.fn(),
|
||||
};
|
||||
mock.refresh.mockResolvedValue(license);
|
||||
mock.createLicensePoller.mockReturnValue({
|
||||
license$: mock.license$,
|
||||
refresh: mock.refresh,
|
||||
});
|
||||
|
||||
return mock;
|
||||
};
|
||||
|
||||
export const licensingMock = {
|
||||
createSetup: createSetupMock,
|
||||
...licenseMock,
|
||||
};
|
||||
|
|
|
@ -6,12 +6,12 @@
|
|||
import { BehaviorSubject } from 'rxjs';
|
||||
import { createOnPreResponseHandler } from './on_pre_response_handler';
|
||||
import { httpServiceMock, httpServerMock } from '../../../../src/core/server/mocks';
|
||||
import { licenseMock } from '../common/licensing.mocks';
|
||||
import { licenseMock } from '../common/licensing.mock';
|
||||
|
||||
describe('createOnPreResponseHandler', () => {
|
||||
it('sets license.signature header immediately for non-error responses', async () => {
|
||||
const refresh = jest.fn();
|
||||
const license$ = new BehaviorSubject(licenseMock.create({ signature: 'foo' }));
|
||||
const license$ = new BehaviorSubject(licenseMock.createLicense({ signature: 'foo' }));
|
||||
const toolkit = httpServiceMock.createOnPreResponseToolkit();
|
||||
|
||||
const interceptor = createOnPreResponseHandler(refresh, license$);
|
||||
|
@ -26,8 +26,8 @@ describe('createOnPreResponseHandler', () => {
|
|||
});
|
||||
});
|
||||
it('sets license.signature header after refresh for non-error responses', async () => {
|
||||
const updatedLicense = licenseMock.create({ signature: 'bar' });
|
||||
const license$ = new BehaviorSubject(licenseMock.create({ signature: 'foo' }));
|
||||
const updatedLicense = licenseMock.createLicense({ signature: 'bar' });
|
||||
const license$ = new BehaviorSubject(licenseMock.createLicense({ signature: 'foo' }));
|
||||
const refresh = jest.fn().mockImplementation(
|
||||
() =>
|
||||
new Promise(resolve => {
|
||||
|
|
|
@ -5,21 +5,13 @@
|
|||
*/
|
||||
|
||||
import { of, BehaviorSubject } from 'rxjs';
|
||||
import { ILicense } from '../../../licensing/public';
|
||||
import { licensingMock } from '../../../licensing/public/mocks';
|
||||
import { SecurityLicenseService } from './license_service';
|
||||
|
||||
function getMockRawLicense({ isAvailable = false } = {}) {
|
||||
return ({
|
||||
isAvailable,
|
||||
isOneOf: jest.fn(),
|
||||
getFeature: jest.fn(),
|
||||
} as unknown) as jest.Mocked<ILicense>;
|
||||
}
|
||||
|
||||
describe('license features', function() {
|
||||
it('should display error when ES is unavailable', () => {
|
||||
const serviceSetup = new SecurityLicenseService().setup({
|
||||
license$: of((undefined as unknown) as ILicense),
|
||||
license$: of(undefined as any),
|
||||
});
|
||||
expect(serviceSetup.license.getFeatures()).toEqual({
|
||||
showLogin: true,
|
||||
|
@ -33,8 +25,10 @@ describe('license features', function() {
|
|||
});
|
||||
|
||||
it('should display error when X-Pack is unavailable', () => {
|
||||
const rawLicenseMock = licensingMock.createLicenseMock();
|
||||
rawLicenseMock.isAvailable = false;
|
||||
const serviceSetup = new SecurityLicenseService().setup({
|
||||
license$: of(getMockRawLicense({ isAvailable: false })),
|
||||
license$: of(rawLicenseMock),
|
||||
});
|
||||
expect(serviceSetup.license.getFeatures()).toEqual({
|
||||
showLogin: true,
|
||||
|
@ -48,7 +42,9 @@ describe('license features', function() {
|
|||
});
|
||||
|
||||
it('should notify consumers of licensed feature changes', () => {
|
||||
const rawLicense$ = new BehaviorSubject(getMockRawLicense({ isAvailable: false }));
|
||||
const rawLicenseMock = licensingMock.createLicenseMock();
|
||||
rawLicenseMock.isAvailable = false;
|
||||
const rawLicense$ = new BehaviorSubject(rawLicenseMock);
|
||||
const serviceSetup = new SecurityLicenseService().setup({
|
||||
license$: rawLicense$,
|
||||
});
|
||||
|
@ -71,7 +67,7 @@ describe('license features', function() {
|
|||
]
|
||||
`);
|
||||
|
||||
rawLicense$.next(getMockRawLicense({ isAvailable: true }));
|
||||
rawLicense$.next(licensingMock.createLicenseMock());
|
||||
expect(subscriptionHandler).toHaveBeenCalledTimes(2);
|
||||
expect(subscriptionHandler.mock.calls[1]).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
|
@ -92,10 +88,8 @@ describe('license features', function() {
|
|||
});
|
||||
|
||||
it('should show login page and other security elements, allow RBAC but forbid document level security if license is not platinum or trial.', () => {
|
||||
const mockRawLicense = getMockRawLicense({ isAvailable: true });
|
||||
mockRawLicense.isOneOf.mockImplementation(licenses =>
|
||||
Array.isArray(licenses) ? licenses.includes('basic') : licenses === 'basic'
|
||||
);
|
||||
const mockRawLicense = licensingMock.createLicenseMock();
|
||||
mockRawLicense.hasAtLeast.mockReturnValue(false);
|
||||
mockRawLicense.getFeature.mockReturnValue({ isEnabled: true, isAvailable: true });
|
||||
|
||||
const serviceSetup = new SecurityLicenseService().setup({
|
||||
|
@ -114,8 +108,8 @@ describe('license features', function() {
|
|||
});
|
||||
|
||||
it('should not show login page or other security elements if security is disabled in Elasticsearch.', () => {
|
||||
const mockRawLicense = getMockRawLicense({ isAvailable: true });
|
||||
mockRawLicense.isOneOf.mockReturnValue(false);
|
||||
const mockRawLicense = licensingMock.createLicenseMock();
|
||||
mockRawLicense.hasAtLeast.mockReturnValue(false);
|
||||
mockRawLicense.getFeature.mockReturnValue({ isEnabled: false, isAvailable: true });
|
||||
|
||||
const serviceSetup = new SecurityLicenseService().setup({
|
||||
|
@ -133,14 +127,9 @@ describe('license features', function() {
|
|||
});
|
||||
|
||||
it('should allow to login, allow RBAC and document level security if license >= platinum', () => {
|
||||
const mockRawLicense = getMockRawLicense({ isAvailable: true });
|
||||
mockRawLicense.isOneOf.mockImplementation(licenses => {
|
||||
const licenseArray = [licenses].flat();
|
||||
return (
|
||||
licenseArray.includes('trial') ||
|
||||
licenseArray.includes('platinum') ||
|
||||
licenseArray.includes('enterprise')
|
||||
);
|
||||
const mockRawLicense = licensingMock.createLicenseMock();
|
||||
mockRawLicense.hasAtLeast.mockImplementation(license => {
|
||||
return license === 'trial' || license === 'platinum' || license === 'enterprise';
|
||||
});
|
||||
mockRawLicense.getFeature.mockReturnValue({ isEnabled: true, isAvailable: true });
|
||||
|
||||
|
|
|
@ -92,7 +92,7 @@ export class SecurityLicenseService {
|
|||
};
|
||||
}
|
||||
|
||||
const isLicensePlatinumOrBetter = rawLicense.isOneOf(['enterprise', 'platinum', 'trial']);
|
||||
const isLicensePlatinumOrBetter = rawLicense.hasAtLeast('platinum');
|
||||
return {
|
||||
showLogin: true,
|
||||
allowLogin: true,
|
||||
|
|
|
@ -21,7 +21,7 @@ const validLicense = {
|
|||
isEnabled: true,
|
||||
};
|
||||
},
|
||||
isOneOf: (...candidates) => true,
|
||||
hasAtLeast: (...candidates) => true,
|
||||
} as ILicense;
|
||||
|
||||
describe('SecurityNavControlService', () => {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue