mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
* Changing the Spaces management section to behave like the other FC controlled sections * Adding those glorious tests and fixing a bug * Fixing some test descriptions * Making the mergeCapabilities operation emulate the old behavior * Fixing privileges test with the addition of the new action * Updating jest snapshot * Adding tests, preventing additional clobbering * Changing requireUICapability to use management.kibana.spaces
This commit is contained in:
parent
e0b9c017db
commit
3362066469
12 changed files with 312 additions and 19 deletions
84
src/legacy/server/capabilities/merge_capabilities.test.ts
Normal file
84
src/legacy/server/capabilities/merge_capabilities.test.ts
Normal file
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { mergeCapabilities } from './merge_capabilities';
|
||||
|
||||
const defaultProps = {
|
||||
catalogue: {},
|
||||
management: {},
|
||||
navLinks: {},
|
||||
};
|
||||
|
||||
test(`"{ foo: {} }" doesn't clobber "{ foo: { bar: true } }"`, () => {
|
||||
const output1 = mergeCapabilities({ foo: { bar: true } }, { foo: {} });
|
||||
expect(output1).toEqual({ ...defaultProps, foo: { bar: true } });
|
||||
|
||||
const output2 = mergeCapabilities({ foo: { bar: true } }, { foo: {} });
|
||||
expect(output2).toEqual({ ...defaultProps, foo: { bar: true } });
|
||||
});
|
||||
|
||||
test(`"{ foo: { bar: true } }" doesn't clobber "{ baz: { quz: true } }"`, () => {
|
||||
const output1 = mergeCapabilities({ foo: { bar: true } }, { baz: { quz: true } });
|
||||
expect(output1).toEqual({ ...defaultProps, foo: { bar: true }, baz: { quz: true } });
|
||||
|
||||
const output2 = mergeCapabilities({ baz: { quz: true } }, { foo: { bar: true } });
|
||||
expect(output2).toEqual({ ...defaultProps, foo: { bar: true }, baz: { quz: true } });
|
||||
});
|
||||
|
||||
test(`"{ foo: { bar: { baz: true } } }" doesn't clobber "{ foo: { bar: { quz: true } } }"`, () => {
|
||||
const output1 = mergeCapabilities(
|
||||
{ foo: { bar: { baz: true } } },
|
||||
{ foo: { bar: { quz: true } } }
|
||||
);
|
||||
expect(output1).toEqual({ ...defaultProps, foo: { bar: { baz: true, quz: true } } });
|
||||
|
||||
const output2 = mergeCapabilities(
|
||||
{ foo: { bar: { quz: true } } },
|
||||
{ foo: { bar: { baz: true } } }
|
||||
);
|
||||
expect(output2).toEqual({ ...defaultProps, foo: { bar: { baz: true, quz: true } } });
|
||||
});
|
||||
|
||||
test(`error is thrown if boolean and object clash`, () => {
|
||||
expect(() => {
|
||||
mergeCapabilities({ foo: { bar: { baz: true } } }, { foo: { bar: true } });
|
||||
}).toThrowErrorMatchingInlineSnapshot(`"a boolean and an object can't be merged"`);
|
||||
|
||||
expect(() => {
|
||||
mergeCapabilities({ foo: { bar: true } }, { foo: { bar: { baz: true } } });
|
||||
}).toThrowErrorMatchingInlineSnapshot(`"a boolean and an object can't be merged"`);
|
||||
});
|
||||
|
||||
test(`supports duplicates as long as the booleans are the same`, () => {
|
||||
const output1 = mergeCapabilities({ foo: { bar: true } }, { foo: { bar: true } });
|
||||
expect(output1).toEqual({ ...defaultProps, foo: { bar: true } });
|
||||
|
||||
const output2 = mergeCapabilities({ foo: { bar: false } }, { foo: { bar: false } });
|
||||
expect(output2).toEqual({ ...defaultProps, foo: { bar: false } });
|
||||
});
|
||||
|
||||
test(`error is thrown if merging "true" and "false"`, () => {
|
||||
expect(() => {
|
||||
mergeCapabilities({ foo: { bar: false } }, { foo: { bar: true } });
|
||||
}).toThrowErrorMatchingInlineSnapshot(`"\\"true\\" and \\"false\\" can't be merged"`);
|
||||
|
||||
expect(() => {
|
||||
mergeCapabilities({ foo: { bar: true } }, { foo: { bar: false } });
|
||||
}).toThrowErrorMatchingInlineSnapshot(`"\\"true\\" and \\"false\\" can't be merged"`);
|
||||
});
|
|
@ -17,23 +17,28 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import typeDetect from 'type-detect';
|
||||
import { merge } from 'lodash';
|
||||
import { Capabilities } from '../../../core/public';
|
||||
|
||||
export const mergeCapabilities = (...sources: Array<Partial<Capabilities>>): Capabilities =>
|
||||
sources.reduce(
|
||||
(capabilities: Capabilities, source) => {
|
||||
Object.entries(source).forEach(([key, value = {}]) => {
|
||||
capabilities[key] = {
|
||||
...value,
|
||||
...capabilities[key],
|
||||
};
|
||||
});
|
||||
|
||||
return capabilities;
|
||||
},
|
||||
merge(
|
||||
{
|
||||
navLinks: {},
|
||||
management: {},
|
||||
catalogue: {},
|
||||
} as Capabilities
|
||||
},
|
||||
...sources,
|
||||
(a: any, b: any) => {
|
||||
if (
|
||||
(typeDetect(a) === 'boolean' && typeDetect(b) === 'Object') ||
|
||||
(typeDetect(b) === 'boolean' && typeDetect(a) === 'Object')
|
||||
) {
|
||||
throw new Error(`a boolean and an object can't be merged`);
|
||||
}
|
||||
|
||||
if (typeDetect(a) === 'boolean' && typeDetect(b) === 'boolean' && a !== b) {
|
||||
throw new Error(`"true" and "false" can't be merged`);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
|
|
@ -318,7 +318,13 @@ describe('features', () => {
|
|||
actions.login,
|
||||
actions.version,
|
||||
...(expectGetFeatures ? [actions.api.get('features')] : []),
|
||||
...(expectManageSpaces ? [actions.space.manage, actions.ui.get('spaces', 'manage')] : []),
|
||||
...(expectManageSpaces
|
||||
? [
|
||||
actions.space.manage,
|
||||
actions.ui.get('spaces', 'manage'),
|
||||
actions.ui.get('management', 'kibana', 'spaces'),
|
||||
]
|
||||
: []),
|
||||
actions.app.get('app-1'),
|
||||
actions.app.get('app-2'),
|
||||
actions.ui.get('catalogue', 'catalogue-1'),
|
||||
|
@ -403,7 +409,13 @@ describe('features', () => {
|
|||
actions.login,
|
||||
actions.version,
|
||||
...(expectGetFeatures ? [actions.api.get('features')] : []),
|
||||
...(expectManageSpaces ? [actions.space.manage, actions.ui.get('spaces', 'manage')] : []),
|
||||
...(expectManageSpaces
|
||||
? [
|
||||
actions.space.manage,
|
||||
actions.ui.get('spaces', 'manage'),
|
||||
actions.ui.get('management', 'kibana', 'spaces'),
|
||||
]
|
||||
: []),
|
||||
actions.ui.get('catalogue', 'bar-catalogue-1'),
|
||||
actions.ui.get('catalogue', 'bar-catalogue-2'),
|
||||
actions.ui.get('management', 'bar-management', 'bar-management-1'),
|
||||
|
@ -614,7 +626,13 @@ describe('features', () => {
|
|||
actions.login,
|
||||
actions.version,
|
||||
...(expectGetFeatures ? [actions.api.get('features')] : []),
|
||||
...(expectManageSpaces ? [actions.space.manage, actions.ui.get('spaces', 'manage')] : []),
|
||||
...(expectManageSpaces
|
||||
? [
|
||||
actions.space.manage,
|
||||
actions.ui.get('spaces', 'manage'),
|
||||
actions.ui.get('management', 'kibana', 'spaces'),
|
||||
]
|
||||
: []),
|
||||
actions.allHack,
|
||||
]);
|
||||
expect(actual).toHaveProperty(`${group}.read`, [actions.login, actions.version]);
|
||||
|
|
|
@ -65,6 +65,7 @@ export function privilegesFactory(actions: Actions, xpackMainPlugin: XPackMainPl
|
|||
actions.api.get('features'),
|
||||
actions.space.manage,
|
||||
actions.ui.get('spaces', 'manage'),
|
||||
actions.ui.get('management', 'kibana', 'spaces'),
|
||||
...allActions,
|
||||
actions.allHack,
|
||||
],
|
||||
|
|
|
@ -49,6 +49,11 @@ export const spaces = (kibana: Record<string, any>) =>
|
|||
spaces: {
|
||||
manage: true,
|
||||
},
|
||||
management: {
|
||||
kibana: {
|
||||
spaces: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
|
|
|
@ -144,7 +144,7 @@ class ManageSpacePageUI extends Component<Props, State> {
|
|||
const { showAlteringActiveSpaceDialog } = this.state;
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<div data-test-subj="spaces-edit-page">
|
||||
{this.getFormHeading()}
|
||||
|
||||
<EuiSpacer size={'s'} />
|
||||
|
@ -188,7 +188,7 @@ class ManageSpacePageUI extends Component<Props, State> {
|
|||
}}
|
||||
/>
|
||||
)}
|
||||
</Fragment>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ import routes from 'ui/routes';
|
|||
import { AdvancedSettingsSubtitle } from './components/advanced_settings_subtitle';
|
||||
import { AdvancedSettingsTitle } from './components/advanced_settings_title';
|
||||
|
||||
const MANAGE_SPACES_KEY = 'manage_spaces';
|
||||
const MANAGE_SPACES_KEY = 'spaces';
|
||||
|
||||
routes.defaults(/\/management/, {
|
||||
resolve: {
|
||||
|
|
|
@ -21,6 +21,7 @@ const reactRootNodeId = 'manageSpacesReactRoot';
|
|||
routes.when('/management/spaces/list', {
|
||||
template,
|
||||
k7Breadcrumbs: getListBreadcrumbs,
|
||||
requireUICapability: 'management.kibana.spaces',
|
||||
controller(
|
||||
$scope: any,
|
||||
$http: any,
|
||||
|
@ -53,6 +54,7 @@ routes.when('/management/spaces/list', {
|
|||
routes.when('/management/spaces/create', {
|
||||
template,
|
||||
k7Breadcrumbs: getCreateBreadcrumbs,
|
||||
requireUICapability: 'management.kibana.spaces',
|
||||
controller(
|
||||
$scope: any,
|
||||
$http: any,
|
||||
|
@ -89,6 +91,7 @@ routes.when('/management/spaces/edit', {
|
|||
routes.when('/management/spaces/edit/:spaceId', {
|
||||
template,
|
||||
k7Breadcrumbs: () => getEditBreadcrumbs(),
|
||||
requireUICapability: 'management.kibana.spaces',
|
||||
controller(
|
||||
$scope: any,
|
||||
$http: any,
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
exports[`SpacesGridPage renders as expected 1`] = `
|
||||
<div
|
||||
className="spcGridPage"
|
||||
data-test-subj="spaces-grid-page"
|
||||
>
|
||||
<EuiPageContent
|
||||
horizontalPosition="center"
|
||||
|
|
|
@ -72,7 +72,7 @@ class SpacesGridPageUI extends Component<Props, State> {
|
|||
|
||||
public render() {
|
||||
return (
|
||||
<div className="spcGridPage">
|
||||
<div className="spcGridPage" data-test-subj="spaces-grid-page">
|
||||
<EuiPageContent horizontalPosition="center">{this.getPageContent()}</EuiPageContent>
|
||||
<SecureSpaceMessage />
|
||||
{this.getConfirmDeleteModal()}
|
||||
|
|
|
@ -0,0 +1,175 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
import expect from '@kbn/expect';
|
||||
import { KibanaFunctionalTestDefaultProviders } from '../../../../types/providers';
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default function({ getPageObjects, getService }: KibanaFunctionalTestDefaultProviders) {
|
||||
const esArchiver = getService('esArchiver');
|
||||
const security = getService('security');
|
||||
const PageObjects = getPageObjects(['common', 'settings', 'security']);
|
||||
const appsMenu = getService('appsMenu');
|
||||
const testSubjects = getService('testSubjects');
|
||||
|
||||
describe('security feature controls', () => {
|
||||
before(async () => {
|
||||
await esArchiver.load('empty_kibana');
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await esArchiver.unload('empty_kibana');
|
||||
});
|
||||
|
||||
describe('global all base privilege', () => {
|
||||
before(async () => {
|
||||
await security.role.create('global_all_role', {
|
||||
kibana: [
|
||||
{
|
||||
base: ['all'],
|
||||
spaces: ['*'],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await security.user.create('global_all_user', {
|
||||
password: 'global_all_user-password',
|
||||
roles: ['global_all_role'],
|
||||
full_name: 'test user',
|
||||
});
|
||||
|
||||
await PageObjects.security.logout();
|
||||
|
||||
await PageObjects.security.login('global_all_user', 'global_all_user-password', {
|
||||
expectSpaceSelector: false,
|
||||
});
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await Promise.all([
|
||||
security.role.delete('global_all_role'),
|
||||
security.user.delete('global_all_user'),
|
||||
PageObjects.security.logout(),
|
||||
]);
|
||||
});
|
||||
|
||||
it('shows management navlink', async () => {
|
||||
const navLinks = (await appsMenu.readLinks()).map(
|
||||
(link: Record<string, string>) => link.text
|
||||
);
|
||||
expect(navLinks).to.contain('Management');
|
||||
});
|
||||
|
||||
it(`displays Spaces management section`, async () => {
|
||||
await PageObjects.settings.navigateTo();
|
||||
await testSubjects.existOrFail('spaces');
|
||||
});
|
||||
|
||||
it(`can navigate to spaces grid page`, async () => {
|
||||
await PageObjects.common.navigateToActualUrl('kibana', 'management/spaces/list', {
|
||||
ensureCurrentUrl: false,
|
||||
shouldLoginIfPrompted: false,
|
||||
});
|
||||
|
||||
await testSubjects.existOrFail('spaces-grid-page');
|
||||
});
|
||||
|
||||
it(`can navigate to create new space page`, async () => {
|
||||
await PageObjects.common.navigateToActualUrl('kibana', 'management/spaces/create', {
|
||||
ensureCurrentUrl: false,
|
||||
shouldLoginIfPrompted: false,
|
||||
});
|
||||
|
||||
await testSubjects.existOrFail('spaces-edit-page');
|
||||
});
|
||||
|
||||
it(`can navigate to edit space page`, async () => {
|
||||
await PageObjects.common.navigateToActualUrl('kibana', 'management/spaces/edit/default', {
|
||||
ensureCurrentUrl: false,
|
||||
shouldLoginIfPrompted: false,
|
||||
});
|
||||
|
||||
await testSubjects.existOrFail('spaces-edit-page');
|
||||
});
|
||||
});
|
||||
|
||||
describe('default space all base privilege', () => {
|
||||
before(async () => {
|
||||
await security.role.create('default_space_all_role', {
|
||||
kibana: [
|
||||
{
|
||||
base: ['all'],
|
||||
spaces: ['default'],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await security.user.create('default_space_all_user', {
|
||||
password: 'default_space_all_user-password',
|
||||
roles: ['default_space_all_role'],
|
||||
full_name: 'test user',
|
||||
});
|
||||
|
||||
await PageObjects.security.logout();
|
||||
|
||||
await PageObjects.security.login(
|
||||
'default_space_all_user',
|
||||
'default_space_all_user-password',
|
||||
{
|
||||
expectSpaceSelector: false,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await Promise.all([
|
||||
security.role.delete('default_space_all_role'),
|
||||
security.user.delete('default_space_all_user'),
|
||||
PageObjects.security.logout(),
|
||||
]);
|
||||
});
|
||||
|
||||
it('shows management navlink', async () => {
|
||||
const navLinks = (await appsMenu.readLinks()).map(
|
||||
(link: Record<string, string>) => link.text
|
||||
);
|
||||
expect(navLinks).to.contain('Management');
|
||||
});
|
||||
|
||||
it(`doesn't display Spaces management section`, async () => {
|
||||
await PageObjects.settings.navigateTo();
|
||||
await testSubjects.existOrFail('objects'); // this ensures we've gotten to the management page
|
||||
await testSubjects.missingOrFail('spaces');
|
||||
});
|
||||
|
||||
it(`can't navigate to spaces grid page`, async () => {
|
||||
await PageObjects.common.navigateToActualUrl('kibana', 'management/spaces/list', {
|
||||
ensureCurrentUrl: false,
|
||||
shouldLoginIfPrompted: false,
|
||||
});
|
||||
|
||||
await testSubjects.existOrFail('homeApp');
|
||||
});
|
||||
|
||||
it(`can't navigate to create new space page`, async () => {
|
||||
await PageObjects.common.navigateToActualUrl('kibana', 'management/spaces/create', {
|
||||
ensureCurrentUrl: false,
|
||||
shouldLoginIfPrompted: false,
|
||||
});
|
||||
|
||||
await testSubjects.existOrFail('homeApp');
|
||||
});
|
||||
|
||||
it(`can't navigate to edit space page`, async () => {
|
||||
await PageObjects.common.navigateToActualUrl('kibana', 'management/spaces/edit/default', {
|
||||
ensureCurrentUrl: false,
|
||||
shouldLoginIfPrompted: false,
|
||||
});
|
||||
|
||||
await testSubjects.existOrFail('homeApp');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
|
@ -10,6 +10,7 @@ export default function spacesApp({ loadTestFile }: KibanaFunctionalTestDefaultP
|
|||
describe('Spaces app', function spacesAppTestSuite() {
|
||||
this.tags('ciGroup4');
|
||||
|
||||
loadTestFile(require.resolve('./feature_controls/spaces_security'));
|
||||
loadTestFile(require.resolve('./spaces_selection'));
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue