[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>
This commit is contained in:
Jonathan Buttner 2021-06-07 09:37:11 -04:00 committed by GitHub
parent 999faecd2c
commit b6c982c3b0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
487 changed files with 33107 additions and 17281 deletions

View file

@ -528,6 +528,10 @@ Array [
"dashboards",
"kibana",
],
"cases": Object {
"all": Array [],
"read": Array [],
},
"catalogue": Array [
"dashboard",
],
@ -673,6 +677,10 @@ Array [
"discover",
"kibana",
],
"cases": Object {
"all": Array [],
"read": Array [],
},
"catalogue": Array [
"discover",
],
@ -915,6 +923,10 @@ Array [
"lens",
"kibana",
],
"cases": Object {
"all": Array [],
"read": Array [],
},
"catalogue": Array [
"visualize",
],
@ -1044,6 +1056,10 @@ Array [
"dashboards",
"kibana",
],
"cases": Object {
"all": Array [],
"read": Array [],
},
"catalogue": Array [
"dashboard",
],
@ -1189,6 +1205,10 @@ Array [
"discover",
"kibana",
],
"cases": Object {
"all": Array [],
"read": Array [],
},
"catalogue": Array [
"discover",
],
@ -1431,6 +1451,10 @@ Array [
"lens",
"kibana",
],
"cases": Object {
"all": Array [],
"read": Array [],
},
"catalogue": Array [
"visualize",
],

View file

@ -55,6 +55,10 @@ describe('featurePrivilegeIterator', () => {
read: [],
},
},
cases: {
all: ['cases-all-type'],
read: ['cases-read-type'],
},
ui: ['ui-action'],
},
read: {
@ -76,6 +80,9 @@ describe('featurePrivilegeIterator', () => {
read: ['alerting-read-type'],
},
},
cases: {
read: ['cases-read-type'],
},
ui: ['ui-action'],
},
},
@ -113,6 +120,10 @@ describe('featurePrivilegeIterator', () => {
read: [],
},
},
cases: {
all: ['cases-all-type'],
read: ['cases-read-type'],
},
ui: ['ui-action'],
},
},
@ -137,6 +148,9 @@ describe('featurePrivilegeIterator', () => {
read: ['alerting-read-type'],
},
},
cases: {
read: ['cases-read-type'],
},
ui: ['ui-action'],
},
},
@ -170,6 +184,10 @@ describe('featurePrivilegeIterator', () => {
read: ['alerting-read-type-alerts'],
},
},
cases: {
all: ['cases-all-type'],
read: ['cases-read-type'],
},
ui: ['ui-action'],
},
read: {
@ -191,6 +209,9 @@ describe('featurePrivilegeIterator', () => {
read: ['alerting-read-type'],
},
},
cases: {
read: ['cases-read-type'],
},
ui: ['ui-action'],
},
},
@ -229,6 +250,10 @@ describe('featurePrivilegeIterator', () => {
read: ['alerting-read-type-alerts'],
},
},
cases: {
all: ['cases-all-type'],
read: ['cases-read-type'],
},
ui: ['ui-action'],
},
},
@ -261,6 +286,10 @@ describe('featurePrivilegeIterator', () => {
read: ['alerting-another-read-type'],
},
},
cases: {
all: ['cases-all-type'],
read: ['cases-read-type'],
},
ui: ['ui-action'],
},
read: {
@ -282,6 +311,9 @@ describe('featurePrivilegeIterator', () => {
read: ['alerting-read-type'],
},
},
cases: {
read: ['cases-read-type'],
},
ui: ['ui-action'],
},
},
@ -311,6 +343,10 @@ describe('featurePrivilegeIterator', () => {
all: ['alerting-all-sub-type'],
},
},
cases: {
all: ['cases-all-sub-type'],
read: ['cases-read-sub-type'],
},
ui: ['ui-sub-type'],
},
],
@ -349,6 +385,10 @@ describe('featurePrivilegeIterator', () => {
read: ['alerting-another-read-type'],
},
},
cases: {
all: ['cases-all-type'],
read: ['cases-read-type'],
},
ui: ['ui-action'],
},
},
@ -373,6 +413,9 @@ describe('featurePrivilegeIterator', () => {
read: ['alerting-read-type'],
},
},
cases: {
read: ['cases-read-type'],
},
ui: ['ui-action'],
},
},
@ -405,6 +448,10 @@ describe('featurePrivilegeIterator', () => {
read: ['alerting-another-read-type'],
},
},
cases: {
all: ['cases-all-type'],
read: ['cases-read-type'],
},
ui: ['ui-action'],
},
read: {
@ -426,6 +473,9 @@ describe('featurePrivilegeIterator', () => {
read: ['alerting-read-type'],
},
},
cases: {
read: ['cases-read-type'],
},
ui: ['ui-action'],
},
},
@ -455,6 +505,10 @@ describe('featurePrivilegeIterator', () => {
all: ['alerting-all-sub-type'],
},
},
cases: {
all: ['cases-all-sub-type'],
read: ['cases-read-sub-type'],
},
ui: ['ui-sub-type'],
},
],
@ -493,6 +547,10 @@ describe('featurePrivilegeIterator', () => {
read: ['alerting-another-read-type'],
},
},
cases: {
all: ['cases-all-type'],
read: ['cases-read-type'],
},
ui: ['ui-action'],
},
},
@ -517,6 +575,9 @@ describe('featurePrivilegeIterator', () => {
read: ['alerting-read-type'],
},
},
cases: {
read: ['cases-read-type'],
},
ui: ['ui-action'],
},
},
@ -549,6 +610,10 @@ describe('featurePrivilegeIterator', () => {
read: ['alerting-another-read-type'],
},
},
cases: {
all: ['cases-all-type'],
read: ['cases-read-type'],
},
ui: ['ui-action'],
},
read: {
@ -570,6 +635,9 @@ describe('featurePrivilegeIterator', () => {
read: ['alerting-read-type'],
},
},
cases: {
read: ['cases-read-type'],
},
ui: ['ui-action'],
},
},
@ -600,6 +668,10 @@ describe('featurePrivilegeIterator', () => {
all: ['alerting-all-sub-type'],
},
},
cases: {
all: ['cases-all-sub-type'],
read: ['cases-read-sub-type'],
},
ui: ['ui-sub-type'],
},
],
@ -641,6 +713,10 @@ describe('featurePrivilegeIterator', () => {
read: ['alerting-another-read-type'],
},
},
cases: {
all: ['cases-all-type', 'cases-all-sub-type'],
read: ['cases-read-type', 'cases-read-sub-type'],
},
ui: ['ui-action', 'ui-sub-type'],
},
},
@ -668,6 +744,10 @@ describe('featurePrivilegeIterator', () => {
read: ['alerting-read-type'],
},
},
cases: {
all: ['cases-all-sub-type'],
read: ['cases-read-type', 'cases-read-sub-type'],
},
ui: ['ui-action', 'ui-sub-type'],
},
},
@ -702,6 +782,10 @@ describe('featurePrivilegeIterator', () => {
read: ['alerting-read-type'],
},
},
cases: {
all: ['cases-all-type'],
read: ['cases-read-type'],
},
ui: ['ui-action'],
},
read: {
@ -723,6 +807,9 @@ describe('featurePrivilegeIterator', () => {
read: ['alerting-read-type'],
},
},
cases: {
read: ['cases-read-type'],
},
ui: ['ui-action'],
},
},
@ -752,6 +839,9 @@ describe('featurePrivilegeIterator', () => {
all: ['alerting-read-type'],
},
},
cases: {
read: ['cases-read-type'],
},
ui: ['ui-action'],
},
],
@ -792,6 +882,10 @@ describe('featurePrivilegeIterator', () => {
read: ['alerting-read-type'],
},
},
cases: {
all: ['cases-all-type'],
read: ['cases-read-type'],
},
ui: ['ui-action'],
},
},
@ -818,6 +912,10 @@ describe('featurePrivilegeIterator', () => {
read: ['alerting-read-type'],
},
},
cases: {
all: [],
read: ['cases-read-type'],
},
ui: ['ui-action'],
},
},
@ -850,6 +948,10 @@ describe('featurePrivilegeIterator', () => {
read: ['alerting-another-read-type'],
},
},
cases: {
all: ['cases-all-type'],
read: ['cases-read-type'],
},
ui: ['ui-action'],
},
read: {
@ -871,6 +973,9 @@ describe('featurePrivilegeIterator', () => {
read: ['alerting-read-type'],
},
},
cases: {
read: ['cases-read-type'],
},
ui: ['ui-action'],
},
},
@ -901,6 +1006,10 @@ describe('featurePrivilegeIterator', () => {
all: ['alerting-all-sub-type'],
},
},
cases: {
all: ['cases-all-sub-type'],
read: ['cases-read-sub-type'],
},
ui: ['ui-sub-type'],
},
],
@ -942,6 +1051,10 @@ describe('featurePrivilegeIterator', () => {
read: ['alerting-another-read-type'],
},
},
cases: {
all: ['cases-all-type', 'cases-all-sub-type'],
read: ['cases-read-type', 'cases-read-sub-type'],
},
ui: ['ui-action', 'ui-sub-type'],
},
},
@ -966,6 +1079,9 @@ describe('featurePrivilegeIterator', () => {
read: ['alerting-read-type'],
},
},
cases: {
read: ['cases-read-type'],
},
ui: ['ui-action'],
},
},
@ -998,6 +1114,10 @@ describe('featurePrivilegeIterator', () => {
read: ['alerting-another-read-type'],
},
},
cases: {
all: ['cases-all-type'],
read: ['cases-read-type'],
},
ui: ['ui-action'],
},
read: {
@ -1019,6 +1139,9 @@ describe('featurePrivilegeIterator', () => {
read: ['alerting-read-type'],
},
},
cases: {
read: ['cases-read-type'],
},
ui: ['ui-action'],
},
},
@ -1050,6 +1173,10 @@ describe('featurePrivilegeIterator', () => {
all: ['alerting-all-sub-type'],
},
},
cases: {
all: ['cases-all-sub-type'],
read: ['cases-read-sub-type'],
},
ui: ['ui-sub-type'],
},
],
@ -1088,6 +1215,10 @@ describe('featurePrivilegeIterator', () => {
read: ['alerting-another-read-type'],
},
},
cases: {
all: ['cases-all-type'],
read: ['cases-read-type'],
},
ui: ['ui-action'],
},
},
@ -1112,6 +1243,9 @@ describe('featurePrivilegeIterator', () => {
read: ['alerting-read-type'],
},
},
cases: {
read: ['cases-read-type'],
},
ui: ['ui-action'],
},
},
@ -1168,6 +1302,10 @@ describe('featurePrivilegeIterator', () => {
read: ['alerting-read-sub-type'],
},
},
cases: {
all: ['cases-all-sub-type'],
read: ['cases-read-sub-type'],
},
ui: ['ui-sub-type'],
},
],
@ -1209,6 +1347,10 @@ describe('featurePrivilegeIterator', () => {
read: [],
},
},
cases: {
all: ['cases-all-sub-type'],
read: ['cases-read-sub-type'],
},
ui: ['ui-sub-type'],
},
},
@ -1236,6 +1378,10 @@ describe('featurePrivilegeIterator', () => {
read: [],
},
},
cases: {
all: ['cases-all-sub-type'],
read: ['cases-read-sub-type'],
},
ui: ['ui-sub-type'],
},
},
@ -1268,6 +1414,10 @@ describe('featurePrivilegeIterator', () => {
read: ['alerting-another-read-type'],
},
},
cases: {
all: ['cases-all-type'],
read: ['cases-read-type'],
},
ui: ['ui-action'],
},
read: {
@ -1289,6 +1439,9 @@ describe('featurePrivilegeIterator', () => {
read: ['alerting-read-type'],
},
},
cases: {
read: ['cases-read-type'],
},
ui: ['ui-action'],
},
},
@ -1347,6 +1500,10 @@ describe('featurePrivilegeIterator', () => {
read: ['alerting-another-read-type'],
},
},
cases: {
all: ['cases-all-type'],
read: ['cases-read-type'],
},
ui: ['ui-action'],
},
},
@ -1373,6 +1530,10 @@ describe('featurePrivilegeIterator', () => {
read: ['alerting-read-type'],
},
},
cases: {
all: [],
read: ['cases-read-type'],
},
ui: ['ui-action'],
},
},

View file

@ -131,6 +131,11 @@ function mergeWithSubFeatures(
),
},
};
mergedConfig.cases = {
all: mergeArrays(mergedConfig.cases?.all ?? [], subFeaturePrivilege.cases?.all ?? []),
read: mergeArrays(mergedConfig.cases?.read ?? [], subFeaturePrivilege.cases?.read ?? []),
};
}
return mergedConfig;
}

View file

@ -1228,6 +1228,180 @@ describe('FeatureRegistry', () => {
);
});
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',

View file

@ -63,6 +63,7 @@ const managementSchema = schema.recordOf(
);
const catalogueSchema = listOfCapabilitiesSchema;
const alertingSchema = schema.arrayOf(schema.string());
const casesSchema = schema.arrayOf(schema.string());
const appCategorySchema = schema.object({
id: schema.string(),
@ -94,6 +95,12 @@ const kibanaPrivilegeSchema = schema.object({
),
})
),
cases: schema.maybe(
schema.object({
all: schema.maybe(casesSchema),
read: schema.maybe(casesSchema),
})
),
savedObject: schema.object({
all: schema.arrayOf(schema.string()),
read: schema.arrayOf(schema.string()),
@ -130,6 +137,12 @@ const kibanaIndependentSubFeaturePrivilegeSchema = schema.object({
),
})
),
cases: schema.maybe(
schema.object({
all: schema.maybe(casesSchema),
read: schema.maybe(casesSchema),
})
),
api: schema.maybe(schema.arrayOf(schema.string())),
app: schema.maybe(schema.arrayOf(schema.string())),
savedObject: schema.object({
@ -187,6 +200,7 @@ const kibanaFeatureSchema = schema.object({
management: schema.maybe(managementSchema),
catalogue: schema.maybe(catalogueSchema),
alerting: schema.maybe(alertingSchema),
cases: schema.maybe(casesSchema),
privileges: schema.oneOf([
schema.literal(null),
schema.object({
@ -252,7 +266,7 @@ export function validateKibanaFeature(feature: KibanaFeatureConfig) {
kibanaFeatureSchema.validate(feature);
// the following validation can't be enforced by the Joi schema, since it'd require us looking "up" the object graph for the list of valid value, which they explicitly forbid.
const { app = [], management = {}, catalogue = [], alerting = [] } = feature;
const { app = [], management = {}, catalogue = [], alerting = [], cases = [] } = feature;
const unseenApps = new Set(app);
@ -267,6 +281,8 @@ export function validateKibanaFeature(feature: KibanaFeatureConfig) {
const unseenAlertTypes = new Set(alerting);
const unseenCasesTypes = new Set(cases);
function validateAppEntry(privilegeId: string, entry: readonly string[] = []) {
entry.forEach((privilegeApp) => unseenApps.delete(privilegeApp));
@ -310,6 +326,23 @@ export function validateKibanaFeature(feature: KibanaFeatureConfig) {
}
}
function validateCasesEntry(privilegeId: string, entry: FeatureKibanaPrivileges['cases']) {
const all = entry?.all ?? [];
const read = entry?.read ?? [];
all.forEach((privilegeCasesTypes) => unseenCasesTypes.delete(privilegeCasesTypes));
read.forEach((privilegeCasesTypes) => unseenCasesTypes.delete(privilegeCasesTypes));
const unknownCasesEntries = difference([...all, ...read], cases);
if (unknownCasesEntries.length > 0) {
throw new Error(
`Feature privilege ${
feature.id
}.${privilegeId} has unknown cases entries: ${unknownCasesEntries.join(', ')}`
);
}
}
function validateManagementEntry(
privilegeId: string,
managementEntry: Record<string, readonly string[]> = {}
@ -371,6 +404,7 @@ export function validateKibanaFeature(feature: KibanaFeatureConfig) {
validateManagementEntry(privilegeId, privilegeDefinition.management);
validateAlertingEntry(privilegeId, privilegeDefinition.alerting);
validateCasesEntry(privilegeId, privilegeDefinition.cases);
});
const subFeatureEntries = feature.subFeatures ?? [];
@ -381,6 +415,7 @@ export function validateKibanaFeature(feature: KibanaFeatureConfig) {
validateCatalogueEntry(subFeaturePrivilege.id, subFeaturePrivilege.catalogue);
validateManagementEntry(subFeaturePrivilege.id, subFeaturePrivilege.management);
validateAlertingEntry(subFeaturePrivilege.id, subFeaturePrivilege.alerting);
validateCasesEntry(subFeaturePrivilege.id, subFeaturePrivilege.cases);
});
});
});
@ -431,6 +466,16 @@ export function validateKibanaFeature(feature: KibanaFeatureConfig) {
).join(',')}`
);
}
if (unseenCasesTypes.size > 0) {
throw new Error(
`Feature ${
feature.id
} specifies cases entries which are not granted to any privileges: ${Array.from(
unseenCasesTypes.values()
).join(',')}`
);
}
}
export function validateElasticsearchFeature(feature: ElasticsearchFeatureConfig) {