kibana/x-pack/plugins/features/server/feature_registry.test.ts
Jonathan Buttner b6c982c3b0
[Cases] RBAC (#95058)
* Adding feature flag for auth

* Hiding SOs and adding consumer field

* First pass at adding security changes

* Consumer as the app's plugin ID

* Create addConsumerToSO migration helper

* Fix mapping's SO consumer

* Add test for CasesActions

* Declare hidden types on SO client

* Restructure integration tests

* Init spaces_only integration tests

* Implementing the cases security string

* Adding security plugin tests for cases

* Rough concept for authorization class

* Adding comments

* Fix merge

* Get requiredPrivileges for classes

* Check privillages

* Ensure that all classes are available

* Success if hasAllRequested is true

* Failure if hasAllRequested is false

* Adding schema updates for feature plugin

* Seperate basic from trial

* Enable SIR on integration tests

* Starting the plumbing for authorization in plugin

* Unit tests working

* Move find route logic to case client

* Create integration test helper functions

* Adding auth to create call

* Create getClassFilter helper

* Add class attribute to find request

* Create getFindAuthorizationFilter

* Ensure savedObject is authorized in find method

* Include fields for authorization

* Combine authorization filter with cases & subcases filter

* Fix isAuthorized flag

* Fix merge issue

* Create/delete spaces & users before and after tests

* Add more user and roles

* [Cases] Convert filters from strings to KueryNode (#95288)

* [Cases] RBAC: Rename class to scope (#95535)

* [Cases][RBAC] Rename scope to owner (#96035)

* [Cases] RBAC: Create & Find integration tests (#95511)

* [Cases] Cases client enchantment (#95923)

* [Cases] Authorization and Client Audit Logger (#95477)

* Starting audit logger

* Finishing auth audit logger

* Fixing tests and types

* Adding audit event creator

* Renaming class to scope

* Adding audit logger messages to create and find

* Adding comments and fixing import issue

* Fixing type errors

* Fixing tests and adding username to message

* Addressing PR feedback

* Removing unneccessary log and generating id

* Fixing module issue and remove expect.anything

* [Cases] Migrate sub cases routes to a client (#96461)

* Adding sub cases client

* Move sub case routes to case client

* Throw when attempting to access the sub cases client

* Fixing throw and removing user ans soclients

* [Cases] RBAC: Migrate routes' unit tests to integration tests (#96374)

Co-authored-by: Jonathan Buttner <jonathan.buttner@elastic.co>

* [Cases] Move remaining HTTP functionality to client (#96507)

* Moving deletes and find for attachments

* Moving rest of comment apis

* Migrating configuration routes to client

* Finished moving routes, starting utils refactor

* Refactoring utilites and fixing integration tests

* Addressing PR feedback

* Fixing mocks and types

* Fixing integration tests

* Renaming status_stats

* Fixing test type errors

* Adding plugins to kibana.json

* Adding cases to required plugin

* [Cases] Refactoring authorization (#97483)

* Refactoring authorization

* Wrapping auth calls in helper for try catch

* Reverting name change

* Hardcoding the saved object types

* Switching ensure to owner array

* [Cases] Add authorization to configuration & cases routes (#97228)

* [Cases] Attachments RBAC (#97756)

* Starting rbac for comments

* Adding authorization to rest of comment apis

* Starting the comment rbac tests

* Fixing some of the rbac tests

* Adding some integration tests

* Starting patch tests

* Working tests for comments

* Working tests

* Fixing some tests

* Fixing type issues from pulling in master

* Fixing connector tests that only work in trial license

* Attempting to fix cypress

* Mock return of array for configure

* Fixing cypress test

* Cleaning up

* Addressing PR comments

* Reducing operations

* [Cases] Add RBAC to remaining Cases APIs (#98762)

* Starting rbac for comments

* Adding authorization to rest of comment apis

* Starting the comment rbac tests

* Fixing some of the rbac tests

* Adding some integration tests

* Starting patch tests

* Working tests for comments

* Working tests

* Fixing some tests

* Fixing type issues from pulling in master

* Fixing connector tests that only work in trial license

* Attempting to fix cypress

* Mock return of array for configure

* Fixing cypress test

* Cleaning up

* Working case update tests

* Addressing PR comments

* Reducing operations

* Working rbac push case tests

* Starting stats apis

* Working status tests

* User action tests and fixing migration errors

* Fixing type errors

* including error in message

* Addressing pr feedback

* Fixing some type errors

* [Cases] Add space only tests (#99409)

* Starting spaces tests

* Finishing space only tests

* Refactoring createCaseWithConnector

* Fixing spelling

* Addressing PR feedback and creating alert tests

* Fixing mocks

* [Cases] Add security only tests (#99679)

* Starting spaces tests

* Finishing space only tests

* Refactoring createCaseWithConnector

* Fixing spelling

* Addressing PR feedback and creating alert tests

* Fixing mocks

* Starting security only tests

* Adding remainder security only tests

* Using helper objects

* Fixing type error for null space

* Renaming utility variables

* Refactoring users and roles for security only tests

* Adding sub feature

* [Cases] Cleaning up the services and TODOs (#99723)

* Cleaning up the service intialization

* Fixing type errors

* Adding comments for the api

* Working test for cases client

* Fix type error

* Adding generated docs

* Adding more docs and cleaning up types

* Cleaning up readme

* More clean up and links

* Changing some file names

* Renaming docs

* Integration tests for cases privs and fixes (#100038)

* [Cases] RBAC on UI (#99478)

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>

* Fixing case ids by alert id route call

* [Cases] Fixing UI feature permissions and adding UI tests (#100074)

* Integration tests for cases privs and fixes

* Fixing ui cases permissions and adding tests

* Adding test for collection failure and fixing jest

* Renaming variables

* Fixing type error

* Adding some comments

* Validate cases features

* Fix new schema

* Adding owner param for the status stats

* Fix get case status tests

* Adjusting permissions text and fixing status

* Address PR feedback

* Adding top level feature back

* Fixing feature privileges

* Renaming

* Removing uneeded else

* Fixing tests and adding cases merge tests

* [Cases][Security Solution] Basic license security solution API tests (#100925)

* Cleaning up the fixture plugins

* Adding basic feature test

* renaming to unsecuredSavedObjectsClient (#101215)

* [Cases] RBAC Refactoring audit logging (#100952)

* Refactoring audit logging

* Adding unit tests for authorization classes

* Addressing feedback and adding util tests

* return undefined on empty array

* fixing eslint

* [Cases] Cleaning up RBAC integration tests (#101324)

* Adding tests for space permissions

* Adding tests for testing a disable feature

Co-authored-by: Christos Nasikas <christos.nasikas@elastic.co>
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
2021-06-07 09:37:11 -04:00

2072 lines
60 KiB
TypeScript

/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { FeatureRegistry } from './feature_registry';
import { ElasticsearchFeatureConfig, KibanaFeatureConfig } from '../common';
import { licensingMock } from '../../licensing/server/mocks';
describe('FeatureRegistry', () => {
describe('Kibana Features', () => {
it('allows a minimal feature to be registered', () => {
const feature: KibanaFeatureConfig = {
id: 'test-feature',
name: 'Test Feature',
app: [],
category: { id: 'foo', label: 'foo' },
privileges: null,
};
const featureRegistry = new FeatureRegistry();
featureRegistry.registerKibanaFeature(feature);
const result = featureRegistry.getAllKibanaFeatures();
expect(result).toHaveLength(1);
// Should be the equal, but not the same instance (i.e., a defensive copy)
expect(result[0].toRaw()).not.toBe(feature);
expect(result[0].toRaw()).toEqual(feature);
});
it('allows a complex feature to be registered', () => {
const feature: KibanaFeatureConfig = {
id: 'test-feature',
name: 'Test Feature',
excludeFromBasePrivileges: true,
app: ['app1'],
category: { id: 'foo', label: 'foo' },
minimumLicense: 'platinum',
catalogue: ['foo'],
management: {
foo: ['bar'],
},
privileges: {
all: {
catalogue: ['foo'],
management: {
foo: ['bar'],
},
app: ['app1'],
savedObject: {
all: ['space', 'etc', 'telemetry'],
read: ['canvas', 'config', 'url'],
},
api: ['someApiEndpointTag', 'anotherEndpointTag'],
ui: ['allowsFoo', 'showBar', 'showBaz'],
},
read: {
savedObject: {
all: [],
read: ['config', 'url', 'telemetry'],
},
ui: [],
},
},
subFeatures: [
{
name: 'sub-feature-1',
privilegeGroups: [
{
groupType: 'independent',
privileges: [
{
id: 'foo',
name: 'foo',
includeIn: 'read',
savedObject: {
all: [],
read: [],
},
ui: [],
},
],
},
{
groupType: 'mutually_exclusive',
privileges: [
{
id: 'bar',
name: 'bar',
includeIn: 'all',
savedObject: {
all: [],
read: [],
},
ui: [],
},
{
id: 'baz',
name: 'baz',
includeIn: 'none',
savedObject: {
all: [],
read: [],
},
ui: [],
},
],
},
],
},
],
privilegesTooltip: 'some fancy tooltip',
reserved: {
privileges: [
{
id: 'reserved',
privilege: {
catalogue: ['foo'],
management: {
foo: ['bar'],
},
app: ['app1'],
savedObject: {
all: ['space', 'etc', 'telemetry'],
read: ['canvas', 'config', 'url'],
},
api: ['someApiEndpointTag', 'anotherEndpointTag'],
ui: ['allowsFoo', 'showBar', 'showBaz'],
},
},
],
description: 'some completely adequate description',
},
};
const featureRegistry = new FeatureRegistry();
featureRegistry.registerKibanaFeature(feature);
const result = featureRegistry.getAllKibanaFeatures();
expect(result).toHaveLength(1);
// Should be the equal, but not the same instance (i.e., a defensive copy)
expect(result[0].toRaw()).not.toBe(feature);
expect(result[0].toRaw()).toEqual(feature);
});
describe('category', () => {
it('is required', () => {
const feature: KibanaFeatureConfig = {
id: 'test-feature',
name: 'Test Feature',
app: [],
privileges: null,
} as any;
const featureRegistry = new FeatureRegistry();
expect(() =>
featureRegistry.registerKibanaFeature(feature)
).toThrowErrorMatchingInlineSnapshot(
`"[category.id]: expected value of type [string] but got [undefined]"`
);
});
it('must have an id', () => {
const feature: KibanaFeatureConfig = {
id: 'test-feature',
name: 'Test Feature',
app: [],
privileges: null,
category: { label: 'foo' },
} as any;
const featureRegistry = new FeatureRegistry();
expect(() =>
featureRegistry.registerKibanaFeature(feature)
).toThrowErrorMatchingInlineSnapshot(
`"[category.id]: expected value of type [string] but got [undefined]"`
);
});
it('must have a label', () => {
const feature: KibanaFeatureConfig = {
id: 'test-feature',
name: 'Test Feature',
app: [],
privileges: null,
category: { id: 'foo' },
} as any;
const featureRegistry = new FeatureRegistry();
expect(() =>
featureRegistry.registerKibanaFeature(feature)
).toThrowErrorMatchingInlineSnapshot(
`"[category.label]: expected value of type [string] but got [undefined]"`
);
});
});
it(`requires a value for privileges`, () => {
const feature: KibanaFeatureConfig = {
id: 'test-feature',
name: 'Test Feature',
app: [],
category: { id: 'foo', label: 'foo' },
} as any;
const featureRegistry = new FeatureRegistry();
expect(() =>
featureRegistry.registerKibanaFeature(feature)
).toThrowErrorMatchingInlineSnapshot(
`"[privileges]: expected at least one defined value but got [undefined]"`
);
});
it(`does not allow sub-features to be registered when no primary privileges are not registered`, () => {
const feature: KibanaFeatureConfig = {
id: 'test-feature',
name: 'Test Feature',
app: [],
category: { id: 'foo', label: 'foo' },
privileges: null,
subFeatures: [
{
name: 'my sub feature',
privilegeGroups: [
{
groupType: 'independent',
privileges: [
{
id: 'my-sub-priv',
name: 'my sub priv',
includeIn: 'none',
savedObject: {
all: [],
read: [],
},
ui: [],
},
],
},
],
},
],
};
const featureRegistry = new FeatureRegistry();
expect(() =>
featureRegistry.registerKibanaFeature(feature)
).toThrowErrorMatchingInlineSnapshot(
`"[subFeatures]: array size is [1], but cannot be greater than [0]"`
);
});
it(`automatically grants 'all' access to telemetry saved objects for the 'all' privilege`, () => {
const feature: KibanaFeatureConfig = {
id: 'test-feature',
name: 'Test Feature',
app: [],
category: { id: 'foo', label: 'foo' },
privileges: {
all: {
ui: [],
savedObject: {
all: [],
read: [],
},
},
read: {
ui: [],
savedObject: {
all: [],
read: [],
},
},
},
};
const featureRegistry = new FeatureRegistry();
featureRegistry.registerKibanaFeature(feature);
const result = featureRegistry.getAllKibanaFeatures();
expect(result[0].privileges).toHaveProperty('all');
expect(result[0].privileges).toHaveProperty('read');
const allPrivilege = result[0].privileges?.all;
expect(allPrivilege?.savedObject.all).toEqual(['telemetry']);
});
it(`automatically grants access to config, url, and telemetry saved objects`, () => {
const feature: KibanaFeatureConfig = {
id: 'test-feature',
name: 'Test Feature',
app: [],
category: { id: 'foo', label: 'foo' },
privileges: {
all: {
ui: [],
savedObject: {
all: [],
read: [],
},
},
read: {
ui: [],
savedObject: {
all: [],
read: [],
},
},
},
};
const featureRegistry = new FeatureRegistry();
featureRegistry.registerKibanaFeature(feature);
const result = featureRegistry.getAllKibanaFeatures();
expect(result[0].privileges).toHaveProperty('all');
expect(result[0].privileges).toHaveProperty('read');
const allPrivilege = result[0].privileges?.all;
const readPrivilege = result[0].privileges?.read;
expect(allPrivilege?.savedObject.read).toEqual(['config', 'url']);
expect(readPrivilege?.savedObject.read).toEqual(['config', 'telemetry', 'url']);
});
it(`automatically grants 'all' access to telemetry and 'read' to [config, url] saved objects for the reserved privilege`, () => {
const feature: KibanaFeatureConfig = {
id: 'test-feature',
name: 'Test Feature',
app: [],
category: { id: 'foo', label: 'foo' },
privileges: null,
reserved: {
description: 'foo',
privileges: [
{
id: 'reserved',
privilege: {
ui: [],
savedObject: {
all: [],
read: [],
},
},
},
],
},
};
const featureRegistry = new FeatureRegistry();
featureRegistry.registerKibanaFeature(feature);
const result = featureRegistry.getAllKibanaFeatures();
const reservedPrivilege = result[0]!.reserved!.privileges[0].privilege;
expect(reservedPrivilege.savedObject.all).toEqual(['telemetry']);
expect(reservedPrivilege.savedObject.read).toEqual(['config', 'url']);
});
it(`does not duplicate the automatic grants if specified on the incoming feature`, () => {
const feature: KibanaFeatureConfig = {
id: 'test-feature',
name: 'Test Feature',
app: [],
category: { id: 'foo', label: 'foo' },
privileges: {
all: {
ui: [],
savedObject: {
all: ['telemetry'],
read: ['config', 'url'],
},
},
read: {
ui: [],
savedObject: {
all: [],
read: ['config', 'url'],
},
},
},
};
const featureRegistry = new FeatureRegistry();
featureRegistry.registerKibanaFeature(feature);
const result = featureRegistry.getAllKibanaFeatures();
expect(result[0].privileges).toHaveProperty('all');
expect(result[0].privileges).toHaveProperty('read');
const allPrivilege = result[0].privileges!.all;
const readPrivilege = result[0].privileges!.read;
expect(allPrivilege?.savedObject.all).toEqual(['telemetry']);
expect(allPrivilege?.savedObject.read).toEqual(['config', 'url']);
expect(readPrivilege?.savedObject.read).toEqual(['config', 'url', 'telemetry']);
});
it(`does not allow duplicate features to be registered`, () => {
const feature: KibanaFeatureConfig = {
id: 'test-feature',
name: 'Test Feature',
app: [],
category: { id: 'foo', label: 'foo' },
privileges: null,
};
const duplicateFeature: KibanaFeatureConfig = {
id: 'test-feature',
name: 'Duplicate Test Feature',
app: [],
category: { id: 'foo', label: 'foo' },
privileges: null,
};
const featureRegistry = new FeatureRegistry();
featureRegistry.registerKibanaFeature(feature);
expect(() =>
featureRegistry.registerKibanaFeature(duplicateFeature)
).toThrowErrorMatchingInlineSnapshot(`"Feature with id test-feature is already registered."`);
});
['contains space', 'contains_invalid()_chars', ''].forEach((prohibitedChars) => {
it(`prevents features from being registered with a management id of "${prohibitedChars}"`, () => {
const featureRegistry = new FeatureRegistry();
expect(() =>
featureRegistry.registerKibanaFeature({
id: 'foo',
name: 'some feature',
management: {
kibana: [prohibitedChars],
},
app: [],
category: { id: 'foo', label: 'foo' },
privileges: null,
})
).toThrowErrorMatchingSnapshot();
});
it(`prevents features from being registered with a catalogue entry of "${prohibitedChars}"`, () => {
const featureRegistry = new FeatureRegistry();
expect(() =>
featureRegistry.registerKibanaFeature({
id: 'foo',
name: 'some feature',
catalogue: [prohibitedChars],
app: [],
category: { id: 'foo', label: 'foo' },
privileges: null,
})
).toThrowErrorMatchingSnapshot();
});
});
['catalogue', 'management', 'navLinks', `doesn't match valid regex`].forEach((prohibitedId) => {
it(`prevents features from being registered with an ID of "${prohibitedId}"`, () => {
const featureRegistry = new FeatureRegistry();
expect(() =>
featureRegistry.registerKibanaFeature({
id: prohibitedId,
name: 'some feature',
app: [],
category: { id: 'foo', label: 'foo' },
privileges: null,
})
).toThrowErrorMatchingSnapshot();
});
});
it('prevents features from being registered with invalid privilege names', () => {
const feature: KibanaFeatureConfig = {
id: 'test-feature',
name: 'Test Feature',
app: ['app1', 'app2'],
category: { id: 'foo', label: 'foo' },
privileges: {
foo: {
name: 'Foo',
app: ['app1', 'app2'],
savedObject: {
all: ['config', 'space', 'etc'],
read: ['canvas'],
},
api: ['someApiEndpointTag', 'anotherEndpointTag'],
ui: ['allowsFoo', 'showBar', 'showBaz'],
},
} as any,
};
const featureRegistry = new FeatureRegistry();
expect(() => featureRegistry.registerKibanaFeature(feature))
.toThrowErrorMatchingInlineSnapshot(`
"[privileges]: types that failed validation:
- [privileges.0]: expected value to equal [null]
- [privileges.1.foo]: definition for this key is missing"
`);
});
it(`prevents privileges from specifying app entries that don't exist at the root level`, () => {
const feature: KibanaFeatureConfig = {
id: 'test-feature',
name: 'Test Feature',
app: ['bar'],
category: { id: 'foo', label: 'foo' },
privileges: {
all: {
savedObject: {
all: [],
read: [],
},
ui: [],
app: ['foo', 'bar', 'baz'],
},
read: {
savedObject: {
all: [],
read: [],
},
ui: [],
app: ['foo', 'bar', 'baz'],
},
},
};
const featureRegistry = new FeatureRegistry();
expect(() =>
featureRegistry.registerKibanaFeature(feature)
).toThrowErrorMatchingInlineSnapshot(
`"Feature privilege test-feature.all has unknown app entries: foo, baz"`
);
});
it(`prevents features from specifying app entries that don't exist at the privilege level`, () => {
const feature: KibanaFeatureConfig = {
id: 'test-feature',
name: 'Test Feature',
app: ['foo', 'bar', 'baz'],
category: { id: 'foo', label: 'foo' },
privileges: {
all: {
savedObject: {
all: [],
read: [],
},
ui: [],
app: ['bar'],
},
read: {
savedObject: {
all: [],
read: [],
},
ui: [],
app: [],
},
},
subFeatures: [
{
name: 'my sub feature',
privilegeGroups: [
{
groupType: 'independent',
privileges: [
{
id: 'cool-sub-feature-privilege',
name: 'cool privilege',
includeIn: 'none',
savedObject: {
all: [],
read: [],
},
ui: [],
app: ['foo'],
},
],
},
],
},
],
};
const featureRegistry = new FeatureRegistry();
expect(() =>
featureRegistry.registerKibanaFeature(feature)
).toThrowErrorMatchingInlineSnapshot(
`"Feature test-feature specifies app entries which are not granted to any privileges: baz"`
);
});
it(`prevents reserved privileges from specifying app entries that don't exist at the root level`, () => {
const feature: KibanaFeatureConfig = {
id: 'test-feature',
name: 'Test Feature',
app: ['bar'],
category: { id: 'foo', label: 'foo' },
privileges: null,
reserved: {
description: 'something',
privileges: [
{
id: 'reserved',
privilege: {
savedObject: {
all: [],
read: [],
},
ui: [],
app: ['foo', 'bar', 'baz'],
},
},
],
},
};
const featureRegistry = new FeatureRegistry();
expect(() =>
featureRegistry.registerKibanaFeature(feature)
).toThrowErrorMatchingInlineSnapshot(
`"Feature privilege test-feature.reserved has unknown app entries: foo, baz"`
);
});
it(`prevents features from specifying app entries that don't exist at the reserved privilege level`, () => {
const feature: KibanaFeatureConfig = {
id: 'test-feature',
name: 'Test Feature',
app: ['foo', 'bar', 'baz'],
category: { id: 'foo', label: 'foo' },
privileges: null,
reserved: {
description: 'something',
privileges: [
{
id: 'reserved',
privilege: {
savedObject: {
all: [],
read: [],
},
ui: [],
app: ['foo', 'bar'],
},
},
],
},
};
const featureRegistry = new FeatureRegistry();
expect(() =>
featureRegistry.registerKibanaFeature(feature)
).toThrowErrorMatchingInlineSnapshot(
`"Feature test-feature specifies app entries which are not granted to any privileges: baz"`
);
});
it(`prevents privileges from specifying catalogue entries that don't exist at the root level`, () => {
const feature: KibanaFeatureConfig = {
id: 'test-feature',
name: 'Test Feature',
app: [],
category: { id: 'foo', label: 'foo' },
catalogue: ['bar'],
privileges: {
all: {
catalogue: ['foo', 'bar', 'baz'],
savedObject: {
all: [],
read: [],
},
ui: [],
app: [],
},
read: {
catalogue: ['foo', 'bar', 'baz'],
savedObject: {
all: [],
read: [],
},
ui: [],
app: [],
},
},
};
const featureRegistry = new FeatureRegistry();
expect(() =>
featureRegistry.registerKibanaFeature(feature)
).toThrowErrorMatchingInlineSnapshot(
`"Feature privilege test-feature.all has unknown catalogue entries: foo, baz"`
);
});
it(`prevents features from specifying catalogue entries that don't exist at the privilege level`, () => {
const feature: KibanaFeatureConfig = {
id: 'test-feature',
name: 'Test Feature',
app: [],
category: { id: 'foo', label: 'foo' },
catalogue: ['foo', 'bar', 'baz'],
privileges: {
all: {
catalogue: ['foo'],
savedObject: {
all: [],
read: [],
},
ui: [],
app: [],
},
read: {
catalogue: ['foo'],
savedObject: {
all: [],
read: [],
},
ui: [],
app: [],
},
},
subFeatures: [
{
name: 'my sub feature',
privilegeGroups: [
{
groupType: 'independent',
privileges: [
{
id: 'cool-sub-feature-privilege',
name: 'cool privilege',
includeIn: 'none',
savedObject: {
all: [],
read: [],
},
ui: [],
catalogue: ['bar'],
},
],
},
],
},
],
};
const featureRegistry = new FeatureRegistry();
expect(() =>
featureRegistry.registerKibanaFeature(feature)
).toThrowErrorMatchingInlineSnapshot(
`"Feature test-feature specifies catalogue entries which are not granted to any privileges: baz"`
);
});
it(`prevents reserved privileges from specifying catalogue entries that don't exist at the root level`, () => {
const feature: KibanaFeatureConfig = {
id: 'test-feature',
name: 'Test Feature',
app: [],
category: { id: 'foo', label: 'foo' },
catalogue: ['bar'],
privileges: null,
reserved: {
description: 'something',
privileges: [
{
id: 'reserved',
privilege: {
catalogue: ['foo', 'bar', 'baz'],
savedObject: {
all: [],
read: [],
},
ui: [],
app: [],
},
},
],
},
};
const featureRegistry = new FeatureRegistry();
expect(() =>
featureRegistry.registerKibanaFeature(feature)
).toThrowErrorMatchingInlineSnapshot(
`"Feature privilege test-feature.reserved has unknown catalogue entries: foo, baz"`
);
});
it(`prevents features from specifying catalogue entries that don't exist at the reserved privilege level`, () => {
const feature: KibanaFeatureConfig = {
id: 'test-feature',
name: 'Test Feature',
app: [],
category: { id: 'foo', label: 'foo' },
catalogue: ['foo', 'bar', 'baz'],
privileges: null,
reserved: {
description: 'something',
privileges: [
{
id: 'reserved',
privilege: {
catalogue: ['foo', 'bar'],
savedObject: {
all: [],
read: [],
},
ui: [],
app: [],
},
},
],
},
};
const featureRegistry = new FeatureRegistry();
expect(() =>
featureRegistry.registerKibanaFeature(feature)
).toThrowErrorMatchingInlineSnapshot(
`"Feature test-feature specifies catalogue entries which are not granted to any privileges: baz"`
);
});
it(`prevents privileges from specifying alerting/rule entries that don't exist at the root level`, () => {
const feature: KibanaFeatureConfig = {
id: 'test-feature',
name: 'Test Feature',
app: [],
category: { id: 'foo', label: 'foo' },
alerting: ['bar'],
privileges: {
all: {
alerting: {
rule: {
all: ['foo', 'bar'],
read: ['baz'],
},
},
savedObject: {
all: [],
read: [],
},
ui: [],
app: [],
},
read: {
alerting: {
rule: {
read: ['foo', 'bar', 'baz'],
},
},
savedObject: {
all: [],
read: [],
},
ui: [],
app: [],
},
},
};
const featureRegistry = new FeatureRegistry();
expect(() =>
featureRegistry.registerKibanaFeature(feature)
).toThrowErrorMatchingInlineSnapshot(
`"Feature privilege test-feature.all has unknown alerting entries: foo, baz"`
);
});
it(`prevents privileges from specifying alerting/alert entries that don't exist at the root level`, () => {
const feature: KibanaFeatureConfig = {
id: 'test-feature',
name: 'Test Feature',
app: [],
category: { id: 'foo', label: 'foo' },
alerting: ['bar'],
privileges: {
all: {
alerting: {
alert: {
all: ['foo', 'bar'],
read: ['baz'],
},
},
savedObject: {
all: [],
read: [],
},
ui: [],
app: [],
},
read: {
alerting: {
alert: {
read: ['foo', 'bar', 'baz'],
},
},
savedObject: {
all: [],
read: [],
},
ui: [],
app: [],
},
},
};
const featureRegistry = new FeatureRegistry();
expect(() =>
featureRegistry.registerKibanaFeature(feature)
).toThrowErrorMatchingInlineSnapshot(
`"Feature privilege test-feature.all has unknown alerting entries: foo, baz"`
);
});
it(`prevents features from specifying alerting/rule entries that don't exist at the privilege level`, () => {
const feature: KibanaFeatureConfig = {
id: 'test-feature',
name: 'Test Feature',
app: [],
category: { id: 'foo', label: 'foo' },
alerting: ['foo', 'bar', 'baz'],
privileges: {
all: {
alerting: {
rule: {
all: ['foo'],
},
},
savedObject: {
all: [],
read: [],
},
ui: [],
app: [],
},
read: {
alerting: {
rule: {
all: ['foo'],
},
},
savedObject: {
all: [],
read: [],
},
ui: [],
app: [],
},
},
subFeatures: [
{
name: 'my sub feature',
privilegeGroups: [
{
groupType: 'independent',
privileges: [
{
id: 'cool-sub-feature-privilege',
name: 'cool privilege',
includeIn: 'none',
savedObject: {
all: [],
read: [],
},
ui: [],
alerting: {
rule: {
all: ['bar'],
},
},
},
],
},
],
},
],
};
const featureRegistry = new FeatureRegistry();
expect(() =>
featureRegistry.registerKibanaFeature(feature)
).toThrowErrorMatchingInlineSnapshot(
`"Feature test-feature specifies alerting entries which are not granted to any privileges: baz"`
);
});
it(`prevents features from specifying alerting/alert entries that don't exist at the privilege level`, () => {
const feature: KibanaFeatureConfig = {
id: 'test-feature',
name: 'Test Feature',
app: [],
category: { id: 'foo', label: 'foo' },
alerting: ['foo', 'bar', 'baz'],
privileges: {
all: {
alerting: {
alert: {
all: ['foo'],
},
},
savedObject: {
all: [],
read: [],
},
ui: [],
app: [],
},
read: {
alerting: {
alert: {
all: ['foo'],
},
},
savedObject: {
all: [],
read: [],
},
ui: [],
app: [],
},
},
subFeatures: [
{
name: 'my sub feature',
privilegeGroups: [
{
groupType: 'independent',
privileges: [
{
id: 'cool-sub-feature-privilege',
name: 'cool privilege',
includeIn: 'none',
savedObject: {
all: [],
read: [],
},
ui: [],
alerting: {
alert: {
all: ['bar'],
},
},
},
],
},
],
},
],
};
const featureRegistry = new FeatureRegistry();
expect(() =>
featureRegistry.registerKibanaFeature(feature)
).toThrowErrorMatchingInlineSnapshot(
`"Feature test-feature specifies alerting entries which are not granted to any privileges: baz"`
);
});
it(`prevents reserved privileges from specifying alerting/rule entries that don't exist at the root level`, () => {
const feature: KibanaFeatureConfig = {
id: 'test-feature',
name: 'Test Feature',
app: [],
category: { id: 'foo', label: 'foo' },
alerting: ['bar'],
privileges: null,
reserved: {
description: 'something',
privileges: [
{
id: 'reserved',
privilege: {
alerting: {
rule: {
all: ['foo', 'bar', 'baz'],
},
},
savedObject: {
all: [],
read: [],
},
ui: [],
app: [],
},
},
],
},
};
const featureRegistry = new FeatureRegistry();
expect(() =>
featureRegistry.registerKibanaFeature(feature)
).toThrowErrorMatchingInlineSnapshot(
`"Feature privilege test-feature.reserved has unknown alerting entries: foo, baz"`
);
});
it(`prevents reserved privileges from specifying alerting/alert entries that don't exist at the root level`, () => {
const feature: KibanaFeatureConfig = {
id: 'test-feature',
name: 'Test Feature',
app: [],
category: { id: 'foo', label: 'foo' },
alerting: ['bar'],
privileges: null,
reserved: {
description: 'something',
privileges: [
{
id: 'reserved',
privilege: {
alerting: {
alert: {
all: ['foo', 'bar', 'baz'],
},
},
savedObject: {
all: [],
read: [],
},
ui: [],
app: [],
},
},
],
},
};
const featureRegistry = new FeatureRegistry();
expect(() =>
featureRegistry.registerKibanaFeature(feature)
).toThrowErrorMatchingInlineSnapshot(
`"Feature privilege test-feature.reserved has unknown alerting entries: foo, baz"`
);
});
it(`prevents features from specifying alerting/rule entries that don't exist at the reserved privilege level`, () => {
const feature: KibanaFeatureConfig = {
id: 'test-feature',
name: 'Test Feature',
app: [],
category: { id: 'foo', label: 'foo' },
alerting: ['foo', 'bar', 'baz'],
privileges: null,
reserved: {
description: 'something',
privileges: [
{
id: 'reserved',
privilege: {
alerting: {
rule: {
all: ['foo', 'bar'],
},
},
savedObject: {
all: [],
read: [],
},
ui: [],
app: [],
},
},
],
},
};
const featureRegistry = new FeatureRegistry();
expect(() =>
featureRegistry.registerKibanaFeature(feature)
).toThrowErrorMatchingInlineSnapshot(
`"Feature test-feature specifies alerting entries which are not granted to any privileges: baz"`
);
});
it(`prevents features from specifying alerting/alert entries that don't exist at the reserved privilege level`, () => {
const feature: KibanaFeatureConfig = {
id: 'test-feature',
name: 'Test Feature',
app: [],
category: { id: 'foo', label: 'foo' },
alerting: ['foo', 'bar', 'baz'],
privileges: null,
reserved: {
description: 'something',
privileges: [
{
id: 'reserved',
privilege: {
alerting: {
alert: {
all: ['foo', 'bar'],
},
},
savedObject: {
all: [],
read: [],
},
ui: [],
app: [],
},
},
],
},
};
const featureRegistry = new FeatureRegistry();
expect(() =>
featureRegistry.registerKibanaFeature(feature)
).toThrowErrorMatchingInlineSnapshot(
`"Feature test-feature specifies alerting entries which are not granted to any privileges: baz"`
);
});
it(`prevents privileges from specifying cases entries that don't exist at the root level`, () => {
const feature: KibanaFeatureConfig = {
id: 'test-feature',
name: 'Test Feature',
app: [],
category: { id: 'foo', label: 'foo' },
cases: ['bar'],
privileges: {
all: {
cases: {
all: ['foo', 'bar'],
read: ['baz'],
},
savedObject: {
all: [],
read: [],
},
ui: [],
app: [],
},
read: {
cases: { read: ['foo', 'bar', 'baz'] },
savedObject: {
all: [],
read: [],
},
ui: [],
app: [],
},
},
};
const featureRegistry = new FeatureRegistry();
expect(() =>
featureRegistry.registerKibanaFeature(feature)
).toThrowErrorMatchingInlineSnapshot(
`"Feature privilege test-feature.all has unknown cases entries: foo, baz"`
);
});
it(`prevents features from specifying cases entries that don't exist at the privilege level`, () => {
const feature: KibanaFeatureConfig = {
id: 'test-feature',
name: 'Test Feature',
app: [],
category: { id: 'foo', label: 'foo' },
cases: ['foo', 'bar', 'baz'],
privileges: {
all: {
cases: { all: ['foo'] },
savedObject: {
all: [],
read: [],
},
ui: [],
app: [],
},
read: {
cases: { all: ['foo'] },
savedObject: {
all: [],
read: [],
},
ui: [],
app: [],
},
},
subFeatures: [
{
name: 'my sub feature',
privilegeGroups: [
{
groupType: 'independent',
privileges: [
{
id: 'cool-sub-feature-privilege',
name: 'cool privilege',
includeIn: 'none',
savedObject: {
all: [],
read: [],
},
ui: [],
cases: { all: ['bar'] },
},
],
},
],
},
],
};
const featureRegistry = new FeatureRegistry();
expect(() =>
featureRegistry.registerKibanaFeature(feature)
).toThrowErrorMatchingInlineSnapshot(
`"Feature test-feature specifies cases entries which are not granted to any privileges: baz"`
);
});
it(`prevents reserved privileges from specifying cases entries that don't exist at the root level`, () => {
const feature: KibanaFeatureConfig = {
id: 'test-feature',
name: 'Test Feature',
app: [],
category: { id: 'foo', label: 'foo' },
cases: ['bar'],
privileges: null,
reserved: {
description: 'something',
privileges: [
{
id: 'reserved',
privilege: {
cases: { all: ['foo', 'bar', 'baz'] },
savedObject: {
all: [],
read: [],
},
ui: [],
app: [],
},
},
],
},
};
const featureRegistry = new FeatureRegistry();
expect(() =>
featureRegistry.registerKibanaFeature(feature)
).toThrowErrorMatchingInlineSnapshot(
`"Feature privilege test-feature.reserved has unknown cases entries: foo, baz"`
);
});
it(`prevents features from specifying cases entries that don't exist at the reserved privilege level`, () => {
const feature: KibanaFeatureConfig = {
id: 'test-feature',
name: 'Test Feature',
app: [],
category: { id: 'foo', label: 'foo' },
cases: ['foo', 'bar', 'baz'],
privileges: null,
reserved: {
description: 'something',
privileges: [
{
id: 'reserved',
privilege: {
cases: { all: ['foo', 'bar'] },
savedObject: {
all: [],
read: [],
},
ui: [],
app: [],
},
},
],
},
};
const featureRegistry = new FeatureRegistry();
expect(() =>
featureRegistry.registerKibanaFeature(feature)
).toThrowErrorMatchingInlineSnapshot(
`"Feature test-feature specifies cases entries which are not granted to any privileges: baz"`
);
});
it(`prevents privileges from specifying management sections that don't exist at the root level`, () => {
const feature: KibanaFeatureConfig = {
id: 'test-feature',
name: 'Test Feature',
app: [],
category: { id: 'foo', label: 'foo' },
catalogue: ['bar'],
management: {
kibana: ['hey'],
},
privileges: {
all: {
catalogue: ['bar'],
management: {
elasticsearch: ['hey'],
},
savedObject: {
all: [],
read: [],
},
ui: [],
app: [],
},
read: {
catalogue: ['bar'],
management: {
elasticsearch: ['hey'],
},
savedObject: {
all: [],
read: [],
},
ui: [],
app: [],
},
},
};
const featureRegistry = new FeatureRegistry();
expect(() =>
featureRegistry.registerKibanaFeature(feature)
).toThrowErrorMatchingInlineSnapshot(
`"Feature privilege test-feature.all has unknown management section: elasticsearch"`
);
});
it(`prevents features from specifying management sections that don't exist at the privilege level`, () => {
const feature: KibanaFeatureConfig = {
id: 'test-feature',
name: 'Test Feature',
app: [],
category: { id: 'foo', label: 'foo' },
catalogue: ['bar'],
management: {
kibana: ['hey'],
elasticsearch: ['hey', 'there'],
},
privileges: {
all: {
catalogue: ['bar'],
management: {
elasticsearch: ['hey'],
},
savedObject: {
all: [],
read: [],
},
ui: [],
app: [],
},
read: {
catalogue: ['bar'],
management: {
elasticsearch: ['hey'],
},
savedObject: {
all: [],
read: [],
},
ui: [],
app: [],
},
},
subFeatures: [
{
name: 'my sub feature',
privilegeGroups: [
{
groupType: 'independent',
privileges: [
{
id: 'cool-sub-feature-privilege',
name: 'cool privilege',
includeIn: 'none',
savedObject: {
all: [],
read: [],
},
ui: [],
management: {
kibana: ['hey'],
elasticsearch: ['hey'],
},
},
],
},
],
},
],
};
const featureRegistry = new FeatureRegistry();
expect(() =>
featureRegistry.registerKibanaFeature(feature)
).toThrowErrorMatchingInlineSnapshot(
`"Feature test-feature specifies management entries which are not granted to any privileges: elasticsearch.there"`
);
});
it(`prevents reserved privileges from specifying management entries that don't exist at the root level`, () => {
const feature: KibanaFeatureConfig = {
id: 'test-feature',
name: 'Test Feature',
app: [],
category: { id: 'foo', label: 'foo' },
catalogue: ['bar'],
management: {
kibana: ['hey'],
},
privileges: null,
reserved: {
description: 'something',
privileges: [
{
id: 'reserved',
privilege: {
catalogue: ['bar'],
management: {
kibana: ['hey-there'],
},
savedObject: {
all: [],
read: [],
},
ui: [],
app: [],
},
},
],
},
};
const featureRegistry = new FeatureRegistry();
expect(() =>
featureRegistry.registerKibanaFeature(feature)
).toThrowErrorMatchingInlineSnapshot(
`"Feature privilege test-feature.reserved has unknown management entries for section kibana: hey-there"`
);
});
it(`prevents features from specifying management entries that don't exist at the reserved privilege level`, () => {
const feature: KibanaFeatureConfig = {
id: 'test-feature',
name: 'Test Feature',
app: [],
category: { id: 'foo', label: 'foo' },
catalogue: ['bar'],
management: {
kibana: ['hey', 'hey-there'],
},
privileges: null,
reserved: {
description: 'something',
privileges: [
{
id: 'reserved',
privilege: {
catalogue: ['bar'],
management: {
kibana: ['hey-there'],
},
savedObject: {
all: [],
read: [],
},
ui: [],
app: [],
},
},
],
},
};
const featureRegistry = new FeatureRegistry();
expect(() =>
featureRegistry.registerKibanaFeature(feature)
).toThrowErrorMatchingInlineSnapshot(
`"Feature test-feature specifies management entries which are not granted to any privileges: kibana.hey"`
);
});
it('allows multiple reserved feature privileges to be registered', () => {
const feature: KibanaFeatureConfig = {
id: 'test-feature',
name: 'Test Feature',
app: [],
category: { id: 'foo', label: 'foo' },
privileges: null,
reserved: {
description: 'my reserved privileges',
privileges: [
{
id: 'a_reserved_1',
privilege: {
savedObject: {
all: [],
read: [],
},
ui: [],
app: [],
},
},
{
id: 'a_reserved_2',
privilege: {
savedObject: {
all: [],
read: [],
},
ui: [],
app: [],
},
},
],
},
};
const featureRegistry = new FeatureRegistry();
featureRegistry.registerKibanaFeature(feature);
const result = featureRegistry.getAllKibanaFeatures();
expect(result).toHaveLength(1);
expect(result[0].reserved?.privileges).toHaveLength(2);
});
it('does not allow reserved privilege ids to start with "reserved_"', () => {
const feature: KibanaFeatureConfig = {
id: 'test-feature',
name: 'Test Feature',
app: [],
category: { id: 'foo', label: 'foo' },
privileges: null,
reserved: {
description: 'my reserved privileges',
privileges: [
{
id: 'reserved_1',
privilege: {
savedObject: {
all: [],
read: [],
},
ui: [],
app: [],
},
},
],
},
};
const featureRegistry = new FeatureRegistry();
expect(() =>
featureRegistry.registerKibanaFeature(feature)
).toThrowErrorMatchingInlineSnapshot(
`"[reserved.privileges.0.id]: Does not satisfy regexp /^(?!reserved_)[a-zA-Z0-9_-]+$/"`
);
});
it('allows independent sub-feature privileges to register a minimumLicense', () => {
const feature1: KibanaFeatureConfig = {
id: 'test-feature',
name: 'Test Feature',
app: [],
category: { id: 'foo', label: 'foo' },
privileges: {
all: {
savedObject: {
all: [],
read: [],
},
ui: [],
},
read: {
savedObject: {
all: [],
read: [],
},
ui: [],
},
},
subFeatures: [
{
name: 'foo',
privilegeGroups: [
{
groupType: 'independent',
privileges: [
{
id: 'foo',
name: 'foo',
minimumLicense: 'platinum',
includeIn: 'all',
savedObject: {
all: [],
read: [],
},
ui: [],
},
],
},
],
},
],
};
const featureRegistry = new FeatureRegistry();
featureRegistry.registerKibanaFeature(feature1);
});
it('prevents mutually exclusive sub-feature privileges from registering a minimumLicense', () => {
const feature1: KibanaFeatureConfig = {
id: 'test-feature',
name: 'Test Feature',
app: [],
category: { id: 'foo', label: 'foo' },
privileges: {
all: {
savedObject: {
all: [],
read: [],
},
ui: [],
},
read: {
savedObject: {
all: [],
read: [],
},
ui: [],
},
},
subFeatures: [
{
name: 'foo',
privilegeGroups: [
{
groupType: 'mutually_exclusive',
privileges: [
{
id: 'foo',
name: 'foo',
minimumLicense: 'platinum',
includeIn: 'all',
savedObject: {
all: [],
read: [],
},
ui: [],
},
{
id: 'bar',
name: 'Bar',
minimumLicense: 'platinum',
includeIn: 'all',
savedObject: {
all: [],
read: [],
},
ui: [],
},
],
},
],
},
],
};
const featureRegistry = new FeatureRegistry();
expect(() => {
featureRegistry.registerKibanaFeature(feature1);
}).toThrowErrorMatchingInlineSnapshot(`
"[subFeatures.0.privilegeGroups.0]: types that failed validation:
- [subFeatures.0.privilegeGroups.0.0.privileges.0.minimumLicense]: a value wasn't expected to be present
- [subFeatures.0.privilegeGroups.0.1.groupType]: expected value to equal [independent]"
`);
});
it('cannot register feature after getAll has been called', () => {
const feature1: KibanaFeatureConfig = {
id: 'test-feature',
name: 'Test Feature',
app: [],
category: { id: 'foo', label: 'foo' },
privileges: null,
};
const feature2: KibanaFeatureConfig = {
id: 'test-feature-2',
name: 'Test Feature 2',
app: [],
category: { id: 'foo', label: 'foo' },
privileges: null,
};
const featureRegistry = new FeatureRegistry();
featureRegistry.registerKibanaFeature(feature1);
featureRegistry.getAllKibanaFeatures();
expect(() => {
featureRegistry.registerKibanaFeature(feature2);
}).toThrowErrorMatchingInlineSnapshot(
`"Features are locked, can't register new features. Attempt to register test-feature-2 failed."`
);
});
describe('#getAllKibanaFeatures', () => {
const features: KibanaFeatureConfig[] = [
{
id: 'gold-feature',
name: 'Test Feature',
app: [],
category: { id: 'foo', label: 'foo' },
minimumLicense: 'gold',
privileges: null,
},
{
id: 'unlicensed-feature',
name: 'Test Feature',
app: [],
category: { id: 'foo', label: 'foo' },
privileges: null,
},
{
id: 'with-sub-feature',
name: 'Test Feature',
app: [],
category: { id: 'foo', label: 'foo' },
privileges: {
all: { savedObject: { all: [], read: [] }, ui: [] },
read: { savedObject: { all: [], read: [] }, ui: [] },
},
minimumLicense: 'platinum',
subFeatures: [
{
name: 'licensed-sub-feature',
privilegeGroups: [
{
groupType: 'independent',
privileges: [
{
id: 'sub-feature',
includeIn: 'all',
minimumLicense: 'enterprise',
name: 'sub feature',
savedObject: { all: [], read: [] },
ui: [],
},
],
},
],
},
],
},
];
const registry = new FeatureRegistry();
features.forEach((f) => registry.registerKibanaFeature(f));
it('returns all features and sub-feature privileges by default', () => {
const result = registry.getAllKibanaFeatures();
expect(result).toHaveLength(3);
const [, , withSubFeature] = result;
expect(withSubFeature.subFeatures).toHaveLength(1);
expect(withSubFeature.subFeatures[0].privilegeGroups).toHaveLength(1);
expect(withSubFeature.subFeatures[0].privilegeGroups[0].privileges).toHaveLength(1);
});
it('returns features which are satisfied by the current license', () => {
const license = licensingMock.createLicense({ license: { type: 'gold' } });
const result = registry.getAllKibanaFeatures(license);
expect(result).toHaveLength(2);
const ids = result.map((f) => f.id);
expect(ids).toEqual(['gold-feature', 'unlicensed-feature']);
});
it('filters out sub-feature privileges which do not match the current license', () => {
const license = licensingMock.createLicense({ license: { type: 'platinum' } });
const result = registry.getAllKibanaFeatures(license);
expect(result).toHaveLength(3);
const ids = result.map((f) => f.id);
expect(ids).toEqual(['gold-feature', 'unlicensed-feature', 'with-sub-feature']);
const [, , withSubFeature] = result;
expect(withSubFeature.subFeatures).toHaveLength(1);
expect(withSubFeature.subFeatures[0].privilegeGroups).toHaveLength(1);
expect(withSubFeature.subFeatures[0].privilegeGroups[0].privileges).toHaveLength(0);
});
});
});
describe('Elasticsearch Features', () => {
it('allows a minimal feature to be registered', () => {
const feature: ElasticsearchFeatureConfig = {
id: 'test-feature',
privileges: [
{
requiredClusterPrivileges: ['all'],
ui: [],
},
],
};
const featureRegistry = new FeatureRegistry();
featureRegistry.registerElasticsearchFeature(feature);
const result = featureRegistry.getAllElasticsearchFeatures();
expect(result).toHaveLength(1);
// Should be the equal, but not the same instance (i.e., a defensive copy)
expect(result[0].toRaw()).not.toBe(feature);
expect(result[0].toRaw()).toEqual(feature);
});
it('allows a complex feature to ge registered', () => {
const feature: ElasticsearchFeatureConfig = {
id: 'test-feature',
management: {
kibana: ['foo'],
data: ['bar'],
},
catalogue: ['foo', 'bar'],
privileges: [
{
requiredClusterPrivileges: ['monitor', 'manage'],
requiredIndexPrivileges: {
foo: ['read'],
bar: ['all'],
baz: ['view_index_metadata'],
},
ui: ['ui_a'],
},
{
requiredClusterPrivileges: [],
requiredRoles: ['some_role'],
ui: ['ui_b'],
},
],
};
const featureRegistry = new FeatureRegistry();
featureRegistry.registerElasticsearchFeature(feature);
const result = featureRegistry.getAllElasticsearchFeatures();
expect(result).toHaveLength(1);
// Should be the equal, but not the same instance (i.e., a defensive copy)
expect(result[0].toRaw()).not.toBe(feature);
expect(result[0].toRaw()).toEqual(feature);
});
it('requires a value for privileges', () => {
const feature: ElasticsearchFeatureConfig = {
id: 'test-feature',
} as any;
const featureRegistry = new FeatureRegistry();
expect(() =>
featureRegistry.registerElasticsearchFeature(feature)
).toThrowErrorMatchingInlineSnapshot(
`"[privileges]: expected value of type [array] but got [undefined]"`
);
});
it('requires privileges to declare some form of required es privileges', () => {
const feature: ElasticsearchFeatureConfig = {
id: 'test-feature',
privileges: [
{
ui: [],
},
],
} as any;
const featureRegistry = new FeatureRegistry();
expect(() =>
featureRegistry.registerElasticsearchFeature(feature)
).toThrowErrorMatchingInlineSnapshot(
`"Feature test-feature has a privilege definition at index 0 without any privileges defined."`
);
});
it('does not allow duplicate privilege ids', () => {
const feature: ElasticsearchFeatureConfig = {
id: 'test-feature',
privileges: [
{
requiredClusterPrivileges: ['all'],
ui: [],
},
],
};
const featureRegistry = new FeatureRegistry();
featureRegistry.registerElasticsearchFeature(feature);
expect(() =>
featureRegistry.registerElasticsearchFeature(feature)
).toThrowErrorMatchingInlineSnapshot(`"Feature with id test-feature is already registered."`);
});
});
it('does not allow a Kibana feature to share an id with an Elasticsearch feature', () => {
const kibanaFeature: KibanaFeatureConfig = {
id: 'test-feature',
name: 'Test Feature',
app: [],
category: { id: 'foo', label: 'foo' },
privileges: null,
};
const elasticsearchFeature: ElasticsearchFeatureConfig = {
id: 'test-feature',
privileges: [
{
requiredClusterPrivileges: ['all'],
ui: [],
},
],
};
const featureRegistry = new FeatureRegistry();
featureRegistry.registerElasticsearchFeature(elasticsearchFeature);
expect(() =>
featureRegistry.registerKibanaFeature(kibanaFeature)
).toThrowErrorMatchingInlineSnapshot(`"Feature with id test-feature is already registered."`);
});
it('does not allow an Elasticsearch feature to share an id with a Kibana feature', () => {
const kibanaFeature: KibanaFeatureConfig = {
id: 'test-feature',
name: 'Test Feature',
app: [],
category: { id: 'foo', label: 'foo' },
privileges: null,
};
const elasticsearchFeature: ElasticsearchFeatureConfig = {
id: 'test-feature',
privileges: [
{
requiredClusterPrivileges: ['all'],
ui: [],
},
],
};
const featureRegistry = new FeatureRegistry();
featureRegistry.registerKibanaFeature(kibanaFeature);
expect(() =>
featureRegistry.registerElasticsearchFeature(elasticsearchFeature)
).toThrowErrorMatchingInlineSnapshot(`"Feature with id test-feature is already registered."`);
});
});