Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hide management sections based on cluster/index privileges #67791

Merged
merged 31 commits into from
Sep 14, 2020
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
bda8e2e
Hide management sections based on ES privileges
legrego May 29, 2020
54af4aa
add manage_templates as a required cluster privilege for index manage…
legrego Aug 5, 2020
14ad75e
Merge branch 'master' of github.com:elastic/kibana into fc/hide-manag…
legrego Aug 5, 2020
0cd465b
fix cluster privilege name
legrego Aug 6, 2020
d68ba6b
Merge branch 'master' of github.com:elastic/kibana into fc/hide-manag…
legrego Aug 6, 2020
0eb691b
Merge branch 'master' into fc/hide-management
elasticmachine Aug 10, 2020
3006fbe
Merge branch 'master' into fc/hide-management
elasticmachine Aug 17, 2020
e70b472
addressing first round of PR feedback
legrego Sep 1, 2020
0603510
Merge branch 'master' of github.com:elastic/kibana into fc/hide-manag…
legrego Sep 1, 2020
776bac2
additional check_privileges tests
legrego Sep 1, 2020
c2274e8
started adding additional disable_ui_capabilities tests
legrego Sep 1, 2020
5e67ede
Merge branch 'fc/hide-management' of github.com:legrego/kibana into f…
legrego Sep 1, 2020
303d20b
fixing the build
legrego Sep 1, 2020
820ddf9
doc updates
legrego Sep 1, 2020
d3f30aa
tweak short-circuit logic for disabling capabilities
legrego Sep 2, 2020
2a07fbe
dont conditionally register ml management app
legrego Sep 2, 2020
920406b
fixes from merge
legrego Sep 2, 2020
688e420
documenting required privileges
legrego Sep 2, 2020
b0dd152
Merge branch 'master' of github.com:elastic/kibana into fc/hide-manag…
legrego Sep 2, 2020
96c0665
Merge branch 'master' into fc/hide-management
elasticmachine Sep 3, 2020
679eaa1
Merge branch 'master' of github.com:elastic/kibana into fc/hide-manag…
legrego Sep 3, 2020
a50ec42
you'll float too
legrego Sep 3, 2020
19e1e00
fix unrelated typo in migration docs
legrego Sep 3, 2020
987c6f4
Addressing second round of feedback
legrego Sep 3, 2020
fc5eb68
Merge branch 'master' into fc/hide-management
elasticmachine Sep 8, 2020
f862899
updating ML access tests
legrego Sep 9, 2020
d28ecd6
ML user should not access the management interface
legrego Sep 9, 2020
fac5cfe
Merge branch 'master' into fc/hide-management
elasticmachine Sep 10, 2020
b28a951
Merge branch 'master' into fc/hide-management
elasticmachine Sep 10, 2020
607f633
Merge branch 'master' of github.com:elastic/kibana into fc/hide-manag…
legrego Sep 14, 2020
1246bd1
Merge branch 'fc/hide-management' of github.com:legrego/kibana into f…
legrego Sep 14, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@
/x-pack/plugins/encrypted_saved_objects/ @elastic/kibana-security
/x-pack/plugins/security/ @elastic/kibana-security
/x-pack/test/api_integration/apis/security/ @elastic/kibana-security
/x-pack/test/ui_capabilities/ @elastic/kibana-security

# Kibana Localization
/src/dev/i18n/ @elastic/kibana-localization
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 @@ -1284,7 +1284,7 @@ _See also: [Server's CoreSetup API Docs](/docs/development/core/server/kibana-pl

| 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
2 changes: 1 addition & 1 deletion src/core/server/capabilities/merge_capabilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { mergeWith } from 'lodash';
import { Capabilities } from './types';

export const mergeCapabilities = (...sources: Array<Partial<Capabilities>>): Capabilities =>
mergeWith({}, ...sources, (a: any, b: any) => {
mergeWith({}, ...sources, (a: any, b: any, key: any) => {
legrego marked this conversation as resolved.
Show resolved Hide resolved
if (
(typeof a === 'boolean' && typeof b === 'object') ||
(typeof a === 'object' && typeof b === 'boolean')
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 @@ -68,6 +74,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 @@ -87,6 +94,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 @@ -84,4 +85,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();
}
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
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,11 @@ export class ActionsAuthorization {
const { authorization } = this;
if (authorization?.mode?.useRbacForRequest(this.request)) {
const checkPrivileges = authorization.checkPrivilegesDynamicallyWithRequest(this.request);
const { hasAllRequested, username } = await checkPrivileges(
operationAlias[operation]
const { hasAllRequested, username } = await checkPrivileges({
kibana: operationAlias[operation]
? operationAlias[operation](authorization)
: authorization.actions.savedObject.get(ACTION_SAVED_OBJECT_TYPE, operation)
);
: authorization.actions.savedObject.get(ACTION_SAVED_OBJECT_TYPE, operation),
});
if (hasAllRequested) {
this.auditLogger.actionsAuthorizationSuccess(username, operation, actionTypeId);
} else {
Expand Down
2 changes: 1 addition & 1 deletion x-pack/plugins/actions/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ export class ActionsPlugin implements Plugin<Promise<PluginSetupContract>, Plugi
);
}

plugins.features.registerFeature(ACTIONS_FEATURE);
plugins.features.registerKibanaFeature(ACTIONS_FEATURE);
setupSavedObjects(core.savedObjects, plugins.encryptedSavedObjects);

plugins.eventLog.registerProviderActions(EVENT_LOG_PROVIDER, Object.values(EVENT_LOG_ACTIONS));
Expand Down
2 changes: 1 addition & 1 deletion x-pack/plugins/alerting_builtins/server/plugin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ describe('AlertingBuiltins Plugin', () => {
"name": "Index threshold",
}
`);
expect(featuresSetup.registerFeature).toHaveBeenCalledWith(BUILT_IN_ALERTS_FEATURE);
expect(featuresSetup.registerKibanaFeature).toHaveBeenCalledWith(BUILT_IN_ALERTS_FEATURE);
});

it('should return a service in the expected shape', async () => {
Expand Down
2 changes: 1 addition & 1 deletion x-pack/plugins/alerting_builtins/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export class AlertingBuiltinsPlugin implements Plugin<IService, IService> {
core: CoreSetup,
{ alerts, features }: AlertingBuiltinsDeps
): Promise<IService> {
features.registerFeature(BUILT_IN_ALERTS_FEATURE);
features.registerKibanaFeature(BUILT_IN_ALERTS_FEATURE);

registerBuiltInAlertTypes({
service: this.service,
Expand Down
4 changes: 2 additions & 2 deletions x-pack/plugins/alerts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ In addition, when users are inside your feature you might want to grant them acc
You can control all of these abilities by assigning privileges to the Alerting Framework from within your own feature, for example:

```typescript
features.registerFeature({
features.registerKibanaFeature({
id: 'my-application-id',
name: 'My Application',
app: [],
Expand Down Expand Up @@ -347,7 +347,7 @@ In this example we can see the following:
It's important to note that any role can be granted a mix of `all` and `read` privileges accross multiple type, for example:

```typescript
features.registerFeature({
features.registerKibanaFeature({
id: 'my-application-id',
name: 'My Application',
app: [],
Expand Down
Loading