Register "minimal" feature privileges regardless of the current license level (#115992)

* bring minimal-* privilege up

* Fix api_integration for minimal_

* attempt to fix minimal_ UI

* fix tests

* Update x-pack/plugins/security/server/authorization/privileges/privileges.test.ts

Co-authored-by: Larry Gregory <lgregorydev@gmail.com>

* fix tests

* fix es-lint

Co-authored-by: Larry Gregory <lgregorydev@gmail.com>
This commit is contained in:
Xavier Mouligneau 2021-10-26 13:49:01 -04:00 committed by GitHub
parent 38a634d543
commit e185afefa2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 84 additions and 78 deletions

View file

@ -678,7 +678,7 @@ describe('FeatureTable', () => {
});
});
it('renders with no privileges granted when minimal feature privileges are assigned, and sub-feature privileges are disallowed', () => {
it('renders with privileges granted when minimal feature privileges are assigned, and sub-feature privileges are disallowed', () => {
const role = createRole([
{
spaces: ['foo'],
@ -710,13 +710,13 @@ describe('FeatureTable', () => {
subFeaturePrivileges: [],
},
with_sub_features: {
primaryFeaturePrivilege: 'none',
primaryFeaturePrivilege: 'all',
subFeaturePrivileges: [],
},
});
});
it('renders with no privileges granted when sub feature privileges are assigned, and sub-feature privileges are disallowed', () => {
it('renders with privileges granted when sub feature privileges are assigned, and sub-feature privileges are disallowed', () => {
const role = createRole([
{
spaces: ['foo'],
@ -748,7 +748,7 @@ describe('FeatureTable', () => {
subFeaturePrivileges: [],
},
with_sub_features: {
primaryFeaturePrivilege: 'none',
primaryFeaturePrivilege: 'read',
subFeaturePrivileges: [],
},
});

View file

@ -278,13 +278,11 @@ export class PrivilegeFormCalculator {
.getMinimalFeaturePrivileges()
.find((mp) => mp.id === correspondingMinimalPrivilegeId)!;
// There are two cases where the minimal privileges aren't available:
// 1. The feature has no registered sub-features
// 2. Sub-feature privileges cannot be customized. When this is the case, the minimal privileges aren't registered with ES,
// There is only one case where the minimal privileges aren't available:
// 1. Sub-feature privileges cannot be customized. When this is the case, the minimal privileges aren't registered with ES,
// so they end up represented in the UI as an empty privilege. Empty privileges cannot be granted other privileges, so if we
// encounter a minimal privilege that isn't granted by it's correspending primary, then we know we've encountered this scenario.
const hasMinimalPrivileges =
feature.subFeatures.length > 0 && fp.grantsPrivilege(correspendingMinimalPrivilege);
const hasMinimalPrivileges = fp.grantsPrivilege(correspendingMinimalPrivilege);
return (
selectedFeaturePrivileges.includes(fp.id) ||
(hasMinimalPrivileges &&

View file

@ -426,7 +426,7 @@ describe('PrivilegeSummaryTable', () => {
with_sub_features: {
'default, space-1': {
hasCustomizedSubFeaturePrivileges: allowSubFeaturePrivileges,
primaryFeaturePrivilege: allowSubFeaturePrivileges ? 'Read' : 'None',
primaryFeaturePrivilege: 'Read',
...maybeExpectSubFeaturePrivileges(allowSubFeaturePrivileges, {
'Cool Sub Feature': [],
}),
@ -693,7 +693,7 @@ describe('PrivilegeSummaryTable', () => {
with_sub_features: {
'*': {
hasCustomizedSubFeaturePrivileges: allowSubFeaturePrivileges,
primaryFeaturePrivilege: allowSubFeaturePrivileges ? 'Read' : 'None',
primaryFeaturePrivilege: 'Read',
...maybeExpectSubFeaturePrivileges(allowSubFeaturePrivileges, {
'Cool Sub Feature': ['All'],
}),
@ -787,7 +787,7 @@ describe('PrivilegeSummaryTable', () => {
with_sub_features: {
'*': {
hasCustomizedSubFeaturePrivileges: allowSubFeaturePrivileges,
primaryFeaturePrivilege: allowSubFeaturePrivileges ? 'Read' : 'None',
primaryFeaturePrivilege: 'Read',
...maybeExpectSubFeaturePrivileges(allowSubFeaturePrivileges, {
'Cool Sub Feature': ['All'],
}),
@ -859,7 +859,7 @@ describe('PrivilegeSummaryTable', () => {
},
'space-1, space-2': {
hasCustomizedSubFeaturePrivileges: allowSubFeaturePrivileges,
primaryFeaturePrivilege: allowSubFeaturePrivileges ? 'All' : 'None',
primaryFeaturePrivilege: 'All',
...maybeExpectSubFeaturePrivileges(allowSubFeaturePrivileges, {
'Cool Sub Feature': ['Cool toggle 2'],
}),

View file

@ -29,14 +29,10 @@ export class SecuredFeature extends KibanaFeature {
([id, privilege]) => new PrimaryFeaturePrivilege(id, privilege, actionMapping[id])
);
if (this.config.subFeatures?.length ?? 0 > 0) {
this.minimalPrimaryFeaturePrivileges = Object.entries(this.config.privileges || {}).map(
([id, privilege]) =>
new PrimaryFeaturePrivilege(`minimal_${id}`, privilege, actionMapping[`minimal_${id}`])
);
} else {
this.minimalPrimaryFeaturePrivileges = [];
}
this.minimalPrimaryFeaturePrivileges = Object.entries(this.config.privileges || {}).map(
([id, privilege]) =>
new PrimaryFeaturePrivilege(`minimal_${id}`, privilege, actionMapping[`minimal_${id}`])
);
this.securedSubFeatures =
this.config.subFeatures?.map((sf) => new SecuredSubFeature(sf, actionMapping)) ?? [];

View file

@ -60,6 +60,8 @@ describe('features', () => {
expect(actual).toHaveProperty('features.foo-feature', {
all: [actions.login, actions.version],
read: [actions.login, actions.version],
minimal_all: [actions.login, actions.version],
minimal_read: [actions.login, actions.version],
});
});
@ -175,6 +177,8 @@ describe('features', () => {
expect(actual).toHaveProperty('features.foo', {
all: [...expectedAllPrivileges],
read: [...expectedReadPrivileges],
minimal_all: [...expectedAllPrivileges],
minimal_read: [...expectedReadPrivileges],
});
});
@ -1627,7 +1631,7 @@ describe('subFeatures', () => {
});
describe(`when license does not allow sub features`, () => {
test(`should augment the primary feature privileges, and should not create minimal or sub-feature privileges`, () => {
test(`should augment the primary feature privileges, and should not create sub-feature privileges`, () => {
const features: KibanaFeature[] = [
new KibanaFeature({
id: 'foo',
@ -1705,7 +1709,11 @@ describe('subFeatures', () => {
actions.ui.get('foo', 'sub-feature-ui'),
]);
expect(actual.features).not.toHaveProperty(`foo.minimal_all`);
expect(actual.features).toHaveProperty(`foo.minimal_all`, [
actions.login,
actions.version,
actions.ui.get('foo', 'foo'),
]);
expect(actual.features).toHaveProperty(`foo.read`, [
actions.login,
@ -1730,7 +1738,11 @@ describe('subFeatures', () => {
actions.ui.get('foo', 'sub-feature-ui'),
]);
expect(actual.features).not.toHaveProperty(`foo.minimal_read`);
expect(actual.features).toHaveProperty(`foo.minimal_read`, [
actions.login,
actions.version,
actions.ui.get('foo', 'foo'),
]);
expect(actual).toHaveProperty('global.all', [
actions.login,

View file

@ -70,18 +70,18 @@ export function privilegesFactory(
];
}
if (allowSubFeaturePrivileges && feature.subFeatures?.length > 0) {
for (const featurePrivilege of featuresService.featurePrivilegeIterator(feature, {
augmentWithSubFeaturePrivileges: false,
licenseHasAtLeast,
})) {
featurePrivileges[feature.id][`minimal_${featurePrivilege.privilegeId}`] = [
actions.login,
actions.version,
...uniq(featurePrivilegeBuilder.getActions(featurePrivilege.privilege, feature)),
];
}
for (const featurePrivilege of featuresService.featurePrivilegeIterator(feature, {
augmentWithSubFeaturePrivileges: false,
licenseHasAtLeast,
})) {
featurePrivileges[feature.id][`minimal_${featurePrivilege.privilegeId}`] = [
actions.login,
actions.version,
...uniq(featurePrivilegeBuilder.getActions(featurePrivilege.privilege, feature)),
];
}
if (allowSubFeaturePrivileges && feature.subFeatures?.length > 0) {
for (const subFeaturePrivilege of featuresService.subFeaturePrivilegeIterator(
feature,
licenseHasAtLeast

View file

@ -45,7 +45,7 @@ export default function ({ getService }: FtrProviderContext) {
});
// Verify that privileges were re-registered.
const expectedBasicLicenseDiscoverPrivileges = ['all', 'read'];
const expectedBasicLicenseDiscoverPrivileges = ['all', 'read', 'minimal_all', 'minimal_read'];
const basicPrivileges = await supertest
.get('/api/security/privileges')
.set('kbn-xsrf', 'xxx')

View file

@ -24,21 +24,21 @@ export default function ({ getService }: FtrProviderContext) {
global: ['all', 'read'],
space: ['all', 'read'],
features: {
graph: ['all', 'read'],
savedObjectsTagging: ['all', 'read'],
canvas: ['all', 'read'],
maps: ['all', 'read'],
observabilityCases: ['all', 'read'],
fleet: ['all', 'read'],
actions: ['all', 'read'],
stackAlerts: ['all', 'read'],
ml: ['all', 'read'],
siem: ['all', 'read'],
uptime: ['all', 'read'],
securitySolutionCases: ['all', 'read'],
infrastructure: ['all', 'read'],
logs: ['all', 'read'],
apm: ['all', 'read'],
graph: ['all', 'read', 'minimal_all', 'minimal_read'],
savedObjectsTagging: ['all', 'read', 'minimal_all', 'minimal_read'],
canvas: ['all', 'read', 'minimal_all', 'minimal_read'],
maps: ['all', 'read', 'minimal_all', 'minimal_read'],
observabilityCases: ['all', 'read', 'minimal_all', 'minimal_read'],
fleet: ['all', 'read', 'minimal_all', 'minimal_read'],
actions: ['all', 'read', 'minimal_all', 'minimal_read'],
stackAlerts: ['all', 'read', 'minimal_all', 'minimal_read'],
ml: ['all', 'read', 'minimal_all', 'minimal_read'],
siem: ['all', 'read', 'minimal_all', 'minimal_read'],
uptime: ['all', 'read', 'minimal_all', 'minimal_read'],
securitySolutionCases: ['all', 'read', 'minimal_all', 'minimal_read'],
infrastructure: ['all', 'read', 'minimal_all', 'minimal_read'],
logs: ['all', 'read', 'minimal_all', 'minimal_read'],
apm: ['all', 'read', 'minimal_all', 'minimal_read'],
discover: [
'all',
'read',
@ -56,10 +56,10 @@ export default function ({ getService }: FtrProviderContext) {
'url_create',
'store_search_session',
],
dev_tools: ['all', 'read'],
advancedSettings: ['all', 'read'],
indexPatterns: ['all', 'read'],
savedObjectsManagement: ['all', 'read'],
dev_tools: ['all', 'read', 'minimal_all', 'minimal_read'],
advancedSettings: ['all', 'read', 'minimal_all', 'minimal_read'],
indexPatterns: ['all', 'read', 'minimal_all', 'minimal_read'],
savedObjectsManagement: ['all', 'read', 'minimal_all', 'minimal_read'],
osquery: [
'all',
'read',

View file

@ -20,29 +20,29 @@ export default function ({ getService }: FtrProviderContext) {
// Roles are associated with these privileges, and we shouldn't be removing them in a minor version.
const expected = {
features: {
discover: ['all', 'read'],
visualize: ['all', 'read'],
dashboard: ['all', 'read'],
dev_tools: ['all', 'read'],
advancedSettings: ['all', 'read'],
indexPatterns: ['all', 'read'],
savedObjectsManagement: ['all', 'read'],
savedObjectsTagging: ['all', 'read'],
graph: ['all', 'read'],
maps: ['all', 'read'],
observabilityCases: ['all', 'read'],
canvas: ['all', 'read'],
infrastructure: ['all', 'read'],
logs: ['all', 'read'],
uptime: ['all', 'read'],
apm: ['all', 'read'],
osquery: ['all', 'read'],
ml: ['all', 'read'],
siem: ['all', 'read'],
securitySolutionCases: ['all', 'read'],
fleet: ['all', 'read'],
stackAlerts: ['all', 'read'],
actions: ['all', 'read'],
discover: ['all', 'read', 'minimal_all', 'minimal_read'],
visualize: ['all', 'read', 'minimal_all', 'minimal_read'],
dashboard: ['all', 'read', 'minimal_all', 'minimal_read'],
dev_tools: ['all', 'read', 'minimal_all', 'minimal_read'],
advancedSettings: ['all', 'read', 'minimal_all', 'minimal_read'],
indexPatterns: ['all', 'read', 'minimal_all', 'minimal_read'],
savedObjectsManagement: ['all', 'read', 'minimal_all', 'minimal_read'],
savedObjectsTagging: ['all', 'read', 'minimal_all', 'minimal_read'],
graph: ['all', 'read', 'minimal_all', 'minimal_read'],
maps: ['all', 'read', 'minimal_all', 'minimal_read'],
observabilityCases: ['all', 'read', 'minimal_all', 'minimal_read'],
canvas: ['all', 'read', 'minimal_all', 'minimal_read'],
infrastructure: ['all', 'read', 'minimal_all', 'minimal_read'],
logs: ['all', 'read', 'minimal_all', 'minimal_read'],
uptime: ['all', 'read', 'minimal_all', 'minimal_read'],
apm: ['all', 'read', 'minimal_all', 'minimal_read'],
osquery: ['all', 'read', 'minimal_all', 'minimal_read'],
ml: ['all', 'read', 'minimal_all', 'minimal_read'],
siem: ['all', 'read', 'minimal_all', 'minimal_read'],
securitySolutionCases: ['all', 'read', 'minimal_all', 'minimal_read'],
fleet: ['all', 'read', 'minimal_all', 'minimal_read'],
stackAlerts: ['all', 'read', 'minimal_all', 'minimal_read'],
actions: ['all', 'read', 'minimal_all', 'minimal_read'],
},
global: ['all', 'read'],
space: ['all', 'read'],