Remove circular dependency between features and security (#100206) (#100341)

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

Co-authored-by: Larry Gregory <larry.gregory@elastic.co>
This commit is contained in:
Kibana Machine 2021-05-21 16:33:37 -04:00 committed by GitHub
parent 0f5a32c429
commit c4b91ace78
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 179 additions and 101 deletions

View file

@ -2101,7 +2101,7 @@
],
"source": {
"path": "x-pack/plugins/features/server/plugin.ts",
"lineNumber": 33
"lineNumber": 41
},
"deprecated": false,
"children": [
@ -2125,7 +2125,7 @@
],
"source": {
"path": "x-pack/plugins/features/server/plugin.ts",
"lineNumber": 34
"lineNumber": 42
},
"deprecated": false,
"children": [
@ -2147,7 +2147,7 @@
],
"source": {
"path": "x-pack/plugins/features/server/plugin.ts",
"lineNumber": 34
"lineNumber": 42
},
"deprecated": false,
"isRequired": true
@ -2175,7 +2175,7 @@
],
"source": {
"path": "x-pack/plugins/features/server/plugin.ts",
"lineNumber": 35
"lineNumber": 43
},
"deprecated": false,
"children": [
@ -2197,7 +2197,7 @@
],
"source": {
"path": "x-pack/plugins/features/server/plugin.ts",
"lineNumber": 35
"lineNumber": 43
},
"deprecated": false,
"isRequired": true
@ -2225,7 +2225,7 @@
],
"source": {
"path": "x-pack/plugins/features/server/plugin.ts",
"lineNumber": 41
"lineNumber": 49
},
"deprecated": false,
"children": [],
@ -2251,7 +2251,7 @@
],
"source": {
"path": "x-pack/plugins/features/server/plugin.ts",
"lineNumber": 47
"lineNumber": 55
},
"deprecated": false,
"children": [],
@ -2270,7 +2270,7 @@
],
"source": {
"path": "x-pack/plugins/features/server/plugin.ts",
"lineNumber": 48
"lineNumber": 56
},
"deprecated": false,
"children": [],
@ -2288,11 +2288,47 @@
],
"source": {
"path": "x-pack/plugins/features/server/plugin.ts",
"lineNumber": 56
"lineNumber": 64
},
"deprecated": false,
"children": [],
"returnComment": []
},
{
"parentPluginId": "features",
"id": "def-server.PluginSetupContract.featurePrivilegeIterator",
"type": "Function",
"tags": [],
"label": "featurePrivilegeIterator",
"description": [
"\nUtility for iterating through all privileges belonging to a specific feature.\n{@see FeaturePrivilegeIterator }"
],
"signature": [
"FeaturePrivilegeIterator"
],
"source": {
"path": "x-pack/plugins/features/server/plugin.ts",
"lineNumber": 70
},
"deprecated": false
},
{
"parentPluginId": "features",
"id": "def-server.PluginSetupContract.subFeaturePrivilegeIterator",
"type": "Function",
"tags": [],
"label": "subFeaturePrivilegeIterator",
"description": [
"\nUtility for iterating through all sub-feature privileges belonging to a specific feature.\n{@see SubFeaturePrivilegeIterator }"
],
"signature": [
"SubFeaturePrivilegeIterator"
],
"source": {
"path": "x-pack/plugins/features/server/plugin.ts",
"lineNumber": 76
},
"deprecated": false
}
],
"initialIsOpen": false
@ -2306,7 +2342,7 @@
"description": [],
"source": {
"path": "x-pack/plugins/features/server/plugin.ts",
"lineNumber": 59
"lineNumber": 79
},
"deprecated": false,
"children": [
@ -2330,7 +2366,7 @@
],
"source": {
"path": "x-pack/plugins/features/server/plugin.ts",
"lineNumber": 60
"lineNumber": 80
},
"deprecated": false,
"children": [],
@ -2356,7 +2392,7 @@
],
"source": {
"path": "x-pack/plugins/features/server/plugin.ts",
"lineNumber": 61
"lineNumber": 81
},
"deprecated": false,
"children": [],

View file

