mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
Add support for licensed sub feature privileges (#80905)
This commit is contained in:
parent
bc3bb2afa8
commit
fe33579272
22 changed files with 1641 additions and 55 deletions
|
@ -198,7 +198,10 @@ server.route({
|
|||
=== Example 3: Discover
|
||||
|
||||
Discover takes advantage of subfeature privileges to allow fine-grained access control. In this example,
|
||||
a single "Create Short URLs" subfeature privilege is defined, which allows users to grant access to this feature without having to grant the `all` privilege to Discover. In other words, you can grant `read` access to Discover, and also grant the ability to create short URLs.
|
||||
two subfeature privileges are defined: "Create Short URLs", and "Generate PDF Reports". These allow users to grant access to this feature without having to grant the `all` privilege to Discover. In other words, you can grant `read` access to Discover, and also grant the ability to create short URLs or generate PDF reports.
|
||||
|
||||
Notice the "Generate PDF Reports" subfeature privilege has an additional `minimumPrivilege` option. Kibana will only offer this subfeature privilege if the
|
||||
license requirement is satisfied.
|
||||
|
||||
["source","javascript"]
|
||||
-----------
|
||||
|
@ -259,6 +262,28 @@ public setup(core, { features }) {
|
|||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
groupType: 'independent',
|
||||
privileges: [
|
||||
{
|
||||
id: 'pdf_generate',
|
||||
name: i18n.translate(
|
||||
'xpack.features.ossFeatures.discoverGeneratePDFReportsPrivilegeName',
|
||||
{
|
||||
defaultMessage: 'Generate PDF Reports',
|
||||
}
|
||||
),
|
||||
minimumLicense: 'platinum',
|
||||
includeIn: 'all',
|
||||
savedObject: {
|
||||
all: [],
|
||||
read: [],
|
||||
},
|
||||
api: ['generatePDFReports'],
|
||||
ui: ['generatePDFReports'],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
|
||||
import { RecursiveReadonly } from '@kbn/utility-types';
|
||||
import { LicenseType } from '../../licensing/common/types';
|
||||
import { FeatureKibanaPrivileges } from './feature_kibana_privileges';
|
||||
|
||||
/**
|
||||
|
@ -68,6 +69,13 @@ export interface SubFeaturePrivilegeConfig
|
|||
* `read` is also included in `all` automatically.
|
||||
*/
|
||||
includeIn: 'all' | 'read' | 'none';
|
||||
|
||||
/**
|
||||
* The minimum supported license level for this sub-feature privilege.
|
||||
* If no license level is supplied, then this privilege will be available for all licences
|
||||
* that are valid for the overall feature.
|
||||
*/
|
||||
minimumLicense?: LicenseType;
|
||||
}
|
||||
|
||||
export class SubFeature {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`buildOSSFeatures returns the advancedSettings feature augmented with appropriate sub feature privileges 1`] = `
|
||||
exports[`buildOSSFeatures with a basic license returns the advancedSettings feature augmented with appropriate sub feature privileges 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"privilege": Object {
|
||||
|
@ -51,7 +51,7 @@ Array [
|
|||
]
|
||||
`;
|
||||
|
||||
exports[`buildOSSFeatures returns the dashboard feature augmented with appropriate sub feature privileges 1`] = `
|
||||
exports[`buildOSSFeatures with a basic license returns the dashboard feature augmented with appropriate sub feature privileges 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"privilege": Object {
|
||||
|
@ -128,7 +128,7 @@ Array [
|
|||
]
|
||||
`;
|
||||
|
||||
exports[`buildOSSFeatures returns the dev_tools feature augmented with appropriate sub feature privileges 1`] = `
|
||||
exports[`buildOSSFeatures with a basic license returns the dev_tools feature augmented with appropriate sub feature privileges 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"privilege": Object {
|
||||
|
@ -182,7 +182,7 @@ Array [
|
|||
]
|
||||
`;
|
||||
|
||||
exports[`buildOSSFeatures returns the discover feature augmented with appropriate sub feature privileges 1`] = `
|
||||
exports[`buildOSSFeatures with a basic license returns the discover feature augmented with appropriate sub feature privileges 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"privilege": Object {
|
||||
|
@ -243,7 +243,7 @@ Array [
|
|||
]
|
||||
`;
|
||||
|
||||
exports[`buildOSSFeatures returns the indexPatterns feature augmented with appropriate sub feature privileges 1`] = `
|
||||
exports[`buildOSSFeatures with a basic license returns the indexPatterns feature augmented with appropriate sub feature privileges 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"privilege": Object {
|
||||
|
@ -296,7 +296,7 @@ Array [
|
|||
]
|
||||
`;
|
||||
|
||||
exports[`buildOSSFeatures returns the savedObjectsManagement feature augmented with appropriate sub feature privileges 1`] = `
|
||||
exports[`buildOSSFeatures with a basic license returns the savedObjectsManagement feature augmented with appropriate sub feature privileges 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"privilege": Object {
|
||||
|
@ -363,7 +363,7 @@ Array [
|
|||
]
|
||||
`;
|
||||
|
||||
exports[`buildOSSFeatures returns the timelion feature augmented with appropriate sub feature privileges 1`] = `
|
||||
exports[`buildOSSFeatures with a basic license returns the timelion feature augmented with appropriate sub feature privileges 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"privilege": Object {
|
||||
|
@ -411,7 +411,489 @@ Array [
|
|||
]
|
||||
`;
|
||||
|
||||
exports[`buildOSSFeatures returns the visualize feature augmented with appropriate sub feature privileges 1`] = `
|
||||
exports[`buildOSSFeatures with a basic license returns the visualize feature augmented with appropriate sub feature privileges 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"privilege": Object {
|
||||
"alerting": Object {
|
||||
"all": Array [],
|
||||
"read": Array [],
|
||||
},
|
||||
"api": Array [],
|
||||
"app": Array [
|
||||
"visualize",
|
||||
"lens",
|
||||
"kibana",
|
||||
],
|
||||
"catalogue": Array [
|
||||
"visualize",
|
||||
],
|
||||
"management": Object {},
|
||||
"savedObject": Object {
|
||||
"all": Array [
|
||||
"visualization",
|
||||
"query",
|
||||
"lens",
|
||||
"url",
|
||||
],
|
||||
"read": Array [
|
||||
"index-pattern",
|
||||
"search",
|
||||
"tag",
|
||||
],
|
||||
},
|
||||
"ui": Array [
|
||||
"show",
|
||||
"delete",
|
||||
"save",
|
||||
"saveQuery",
|
||||
"createShortUrl",
|
||||
],
|
||||
},
|
||||
"privilegeId": "all",
|
||||
},
|
||||
Object {
|
||||
"privilege": Object {
|
||||
"app": Array [
|
||||
"visualize",
|
||||
"lens",
|
||||
"kibana",
|
||||
],
|
||||
"catalogue": Array [
|
||||
"visualize",
|
||||
],
|
||||
"savedObject": Object {
|
||||
"all": Array [],
|
||||
"read": Array [
|
||||
"index-pattern",
|
||||
"search",
|
||||
"visualization",
|
||||
"query",
|
||||
"lens",
|
||||
"tag",
|
||||
],
|
||||
},
|
||||
"ui": Array [
|
||||
"show",
|
||||
],
|
||||
},
|
||||
"privilegeId": "read",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`buildOSSFeatures with a enterprise license returns the advancedSettings feature augmented with appropriate sub feature privileges 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"privilege": Object {
|
||||
"app": Array [
|
||||
"kibana",
|
||||
],
|
||||
"catalogue": Array [
|
||||
"advanced_settings",
|
||||
],
|
||||
"management": Object {
|
||||
"kibana": Array [
|
||||
"settings",
|
||||
],
|
||||
},
|
||||
"savedObject": Object {
|
||||
"all": Array [
|
||||
"config",
|
||||
],
|
||||
"read": Array [],
|
||||
},
|
||||
"ui": Array [
|
||||
"save",
|
||||
],
|
||||
},
|
||||
"privilegeId": "all",
|
||||
},
|
||||
Object {
|
||||
"privilege": Object {
|
||||
"app": Array [
|
||||
"kibana",
|
||||
],
|
||||
"catalogue": Array [
|
||||
"advanced_settings",
|
||||
],
|
||||
"management": Object {
|
||||
"kibana": Array [
|
||||
"settings",
|
||||
],
|
||||
},
|
||||
"savedObject": Object {
|
||||
"all": Array [],
|
||||
"read": Array [],
|
||||
},
|
||||
"ui": Array [],
|
||||
},
|
||||
"privilegeId": "read",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`buildOSSFeatures with a enterprise license returns the dashboard feature augmented with appropriate sub feature privileges 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"privilege": Object {
|
||||
"alerting": Object {
|
||||
"all": Array [],
|
||||
"read": Array [],
|
||||
},
|
||||
"api": Array [],
|
||||
"app": Array [
|
||||
"dashboards",
|
||||
"kibana",
|
||||
],
|
||||
"catalogue": Array [
|
||||
"dashboard",
|
||||
],
|
||||
"management": Object {},
|
||||
"savedObject": Object {
|
||||
"all": Array [
|
||||
"dashboard",
|
||||
"query",
|
||||
"url",
|
||||
],
|
||||
"read": Array [
|
||||
"index-pattern",
|
||||
"search",
|
||||
"visualization",
|
||||
"timelion-sheet",
|
||||
"canvas-workpad",
|
||||
"lens",
|
||||
"map",
|
||||
"tag",
|
||||
],
|
||||
},
|
||||
"ui": Array [
|
||||
"createNew",
|
||||
"show",
|
||||
"showWriteControls",
|
||||
"saveQuery",
|
||||
"createShortUrl",
|
||||
],
|
||||
},
|
||||
"privilegeId": "all",
|
||||
},
|
||||
Object {
|
||||
"privilege": Object {
|
||||
"app": Array [
|
||||
"dashboards",
|
||||
"kibana",
|
||||
],
|
||||
"catalogue": Array [
|
||||
"dashboard",
|
||||
],
|
||||
"savedObject": Object {
|
||||
"all": Array [],
|
||||
"read": Array [
|
||||
"index-pattern",
|
||||
"search",
|
||||
"visualization",
|
||||
"timelion-sheet",
|
||||
"canvas-workpad",
|
||||
"lens",
|
||||
"map",
|
||||
"dashboard",
|
||||
"query",
|
||||
"tag",
|
||||
],
|
||||
},
|
||||
"ui": Array [
|
||||
"show",
|
||||
],
|
||||
},
|
||||
"privilegeId": "read",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`buildOSSFeatures with a enterprise license returns the dev_tools feature augmented with appropriate sub feature privileges 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"privilege": Object {
|
||||
"api": Array [
|
||||
"console",
|
||||
],
|
||||
"app": Array [
|
||||
"dev_tools",
|
||||
"kibana",
|
||||
],
|
||||
"catalogue": Array [
|
||||
"console",
|
||||
"searchprofiler",
|
||||
"grokdebugger",
|
||||
],
|
||||
"savedObject": Object {
|
||||
"all": Array [],
|
||||
"read": Array [],
|
||||
},
|
||||
"ui": Array [
|
||||
"show",
|
||||
"save",
|
||||
],
|
||||
},
|
||||
"privilegeId": "all",
|
||||
},
|
||||
Object {
|
||||
"privilege": Object {
|
||||
"api": Array [
|
||||
"console",
|
||||
],
|
||||
"app": Array [
|
||||
"dev_tools",
|
||||
"kibana",
|
||||
],
|
||||
"catalogue": Array [
|
||||
"console",
|
||||
"searchprofiler",
|
||||
"grokdebugger",
|
||||
],
|
||||
"savedObject": Object {
|
||||
"all": Array [],
|
||||
"read": Array [],
|
||||
},
|
||||
"ui": Array [
|
||||
"show",
|
||||
],
|
||||
},
|
||||
"privilegeId": "read",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`buildOSSFeatures with a enterprise license returns the discover feature augmented with appropriate sub feature privileges 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"privilege": Object {
|
||||
"alerting": Object {
|
||||
"all": Array [],
|
||||
"read": Array [],
|
||||
},
|
||||
"api": Array [],
|
||||
"app": Array [
|
||||
"discover",
|
||||
"kibana",
|
||||
],
|
||||
"catalogue": Array [
|
||||
"discover",
|
||||
],
|
||||
"management": Object {},
|
||||
"savedObject": Object {
|
||||
"all": Array [
|
||||
"search",
|
||||
"query",
|
||||
"index-pattern",
|
||||
"url",
|
||||
],
|
||||
"read": Array [],
|
||||
},
|
||||
"ui": Array [
|
||||
"show",
|
||||
"save",
|
||||
"saveQuery",
|
||||
"createShortUrl",
|
||||
],
|
||||
},
|
||||
"privilegeId": "all",
|
||||
},
|
||||
Object {
|
||||
"privilege": Object {
|
||||
"app": Array [
|
||||
"discover",
|
||||
"kibana",
|
||||
],
|
||||
"catalogue": Array [
|
||||
"discover",
|
||||
],
|
||||
"savedObject": Object {
|
||||
"all": Array [],
|
||||
"read": Array [
|
||||
"index-pattern",
|
||||
"search",
|
||||
"query",
|
||||
],
|
||||
},
|
||||
"ui": Array [
|
||||
"show",
|
||||
],
|
||||
},
|
||||
"privilegeId": "read",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`buildOSSFeatures with a enterprise license returns the indexPatterns feature augmented with appropriate sub feature privileges 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"privilege": Object {
|
||||
"app": Array [
|
||||
"kibana",
|
||||
],
|
||||
"catalogue": Array [
|
||||
"indexPatterns",
|
||||
],
|
||||
"management": Object {
|
||||
"kibana": Array [
|
||||
"indexPatterns",
|
||||
],
|
||||
},
|
||||
"savedObject": Object {
|
||||
"all": Array [
|
||||
"index-pattern",
|
||||
],
|
||||
"read": Array [],
|
||||
},
|
||||
"ui": Array [
|
||||
"save",
|
||||
],
|
||||
},
|
||||
"privilegeId": "all",
|
||||
},
|
||||
Object {
|
||||
"privilege": Object {
|
||||
"app": Array [
|
||||
"kibana",
|
||||
],
|
||||
"catalogue": Array [
|
||||
"indexPatterns",
|
||||
],
|
||||
"management": Object {
|
||||
"kibana": Array [
|
||||
"indexPatterns",
|
||||
],
|
||||
},
|
||||
"savedObject": Object {
|
||||
"all": Array [],
|
||||
"read": Array [
|
||||
"index-pattern",
|
||||
],
|
||||
},
|
||||
"ui": Array [],
|
||||
},
|
||||
"privilegeId": "read",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`buildOSSFeatures with a enterprise license returns the savedObjectsManagement feature augmented with appropriate sub feature privileges 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"privilege": Object {
|
||||
"api": Array [
|
||||
"copySavedObjectsToSpaces",
|
||||
],
|
||||
"app": Array [
|
||||
"kibana",
|
||||
],
|
||||
"catalogue": Array [
|
||||
"saved_objects",
|
||||
],
|
||||
"management": Object {
|
||||
"kibana": Array [
|
||||
"objects",
|
||||
],
|
||||
},
|
||||
"savedObject": Object {
|
||||
"all": Array [
|
||||
"foo",
|
||||
"bar",
|
||||
],
|
||||
"read": Array [],
|
||||
},
|
||||
"ui": Array [
|
||||
"read",
|
||||
"edit",
|
||||
"delete",
|
||||
"copyIntoSpace",
|
||||
"shareIntoSpace",
|
||||
],
|
||||
},
|
||||
"privilegeId": "all",
|
||||
},
|
||||
Object {
|
||||
"privilege": Object {
|
||||
"api": Array [
|
||||
"copySavedObjectsToSpaces",
|
||||
],
|
||||
"app": Array [
|
||||
"kibana",
|
||||
],
|
||||
"catalogue": Array [
|
||||
"saved_objects",
|
||||
],
|
||||
"management": Object {
|
||||
"kibana": Array [
|
||||
"objects",
|
||||
],
|
||||
},
|
||||
"savedObject": Object {
|
||||
"all": Array [],
|
||||
"read": Array [
|
||||
"foo",
|
||||
"bar",
|
||||
],
|
||||
},
|
||||
"ui": Array [
|
||||
"read",
|
||||
],
|
||||
},
|
||||
"privilegeId": "read",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`buildOSSFeatures with a enterprise license returns the timelion feature augmented with appropriate sub feature privileges 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"privilege": Object {
|
||||
"app": Array [
|
||||
"timelion",
|
||||
"kibana",
|
||||
],
|
||||
"catalogue": Array [
|
||||
"timelion",
|
||||
],
|
||||
"savedObject": Object {
|
||||
"all": Array [
|
||||
"timelion-sheet",
|
||||
],
|
||||
"read": Array [
|
||||
"index-pattern",
|
||||
],
|
||||
},
|
||||
"ui": Array [
|
||||
"save",
|
||||
],
|
||||
},
|
||||
"privilegeId": "all",
|
||||
},
|
||||
Object {
|
||||
"privilege": Object {
|
||||
"app": Array [
|
||||
"timelion",
|
||||
"kibana",
|
||||
],
|
||||
"catalogue": Array [
|
||||
"timelion",
|
||||
],
|
||||
"savedObject": Object {
|
||||
"all": Array [],
|
||||
"read": Array [
|
||||
"index-pattern",
|
||||
"timelion-sheet",
|
||||
],
|
||||
},
|
||||
"ui": Array [],
|
||||
},
|
||||
"privilegeId": "read",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`buildOSSFeatures with a enterprise license returns the visualize feature augmented with appropriate sub feature privileges 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"privilege": Object {
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
import { FeatureRegistry } from './feature_registry';
|
||||
import { ElasticsearchFeatureConfig, KibanaFeatureConfig } from '../common';
|
||||
import { licensingMock } from '../../licensing/server/mocks';
|
||||
|
||||
describe('FeatureRegistry', () => {
|
||||
describe('Kibana Features', () => {
|
||||
|
@ -1280,6 +1281,123 @@ describe('FeatureRegistry', () => {
|
|||
);
|
||||
});
|
||||
|
||||
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(
|
||||
`"child \\"subFeatures\\" fails because [\\"subFeatures\\" at position 0 fails because [child \\"privilegeGroups\\" fails because [\\"privilegeGroups\\" at position 0 fails because [child \\"privileges\\" fails because [\\"privileges\\" at position 0 fails because [child \\"minimumLicense\\" fails because [\\"minimumLicense\\" is not allowed]]]]]]]"`
|
||||
);
|
||||
});
|
||||
|
||||
it('cannot register feature after getAll has been called', () => {
|
||||
const feature1: KibanaFeatureConfig = {
|
||||
id: 'test-feature',
|
||||
|
@ -1305,6 +1423,89 @@ describe('FeatureRegistry', () => {
|
|||
`"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', () => {
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
|
||||
import { cloneDeep, uniq } from 'lodash';
|
||||
import { ILicense } from '../../licensing/server';
|
||||
import {
|
||||
KibanaFeatureConfig,
|
||||
KibanaFeature,
|
||||
|
@ -55,11 +56,30 @@ export class FeatureRegistry {
|
|||
this.esFeatures[feature.id] = featureCopy;
|
||||
}
|
||||
|
||||
public getAllKibanaFeatures(): KibanaFeature[] {
|
||||
public getAllKibanaFeatures(license?: ILicense, ignoreLicense = false): KibanaFeature[] {
|
||||
this.locked = true;
|
||||
return Object.values(this.kibanaFeatures).map(
|
||||
(featureConfig) => new KibanaFeature(featureConfig)
|
||||
);
|
||||
let features = Object.values(this.kibanaFeatures);
|
||||
|
||||
const performLicenseCheck = license && !ignoreLicense;
|
||||
|
||||
if (performLicenseCheck) {
|
||||
features = features.filter((feature) => {
|
||||
const filter = !feature.minimumLicense || license!.hasAtLeast(feature.minimumLicense);
|
||||
if (!filter) return false;
|
||||
|
||||
feature.subFeatures?.forEach((subFeature) => {
|
||||
subFeature.privilegeGroups.forEach((group) => {
|
||||
group.privileges = group.privileges.filter(
|
||||
(privilege) =>
|
||||
!privilege.minimumLicense || license!.hasAtLeast(privilege.minimumLicense)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
return features.map((featureConfig) => new KibanaFeature(featureConfig));
|
||||
}
|
||||
|
||||
public getAllElasticsearchFeatures(): ElasticsearchFeature[] {
|
||||
|
|
|
@ -21,6 +21,11 @@ const managementSectionIdRegex = /^[a-zA-Z0-9_-]+$/;
|
|||
const reservedFeaturePrrivilegePartRegex = /^(?!reserved_)[a-zA-Z0-9_-]+$/;
|
||||
export const uiCapabilitiesRegex = /^[a-zA-Z0-9:_-]+$/;
|
||||
|
||||
const validLicenses = ['basic', 'standard', 'gold', 'platinum', 'enterprise', 'trial'];
|
||||
// sub-feature privileges are only available with a `gold` license or better, so restricting sub-feature privileges
|
||||
// for `gold` or below doesn't make a whole lot of sense.
|
||||
const validSubFeaturePrivilegeLicenses = ['platinum', 'enterprise', 'trial'];
|
||||
|
||||
const managementSchema = Joi.object().pattern(
|
||||
managementSectionIdRegex,
|
||||
Joi.array().items(Joi.string().regex(uiCapabilitiesRegex))
|
||||
|
@ -53,10 +58,11 @@ const kibanaPrivilegeSchema = Joi.object({
|
|||
ui: Joi.array().items(Joi.string().regex(uiCapabilitiesRegex)).required(),
|
||||
});
|
||||
|
||||
const kibanaSubFeaturePrivilegeSchema = Joi.object({
|
||||
const kibanaIndependentSubFeaturePrivilegeSchema = Joi.object({
|
||||
id: Joi.string().regex(subFeaturePrivilegePartRegex).required(),
|
||||
name: Joi.string().required(),
|
||||
includeIn: Joi.string().allow('all', 'read', 'none').required(),
|
||||
minimumLicense: Joi.string().valid(...validSubFeaturePrivilegeLicenses),
|
||||
management: managementSchema,
|
||||
catalogue: catalogueSchema,
|
||||
alerting: Joi.object({
|
||||
|
@ -72,12 +78,22 @@ const kibanaSubFeaturePrivilegeSchema = Joi.object({
|
|||
ui: Joi.array().items(Joi.string().regex(uiCapabilitiesRegex)).required(),
|
||||
});
|
||||
|
||||
const kibanaMutuallyExclusiveSubFeaturePrivilegeSchema = kibanaIndependentSubFeaturePrivilegeSchema.keys(
|
||||
{
|
||||
minimumLicense: Joi.forbidden(),
|
||||
}
|
||||
);
|
||||
|
||||
const kibanaSubFeatureSchema = Joi.object({
|
||||
name: Joi.string().required(),
|
||||
privilegeGroups: Joi.array().items(
|
||||
Joi.object({
|
||||
groupType: Joi.string().valid('mutually_exclusive', 'independent').required(),
|
||||
privileges: Joi.array().items(kibanaSubFeaturePrivilegeSchema).min(1),
|
||||
privileges: Joi.when('groupType', {
|
||||
is: 'mutually_exclusive',
|
||||
then: Joi.array().items(kibanaMutuallyExclusiveSubFeaturePrivilegeSchema).min(1),
|
||||
otherwise: Joi.array().items(kibanaIndependentSubFeaturePrivilegeSchema).min(1),
|
||||
}),
|
||||
})
|
||||
),
|
||||
});
|
||||
|
@ -91,14 +107,7 @@ const kibanaFeatureSchema = Joi.object({
|
|||
category: appCategorySchema,
|
||||
order: Joi.number(),
|
||||
excludeFromBasePrivileges: Joi.boolean(),
|
||||
minimumLicense: Joi.string().valid(
|
||||
'basic',
|
||||
'standard',
|
||||
'gold',
|
||||
'platinum',
|
||||
'enterprise',
|
||||
'trial'
|
||||
),
|
||||
minimumLicense: Joi.string().valid(...validLicenses),
|
||||
app: Joi.array().items(Joi.string()).required(),
|
||||
management: managementSchema,
|
||||
catalogue: catalogueSchema,
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
import { buildOSSFeatures } from './oss_features';
|
||||
import { featurePrivilegeIterator } from '../../security/server/authorization';
|
||||
import { KibanaFeature } from '.';
|
||||
import { LicenseType } from '../../licensing/server';
|
||||
|
||||
describe('buildOSSFeatures', () => {
|
||||
it('returns features including timelion', () => {
|
||||
|
@ -46,14 +47,22 @@ Array [
|
|||
|
||||
const features = buildOSSFeatures({ savedObjectTypes: ['foo', 'bar'], includeTimelion: true });
|
||||
features.forEach((featureConfig) => {
|
||||
it(`returns the ${featureConfig.id} feature augmented with appropriate sub feature privileges`, () => {
|
||||
const privileges = [];
|
||||
for (const featurePrivilege of featurePrivilegeIterator(new KibanaFeature(featureConfig), {
|
||||
augmentWithSubFeaturePrivileges: true,
|
||||
})) {
|
||||
privileges.push(featurePrivilege);
|
||||
}
|
||||
expect(privileges).toMatchSnapshot();
|
||||
(['enterprise', 'basic'] as LicenseType[]).forEach((licenseType) => {
|
||||
describe(`with a ${licenseType} license`, () => {
|
||||
it(`returns the ${featureConfig.id} feature augmented with appropriate sub feature privileges`, () => {
|
||||
const privileges = [];
|
||||
for (const featurePrivilege of featurePrivilegeIterator(
|
||||
new KibanaFeature(featureConfig),
|
||||
{
|
||||
augmentWithSubFeaturePrivileges: true,
|
||||
licenseType,
|
||||
}
|
||||
)) {
|
||||
privileges.push(featurePrivilege);
|
||||
}
|
||||
expect(privileges).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -11,15 +11,78 @@ import { httpServerMock, httpServiceMock, coreMock } from '../../../../../src/co
|
|||
import { LicenseType } from '../../../licensing/server/';
|
||||
import { licensingMock } from '../../../licensing/server/mocks';
|
||||
import { RequestHandler } from '../../../../../src/core/server';
|
||||
import { KibanaFeatureConfig } from '../../common';
|
||||
import { FeatureKibanaPrivileges, KibanaFeatureConfig, SubFeatureConfig } from '../../common';
|
||||
|
||||
function createContextMock(licenseType: LicenseType = 'gold') {
|
||||
function createContextMock(licenseType: LicenseType = 'platinum') {
|
||||
return {
|
||||
core: coreMock.createRequestHandlerContext(),
|
||||
licensing: licensingMock.createRequestHandlerContext({ license: { type: licenseType } }),
|
||||
};
|
||||
}
|
||||
|
||||
function createPrivilege(partial: Partial<FeatureKibanaPrivileges> = {}): FeatureKibanaPrivileges {
|
||||
return {
|
||||
savedObject: {
|
||||
all: [],
|
||||
read: [],
|
||||
},
|
||||
ui: [],
|
||||
...partial,
|
||||
};
|
||||
}
|
||||
|
||||
function getExpectedSubFeatures(licenseType: LicenseType = 'platinum'): SubFeatureConfig[] {
|
||||
return [
|
||||
{
|
||||
name: 'basicFeature',
|
||||
privilegeGroups: [
|
||||
{
|
||||
groupType: 'independent',
|
||||
privileges: [
|
||||
{
|
||||
id: 'basicSub1',
|
||||
name: 'basic sub 1',
|
||||
includeIn: 'all',
|
||||
...createPrivilege(),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'platinumFeature',
|
||||
privilegeGroups: [
|
||||
{
|
||||
groupType: 'independent',
|
||||
privileges:
|
||||
licenseType !== 'basic'
|
||||
? [
|
||||
{
|
||||
id: 'platinumFeatureSub1',
|
||||
name: 'platinum sub 1',
|
||||
includeIn: 'all',
|
||||
minimumLicense: 'platinum',
|
||||
...createPrivilege(),
|
||||
},
|
||||
]
|
||||
: [],
|
||||
},
|
||||
{
|
||||
groupType: 'mutually_exclusive',
|
||||
privileges: [
|
||||
{
|
||||
id: 'platinumFeatureMutExSub1',
|
||||
name: 'platinum sub 1',
|
||||
includeIn: 'all',
|
||||
...createPrivilege(),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
describe('GET /api/features', () => {
|
||||
let routeHandler: RequestHandler<any, any, any>;
|
||||
beforeEach(() => {
|
||||
|
@ -29,7 +92,11 @@ describe('GET /api/features', () => {
|
|||
name: 'Feature 1',
|
||||
app: [],
|
||||
category: { id: 'foo', label: 'foo' },
|
||||
privileges: null,
|
||||
privileges: {
|
||||
all: createPrivilege(),
|
||||
read: createPrivilege(),
|
||||
},
|
||||
subFeatures: getExpectedSubFeatures(),
|
||||
});
|
||||
|
||||
featureRegistry.registerKibanaFeature({
|
||||
|
@ -76,7 +143,12 @@ describe('GET /api/features', () => {
|
|||
const [call] = mockResponse.ok.mock.calls;
|
||||
const body = call[0]!.body as KibanaFeatureConfig[];
|
||||
|
||||
const features = body.map((feature) => ({ id: feature.id, order: feature.order }));
|
||||
const features = body.map((feature) => ({
|
||||
id: feature.id,
|
||||
order: feature.order,
|
||||
subFeatures: feature.subFeatures,
|
||||
}));
|
||||
|
||||
expect(features).toEqual([
|
||||
{
|
||||
id: 'feature_3',
|
||||
|
@ -89,6 +161,7 @@ describe('GET /api/features', () => {
|
|||
{
|
||||
id: 'feature_1',
|
||||
order: undefined,
|
||||
subFeatures: getExpectedSubFeatures(),
|
||||
},
|
||||
{
|
||||
id: 'licensed_feature',
|
||||
|
@ -105,7 +178,11 @@ describe('GET /api/features', () => {
|
|||
const [call] = mockResponse.ok.mock.calls;
|
||||
const body = call[0]!.body as KibanaFeatureConfig[];
|
||||
|
||||
const features = body.map((feature) => ({ id: feature.id, order: feature.order }));
|
||||
const features = body.map((feature) => ({
|
||||
id: feature.id,
|
||||
order: feature.order,
|
||||
subFeatures: feature.subFeatures,
|
||||
}));
|
||||
|
||||
expect(features).toEqual([
|
||||
{
|
||||
|
@ -119,6 +196,7 @@ describe('GET /api/features', () => {
|
|||
{
|
||||
id: 'feature_1',
|
||||
order: undefined,
|
||||
subFeatures: getExpectedSubFeatures('basic'),
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
@ -135,7 +213,11 @@ describe('GET /api/features', () => {
|
|||
const [call] = mockResponse.ok.mock.calls;
|
||||
const body = call[0]!.body as KibanaFeatureConfig[];
|
||||
|
||||
const features = body.map((feature) => ({ id: feature.id, order: feature.order }));
|
||||
const features = body.map((feature) => ({
|
||||
id: feature.id,
|
||||
order: feature.order,
|
||||
subFeatures: feature.subFeatures,
|
||||
}));
|
||||
|
||||
expect(features).toEqual([
|
||||
{
|
||||
|
@ -149,6 +231,7 @@ describe('GET /api/features', () => {
|
|||
{
|
||||
id: 'feature_1',
|
||||
order: undefined,
|
||||
subFeatures: getExpectedSubFeatures('basic'),
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
@ -165,7 +248,11 @@ describe('GET /api/features', () => {
|
|||
const [call] = mockResponse.ok.mock.calls;
|
||||
const body = call[0]!.body as KibanaFeatureConfig[];
|
||||
|
||||
const features = body.map((feature) => ({ id: feature.id, order: feature.order }));
|
||||
const features = body.map((feature) => ({
|
||||
id: feature.id,
|
||||
order: feature.order,
|
||||
subFeatures: feature.subFeatures,
|
||||
}));
|
||||
|
||||
expect(features).toEqual([
|
||||
{
|
||||
|
@ -179,6 +266,7 @@ describe('GET /api/features', () => {
|
|||
{
|
||||
id: 'feature_1',
|
||||
order: undefined,
|
||||
subFeatures: getExpectedSubFeatures(),
|
||||
},
|
||||
{
|
||||
id: 'licensed_feature',
|
||||
|
|
|
@ -26,17 +26,15 @@ export function defineRoutes({ router, featureRegistry }: RouteDefinitionParams)
|
|||
},
|
||||
},
|
||||
(context, request, response) => {
|
||||
const allFeatures = featureRegistry.getAllKibanaFeatures();
|
||||
const currentLicense = context.licensing!.license;
|
||||
|
||||
const allFeatures = featureRegistry.getAllKibanaFeatures(
|
||||
currentLicense,
|
||||
request.query.ignoreValidLicenses
|
||||
);
|
||||
|
||||
return response.ok({
|
||||
body: allFeatures
|
||||
.filter(
|
||||
(feature) =>
|
||||
request.query.ignoreValidLicenses ||
|
||||
!feature.minimumLicense ||
|
||||
(context.licensing!.license &&
|
||||
context.licensing!.license.hasAtLeast(feature.minimumLicense))
|
||||
)
|
||||
.sort(
|
||||
(f1, f2) =>
|
||||
(f1.order ?? Number.MAX_SAFE_INTEGER) - (f2.order ?? Number.MAX_SAFE_INTEGER)
|
||||
|
|
|
@ -11,6 +11,7 @@ export const licenseMock = {
|
|||
create: (): jest.Mocked<SecurityLicense> => ({
|
||||
isLicenseAvailable: jest.fn(),
|
||||
isEnabled: jest.fn().mockReturnValue(true),
|
||||
getType: jest.fn().mockReturnValue('basic'),
|
||||
getFeatures: jest.fn(),
|
||||
features$: of(),
|
||||
}),
|
||||
|
|
|
@ -6,12 +6,13 @@
|
|||
|
||||
import { Observable, Subscription } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { ILicense } from '../../../licensing/common/types';
|
||||
import { ILicense, LicenseType } from '../../../licensing/common/types';
|
||||
import { SecurityLicenseFeatures } from './license_features';
|
||||
|
||||
export interface SecurityLicense {
|
||||
isLicenseAvailable(): boolean;
|
||||
isEnabled(): boolean;
|
||||
getType(): LicenseType | undefined;
|
||||
getFeatures(): SecurityLicenseFeatures;
|
||||
features$: Observable<SecurityLicenseFeatures>;
|
||||
}
|
||||
|
@ -36,6 +37,8 @@ export class SecurityLicenseService {
|
|||
|
||||
isEnabled: () => this.isSecurityEnabledFromRawLicense(rawLicense),
|
||||
|
||||
getType: () => rawLicense?.type,
|
||||
|
||||
getFeatures: () => this.calculateFeaturesFromRawLicense(rawLicense),
|
||||
|
||||
features$: license$.pipe(
|
||||
|
|
|
@ -23,6 +23,7 @@ export const createRawKibanaPrivileges = (
|
|||
|
||||
const licensingService = {
|
||||
getFeatures: () => ({ allowSubFeaturePrivileges } as SecurityLicenseFeatures),
|
||||
getType: () => 'basic' as const,
|
||||
};
|
||||
|
||||
return privilegesFactory(
|
||||
|
|
|
@ -183,6 +183,7 @@ exports[`it renders without crashing 1`] = `
|
|||
"_subscribe": [Function],
|
||||
},
|
||||
"getFeatures": [MockFunction],
|
||||
"getType": [MockFunction],
|
||||
"isEnabled": [MockFunction],
|
||||
"isLicenseAvailable": [MockFunction],
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ import { mountWithIntl } from '@kbn/test/jest';
|
|||
import { SubFeatureForm } from './sub_feature_form';
|
||||
import { EuiCheckbox, EuiButtonGroup } from '@elastic/eui';
|
||||
import { act } from '@testing-library/react';
|
||||
import { KibanaFeature } from '../../../../../../../../features/public';
|
||||
|
||||
// Note: these tests are not concerned with the proper display of privileges,
|
||||
// as that is verified by the feature_table and privilege_space_form tests.
|
||||
|
@ -234,4 +235,65 @@ describe('SubFeatureForm', () => {
|
|||
|
||||
expect(onChange).toHaveBeenCalledWith([]);
|
||||
});
|
||||
|
||||
it('does not render empty privilege groups', () => {
|
||||
// privilege groups are filtered server-side to only include the
|
||||
// sub-feature privileges that are allowed by the current license.
|
||||
|
||||
const role = createRole([
|
||||
{
|
||||
base: [],
|
||||
feature: {
|
||||
with_sub_features: ['cool_all'],
|
||||
},
|
||||
spaces: [],
|
||||
},
|
||||
]);
|
||||
const feature = new KibanaFeature({
|
||||
id: 'test_feature',
|
||||
name: 'test feature',
|
||||
category: { id: 'test', label: 'test' },
|
||||
app: [],
|
||||
privileges: {
|
||||
all: {
|
||||
savedObject: { all: [], read: [] },
|
||||
ui: [],
|
||||
},
|
||||
read: {
|
||||
savedObject: { all: [], read: [] },
|
||||
ui: [],
|
||||
},
|
||||
},
|
||||
subFeatures: [
|
||||
{
|
||||
name: 'subFeature1',
|
||||
privilegeGroups: [
|
||||
{
|
||||
groupType: 'independent',
|
||||
privileges: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
const subFeature1 = new SecuredSubFeature(feature.toRaw().subFeatures![0]);
|
||||
const kibanaPrivileges = createKibanaPrivileges([feature]);
|
||||
const calculator = new PrivilegeFormCalculator(kibanaPrivileges, role);
|
||||
|
||||
const onChange = jest.fn();
|
||||
|
||||
const wrapper = mountWithIntl(
|
||||
<SubFeatureForm
|
||||
featureId={feature.id}
|
||||
subFeature={subFeature1}
|
||||
selectedFeaturePrivileges={['cool_all']}
|
||||
privilegeCalculator={calculator}
|
||||
privilegeIndex={0}
|
||||
onChange={onChange}
|
||||
disabled={false}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(wrapper.children()).toMatchInlineSnapshot(`null`);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -27,12 +27,20 @@ interface Props {
|
|||
}
|
||||
|
||||
export const SubFeatureForm = (props: Props) => {
|
||||
const groupsWithPrivileges = props.subFeature
|
||||
.getPrivilegeGroups()
|
||||
.filter((group) => group.privileges.length > 0);
|
||||
|
||||
if (groupsWithPrivileges.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem>
|
||||
<EuiText size="s">{props.subFeature.name}</EuiText>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>{props.subFeature.getPrivilegeGroups().map(renderPrivilegeGroup)}</EuiFlexItem>
|
||||
<EuiFlexItem>{groupsWithPrivileges.map(renderPrivilegeGroup)}</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
|
||||
|
|
|
@ -45,6 +45,7 @@ describe('Security Plugin', () => {
|
|||
license: {
|
||||
isLicenseAvailable: expect.any(Function),
|
||||
isEnabled: expect.any(Function),
|
||||
getType: expect.any(Function),
|
||||
getFeatures: expect.any(Function),
|
||||
features$: expect.any(Observable),
|
||||
},
|
||||
|
@ -73,6 +74,7 @@ describe('Security Plugin', () => {
|
|||
license: {
|
||||
isLicenseAvailable: expect.any(Function),
|
||||
isEnabled: expect.any(Function),
|
||||
getType: expect.any(Function),
|
||||
getFeatures: expect.any(Function),
|
||||
features$: expect.any(Observable),
|
||||
},
|
||||
|
|
|
@ -20,6 +20,7 @@ describe('featurePrivilegeIterator', () => {
|
|||
const actualPrivileges = Array.from(
|
||||
featurePrivilegeIterator(feature, {
|
||||
augmentWithSubFeaturePrivileges: true,
|
||||
licenseType: 'basic',
|
||||
})
|
||||
);
|
||||
|
||||
|
@ -72,6 +73,7 @@ describe('featurePrivilegeIterator', () => {
|
|||
const actualPrivileges = Array.from(
|
||||
featurePrivilegeIterator(feature, {
|
||||
augmentWithSubFeaturePrivileges: true,
|
||||
licenseType: 'basic',
|
||||
})
|
||||
);
|
||||
|
||||
|
@ -164,6 +166,7 @@ describe('featurePrivilegeIterator', () => {
|
|||
const actualPrivileges = Array.from(
|
||||
featurePrivilegeIterator(feature, {
|
||||
augmentWithSubFeaturePrivileges: true,
|
||||
licenseType: 'basic',
|
||||
predicate: (privilegeId) => privilegeId === 'all',
|
||||
})
|
||||
);
|
||||
|
@ -270,6 +273,7 @@ describe('featurePrivilegeIterator', () => {
|
|||
const actualPrivileges = Array.from(
|
||||
featurePrivilegeIterator(feature, {
|
||||
augmentWithSubFeaturePrivileges: false,
|
||||
licenseType: 'basic',
|
||||
})
|
||||
);
|
||||
|
||||
|
@ -394,6 +398,7 @@ describe('featurePrivilegeIterator', () => {
|
|||
const actualPrivileges = Array.from(
|
||||
featurePrivilegeIterator(feature, {
|
||||
augmentWithSubFeaturePrivileges: true,
|
||||
licenseType: 'basic',
|
||||
})
|
||||
);
|
||||
|
||||
|
@ -519,6 +524,7 @@ describe('featurePrivilegeIterator', () => {
|
|||
const actualPrivileges = Array.from(
|
||||
featurePrivilegeIterator(feature, {
|
||||
augmentWithSubFeaturePrivileges: true,
|
||||
licenseType: 'basic',
|
||||
})
|
||||
);
|
||||
|
||||
|
@ -645,6 +651,7 @@ describe('featurePrivilegeIterator', () => {
|
|||
const actualPrivileges = Array.from(
|
||||
featurePrivilegeIterator(feature, {
|
||||
augmentWithSubFeaturePrivileges: true,
|
||||
licenseType: 'basic',
|
||||
})
|
||||
);
|
||||
|
||||
|
@ -771,6 +778,7 @@ describe('featurePrivilegeIterator', () => {
|
|||
const actualPrivileges = Array.from(
|
||||
featurePrivilegeIterator(feature, {
|
||||
augmentWithSubFeaturePrivileges: true,
|
||||
licenseType: 'basic',
|
||||
})
|
||||
);
|
||||
|
||||
|
@ -818,6 +826,133 @@ describe('featurePrivilegeIterator', () => {
|
|||
]);
|
||||
});
|
||||
|
||||
it('excludes sub feature privileges when the minimum license is not met', () => {
|
||||
const feature = new KibanaFeature({
|
||||
id: 'foo',
|
||||
name: 'foo',
|
||||
app: [],
|
||||
category: { id: 'foo', label: 'foo' },
|
||||
privileges: {
|
||||
all: {
|
||||
api: ['all-api', 'read-api'],
|
||||
app: ['foo'],
|
||||
catalogue: ['foo-catalogue'],
|
||||
management: {
|
||||
section: ['foo-management'],
|
||||
},
|
||||
savedObject: {
|
||||
all: ['all-type'],
|
||||
read: ['read-type'],
|
||||
},
|
||||
alerting: {
|
||||
all: ['alerting-all-type'],
|
||||
read: ['alerting-read-type'],
|
||||
},
|
||||
ui: ['ui-action'],
|
||||
},
|
||||
read: {
|
||||
api: ['read-api'],
|
||||
app: ['foo'],
|
||||
catalogue: ['foo-catalogue'],
|
||||
management: {
|
||||
section: ['foo-management'],
|
||||
},
|
||||
savedObject: {
|
||||
all: [],
|
||||
read: ['read-type'],
|
||||
},
|
||||
alerting: {
|
||||
read: ['alerting-read-type'],
|
||||
},
|
||||
ui: ['ui-action'],
|
||||
},
|
||||
},
|
||||
subFeatures: [
|
||||
{
|
||||
name: 'sub feature 1',
|
||||
privilegeGroups: [
|
||||
{
|
||||
groupType: 'independent',
|
||||
privileges: [
|
||||
{
|
||||
id: 'sub-feature-priv-1',
|
||||
name: 'first sub feature privilege',
|
||||
includeIn: 'all',
|
||||
minimumLicense: 'gold',
|
||||
api: ['sub-feature-api'],
|
||||
app: ['sub-app'],
|
||||
catalogue: ['sub-catalogue'],
|
||||
management: {
|
||||
section: ['other-sub-management'],
|
||||
kibana: ['sub-management'],
|
||||
},
|
||||
savedObject: {
|
||||
all: ['all-sub-type'],
|
||||
read: ['read-sub-type'],
|
||||
},
|
||||
alerting: {
|
||||
all: ['alerting-all-sub-type'],
|
||||
read: ['alerting-read-sub-type'],
|
||||
},
|
||||
ui: ['ui-sub-type'],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const actualPrivileges = Array.from(
|
||||
featurePrivilegeIterator(feature, {
|
||||
augmentWithSubFeaturePrivileges: true,
|
||||
licenseType: 'basic',
|
||||
})
|
||||
);
|
||||
|
||||
expect(actualPrivileges).toEqual([
|
||||
{
|
||||
privilegeId: 'all',
|
||||
privilege: {
|
||||
api: ['all-api', 'read-api'],
|
||||
app: ['foo'],
|
||||
catalogue: ['foo-catalogue'],
|
||||
management: {
|
||||
section: ['foo-management'],
|
||||
},
|
||||
savedObject: {
|
||||
all: ['all-type'],
|
||||
read: ['read-type'],
|
||||
},
|
||||
alerting: {
|
||||
all: ['alerting-all-type'],
|
||||
read: ['alerting-read-type'],
|
||||
},
|
||||
ui: ['ui-action'],
|
||||
},
|
||||
},
|
||||
{
|
||||
privilegeId: 'read',
|
||||
privilege: {
|
||||
api: ['read-api'],
|
||||
app: ['foo'],
|
||||
catalogue: ['foo-catalogue'],
|
||||
management: {
|
||||
section: ['foo-management'],
|
||||
},
|
||||
savedObject: {
|
||||
all: [],
|
||||
read: ['read-type'],
|
||||
},
|
||||
alerting: {
|
||||
read: ['alerting-read-type'],
|
||||
},
|
||||
ui: ['ui-action'],
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it(`can augment primary feature privileges even if they don't specify their own`, () => {
|
||||
const feature = new KibanaFeature({
|
||||
id: 'foo',
|
||||
|
@ -878,6 +1013,7 @@ describe('featurePrivilegeIterator', () => {
|
|||
const actualPrivileges = Array.from(
|
||||
featurePrivilegeIterator(feature, {
|
||||
augmentWithSubFeaturePrivileges: true,
|
||||
licenseType: 'basic',
|
||||
})
|
||||
);
|
||||
|
||||
|
@ -995,6 +1131,7 @@ describe('featurePrivilegeIterator', () => {
|
|||
const actualPrivileges = Array.from(
|
||||
featurePrivilegeIterator(feature, {
|
||||
augmentWithSubFeaturePrivileges: true,
|
||||
licenseType: 'basic',
|
||||
})
|
||||
);
|
||||
|
||||
|
|
|
@ -5,11 +5,13 @@
|
|||
*/
|
||||
|
||||
import _ from 'lodash';
|
||||
import { LicenseType } from '../../../../../licensing/server';
|
||||
import { KibanaFeature, FeatureKibanaPrivileges } from '../../../../../features/server';
|
||||
import { subFeaturePrivilegeIterator } from './sub_feature_privilege_iterator';
|
||||
|
||||
interface IteratorOptions {
|
||||
augmentWithSubFeaturePrivileges: boolean;
|
||||
licenseType: LicenseType;
|
||||
predicate?: (privilegeId: string, privilege: FeatureKibanaPrivileges) => boolean;
|
||||
}
|
||||
|
||||
|
@ -25,7 +27,10 @@ export function* featurePrivilegeIterator(
|
|||
}
|
||||
|
||||
if (options.augmentWithSubFeaturePrivileges) {
|
||||
yield { privilegeId, privilege: mergeWithSubFeatures(privilegeId, privilege, feature) };
|
||||
yield {
|
||||
privilegeId,
|
||||
privilege: mergeWithSubFeatures(privilegeId, privilege, feature, options.licenseType),
|
||||
};
|
||||
} else {
|
||||
yield { privilegeId, privilege };
|
||||
}
|
||||
|
@ -35,10 +40,11 @@ export function* featurePrivilegeIterator(
|
|||
function mergeWithSubFeatures(
|
||||
privilegeId: string,
|
||||
privilege: FeatureKibanaPrivileges,
|
||||
feature: KibanaFeature
|
||||
feature: KibanaFeature,
|
||||
licenseType: LicenseType
|
||||
) {
|
||||
const mergedConfig = _.cloneDeep(privilege);
|
||||
for (const subFeaturePrivilege of subFeaturePrivilegeIterator(feature)) {
|
||||
for (const subFeaturePrivilege of subFeaturePrivilegeIterator(feature, licenseType)) {
|
||||
if (subFeaturePrivilege.includeIn !== 'read' && subFeaturePrivilege.includeIn !== privilegeId) {
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -4,14 +4,19 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { LicenseType } from '../../../../../licensing/server';
|
||||
|
||||
import { KibanaFeature, SubFeaturePrivilegeConfig } from '../../../../../features/common';
|
||||
|
||||
export function* subFeaturePrivilegeIterator(
|
||||
feature: KibanaFeature
|
||||
feature: KibanaFeature,
|
||||
licenseType: LicenseType
|
||||
): IterableIterator<SubFeaturePrivilegeConfig> {
|
||||
for (const subFeature of feature.subFeatures) {
|
||||
for (const group of subFeature.privilegeGroups) {
|
||||
yield* group.privileges;
|
||||
yield* group.privileges.filter(
|
||||
(privilege) => !privilege.minimumLicense || privilege.minimumLicense <= licenseType
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@ describe('features', () => {
|
|||
|
||||
const mockLicenseService = {
|
||||
getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: true }),
|
||||
getType: jest.fn().mockReturnValue('basic'),
|
||||
};
|
||||
const privileges = privilegesFactory(actions, mockFeaturesService, mockLicenseService);
|
||||
|
||||
|
@ -89,6 +90,7 @@ describe('features', () => {
|
|||
};
|
||||
const mockLicenseService = {
|
||||
getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: true }),
|
||||
getType: jest.fn().mockReturnValue('basic'),
|
||||
};
|
||||
const privileges = privilegesFactory(actions, mockFeaturesPlugin as any, mockLicenseService);
|
||||
|
||||
|
@ -177,6 +179,7 @@ describe('features', () => {
|
|||
};
|
||||
const mockLicenseService = {
|
||||
getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: true }),
|
||||
getType: jest.fn().mockReturnValue('basic'),
|
||||
};
|
||||
const privileges = privilegesFactory(actions, mockFeaturesPlugin as any, mockLicenseService);
|
||||
|
||||
|
@ -245,6 +248,7 @@ describe('features', () => {
|
|||
};
|
||||
const mockLicenseService = {
|
||||
getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: true }),
|
||||
getType: jest.fn().mockReturnValue('basic'),
|
||||
};
|
||||
const privileges = privilegesFactory(actions, mockFeaturesPlugin as any, mockLicenseService);
|
||||
|
||||
|
@ -368,6 +372,7 @@ describe('features', () => {
|
|||
};
|
||||
const mockLicenseService = {
|
||||
getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: true }),
|
||||
getType: jest.fn().mockReturnValue('basic'),
|
||||
};
|
||||
const privileges = privilegesFactory(actions, mockFeaturesPlugin as any, mockLicenseService);
|
||||
|
||||
|
@ -443,6 +448,7 @@ describe('features', () => {
|
|||
};
|
||||
const mockLicenseService = {
|
||||
getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: true }),
|
||||
getType: jest.fn().mockReturnValue('basic'),
|
||||
};
|
||||
const privileges = privilegesFactory(actions, mockFeaturesPlugin as any, mockLicenseService);
|
||||
|
||||
|
@ -508,6 +514,7 @@ describe('features', () => {
|
|||
};
|
||||
const mockLicenseService = {
|
||||
getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: true }),
|
||||
getType: jest.fn().mockReturnValue('basic'),
|
||||
};
|
||||
const privileges = privilegesFactory(actions, mockFeaturesPlugin as any, mockLicenseService);
|
||||
|
||||
|
@ -574,6 +581,7 @@ describe('features', () => {
|
|||
};
|
||||
const mockLicenseService = {
|
||||
getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: true }),
|
||||
getType: jest.fn().mockReturnValue('basic'),
|
||||
};
|
||||
const privileges = privilegesFactory(actions, mockFeaturesPlugin as any, mockLicenseService);
|
||||
|
||||
|
@ -633,6 +641,7 @@ describe('reserved', () => {
|
|||
};
|
||||
const mockLicenseService = {
|
||||
getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: true }),
|
||||
getType: jest.fn().mockReturnValue('basic'),
|
||||
};
|
||||
const privileges = privilegesFactory(actions, mockFeaturesPlugin as any, mockLicenseService);
|
||||
|
||||
|
@ -671,6 +680,7 @@ describe('reserved', () => {
|
|||
};
|
||||
const mockLicenseService = {
|
||||
getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: true }),
|
||||
getType: jest.fn().mockReturnValue('basic'),
|
||||
};
|
||||
const privileges = privilegesFactory(actions, mockFeaturesPlugin as any, mockLicenseService);
|
||||
|
||||
|
@ -737,6 +747,7 @@ describe('reserved', () => {
|
|||
};
|
||||
const mockLicenseService = {
|
||||
getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: true }),
|
||||
getType: jest.fn().mockReturnValue('basic'),
|
||||
};
|
||||
const privileges = privilegesFactory(actions, mockFeaturesPlugin as any, mockLicenseService);
|
||||
|
||||
|
@ -800,6 +811,7 @@ describe('subFeatures', () => {
|
|||
};
|
||||
const mockLicenseService = {
|
||||
getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: true }),
|
||||
getType: jest.fn().mockReturnValue('basic'),
|
||||
};
|
||||
const privileges = privilegesFactory(actions, mockFeaturesPlugin as any, mockLicenseService);
|
||||
|
||||
|
@ -928,6 +940,7 @@ describe('subFeatures', () => {
|
|||
};
|
||||
const mockLicenseService = {
|
||||
getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: true }),
|
||||
getType: jest.fn().mockReturnValue('basic'),
|
||||
};
|
||||
const privileges = privilegesFactory(actions, mockFeaturesPlugin as any, mockLicenseService);
|
||||
|
||||
|
@ -1135,6 +1148,7 @@ describe('subFeatures', () => {
|
|||
};
|
||||
const mockLicenseService = {
|
||||
getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: true }),
|
||||
getType: jest.fn().mockReturnValue('basic'),
|
||||
};
|
||||
const privileges = privilegesFactory(actions, mockFeaturesPlugin as any, mockLicenseService);
|
||||
|
||||
|
@ -1278,6 +1292,7 @@ describe('subFeatures', () => {
|
|||
};
|
||||
const mockLicenseService = {
|
||||
getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: true }),
|
||||
getType: jest.fn().mockReturnValue('basic'),
|
||||
};
|
||||
const privileges = privilegesFactory(actions, mockFeaturesPlugin as any, mockLicenseService);
|
||||
|
||||
|
@ -1446,6 +1461,7 @@ describe('subFeatures', () => {
|
|||
};
|
||||
const mockLicenseService = {
|
||||
getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: true }),
|
||||
getType: jest.fn().mockReturnValue('basic'),
|
||||
};
|
||||
const privileges = privilegesFactory(actions, mockFeaturesPlugin as any, mockLicenseService);
|
||||
|
||||
|
@ -1576,6 +1592,7 @@ describe('subFeatures', () => {
|
|||
};
|
||||
const mockLicenseService = {
|
||||
getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: false }),
|
||||
getType: jest.fn().mockReturnValue('basic'),
|
||||
};
|
||||
const privileges = privilegesFactory(actions, mockFeaturesPlugin as any, mockLicenseService);
|
||||
|
||||
|
@ -1705,4 +1722,502 @@ describe('subFeatures', () => {
|
|||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe(`when license allows subfeatures, but not a specific sub feature`, () => {
|
||||
test(`should create minimal privileges, but not augment the primary feature privileges or create the disallowed sub-feature privileges`, () => {
|
||||
const features: KibanaFeature[] = [
|
||||
new KibanaFeature({
|
||||
id: 'foo',
|
||||
name: 'Foo KibanaFeature',
|
||||
app: [],
|
||||
category: { id: 'foo', label: 'foo' },
|
||||
privileges: {
|
||||
all: {
|
||||
savedObject: {
|
||||
all: [],
|
||||
read: [],
|
||||
},
|
||||
ui: ['foo'],
|
||||
},
|
||||
read: {
|
||||
savedObject: {
|
||||
all: [],
|
||||
read: [],
|
||||
},
|
||||
ui: ['foo'],
|
||||
},
|
||||
},
|
||||
subFeatures: [
|
||||
{
|
||||
name: 'subFeature1',
|
||||
privilegeGroups: [
|
||||
{
|
||||
groupType: 'independent',
|
||||
privileges: [
|
||||
{
|
||||
id: 'subFeaturePriv1',
|
||||
name: 'sub feature priv 1',
|
||||
includeIn: 'read',
|
||||
savedObject: {
|
||||
all: ['all-sub-feature-type'],
|
||||
read: ['read-sub-feature-type'],
|
||||
},
|
||||
ui: ['sub-feature-ui'],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
groupType: 'independent',
|
||||
privileges: [
|
||||
{
|
||||
id: 'licensedSubFeaturePriv',
|
||||
name: 'licensed sub feature priv',
|
||||
includeIn: 'read',
|
||||
minimumLicense: 'platinum',
|
||||
savedObject: {
|
||||
all: ['all-licensed-sub-feature-type'],
|
||||
read: ['read-licensed-sub-feature-type'],
|
||||
},
|
||||
ui: ['licensed-sub-feature-ui'],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}),
|
||||
];
|
||||
|
||||
const mockFeaturesPlugin = {
|
||||
getKibanaFeatures: jest.fn().mockReturnValue(features),
|
||||
};
|
||||
const mockLicenseService = {
|
||||
getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: true }),
|
||||
getType: jest.fn().mockReturnValue('gold'),
|
||||
};
|
||||
const privileges = privilegesFactory(actions, mockFeaturesPlugin as any, mockLicenseService);
|
||||
|
||||
const actual = privileges.get();
|
||||
expect(actual.features).toHaveProperty(`foo.subFeaturePriv1`);
|
||||
expect(actual.features).not.toHaveProperty(`foo.licensedSubFeaturePriv`);
|
||||
|
||||
expect(actual.features).toHaveProperty(`foo.all`, [
|
||||
actions.login,
|
||||
actions.version,
|
||||
actions.savedObject.get('all-sub-feature-type', 'bulk_get'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'get'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'find'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'create'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'bulk_create'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'update'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'bulk_update'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'delete'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'share_to_space'),
|
||||
actions.savedObject.get('read-sub-feature-type', 'bulk_get'),
|
||||
actions.savedObject.get('read-sub-feature-type', 'get'),
|
||||
actions.savedObject.get('read-sub-feature-type', 'find'),
|
||||
actions.ui.get('foo', 'foo'),
|
||||
actions.ui.get('foo', 'sub-feature-ui'),
|
||||
]);
|
||||
|
||||
expect(actual.features).toHaveProperty(`foo.minimal_all`, [
|
||||
actions.login,
|
||||
actions.version,
|
||||
actions.ui.get('foo', 'foo'),
|
||||
]);
|
||||
|
||||
expect(actual.features).toHaveProperty(`foo.read`, [
|
||||
actions.login,
|
||||
actions.version,
|
||||
actions.savedObject.get('all-sub-feature-type', 'bulk_get'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'get'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'find'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'create'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'bulk_create'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'update'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'bulk_update'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'delete'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'share_to_space'),
|
||||
actions.savedObject.get('read-sub-feature-type', 'bulk_get'),
|
||||
actions.savedObject.get('read-sub-feature-type', 'get'),
|
||||
actions.savedObject.get('read-sub-feature-type', 'find'),
|
||||
actions.ui.get('foo', 'foo'),
|
||||
actions.ui.get('foo', 'sub-feature-ui'),
|
||||
]);
|
||||
|
||||
expect(actual.features).toHaveProperty(`foo.minimal_read`, [
|
||||
actions.login,
|
||||
actions.version,
|
||||
actions.ui.get('foo', 'foo'),
|
||||
]);
|
||||
|
||||
expect(actual).toHaveProperty('global.all', [
|
||||
actions.login,
|
||||
actions.version,
|
||||
actions.api.get('features'),
|
||||
actions.space.manage,
|
||||
actions.ui.get('spaces', 'manage'),
|
||||
actions.ui.get('management', 'kibana', 'spaces'),
|
||||
actions.ui.get('catalogue', 'spaces'),
|
||||
actions.ui.get('enterpriseSearch', 'all'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'bulk_get'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'get'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'find'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'create'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'bulk_create'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'update'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'bulk_update'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'delete'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'share_to_space'),
|
||||
actions.savedObject.get('read-sub-feature-type', 'bulk_get'),
|
||||
actions.savedObject.get('read-sub-feature-type', 'get'),
|
||||
actions.savedObject.get('read-sub-feature-type', 'find'),
|
||||
actions.ui.get('foo', 'foo'),
|
||||
actions.ui.get('foo', 'sub-feature-ui'),
|
||||
]);
|
||||
expect(actual).toHaveProperty('global.read', [
|
||||
actions.login,
|
||||
actions.version,
|
||||
actions.savedObject.get('all-sub-feature-type', 'bulk_get'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'get'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'find'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'create'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'bulk_create'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'update'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'bulk_update'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'delete'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'share_to_space'),
|
||||
actions.savedObject.get('read-sub-feature-type', 'bulk_get'),
|
||||
actions.savedObject.get('read-sub-feature-type', 'get'),
|
||||
actions.savedObject.get('read-sub-feature-type', 'find'),
|
||||
actions.ui.get('foo', 'foo'),
|
||||
actions.ui.get('foo', 'sub-feature-ui'),
|
||||
]);
|
||||
|
||||
expect(actual).toHaveProperty('space.all', [
|
||||
actions.login,
|
||||
actions.version,
|
||||
actions.savedObject.get('all-sub-feature-type', 'bulk_get'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'get'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'find'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'create'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'bulk_create'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'update'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'bulk_update'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'delete'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'share_to_space'),
|
||||
actions.savedObject.get('read-sub-feature-type', 'bulk_get'),
|
||||
actions.savedObject.get('read-sub-feature-type', 'get'),
|
||||
actions.savedObject.get('read-sub-feature-type', 'find'),
|
||||
actions.ui.get('foo', 'foo'),
|
||||
actions.ui.get('foo', 'sub-feature-ui'),
|
||||
]);
|
||||
expect(actual).toHaveProperty('space.read', [
|
||||
actions.login,
|
||||
actions.version,
|
||||
actions.savedObject.get('all-sub-feature-type', 'bulk_get'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'get'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'find'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'create'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'bulk_create'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'update'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'bulk_update'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'delete'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'share_to_space'),
|
||||
actions.savedObject.get('read-sub-feature-type', 'bulk_get'),
|
||||
actions.savedObject.get('read-sub-feature-type', 'get'),
|
||||
actions.savedObject.get('read-sub-feature-type', 'find'),
|
||||
actions.ui.get('foo', 'foo'),
|
||||
actions.ui.get('foo', 'sub-feature-ui'),
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe(`when license allows subfeatures, but and a licensed sub feature`, () => {
|
||||
test(`should create minimal privileges, augment the primary feature privileges, and create the licensed sub-feature privileges`, () => {
|
||||
const features: KibanaFeature[] = [
|
||||
new KibanaFeature({
|
||||
id: 'foo',
|
||||
name: 'Foo KibanaFeature',
|
||||
app: [],
|
||||
category: { id: 'foo', label: 'foo' },
|
||||
privileges: {
|
||||
all: {
|
||||
savedObject: {
|
||||
all: [],
|
||||
read: [],
|
||||
},
|
||||
ui: ['foo'],
|
||||
},
|
||||
read: {
|
||||
savedObject: {
|
||||
all: [],
|
||||
read: [],
|
||||
},
|
||||
ui: ['foo'],
|
||||
},
|
||||
},
|
||||
subFeatures: [
|
||||
{
|
||||
name: 'subFeature1',
|
||||
privilegeGroups: [
|
||||
{
|
||||
groupType: 'independent',
|
||||
privileges: [
|
||||
{
|
||||
id: 'subFeaturePriv1',
|
||||
name: 'sub feature priv 1',
|
||||
includeIn: 'read',
|
||||
savedObject: {
|
||||
all: ['all-sub-feature-type'],
|
||||
read: ['read-sub-feature-type'],
|
||||
},
|
||||
ui: ['sub-feature-ui'],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
groupType: 'independent',
|
||||
privileges: [
|
||||
{
|
||||
id: 'licensedSubFeaturePriv',
|
||||
name: 'licensed sub feature priv',
|
||||
includeIn: 'read',
|
||||
minimumLicense: 'platinum',
|
||||
savedObject: {
|
||||
all: ['all-licensed-sub-feature-type'],
|
||||
read: ['read-licensed-sub-feature-type'],
|
||||
},
|
||||
ui: ['licensed-sub-feature-ui'],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}),
|
||||
];
|
||||
|
||||
const mockFeaturesPlugin = {
|
||||
getKibanaFeatures: jest.fn().mockReturnValue(features),
|
||||
};
|
||||
const mockLicenseService = {
|
||||
getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: true }),
|
||||
getType: jest.fn().mockReturnValue('platinum'),
|
||||
};
|
||||
const privileges = privilegesFactory(actions, mockFeaturesPlugin as any, mockLicenseService);
|
||||
|
||||
const actual = privileges.get();
|
||||
expect(actual.features).toHaveProperty(`foo.subFeaturePriv1`);
|
||||
expect(actual.features).toHaveProperty(`foo.licensedSubFeaturePriv`);
|
||||
|
||||
expect(actual.features).toHaveProperty(`foo.all`, [
|
||||
actions.login,
|
||||
actions.version,
|
||||
actions.savedObject.get('all-sub-feature-type', 'bulk_get'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'get'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'find'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'create'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'bulk_create'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'update'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'bulk_update'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'delete'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'share_to_space'),
|
||||
actions.savedObject.get('all-licensed-sub-feature-type', 'bulk_get'),
|
||||
actions.savedObject.get('all-licensed-sub-feature-type', 'get'),
|
||||
actions.savedObject.get('all-licensed-sub-feature-type', 'find'),
|
||||
actions.savedObject.get('all-licensed-sub-feature-type', 'create'),
|
||||
actions.savedObject.get('all-licensed-sub-feature-type', 'bulk_create'),
|
||||
actions.savedObject.get('all-licensed-sub-feature-type', 'update'),
|
||||
actions.savedObject.get('all-licensed-sub-feature-type', 'bulk_update'),
|
||||
actions.savedObject.get('all-licensed-sub-feature-type', 'delete'),
|
||||
actions.savedObject.get('all-licensed-sub-feature-type', 'share_to_space'),
|
||||
actions.savedObject.get('read-sub-feature-type', 'bulk_get'),
|
||||
actions.savedObject.get('read-sub-feature-type', 'get'),
|
||||
actions.savedObject.get('read-sub-feature-type', 'find'),
|
||||
actions.savedObject.get('read-licensed-sub-feature-type', 'bulk_get'),
|
||||
actions.savedObject.get('read-licensed-sub-feature-type', 'get'),
|
||||
actions.savedObject.get('read-licensed-sub-feature-type', 'find'),
|
||||
actions.ui.get('foo', 'foo'),
|
||||
actions.ui.get('foo', 'sub-feature-ui'),
|
||||
actions.ui.get('foo', 'licensed-sub-feature-ui'),
|
||||
]);
|
||||
|
||||
expect(actual.features).toHaveProperty(`foo.minimal_all`, [
|
||||
actions.login,
|
||||
actions.version,
|
||||
actions.ui.get('foo', 'foo'),
|
||||
]);
|
||||
|
||||
expect(actual.features).toHaveProperty(`foo.read`, [
|
||||
actions.login,
|
||||
actions.version,
|
||||
actions.savedObject.get('all-sub-feature-type', 'bulk_get'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'get'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'find'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'create'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'bulk_create'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'update'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'bulk_update'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'delete'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'share_to_space'),
|
||||
actions.savedObject.get('all-licensed-sub-feature-type', 'bulk_get'),
|
||||
actions.savedObject.get('all-licensed-sub-feature-type', 'get'),
|
||||
actions.savedObject.get('all-licensed-sub-feature-type', 'find'),
|
||||
actions.savedObject.get('all-licensed-sub-feature-type', 'create'),
|
||||
actions.savedObject.get('all-licensed-sub-feature-type', 'bulk_create'),
|
||||
actions.savedObject.get('all-licensed-sub-feature-type', 'update'),
|
||||
actions.savedObject.get('all-licensed-sub-feature-type', 'bulk_update'),
|
||||
actions.savedObject.get('all-licensed-sub-feature-type', 'delete'),
|
||||
actions.savedObject.get('all-licensed-sub-feature-type', 'share_to_space'),
|
||||
actions.savedObject.get('read-sub-feature-type', 'bulk_get'),
|
||||
actions.savedObject.get('read-sub-feature-type', 'get'),
|
||||
actions.savedObject.get('read-sub-feature-type', 'find'),
|
||||
actions.savedObject.get('read-licensed-sub-feature-type', 'bulk_get'),
|
||||
actions.savedObject.get('read-licensed-sub-feature-type', 'get'),
|
||||
actions.savedObject.get('read-licensed-sub-feature-type', 'find'),
|
||||
actions.ui.get('foo', 'foo'),
|
||||
actions.ui.get('foo', 'sub-feature-ui'),
|
||||
actions.ui.get('foo', 'licensed-sub-feature-ui'),
|
||||
]);
|
||||
|
||||
expect(actual.features).toHaveProperty(`foo.minimal_read`, [
|
||||
actions.login,
|
||||
actions.version,
|
||||
actions.ui.get('foo', 'foo'),
|
||||
]);
|
||||
|
||||
expect(actual).toHaveProperty('global.all', [
|
||||
actions.login,
|
||||
actions.version,
|
||||
actions.api.get('features'),
|
||||
actions.space.manage,
|
||||
actions.ui.get('spaces', 'manage'),
|
||||
actions.ui.get('management', 'kibana', 'spaces'),
|
||||
actions.ui.get('catalogue', 'spaces'),
|
||||
actions.ui.get('enterpriseSearch', 'all'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'bulk_get'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'get'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'find'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'create'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'bulk_create'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'update'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'bulk_update'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'delete'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'share_to_space'),
|
||||
actions.savedObject.get('all-licensed-sub-feature-type', 'bulk_get'),
|
||||
actions.savedObject.get('all-licensed-sub-feature-type', 'get'),
|
||||
actions.savedObject.get('all-licensed-sub-feature-type', 'find'),
|
||||
actions.savedObject.get('all-licensed-sub-feature-type', 'create'),
|
||||
actions.savedObject.get('all-licensed-sub-feature-type', 'bulk_create'),
|
||||
actions.savedObject.get('all-licensed-sub-feature-type', 'update'),
|
||||
actions.savedObject.get('all-licensed-sub-feature-type', 'bulk_update'),
|
||||
actions.savedObject.get('all-licensed-sub-feature-type', 'delete'),
|
||||
actions.savedObject.get('all-licensed-sub-feature-type', 'share_to_space'),
|
||||
actions.savedObject.get('read-sub-feature-type', 'bulk_get'),
|
||||
actions.savedObject.get('read-sub-feature-type', 'get'),
|
||||
actions.savedObject.get('read-sub-feature-type', 'find'),
|
||||
actions.savedObject.get('read-licensed-sub-feature-type', 'bulk_get'),
|
||||
actions.savedObject.get('read-licensed-sub-feature-type', 'get'),
|
||||
actions.savedObject.get('read-licensed-sub-feature-type', 'find'),
|
||||
actions.ui.get('foo', 'foo'),
|
||||
actions.ui.get('foo', 'sub-feature-ui'),
|
||||
actions.ui.get('foo', 'licensed-sub-feature-ui'),
|
||||
]);
|
||||
expect(actual).toHaveProperty('global.read', [
|
||||
actions.login,
|
||||
actions.version,
|
||||
actions.savedObject.get('all-sub-feature-type', 'bulk_get'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'get'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'find'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'create'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'bulk_create'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'update'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'bulk_update'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'delete'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'share_to_space'),
|
||||
actions.savedObject.get('all-licensed-sub-feature-type', 'bulk_get'),
|
||||
actions.savedObject.get('all-licensed-sub-feature-type', 'get'),
|
||||
actions.savedObject.get('all-licensed-sub-feature-type', 'find'),
|
||||
actions.savedObject.get('all-licensed-sub-feature-type', 'create'),
|
||||
actions.savedObject.get('all-licensed-sub-feature-type', 'bulk_create'),
|
||||
actions.savedObject.get('all-licensed-sub-feature-type', 'update'),
|
||||
actions.savedObject.get('all-licensed-sub-feature-type', 'bulk_update'),
|
||||
actions.savedObject.get('all-licensed-sub-feature-type', 'delete'),
|
||||
actions.savedObject.get('all-licensed-sub-feature-type', 'share_to_space'),
|
||||
actions.savedObject.get('read-sub-feature-type', 'bulk_get'),
|
||||
actions.savedObject.get('read-sub-feature-type', 'get'),
|
||||
actions.savedObject.get('read-sub-feature-type', 'find'),
|
||||
actions.savedObject.get('read-licensed-sub-feature-type', 'bulk_get'),
|
||||
actions.savedObject.get('read-licensed-sub-feature-type', 'get'),
|
||||
actions.savedObject.get('read-licensed-sub-feature-type', 'find'),
|
||||
actions.ui.get('foo', 'foo'),
|
||||
actions.ui.get('foo', 'sub-feature-ui'),
|
||||
actions.ui.get('foo', 'licensed-sub-feature-ui'),
|
||||
]);
|
||||
|
||||
expect(actual).toHaveProperty('space.all', [
|
||||
actions.login,
|
||||
actions.version,
|
||||
actions.savedObject.get('all-sub-feature-type', 'bulk_get'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'get'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'find'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'create'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'bulk_create'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'update'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'bulk_update'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'delete'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'share_to_space'),
|
||||
actions.savedObject.get('all-licensed-sub-feature-type', 'bulk_get'),
|
||||
actions.savedObject.get('all-licensed-sub-feature-type', 'get'),
|
||||
actions.savedObject.get('all-licensed-sub-feature-type', 'find'),
|
||||
actions.savedObject.get('all-licensed-sub-feature-type', 'create'),
|
||||
actions.savedObject.get('all-licensed-sub-feature-type', 'bulk_create'),
|
||||
actions.savedObject.get('all-licensed-sub-feature-type', 'update'),
|
||||
actions.savedObject.get('all-licensed-sub-feature-type', 'bulk_update'),
|
||||
actions.savedObject.get('all-licensed-sub-feature-type', 'delete'),
|
||||
actions.savedObject.get('all-licensed-sub-feature-type', 'share_to_space'),
|
||||
actions.savedObject.get('read-sub-feature-type', 'bulk_get'),
|
||||
actions.savedObject.get('read-sub-feature-type', 'get'),
|
||||
actions.savedObject.get('read-sub-feature-type', 'find'),
|
||||
actions.savedObject.get('read-licensed-sub-feature-type', 'bulk_get'),
|
||||
actions.savedObject.get('read-licensed-sub-feature-type', 'get'),
|
||||
actions.savedObject.get('read-licensed-sub-feature-type', 'find'),
|
||||
actions.ui.get('foo', 'foo'),
|
||||
actions.ui.get('foo', 'sub-feature-ui'),
|
||||
actions.ui.get('foo', 'licensed-sub-feature-ui'),
|
||||
]);
|
||||
expect(actual).toHaveProperty('space.read', [
|
||||
actions.login,
|
||||
actions.version,
|
||||
actions.savedObject.get('all-sub-feature-type', 'bulk_get'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'get'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'find'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'create'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'bulk_create'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'update'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'bulk_update'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'delete'),
|
||||
actions.savedObject.get('all-sub-feature-type', 'share_to_space'),
|
||||
actions.savedObject.get('all-licensed-sub-feature-type', 'bulk_get'),
|
||||
actions.savedObject.get('all-licensed-sub-feature-type', 'get'),
|
||||
actions.savedObject.get('all-licensed-sub-feature-type', 'find'),
|
||||
actions.savedObject.get('all-licensed-sub-feature-type', 'create'),
|
||||
actions.savedObject.get('all-licensed-sub-feature-type', 'bulk_create'),
|
||||
actions.savedObject.get('all-licensed-sub-feature-type', 'update'),
|
||||
actions.savedObject.get('all-licensed-sub-feature-type', 'bulk_update'),
|
||||
actions.savedObject.get('all-licensed-sub-feature-type', 'delete'),
|
||||
actions.savedObject.get('all-licensed-sub-feature-type', 'share_to_space'),
|
||||
actions.savedObject.get('read-sub-feature-type', 'bulk_get'),
|
||||
actions.savedObject.get('read-sub-feature-type', 'get'),
|
||||
actions.savedObject.get('read-sub-feature-type', 'find'),
|
||||
actions.savedObject.get('read-licensed-sub-feature-type', 'bulk_get'),
|
||||
actions.savedObject.get('read-licensed-sub-feature-type', 'get'),
|
||||
actions.savedObject.get('read-licensed-sub-feature-type', 'find'),
|
||||
actions.ui.get('foo', 'foo'),
|
||||
actions.ui.get('foo', 'sub-feature-ui'),
|
||||
actions.ui.get('foo', 'licensed-sub-feature-ui'),
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -25,7 +25,7 @@ export interface PrivilegesService {
|
|||
export function privilegesFactory(
|
||||
actions: Actions,
|
||||
featuresService: FeaturesPluginSetup,
|
||||
licenseService: Pick<SecurityLicense, 'getFeatures'>
|
||||
licenseService: Pick<SecurityLicense, 'getFeatures' | 'getType'>
|
||||
) {
|
||||
const featurePrivilegeBuilder = featurePrivilegeBuilderFactory(actions);
|
||||
|
||||
|
@ -33,6 +33,7 @@ export function privilegesFactory(
|
|||
get() {
|
||||
const features = featuresService.getKibanaFeatures();
|
||||
const { allowSubFeaturePrivileges } = licenseService.getFeatures();
|
||||
const licenseType = licenseService.getType()!;
|
||||
const basePrivilegeFeatures = features.filter(
|
||||
(feature) => !feature.excludeFromBasePrivileges
|
||||
);
|
||||
|
@ -43,6 +44,7 @@ export function privilegesFactory(
|
|||
basePrivilegeFeatures.forEach((feature) => {
|
||||
for (const { privilegeId, privilege } of featurePrivilegeIterator(feature, {
|
||||
augmentWithSubFeaturePrivileges: true,
|
||||
licenseType,
|
||||
predicate: (pId, featurePrivilege) => !featurePrivilege.excludeFromBasePrivileges,
|
||||
})) {
|
||||
const privilegeActions = featurePrivilegeBuilder.getActions(privilege, feature);
|
||||
|
@ -61,6 +63,7 @@ export function privilegesFactory(
|
|||
featurePrivileges[feature.id] = {};
|
||||
for (const featurePrivilege of featurePrivilegeIterator(feature, {
|
||||
augmentWithSubFeaturePrivileges: true,
|
||||
licenseType,
|
||||
})) {
|
||||
featurePrivileges[feature.id][featurePrivilege.privilegeId] = [
|
||||
actions.login,
|
||||
|
@ -72,6 +75,7 @@ export function privilegesFactory(
|
|||
if (allowSubFeaturePrivileges && feature.subFeatures?.length > 0) {
|
||||
for (const featurePrivilege of featurePrivilegeIterator(feature, {
|
||||
augmentWithSubFeaturePrivileges: false,
|
||||
licenseType,
|
||||
})) {
|
||||
featurePrivileges[feature.id][`minimal_${featurePrivilege.privilegeId}`] = [
|
||||
actions.login,
|
||||
|
@ -80,7 +84,7 @@ export function privilegesFactory(
|
|||
];
|
||||
}
|
||||
|
||||
for (const subFeaturePrivilege of subFeaturePrivilegeIterator(feature)) {
|
||||
for (const subFeaturePrivilege of subFeaturePrivilegeIterator(feature, licenseType)) {
|
||||
featurePrivileges[feature.id][subFeaturePrivilege.id] = [
|
||||
actions.login,
|
||||
actions.version,
|
||||
|
|
|
@ -110,6 +110,7 @@ describe('Security Plugin', () => {
|
|||
},
|
||||
},
|
||||
"getFeatures": [Function],
|
||||
"getType": [Function],
|
||||
"isEnabled": [Function],
|
||||
"isLicenseAvailable": [Function],
|
||||
},
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue