[Alerting] Split alerting feature privilege between rules and alerts and handle subfeature privilege specification (#100127)

* WIP - creating alerting authorization client factory and exposing authorization client on plugin start contract

* Updating alerting feature privilege builder to handle different alerting types

* Passing in alerting authorization type to AlertingActions class string builder

* Passing in authorization type in each function call

* Passing in exempt consumer ids. Adding authorization type to audit logger

* Changing alertType to ruleType

* Changing alertType to ruleType

* Updating unit tests

* Updating unit tests

* Passing field names into authorization query builder. Adding kql/es dsl option

* Converting to es query if requested

* Fixing functional tests

* Removing ability to specify feature privilege name in constructor

* Fixing some types and tests

* Consolidating alerting authorization kuery filter options

* Cleanup and tests

* Cleanup and tests

* Initial commit with changes needed for subfeature privilege

* Throwing error when AlertingAuthorizationClientFactory is not defined

* Renaming authorizationType to entity

* Renaming AlertsAuthorization to AlertingAuthorization

* Fixing unit tests

* Changing schema of alerting feature privilege

* Changing schema of alerting feature privilege

* Updating feature privilege iterator

* Updating feature privilege builder

* Fixing types check

* Updating privilege string terminology

* Updating privilege string terminology

* Wip

* Fixing unit tests

* Unit tests

* Updating README and removing stack subfeature privilege changes

* Fixing README

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
ymao1 2021-05-27 14:59:02 -04:00 committed by GitHub
parent f0e11bcd1b
commit 71379b755a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 1652 additions and 358 deletions

View file

@ -512,8 +512,14 @@ Array [
Object {
"privilege": Object {
"alerting": Object {
"all": Array [],
"read": Array [],
"alert": Object {
"all": Array [],
"read": Array [],
},
"rule": Object {
"all": Array [],
"read": Array [],
},
},
"api": Array [
"store_search_session",
@ -651,8 +657,14 @@ Array [
Object {
"privilege": Object {
"alerting": Object {
"all": Array [],
"read": Array [],
"alert": Object {
"all": Array [],
"read": Array [],
},
"rule": Object {
"all": Array [],
"read": Array [],
},
},
"api": Array [
"store_search_session",
@ -888,8 +900,14 @@ Array [
Object {
"privilege": Object {
"alerting": Object {
"all": Array [],
"read": Array [],
"alert": Object {
"all": Array [],
"read": Array [],
},
"rule": Object {
"all": Array [],
"read": Array [],
},
},
"api": Array [],
"app": Array [
@ -1010,8 +1028,14 @@ Array [
Object {
"privilege": Object {
"alerting": Object {
"all": Array [],
"read": Array [],
"alert": Object {
"all": Array [],
"read": Array [],
},
"rule": Object {
"all": Array [],
"read": Array [],
},
},
"api": Array [
"store_search_session",
@ -1149,8 +1173,14 @@ Array [
Object {
"privilege": Object {
"alerting": Object {
"all": Array [],
"read": Array [],
"alert": Object {
"all": Array [],
"read": Array [],
},
"rule": Object {
"all": Array [],
"read": Array [],
},
},
"api": Array [
"store_search_session",
@ -1386,8 +1416,14 @@ Array [
Object {
"privilege": Object {
"alerting": Object {
"all": Array [],
"read": Array [],
"alert": Object {
"all": Array [],
"read": Array [],
},
"rule": Object {
"all": Array [],
"read": Array [],
},
},
"api": Array [],
"app": Array [

View file

@ -46,8 +46,14 @@ describe('featurePrivilegeIterator', () => {
read: ['read-type'],
},
alerting: {
all: ['alerting-all-type'],
read: ['alerting-read-type'],
rule: {
all: ['alerting-all-type'],
read: ['alerting-read-type'],
},
alert: {
all: ['alerting-all-type'],
read: [],
},
},
ui: ['ui-action'],
},
@ -63,7 +69,12 @@ describe('featurePrivilegeIterator', () => {
read: ['read-type'],
},
alerting: {
read: ['alerting-read-type'],
rule: {
read: ['alerting-read-type'],
},
alert: {
read: ['alerting-read-type'],
},
},
ui: ['ui-action'],
},
@ -93,8 +104,14 @@ describe('featurePrivilegeIterator', () => {
read: ['read-type'],
},
alerting: {
all: ['alerting-all-type'],
read: ['alerting-read-type'],
rule: {
all: ['alerting-all-type'],
read: ['alerting-read-type'],
},
alert: {
all: ['alerting-all-type'],
read: [],
},
},
ui: ['ui-action'],
},
@ -113,7 +130,12 @@ describe('featurePrivilegeIterator', () => {
read: ['read-type'],
},
alerting: {
read: ['alerting-read-type'],
rule: {
read: ['alerting-read-type'],
},
alert: {
read: ['alerting-read-type'],
},
},
ui: ['ui-action'],
},
@ -139,8 +161,14 @@ describe('featurePrivilegeIterator', () => {
read: ['read-type'],
},
alerting: {
all: ['alerting-all-type'],
read: ['alerting-read-type'],
rule: {
all: ['alerting-all-type'],
read: ['alerting-read-type'],
},
alert: {
all: ['alerting-all-type'],
read: ['alerting-read-type-alerts'],
},
},
ui: ['ui-action'],
},
@ -156,7 +184,12 @@ describe('featurePrivilegeIterator', () => {
read: ['read-type'],
},
alerting: {
read: ['alerting-read-type'],
rule: {
read: ['alerting-read-type'],
},
alert: {
read: ['alerting-read-type'],
},
},
ui: ['ui-action'],
},
@ -187,8 +220,14 @@ describe('featurePrivilegeIterator', () => {
read: ['read-type'],
},
alerting: {
all: ['alerting-all-type'],
read: ['alerting-read-type'],
rule: {
all: ['alerting-all-type'],
read: ['alerting-read-type'],
},
alert: {
all: ['alerting-all-type'],
read: ['alerting-read-type-alerts'],
},
},
ui: ['ui-action'],
},
@ -212,11 +251,15 @@ describe('featurePrivilegeIterator', () => {
},
savedObject: {
all: ['all-type'],
read: ['read-type'],
read: [],
},
alerting: {
all: ['alerting-all-type'],
read: ['alerting-read-type'],
rule: {
all: ['alerting-all-type'],
},
alert: {
read: ['alerting-another-read-type'],
},
},
ui: ['ui-action'],
},
@ -232,7 +275,12 @@ describe('featurePrivilegeIterator', () => {
read: ['read-type'],
},
alerting: {
read: ['alerting-read-type'],
rule: {
read: ['alerting-read-type'],
},
alert: {
read: ['alerting-read-type'],
},
},
ui: ['ui-action'],
},
@ -259,8 +307,9 @@ describe('featurePrivilegeIterator', () => {
read: ['read-sub-type'],
},
alerting: {
all: ['alerting-all-sub-type'],
read: ['alerting-read-sub-type'],
alert: {
all: ['alerting-all-sub-type'],
},
},
ui: ['ui-sub-type'],
},
@ -290,11 +339,15 @@ describe('featurePrivilegeIterator', () => {
},
savedObject: {
all: ['all-type'],
read: ['read-type'],
read: [],
},
alerting: {
all: ['alerting-all-type'],
read: ['alerting-read-type'],
rule: {
all: ['alerting-all-type'],
},
alert: {
read: ['alerting-another-read-type'],
},
},
ui: ['ui-action'],
},
@ -313,7 +366,12 @@ describe('featurePrivilegeIterator', () => {
read: ['read-type'],
},
alerting: {
read: ['alerting-read-type'],
rule: {
read: ['alerting-read-type'],
},
alert: {
read: ['alerting-read-type'],
},
},
ui: ['ui-action'],
},
@ -340,8 +398,12 @@ describe('featurePrivilegeIterator', () => {
read: ['read-type'],
},
alerting: {
all: ['alerting-all-type'],
read: ['alerting-read-type'],
rule: {
all: ['alerting-all-type'],
},
alert: {
read: ['alerting-another-read-type'],
},
},
ui: ['ui-action'],
},
@ -357,7 +419,12 @@ describe('featurePrivilegeIterator', () => {
read: ['read-type'],
},
alerting: {
read: ['alerting-read-type'],
rule: {
read: ['alerting-read-type'],
},
alert: {
read: ['alerting-read-type'],
},
},
ui: ['ui-action'],
},
@ -384,8 +451,9 @@ describe('featurePrivilegeIterator', () => {
read: ['read-sub-type'],
},
alerting: {
all: ['alerting-all-sub-type'],
read: ['alerting-read-sub-type'],
alert: {
all: ['alerting-all-sub-type'],
},
},
ui: ['ui-sub-type'],
},
@ -418,8 +486,12 @@ describe('featurePrivilegeIterator', () => {
read: ['read-type'],
},
alerting: {
all: ['alerting-all-type'],
read: ['alerting-read-type'],
rule: {
all: ['alerting-all-type'],
},
alert: {
read: ['alerting-another-read-type'],
},
},
ui: ['ui-action'],
},
@ -438,7 +510,12 @@ describe('featurePrivilegeIterator', () => {
read: ['read-type'],
},
alerting: {
read: ['alerting-read-type'],
rule: {
read: ['alerting-read-type'],
},
alert: {
read: ['alerting-read-type'],
},
},
ui: ['ui-action'],
},
@ -465,8 +542,12 @@ describe('featurePrivilegeIterator', () => {
read: ['read-type'],
},
alerting: {
all: ['alerting-all-type'],
read: ['alerting-read-type'],
rule: {
all: ['alerting-all-type'],
},
alert: {
read: ['alerting-another-read-type'],
},
},
ui: ['ui-action'],
},
@ -482,7 +563,12 @@ describe('featurePrivilegeIterator', () => {
read: ['read-type'],
},
alerting: {
read: ['alerting-read-type'],
rule: {
read: ['alerting-read-type'],
},
alert: {
read: ['alerting-read-type'],
},
},
ui: ['ui-action'],
},
@ -510,8 +596,9 @@ describe('featurePrivilegeIterator', () => {
read: ['read-sub-type'],
},
alerting: {
all: ['alerting-all-sub-type'],
read: ['alerting-read-sub-type'],
alert: {
all: ['alerting-all-sub-type'],
},
},
ui: ['ui-sub-type'],
},
@ -545,8 +632,14 @@ describe('featurePrivilegeIterator', () => {
read: ['read-type', 'read-sub-type'],
},
alerting: {
all: ['alerting-all-type', 'alerting-all-sub-type'],
read: ['alerting-read-type', 'alerting-read-sub-type'],
rule: {
all: ['alerting-all-type'],
read: [],
},
alert: {
all: ['alerting-all-sub-type'],
read: ['alerting-another-read-type'],
},
},
ui: ['ui-action', 'ui-sub-type'],
},
@ -566,8 +659,14 @@ describe('featurePrivilegeIterator', () => {
read: ['read-type', 'read-sub-type'],
},
alerting: {
all: ['alerting-all-sub-type'],
read: ['alerting-read-type', 'alerting-read-sub-type'],
rule: {
all: [],
read: ['alerting-read-type'],
},
alert: {
all: ['alerting-all-sub-type'],
read: ['alerting-read-type'],
},
},
ui: ['ui-action', 'ui-sub-type'],
},
@ -594,8 +693,14 @@ describe('featurePrivilegeIterator', () => {
read: ['read-type'],
},
alerting: {
all: ['alerting-all-type'],
read: ['alerting-read-type'],
rule: {
all: ['alerting-all-type'],
read: ['alerting-read-type'],
},
alert: {
all: [],
read: ['alerting-read-type'],
},
},
ui: ['ui-action'],
},
@ -611,7 +716,12 @@ describe('featurePrivilegeIterator', () => {
read: ['read-type'],
},
alerting: {
read: ['alerting-read-type'],
rule: {
read: ['alerting-read-type'],
},
alert: {
read: ['alerting-read-type'],
},
},
ui: ['ui-action'],
},
@ -638,7 +748,9 @@ describe('featurePrivilegeIterator', () => {
read: ['read-type'],
},
alerting: {
read: ['alerting-read-type'],
alert: {
all: ['alerting-read-type'],
},
},
ui: ['ui-action'],
},
@ -671,8 +783,14 @@ describe('featurePrivilegeIterator', () => {
read: ['read-type'],
},
alerting: {
all: ['alerting-all-type'],
read: ['alerting-read-type'],
rule: {
all: ['alerting-all-type'],
read: ['alerting-read-type'],
},
alert: {
all: ['alerting-read-type'],
read: ['alerting-read-type'],
},
},
ui: ['ui-action'],
},
@ -691,8 +809,14 @@ describe('featurePrivilegeIterator', () => {
read: ['read-type'],
},
alerting: {
all: [],
read: ['alerting-read-type'],
rule: {
all: [],
read: ['alerting-read-type'],
},
alert: {
all: ['alerting-read-type'],
read: ['alerting-read-type'],
},
},
ui: ['ui-action'],
},
@ -719,8 +843,12 @@ describe('featurePrivilegeIterator', () => {
read: ['read-type'],
},
alerting: {
all: ['alerting-all-type'],
read: ['alerting-read-type'],
rule: {
all: ['alerting-all-type'],
},
alert: {
read: ['alerting-another-read-type'],
},
},
ui: ['ui-action'],
},
@ -736,7 +864,12 @@ describe('featurePrivilegeIterator', () => {
read: ['read-type'],
},
alerting: {
read: ['alerting-read-type'],
rule: {
read: ['alerting-read-type'],
},
alert: {
read: ['alerting-read-type'],
},
},
ui: ['ui-action'],
},
@ -764,8 +897,9 @@ describe('featurePrivilegeIterator', () => {
read: ['read-sub-type'],
},
alerting: {
all: ['alerting-all-sub-type'],
read: ['alerting-read-sub-type'],
alert: {
all: ['alerting-all-sub-type'],
},
},
ui: ['ui-sub-type'],
},
@ -799,8 +933,14 @@ describe('featurePrivilegeIterator', () => {
read: ['read-type', 'read-sub-type'],
},
alerting: {
all: ['alerting-all-type', 'alerting-all-sub-type'],
read: ['alerting-read-type', 'alerting-read-sub-type'],
rule: {
all: ['alerting-all-type'],
read: [],
},
alert: {
all: ['alerting-all-sub-type'],
read: ['alerting-another-read-type'],
},
},
ui: ['ui-action', 'ui-sub-type'],
},
@ -819,7 +959,12 @@ describe('featurePrivilegeIterator', () => {
read: ['read-type'],
},
alerting: {
read: ['alerting-read-type'],
rule: {
read: ['alerting-read-type'],
},
alert: {
read: ['alerting-read-type'],
},
},
ui: ['ui-action'],
},
@ -846,8 +991,12 @@ describe('featurePrivilegeIterator', () => {
read: ['read-type'],
},
alerting: {
all: ['alerting-all-type'],
read: ['alerting-read-type'],
rule: {
all: ['alerting-all-type'],
},
alert: {
read: ['alerting-another-read-type'],
},
},
ui: ['ui-action'],
},
@ -863,7 +1012,12 @@ describe('featurePrivilegeIterator', () => {
read: ['read-type'],
},
alerting: {
read: ['alerting-read-type'],
rule: {
read: ['alerting-read-type'],
},
alert: {
read: ['alerting-read-type'],
},
},
ui: ['ui-action'],
},
@ -892,8 +1046,9 @@ describe('featurePrivilegeIterator', () => {
read: ['read-sub-type'],
},
alerting: {
all: ['alerting-all-sub-type'],
read: ['alerting-read-sub-type'],
alert: {
all: ['alerting-all-sub-type'],
},
},
ui: ['ui-sub-type'],
},
@ -926,8 +1081,12 @@ describe('featurePrivilegeIterator', () => {
read: ['read-type'],
},
alerting: {
all: ['alerting-all-type'],
read: ['alerting-read-type'],
rule: {
all: ['alerting-all-type'],
},
alert: {
read: ['alerting-another-read-type'],
},
},
ui: ['ui-action'],
},
@ -946,7 +1105,12 @@ describe('featurePrivilegeIterator', () => {
read: ['read-type'],
},
alerting: {
read: ['alerting-read-type'],
rule: {
read: ['alerting-read-type'],
},
alert: {
read: ['alerting-read-type'],
},
},
ui: ['ui-action'],
},
@ -999,8 +1163,10 @@ describe('featurePrivilegeIterator', () => {
read: ['read-sub-type'],
},
alerting: {
all: ['alerting-all-sub-type'],
read: ['alerting-read-sub-type'],
rule: {
all: ['alerting-all-sub-type'],
read: ['alerting-read-sub-type'],
},
},
ui: ['ui-sub-type'],
},
@ -1034,8 +1200,14 @@ describe('featurePrivilegeIterator', () => {
read: ['read-sub-type'],
},
alerting: {
all: ['alerting-all-sub-type'],
read: ['alerting-read-sub-type'],
rule: {
all: ['alerting-all-sub-type'],
read: ['alerting-read-sub-type'],
},
alert: {
all: [],
read: [],
},
},
ui: ['ui-sub-type'],
},
@ -1055,8 +1227,14 @@ describe('featurePrivilegeIterator', () => {
read: ['read-sub-type'],
},
alerting: {
all: ['alerting-all-sub-type'],
read: ['alerting-read-sub-type'],
rule: {
all: ['alerting-all-sub-type'],
read: ['alerting-read-sub-type'],
},
alert: {
all: [],
read: [],
},
},
ui: ['ui-sub-type'],
},
@ -1083,8 +1261,12 @@ describe('featurePrivilegeIterator', () => {
read: ['read-type'],
},
alerting: {
all: ['alerting-all-type'],
read: ['alerting-read-type'],
rule: {
all: ['alerting-all-type'],
},
alert: {
read: ['alerting-another-read-type'],
},
},
ui: ['ui-action'],
},
@ -1100,7 +1282,12 @@ describe('featurePrivilegeIterator', () => {
read: ['read-type'],
},
alerting: {
read: ['alerting-read-type'],
rule: {
read: ['alerting-read-type'],
},
alert: {
read: ['alerting-read-type'],
},
},
ui: ['ui-action'],
},
@ -1151,8 +1338,14 @@ describe('featurePrivilegeIterator', () => {
read: ['read-type'],
},
alerting: {
all: ['alerting-all-type'],
read: ['alerting-read-type'],
rule: {
all: ['alerting-all-type'],
read: [],
},
alert: {
all: [],
read: ['alerting-another-read-type'],
},
},
ui: ['ui-action'],
},
@ -1171,8 +1364,14 @@ describe('featurePrivilegeIterator', () => {
read: ['read-type'],
},
alerting: {
all: [],
read: ['alerting-read-type'],
rule: {
all: [],
read: ['alerting-read-type'],
},
alert: {
all: [],
read: ['alerting-read-type'],
},
},
ui: ['ui-action'],
},

View file

@ -110,11 +110,26 @@ function mergeWithSubFeatures(
);
mergedConfig.alerting = {
all: mergeArrays(mergedConfig.alerting?.all ?? [], subFeaturePrivilege.alerting?.all ?? []),
read: mergeArrays(
mergedConfig.alerting?.read ?? [],
subFeaturePrivilege.alerting?.read ?? []
),
rule: {
all: mergeArrays(
mergedConfig.alerting?.rule?.all ?? [],
subFeaturePrivilege.alerting?.rule?.all ?? []
),
read: mergeArrays(
mergedConfig.alerting?.rule?.read ?? [],
subFeaturePrivilege.alerting?.rule?.read ?? []
),
},
alert: {
all: mergeArrays(
mergedConfig.alerting?.alert?.all ?? [],
subFeaturePrivilege.alerting?.alert?.all ?? []
),
read: mergeArrays(
mergedConfig.alerting?.alert?.read ?? [],
subFeaturePrivilege.alerting?.alert?.read ?? []
),
},
};
}
return mergedConfig;

View file

@ -828,7 +828,7 @@ describe('FeatureRegistry', () => {
);
});
it(`prevents privileges from specifying alerting entries that don't exist at the root level`, () => {
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',
@ -838,8 +838,10 @@ describe('FeatureRegistry', () => {
privileges: {
all: {
alerting: {
all: ['foo', 'bar'],
read: ['baz'],
rule: {
all: ['foo', 'bar'],
read: ['baz'],
},
},
savedObject: {
all: [],
@ -849,7 +851,11 @@ describe('FeatureRegistry', () => {
app: [],
},
read: {
alerting: { read: ['foo', 'bar', 'baz'] },
alerting: {
rule: {
read: ['foo', 'bar', 'baz'],
},
},
savedObject: {
all: [],
read: [],
@ -869,16 +875,21 @@ describe('FeatureRegistry', () => {
);
});
it(`prevents features from specifying alerting entries that don't exist at the privilege level`, () => {
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: ['foo', 'bar', 'baz'],
alerting: ['bar'],
privileges: {
all: {
alerting: { all: ['foo'] },
alerting: {
alert: {
all: ['foo', 'bar'],
read: ['baz'],
},
},
savedObject: {
all: [],
read: [],
@ -887,7 +898,57 @@ describe('FeatureRegistry', () => {
app: [],
},
read: {
alerting: { all: ['foo'] },
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: [],
@ -912,7 +973,11 @@ describe('FeatureRegistry', () => {
read: [],
},
ui: [],
alerting: { all: ['bar'] },
alerting: {
rule: {
all: ['bar'],
},
},
},
],
},
@ -930,7 +995,80 @@ describe('FeatureRegistry', () => {
);
});
it(`prevents reserved privileges from specifying alerting entries that don't exist at the root level`, () => {
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',
@ -944,7 +1082,11 @@ describe('FeatureRegistry', () => {
{
id: 'reserved',
privilege: {
alerting: { all: ['foo', 'bar', 'baz'] },
alerting: {
rule: {
all: ['foo', 'bar', 'baz'],
},
},
savedObject: {
all: [],
read: [],
@ -966,7 +1108,47 @@ describe('FeatureRegistry', () => {
);
});
it(`prevents features from specifying alerting entries that don't exist at the reserved privilege level`, () => {
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',
@ -980,7 +1162,51 @@ describe('FeatureRegistry', () => {
{
id: 'reserved',
privilege: {
alerting: { all: ['foo', 'bar'] },
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: [],

View file

@ -80,8 +80,18 @@ const kibanaPrivilegeSchema = schema.object({
app: schema.maybe(schema.arrayOf(schema.string())),
alerting: schema.maybe(
schema.object({
all: schema.maybe(alertingSchema),
read: schema.maybe(alertingSchema),
rule: schema.maybe(
schema.object({
all: schema.maybe(alertingSchema),
read: schema.maybe(alertingSchema),
})
),
alert: schema.maybe(
schema.object({
all: schema.maybe(alertingSchema),
read: schema.maybe(alertingSchema),
})
),
})
),
savedObject: schema.object({
@ -106,8 +116,18 @@ const kibanaIndependentSubFeaturePrivilegeSchema = schema.object({
catalogue: schema.maybe(catalogueSchema),
alerting: schema.maybe(
schema.object({
all: schema.maybe(alertingSchema),
read: schema.maybe(alertingSchema),
rule: schema.maybe(
schema.object({
all: schema.maybe(alertingSchema),
read: schema.maybe(alertingSchema),
})
),
alert: schema.maybe(
schema.object({
all: schema.maybe(alertingSchema),
read: schema.maybe(alertingSchema),
})
),
})
),
api: schema.maybe(schema.arrayOf(schema.string())),
@ -274,8 +294,8 @@ export function validateKibanaFeature(feature: KibanaFeatureConfig) {
}
function validateAlertingEntry(privilegeId: string, entry: FeatureKibanaPrivileges['alerting']) {
const all = entry?.all ?? [];
const read = entry?.read ?? [];
const all: string[] = [...(entry?.rule?.all ?? []), ...(entry?.alert?.all ?? [])];
const read: string[] = [...(entry?.rule?.read ?? []), ...(entry?.alert?.read ?? [])];
all.forEach((privilegeAlertTypes) => unseenAlertTypes.delete(privilegeAlertTypes));
read.forEach((privilegeAlertTypes) => unseenAlertTypes.delete(privilegeAlertTypes));