Skip to content

Commit

Permalink
[7.x] Hide management sections based on cluster/index privileges (#67791
Browse files Browse the repository at this point in the history
) (#77345)

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
  • Loading branch information
legrego and elasticmachine authored Sep 14, 2020
1 parent c3a2451 commit 047152f
Show file tree
Hide file tree
Showing 238 changed files with 6,997 additions and 2,378 deletions.
48 changes: 24 additions & 24 deletions docs/developer/architecture/security/feature-registration.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,12 @@ Registering features also gives your plugin access to “UI Capabilities”. The

=== Registering a feature

Feature registration is controlled via the built-in `xpack_main` plugin. To register a feature, call `xpack_main`'s `registerFeature` function from your plugin's `init` function, and provide the appropriate details:
Feature registration is controlled via the built-in `features` plugin. To register a feature, call `features`'s `registerKibanaFeature` function from your plugin's `setup` lifecycle function, and provide the appropriate details:

["source","javascript"]
-----------
init(server) {
const xpackMainPlugin = server.plugins.xpack_main;
xpackMainPlugin.registerFeature({
setup(core, { features }) {
features.registerKibanaFeature({
// feature details here.
});
}
Expand Down Expand Up @@ -45,12 +44,12 @@ Registering a feature consists of the following fields. For more information, co
|An array of applications this feature enables. Typically, all of your plugin's apps (from `uiExports`) will be included here.

|`privileges` (required)
|{kib-repo}blob/{branch}/x-pack/plugins/features/common/feature.ts[`FeatureConfig`].
|{kib-repo}blob/{branch}/x-pack/plugins/features/common/feature.ts[`KibanaFeatureConfig`].
|See <<example-1-canvas,Example 1>> and <<example-2-dev-tools,Example 2>>
|The set of privileges this feature requires to function.

|`subFeatures` (optional)
|{kib-repo}blob/{branch}/x-pack/plugins/features/common/feature.ts[`FeatureConfig`].
|{kib-repo}blob/{branch}/x-pack/plugins/features/common/feature.ts[`KibanaFeatureConfig`].
|See <<example-3-discover,Example 3>>
|The set of subfeatures that enables finer access control than the `all` and `read` feature privileges. These options are only available in the Gold subscription level and higher.

Expand All @@ -73,25 +72,26 @@ For a full explanation of fields and options, consult the {kib-repo}blob/{branch
=== Using UI Capabilities

UI Capabilities are available to your public (client) plugin code. These capabilities are read-only, and are used to inform the UI. This object is namespaced by feature id. For example, if your feature id is “foo”, then your UI Capabilities are stored at `uiCapabilities.foo`.
To access capabilities, import them from `ui/capabilities`:
Capabilities can be accessed from your plugin's `start` lifecycle from the `core.application` service:

["source","javascript"]
-----------
import { uiCapabilities } from 'ui/capabilities';
public start(core) {
const { capabilities } = core.application;
const canUserSave = uiCapabilities.foo.save;
if (canUserSave) {
// show save button
const canUserSave = capabilities.foo.save;
if (canUserSave) {
// show save button
}
}
-----------

[[example-1-canvas]]
=== Example 1: Canvas Application
["source","javascript"]
-----------
init(server) {
const xpackMainPlugin = server.plugins.xpack_main;
xpackMainPlugin.registerFeature({
public setup(core, { features }) {
features.registerKibanaFeature({
id: 'canvas',
name: 'Canvas',
icon: 'canvasApp',
Expand Down Expand Up @@ -130,11 +130,13 @@ The `all` privilege defines a single “save” UI Capability. To access this in

["source","javascript"]
-----------
import { uiCapabilities } from 'ui/capabilities';
public start(core) {
const { capabilities } = core.application;
const canUserSave = uiCapabilities.canvas.save;
if (canUserSave) {
// show save button
const canUserSave = capabilities.canvas.save;
if (canUserSave) {
// show save button
}
}
-----------

Expand All @@ -145,9 +147,8 @@ Because the `read` privilege does not define the `save` capability, users with r

["source","javascript"]
-----------
init(server) {
const xpackMainPlugin = server.plugins.xpack_main;
xpackMainPlugin.registerFeature({
public setup(core, { features }) {
features.registerKibanaFeature({
id: 'dev_tools',
name: i18n.translate('xpack.features.devToolsFeatureName', {
defaultMessage: 'Dev Tools',
Expand Down Expand Up @@ -206,9 +207,8 @@ a single "Create Short URLs" subfeature privilege is defined, which allows users

["source","javascript"]
-----------
init(server) {
const xpackMainPlugin = server.plugins.xpack_main;
xpackMainPlugin.registerFeature({
public setup(core, { features }) {
features.registerKibanaFeature({
{
id: 'discover',
name: i18n.translate('xpack.features.discoverFeatureName', {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,10 @@ created index. For more information, see {ref}/indices-templates.html[Index temp
* *Delete a policy.* You can’t delete a policy that is currently in use or
recover a deleted index.

[float]
=== Required permissions

The `manage_ilm` cluster privilege is required to access *Index lifecycle policies*.

You can add these privileges in *Stack Management > Security > Roles*.

7 changes: 7 additions & 0 deletions docs/management/managing-ccr.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ image::images/cross-cluster-replication-list-view.png[][Cross-cluster replicatio
* The Elasticsearch version of the local cluster must be the same as or newer than the remote cluster.
Refer to {ref}/ccr-overview.html[this document] for more information.

[float]
=== Required permissions

The `manage` and `manage_ccr` cluster privileges are required to access *Cross-Cluster Replication*.

You can add these privileges in *Stack Management > Security > Roles*.

[float]
[[configure-replication]]
=== Configure replication
Expand Down
7 changes: 7 additions & 0 deletions docs/management/managing-licenses.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ See {ref}/encrypting-communications.html[Encrypting communications].
{kib} and the {ref}/start-basic.html[start basic API] provide a list of all of
the features that will no longer be supported if you revert to a basic license.

[float]
=== Required permissions

The `manage` cluster privilege is required to access *License Management*.

You can add this privilege in *Stack Management > Security > Roles*.

[discrete]
[[update-license]]
=== Update your license
Expand Down
7 changes: 7 additions & 0 deletions docs/management/managing-remote-clusters.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ To get started, open the menu, then go to *Stack Management > Data > Remote Clus
[role="screenshot"]
image::images/remote-clusters-list-view.png[Remote Clusters list view, including Add a remote cluster button]

[float]
=== Required permissions

The `manage` cluster privilege is required to access *Remote Clusters*.

You can add this privilege in *Stack Management > Security > Roles*.

[float]
[[managing-remote-clusters]]
=== Add a remote cluster
Expand Down
7 changes: 7 additions & 0 deletions docs/management/rollups/create_and_manage_rollups.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ image::images/management_rollup_list.png[][List of currently active rollup jobs]
Before using this feature, you should be familiar with how rollups work.
{ref}/xpack-rollup.html[Rolling up historical data] is a good source for more detailed information.

[float]
=== Required permissions

The `manage_rollup` cluster privilege is required to access *Rollup jobs*.

You can add this privilege in *Stack Management > Security > Roles*.

[float]
[[create-and-manage-rollup-job]]
=== Create a rollup job
Expand Down
8 changes: 8 additions & 0 deletions docs/management/upgrade-assistant/index.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ Before you upgrade, make sure that you are using the latest released minor
version of {es} to see the most up-to-date deprecation issues.
For example, if you want to upgrade to to 7.0, make sure that you are using 6.8.

[float]
=== Required permissions

The `manage` cluster privilege is required to access the *Upgrade assistant*.
Additional privileges may be needed to perform certain actions.

You can add this privilege in *Stack Management > Security > Roles*.

[float]
=== Reindexing

Expand Down
2 changes: 1 addition & 1 deletion examples/alerting_example/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export class AlertingExamplePlugin implements Plugin<void, void, AlertingExample
alerts.registerType(alwaysFiringAlert);
alerts.registerType(peopleInSpaceAlert);

features.registerFeature({
features.registerKibanaFeature({
id: ALERTING_EXAMPLE_APP_ID,
name: i18n.translate('alertsExample.featureRegistry.alertsExampleFeatureName', {
defaultMessage: 'Alerts Example',
Expand Down
2 changes: 1 addition & 1 deletion src/core/MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -1283,7 +1283,7 @@ _See also: [Server's CoreSetup API Docs](/docs/development/core/server/kibana-pl
##### Plugin services
| Legacy Platform | New Platform | Notes |
| ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | ----- |
| `server.plugins.xpack_main.registerFeature` | [`plugins.features.registerFeature`](x-pack/plugins/features/server/plugin.ts) | |
| `server.plugins.xpack_main.registerFeature` | [`plugins.features.registerKibanaFeature`](x-pack/plugins/features/server/plugin.ts) | |
| `server.plugins.xpack_main.feature(pluginID).registerLicenseCheckResultsGenerator` | [`x-pack licensing plugin`](/x-pack/plugins/licensing/README.md) | |

#### UI Exports
Expand Down
20 changes: 20 additions & 0 deletions src/plugins/management/public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*/

import { i18n } from '@kbn/i18n';
import { BehaviorSubject } from 'rxjs';
import { ManagementSetup, ManagementStart } from './types';
import { FeatureCatalogueCategory, HomePublicPluginSetup } from '../../home/public';
import {
Expand All @@ -27,6 +28,9 @@ import {
DEFAULT_APP_CATEGORIES,
PluginInitializerContext,
AppMountParameters,
AppUpdater,
AppStatus,
AppNavLinkStatus,
} from '../../../core/public';

import {
Expand All @@ -41,6 +45,8 @@ interface ManagementSetupDependencies {
export class ManagementPlugin implements Plugin<ManagementSetup, ManagementStart> {
private readonly managementSections = new ManagementSectionsService();

private readonly appUpdater = new BehaviorSubject<AppUpdater>(() => ({}));

constructor(private initializerContext: PluginInitializerContext) {}

public setup(core: CoreSetup, { home }: ManagementSetupDependencies) {
Expand Down Expand Up @@ -70,6 +76,7 @@ export class ManagementPlugin implements Plugin<ManagementSetup, ManagementStart
order: 9040,
euiIconType: 'managementApp',
category: DEFAULT_APP_CATEGORIES.management,
updater$: this.appUpdater,
async mount(params: AppMountParameters) {
const { renderApp } = await import('./application');
const [coreStart] = await core.getStartServices();
Expand All @@ -89,6 +96,19 @@ export class ManagementPlugin implements Plugin<ManagementSetup, ManagementStart

public start(core: CoreStart) {
this.managementSections.start({ capabilities: core.application.capabilities });
const hasAnyEnabledApps = getSectionsServiceStartPrivate()
.getSectionsEnabled()
.some((section) => section.getAppsEnabled().length > 0);

if (!hasAnyEnabledApps) {
this.appUpdater.next(() => {
return {
status: AppStatus.inaccessible,
navLinkStatus: AppNavLinkStatus.hidden,
};
});
}

return {};
}
}
4 changes: 2 additions & 2 deletions test/common/services/security/test_user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,9 @@ export async function createTestUserService(
}

return new (class TestUser {
async restoreDefaults() {
async restoreDefaults(shouldRefreshBrowser: boolean = true) {
if (isEnabled()) {
await this.setRoles(config.get('security.defaultRoles'));
await this.setRoles(config.get('security.defaultRoles'), shouldRefreshBrowser);
}
}

Expand Down
2 changes: 2 additions & 0 deletions test/functional/services/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import { FilterBarProvider } from './filter_bar';
import { FlyoutProvider } from './flyout';
import { GlobalNavProvider } from './global_nav';
import { InspectorProvider } from './inspector';
import { ManagementMenuProvider } from './management';
import { QueryBarProvider } from './query_bar';
import { RemoteProvider } from './remote';
import { RenderableProvider } from './renderable';
Expand Down Expand Up @@ -91,4 +92,5 @@ export const services = {
savedQueryManagementComponent: SavedQueryManagementComponentProvider,
elasticChart: ElasticChartProvider,
supertest: KibanaSupertestProvider,
managementMenu: ManagementMenuProvider,
};
20 changes: 20 additions & 0 deletions test/functional/services/management/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* 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.
*/

export { ManagementMenuProvider } from './management_menu';
51 changes: 51 additions & 0 deletions test/functional/services/management/management_menu.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* 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 { FtrProviderContext } from 'test/functional/ftr_provider_context';

export function ManagementMenuProvider({ getService }: FtrProviderContext) {
const find = getService('find');

class ManagementMenu {
public async getSections() {
const sectionsElements = await find.allByCssSelector(
'.mgtSideBarNav > .euiSideNav__content > .euiSideNavItem'
);

const sections = [];

for (const el of sectionsElements) {
const sectionId = await (await el.findByClassName('euiSideNavItemButton')).getAttribute(
'data-test-subj'
);
const sectionLinks = await Promise.all(
(await el.findAllByCssSelector('.euiSideNavItem > a.euiSideNavItemButton')).map((item) =>
item.getAttribute('data-test-subj')
)
);

sections.push({ sectionId, sectionLinks });
}

return sections;
}
}

return new ManagementMenu();
}
2 changes: 1 addition & 1 deletion x-pack/legacy/plugins/xpack_main/server/xpack_main.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*/

import KbnServer from 'src/legacy/server/kbn_server';
import { Feature, FeatureConfig } from '../../../../plugins/features/server';
import { KibanaFeature } from '../../../../plugins/features/server';
import { XPackInfo, XPackInfoOptions } from './lib/xpack_info';
export { XPackFeature } from './lib/xpack_info';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,9 @@ describe('ensureAuthorized', () => {
await actionsAuthorization.ensureAuthorized('create', 'myType');

expect(authorization.actions.savedObject.get).toHaveBeenCalledWith('action', 'create');
expect(checkPrivileges).toHaveBeenCalledWith(mockAuthorizationAction('action', 'create'));
expect(checkPrivileges).toHaveBeenCalledWith({
kibana: mockAuthorizationAction('action', 'create'),
});

expect(auditLogger.actionsAuthorizationSuccess).toHaveBeenCalledTimes(1);
expect(auditLogger.actionsAuthorizationFailure).not.toHaveBeenCalled();
Expand Down Expand Up @@ -131,10 +133,12 @@ describe('ensureAuthorized', () => {
ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE,
'create'
);
expect(checkPrivileges).toHaveBeenCalledWith([
mockAuthorizationAction(ACTION_SAVED_OBJECT_TYPE, 'get'),
mockAuthorizationAction(ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE, 'create'),
]);
expect(checkPrivileges).toHaveBeenCalledWith({
kibana: [
mockAuthorizationAction(ACTION_SAVED_OBJECT_TYPE, 'get'),
mockAuthorizationAction(ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE, 'create'),
],
});

expect(auditLogger.actionsAuthorizationSuccess).toHaveBeenCalledTimes(1);
expect(auditLogger.actionsAuthorizationFailure).not.toHaveBeenCalled();
Expand Down
Loading

0 comments on commit 047152f

Please sign in to comment.