@ -1,11 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
// the file created to remove TS cicular dependency between features and security pluin
// https://github.com/elastic/kibana/issues/87388
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
export { featurePrivilegeIterator } from '../../security/server/authorization';

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import { KibanaFeature } from '../../../../../features/server';
import { KibanaFeature } from '../';
import { featurePrivilegeIterator } from './feature_privilege_iterator';
describe('featurePrivilegeIterator', () => {

View file

@ -7,20 +7,48 @@
import _ from 'lodash';
import type { FeatureKibanaPrivileges, KibanaFeature } from '../../../../../features/server';
import type { LicenseType } from '../../../../../licensing/server';
import type { FeatureKibanaPrivileges, KibanaFeature } from '../';
import type { LicenseType } from '../../../licensing/server';
import { subFeaturePrivilegeIterator } from './sub_feature_privilege_iterator';
interface IteratorOptions {
/**
* Options to control feature privilege iteration.
*/
export interface FeaturePrivilegeIteratorOptions {
/**
* Augment each privilege definition with its sub-feature privileges.
*/
augmentWithSubFeaturePrivileges: boolean;
/**
* The current license type. Controls which sub-features are returned, as they may have different license terms than the overall feature.
*/
licenseType: LicenseType;
/**
* Optional predicate to filter the returned set of privileges.
*/
predicate?: (privilegeId: string, privilege: FeatureKibanaPrivileges) => boolean;
}
export function* featurePrivilegeIterator(
/**
* Utility for iterating through all privileges belonging to a specific feature.
* Iteration can be customized in several ways:
* - Filter privileges with a given predicate.
* - Augment privileges with their respective sub-feature privileges.
*
* @param feature the feature whose privileges to iterate through.
* @param options options to control iteration.
*/
export type FeaturePrivilegeIterator = (
feature: KibanaFeature,
options: IteratorOptions
): IterableIterator<{ privilegeId: string; privilege: FeatureKibanaPrivileges }> {
options: FeaturePrivilegeIteratorOptions
) => IterableIterator<{ privilegeId: string; privilege: FeatureKibanaPrivileges }>;
const featurePrivilegeIterator: FeaturePrivilegeIterator = function* featurePrivilegeIterator(
feature: KibanaFeature,
options: FeaturePrivilegeIteratorOptions
) {
for (const entry of Object.entries(feature.privileges ?? {})) {
const [privilegeId, privilege] = entry;
@ -37,7 +65,7 @@ export function* featurePrivilegeIterator(
yield { privilegeId, privilege };
}
}
}
};
function mergeWithSubFeatures(
privilegeId: string,
@ -97,3 +125,5 @@ function mergeArrays(input1: readonly string[] | undefined, input2: readonly str
const second = input2 ?? [];
return Array.from(new Set([...first, ...second]));
}
export { featurePrivilegeIterator };

View file

@ -5,5 +5,10 @@
* 2.0.
*/
export type {
FeaturePrivilegeIterator,
FeaturePrivilegeIteratorOptions,
} from './feature_privilege_iterator';
export type { SubFeaturePrivilegeIterator } from './sub_feature_privilege_iterator';
export { featurePrivilegeIterator } from './feature_privilege_iterator';
export { subFeaturePrivilegeIterator } from './sub_feature_privilege_iterator';

View file

@ -5,10 +5,21 @@
* 2.0.
*/
import type { KibanaFeature, SubFeaturePrivilegeConfig } from '../../../../../features/common';
import type { LicenseType } from '../../../../../licensing/server';
import type { KibanaFeature, SubFeaturePrivilegeConfig } from '../../common';
import type { LicenseType } from '../../../licensing/server';
export function* subFeaturePrivilegeIterator(
/**
* Utility for iterating through all sub-feature privileges belonging to a specific feature.
*
* @param feature the feature whose sub-feature privileges to iterate through.
* @param licenseType the current license.
*/
export type SubFeaturePrivilegeIterator = (
feature: KibanaFeature,
licenseType: LicenseType
) => IterableIterator<SubFeaturePrivilegeConfig>;
const subFeaturePrivilegeIterator: SubFeaturePrivilegeIterator = function* subFeaturePrivilegeIterator(
feature: KibanaFeature,
licenseType: LicenseType
): IterableIterator<SubFeaturePrivilegeConfig> {
@ -19,4 +30,6 @@ export function* subFeaturePrivilegeIterator(
);
}
}
}
};
export { subFeaturePrivilegeIterator };

View file

@ -6,6 +6,10 @@
*/
import { PluginSetupContract, PluginStartContract } from './plugin';
import {
featurePrivilegeIterator,
subFeaturePrivilegeIterator,
} from './feature_privilege_iterator';
const createSetup = (): jest.Mocked<PluginSetupContract> => {
return {
@ -15,6 +19,8 @@ const createSetup = (): jest.Mocked<PluginSetupContract> => {
registerKibanaFeature: jest.fn(),
registerElasticsearchFeature: jest.fn(),
enableReportingUiCapabilities: jest.fn(),
featurePrivilegeIterator: jest.fn().mockImplementation(featurePrivilegeIterator),
subFeaturePrivilegeIterator: jest.fn().mockImplementation(subFeaturePrivilegeIterator),
};
};

View file

@ -6,7 +6,6 @@
*/
import { buildOSSFeatures } from './oss_features';
// @ts-expect-error
import { featurePrivilegeIterator } from './feature_privilege_iterator';
import { KibanaFeature } from '.';
import { LicenseType } from '../../licensing/server';

View file

@ -26,6 +26,14 @@ import {
KibanaFeature,
KibanaFeatureConfig,
} from '../common';
import type {
FeaturePrivilegeIterator,
SubFeaturePrivilegeIterator,
} from './feature_privilege_iterator';
import {
featurePrivilegeIterator,
subFeaturePrivilegeIterator,
} from './feature_privilege_iterator';
/**
* Describes public Features plugin contract returned at the `setup` stage.
@ -54,6 +62,18 @@ export interface PluginSetupContract {
* `features` to include Reporting when registering OSS features.
*/
enableReportingUiCapabilities(): void;
/**
* Utility for iterating through all privileges belonging to a specific feature.
* {@see FeaturePrivilegeIterator }
*/
featurePrivilegeIterator: FeaturePrivilegeIterator;
/**
* Utility for iterating through all sub-feature privileges belonging to a specific feature.
* {@see SubFeaturePrivilegeIterator }
*/
subFeaturePrivilegeIterator: SubFeaturePrivilegeIterator;
}
export interface PluginStartContract {
@ -110,6 +130,8 @@ export class FeaturesPlugin
),
getFeaturesUICapabilities,
enableReportingUiCapabilities: this.enableReportingUiCapabilities.bind(this),
featurePrivilegeIterator,
subFeaturePrivilegeIterator,
});
}

View file

@ -51,7 +51,6 @@ import { validateReservedPrivileges } from './validate_reserved_privileges';
export { Actions } from './actions';
export { CheckSavedObjectsPrivileges } from './check_saved_objects_privileges';
export { featurePrivilegeIterator } from './privileges';
interface AuthorizationServiceSetupParams {
packageVersion: string;

View file

@ -8,5 +8,4 @@
export { Actions } from './actions';
export { AuthorizationService, AuthorizationServiceSetup } from './authorization_service';
export { CheckSavedObjectsPrivileges } from './check_saved_objects_privileges';
export { featurePrivilegeIterator } from './privileges';
export { CheckPrivilegesPayload } from './types';

View file

@ -6,4 +6,3 @@
*/
export { privilegesFactory, PrivilegesService } from './privileges';
export { featurePrivilegeIterator } from './feature_privilege_iterator';

View file

@ -85,9 +85,8 @@ describe('features', () => {
}),
];
const mockFeaturesPlugin = {
getKibanaFeatures: jest.fn().mockReturnValue(features),
};
const mockFeaturesPlugin = featuresPluginMock.createSetup();
mockFeaturesPlugin.getKibanaFeatures.mockReturnValue(features);
const mockLicenseService = {
getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: true }),
getType: jest.fn().mockReturnValue('basic'),
@ -190,9 +189,8 @@ describe('features', () => {
}),
];
const mockFeaturesPlugin = {
getKibanaFeatures: jest.fn().mockReturnValue(features),
};
const mockFeaturesPlugin = featuresPluginMock.createSetup();
mockFeaturesPlugin.getKibanaFeatures.mockReturnValue(features);
const mockLicenseService = {
getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: true }),
getType: jest.fn().mockReturnValue('basic'),
@ -268,9 +266,8 @@ describe('features', () => {
}),
];
const mockFeaturesPlugin = {
getKibanaFeatures: jest.fn().mockReturnValue(features),
};
const mockFeaturesPlugin = featuresPluginMock.createSetup();
mockFeaturesPlugin.getKibanaFeatures.mockReturnValue(features);
const mockLicenseService = {
getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: true }),
getType: jest.fn().mockReturnValue('basic'),
@ -413,9 +410,8 @@ describe('features', () => {
}),
];
const mockFeaturesPlugin = {
getKibanaFeatures: jest.fn().mockReturnValue(features),
};
const mockFeaturesPlugin = featuresPluginMock.createSetup();
mockFeaturesPlugin.getKibanaFeatures.mockReturnValue(features);
const mockLicenseService = {
getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: true }),
getType: jest.fn().mockReturnValue('basic'),
@ -502,9 +498,8 @@ describe('features', () => {
}),
];
const mockFeaturesPlugin = {
getKibanaFeatures: jest.fn().mockReturnValue(features),
};
const mockFeaturesPlugin = featuresPluginMock.createSetup();
mockFeaturesPlugin.getKibanaFeatures.mockReturnValue(features);
const mockLicenseService = {
getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: true }),
getType: jest.fn().mockReturnValue('basic'),
@ -577,9 +572,8 @@ describe('features', () => {
}),
];
const mockFeaturesPlugin = {
getKibanaFeatures: jest.fn().mockReturnValue(features),
};
const mockFeaturesPlugin = featuresPluginMock.createSetup();
mockFeaturesPlugin.getKibanaFeatures.mockReturnValue(features);
const mockLicenseService = {
getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: true }),
getType: jest.fn().mockReturnValue('basic'),
@ -653,9 +647,8 @@ describe('features', () => {
}),
];
const mockFeaturesPlugin = {
getKibanaFeatures: jest.fn().mockReturnValue(features),
};
const mockFeaturesPlugin = featuresPluginMock.createSetup();
mockFeaturesPlugin.getKibanaFeatures.mockReturnValue(features);
const mockLicenseService = {
getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: true }),
getType: jest.fn().mockReturnValue('basic'),
@ -723,9 +716,8 @@ describe('reserved', () => {
}),
];
const mockFeaturesPlugin = {
getKibanaFeatures: jest.fn().mockReturnValue(features),
};
const mockFeaturesPlugin = featuresPluginMock.createSetup();
mockFeaturesPlugin.getKibanaFeatures.mockReturnValue(features);
const mockLicenseService = {
getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: true }),
getType: jest.fn().mockReturnValue('basic'),
@ -762,9 +754,8 @@ describe('reserved', () => {
}),
];
const mockFeaturesPlugin = {
getKibanaFeatures: jest.fn().mockReturnValue(features),
};
const mockFeaturesPlugin = featuresPluginMock.createSetup();
mockFeaturesPlugin.getKibanaFeatures.mockReturnValue(features);
const mockLicenseService = {
getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: true }),
getType: jest.fn().mockReturnValue('basic'),
@ -837,9 +828,8 @@ describe('reserved', () => {
}),
];
const mockFeaturesPlugin = {
getKibanaFeatures: jest.fn().mockReturnValue(features),
};
const mockFeaturesPlugin = featuresPluginMock.createSetup();
mockFeaturesPlugin.getKibanaFeatures.mockReturnValue(features);
const mockLicenseService = {
getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: true }),
getType: jest.fn().mockReturnValue('basic'),
@ -901,9 +891,8 @@ describe('subFeatures', () => {
}),
];
const mockFeaturesPlugin = {
getKibanaFeatures: jest.fn().mockReturnValue(features),
};
const mockFeaturesPlugin = featuresPluginMock.createSetup();
mockFeaturesPlugin.getKibanaFeatures.mockReturnValue(features);
const mockLicenseService = {
getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: true }),
getType: jest.fn().mockReturnValue('basic'),
@ -1036,9 +1025,8 @@ describe('subFeatures', () => {
}),
];
const mockFeaturesPlugin = {
getKibanaFeatures: jest.fn().mockReturnValue(features),
};
const mockFeaturesPlugin = featuresPluginMock.createSetup();
mockFeaturesPlugin.getKibanaFeatures.mockReturnValue(features);
const mockLicenseService = {
getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: true }),
getType: jest.fn().mockReturnValue('basic'),
@ -1274,9 +1262,8 @@ describe('subFeatures', () => {
}),
];
const mockFeaturesPlugin = {
getKibanaFeatures: jest.fn().mockReturnValue(features),
};
const mockFeaturesPlugin = featuresPluginMock.createSetup();
mockFeaturesPlugin.getKibanaFeatures.mockReturnValue(features);
const mockLicenseService = {
getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: true }),
getType: jest.fn().mockReturnValue('basic'),
@ -1435,9 +1422,8 @@ describe('subFeatures', () => {
}),
];
const mockFeaturesPlugin = {
getKibanaFeatures: jest.fn().mockReturnValue(features),
};
const mockFeaturesPlugin = featuresPluginMock.createSetup();
mockFeaturesPlugin.getKibanaFeatures.mockReturnValue(features);
const mockLicenseService = {
getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: true }),
getType: jest.fn().mockReturnValue('basic'),
@ -1622,9 +1608,8 @@ describe('subFeatures', () => {
}),
];
const mockFeaturesPlugin = {
getKibanaFeatures: jest.fn().mockReturnValue(features),
};
const mockFeaturesPlugin = featuresPluginMock.createSetup();
mockFeaturesPlugin.getKibanaFeatures.mockReturnValue(features);
const mockLicenseService = {
getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: true }),
getType: jest.fn().mockReturnValue('basic'),
@ -1766,9 +1751,8 @@ describe('subFeatures', () => {
}),
];
const mockFeaturesPlugin = {
getKibanaFeatures: jest.fn().mockReturnValue(features),
};
const mockFeaturesPlugin = featuresPluginMock.createSetup();
mockFeaturesPlugin.getKibanaFeatures.mockReturnValue(features);
const mockLicenseService = {
getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: false }),
getType: jest.fn().mockReturnValue('basic'),
@ -1993,9 +1977,8 @@ describe('subFeatures', () => {
}),
];
const mockFeaturesPlugin = {
getKibanaFeatures: jest.fn().mockReturnValue(features),
};
const mockFeaturesPlugin = featuresPluginMock.createSetup();
mockFeaturesPlugin.getKibanaFeatures.mockReturnValue(features);
const mockLicenseService = {
getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: true }),
getType: jest.fn().mockReturnValue('gold'),
@ -2229,9 +2212,8 @@ describe('subFeatures', () => {
}),
];
const mockFeaturesPlugin = {
getKibanaFeatures: jest.fn().mockReturnValue(features),
};
const mockFeaturesPlugin = featuresPluginMock.createSetup();
mockFeaturesPlugin.getKibanaFeatures.mockReturnValue(features);
const mockLicenseService = {
getFeatures: jest.fn().mockReturnValue({ allowSubFeaturePrivileges: true }),
getType: jest.fn().mockReturnValue('platinum'),

View file

@ -15,10 +15,6 @@ import type { SecurityLicense } from '../../../common/licensing';
import type { RawKibanaPrivileges } from '../../../common/model';
import type { Actions } from '../actions';
import { featurePrivilegeBuilderFactory } from './feature_privilege_builder';
import {
featurePrivilegeIterator,
subFeaturePrivilegeIterator,
} from './feature_privilege_iterator';
export interface PrivilegesService {
get(): RawKibanaPrivileges;
@ -44,7 +40,7 @@ export function privilegesFactory(
let readActions: string[] = [];
basePrivilegeFeatures.forEach((feature) => {
for (const { privilegeId, privilege } of featurePrivilegeIterator(feature, {
for (const { privilegeId, privilege } of featuresService.featurePrivilegeIterator(feature, {
augmentWithSubFeaturePrivileges: true,
licenseType,
predicate: (pId, featurePrivilege) => !featurePrivilege.excludeFromBasePrivileges,
@ -63,7 +59,7 @@ export function privilegesFactory(
const featurePrivileges: Record<string, Record<string, string[]>> = {};
for (const feature of features) {
featurePrivileges[feature.id] = {};
for (const featurePrivilege of featurePrivilegeIterator(feature, {
for (const featurePrivilege of featuresService.featurePrivilegeIterator(feature, {
augmentWithSubFeaturePrivileges: true,
licenseType,
})) {
@ -75,7 +71,7 @@ export function privilegesFactory(
}
if (allowSubFeaturePrivileges && feature.subFeatures?.length > 0) {
for (const featurePrivilege of featurePrivilegeIterator(feature, {
for (const featurePrivilege of featuresService.featurePrivilegeIterator(feature, {
augmentWithSubFeaturePrivileges: false,
licenseType,
})) {
@ -86,7 +82,10 @@ export function privilegesFactory(
];
}
for (const subFeaturePrivilege of subFeaturePrivilegeIterator(feature, licenseType)) {
for (const subFeaturePrivilege of featuresService.subFeaturePrivilegeIterator(
feature,
licenseType
)) {
featurePrivileges[feature.id][subFeaturePrivilege.id] = [
actions.login,
actions.version,