[BeatsCM] error on issues dont disable (#25149)

* display error messages for failed checks vs disableing plugin

* update text

* change order of check

* tweak security check

* re-disable on invalid license, update text

* update text

* tweaks

* tweak text
This commit is contained in:
Matt Apperson 2018-11-06 21:33:07 -05:00
parent 36de9e8325
commit 73ce55289b
7 changed files with 61 additions and 18 deletions

View file

@ -58,13 +58,20 @@ export class KibanaFrameworkAdapter implements FrameworkAdapter {
this.rootComponent = component; this.rootComponent = component;
}; };
public hadValidLicense() { public hasValidLicense() {
if (!this.xpackInfo) { if (!this.xpackInfo) {
return false; return false;
} }
return this.xpackInfo.get('features.beats_management.licenseValid', false); return this.xpackInfo.get('features.beats_management.licenseValid', false);
} }
public licenseExpired() {
if (!this.xpackInfo) {
return false;
}
return this.xpackInfo.get('features.beats_management.licenseExpired', false);
}
public securityEnabled() { public securityEnabled() {
if (!this.xpackInfo) { if (!this.xpackInfo) {
return false; return false;
@ -83,8 +90,9 @@ export class KibanaFrameworkAdapter implements FrameworkAdapter {
public registerManagementSection(pluginId: string, displayName: string, basePath: string) { public registerManagementSection(pluginId: string, displayName: string, basePath: string) {
this.register(this.uiModule); this.register(this.uiModule);
this.hookAngular(() => { this.hookAngular(() => {
if (this.hadValidLicense() && this.securityEnabled()) { if (this.hasValidLicense()) {
const registerSection = () => const registerSection = () =>
this.management.register(pluginId, { this.management.register(pluginId, {
display: 'Beats', // TODO these need to be config options not hard coded in the adapter display: 'Beats', // TODO these need to be config options not hard coded in the adapter
@ -92,7 +100,6 @@ export class KibanaFrameworkAdapter implements FrameworkAdapter {
order: 30, order: 30,
}); });
const getSection = () => this.management.getSection(pluginId); const getSection = () => this.management.getSection(pluginId);
const section = this.management.hasItem(pluginId) ? getSection() : registerSection(); const section = this.management.hasItem(pluginId) ? getSection() : registerSection();
section.register(pluginId, { section.register(pluginId, {
@ -132,7 +139,13 @@ export class KibanaFrameworkAdapter implements FrameworkAdapter {
const xpackInfo = Private(this.XPackInfoProvider); const xpackInfo = Private(this.XPackInfoProvider);
this.xpackInfo = xpackInfo; this.xpackInfo = xpackInfo;
this.shieldUser = await $injector.get('ShieldUser').getCurrent().$promise; if (this.securityEnabled()) {
try {
this.shieldUser = await $injector.get('ShieldUser').getCurrent().$promise;
} catch (e) {
// errors when security disabled, even though we check first because angular
}
}
done(); done();
}); });

View file

@ -55,6 +55,9 @@ export interface FrameworkAdapter {
scope: string[]; scope: string[];
username: string; username: string;
}; };
licenseExpired(): boolean;
securityEnabled(): boolean;
hasValidLicense(): boolean;
setUISettings(key: string, value: any): void; setUISettings(key: string, value: any): void;
render(component: React.ReactElement<any>): void; render(component: React.ReactElement<any>): void;
} }

View file

@ -0,0 +1,13 @@
/*
* 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 * as React from 'react';
import { NoDataLayout } from '../components/layouts/no_data';
export const EnforceSecurityPage: React.SFC<any> = () => (
<NoDataLayout title="Security is not enabled" actionSection={[]}>
<p>You must enable security in Kibana and Elasticsearch to use Beats central management.</p>
</NoDataLayout>
);

View file

@ -0,0 +1,16 @@
/*
* 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 * as React from 'react';
import { NoDataLayout } from '../components/layouts/no_data';
export const InvalidLicensePage: React.SFC<any> = () => (
<NoDataLayout title="Expired license" actionSection={[]}>
<p>
Your current license is expired. Enrolled Beats will continue to work, but you need a valid
license to access the Beats Management UI.
</p>
</NoDataLayout>
);

View file

@ -7,7 +7,7 @@ import * as React from 'react';
import { NoDataLayout } from '../components/layouts/no_data'; import { NoDataLayout } from '../components/layouts/no_data';
export const NoAccessPage: React.SFC<any> = () => ( export const NoAccessPage: React.SFC<any> = () => (
<NoDataLayout title="Access Denied" actionSection={[]}> <NoDataLayout title="Access denied" actionSection={[]}>
<p> <p>
You are not authorized to access Beats central management. To use Beats central management, You are not authorized to access Beats central management. To use Beats central management,
you need the privileges granted by the `beats_admin` role. you need the privileges granted by the `beats_admin` role.

View file

@ -6,11 +6,12 @@
import React from 'react'; import React from 'react';
import { HashRouter, Redirect, Route, Switch } from 'react-router-dom'; import { HashRouter, Redirect, Route, Switch } from 'react-router-dom';
import { Header } from './components/layouts/header'; import { Header } from './components/layouts/header';
import { BreadcrumbConsumer, RouteWithBreadcrumb } from './components/route_with_breadcrumb'; import { BreadcrumbConsumer, RouteWithBreadcrumb } from './components/route_with_breadcrumb';
import { FrontendLibs } from './lib/lib'; import { FrontendLibs } from './lib/lib';
import { BeatDetailsPage } from './pages/beat'; import { BeatDetailsPage } from './pages/beat';
import { EnforceSecurityPage } from './pages/enforce_security';
import { InvalidLicensePage } from './pages/invalid_license';
import { MainPages } from './pages/main'; import { MainPages } from './pages/main';
import { NoAccessPage } from './pages/no_access'; import { NoAccessPage } from './pages/no_access';
import { TagPage } from './pages/tag'; import { TagPage } from './pages/tag';
@ -37,6 +38,8 @@ export const PageRouter: React.SFC<{ libs: FrontendLibs }> = ({ libs }) => {
)} )}
</BreadcrumbConsumer> </BreadcrumbConsumer>
<Switch> <Switch>
{libs.framework.licenseExpired() && <Route render={() => <InvalidLicensePage />} />}
{!libs.framework.securityEnabled() && <Route render={() => <EnforceSecurityPage />} />}
{!libs.framework.getCurrentUser() || {!libs.framework.getCurrentUser() ||
(!libs.framework.getCurrentUser().roles.includes('beats_admin') && (!libs.framework.getCurrentUser().roles.includes('beats_admin') &&
!libs.framework.getCurrentUser().roles.includes('superuser') && ( !libs.framework.getCurrentUser().roles.includes('superuser') && (

View file

@ -62,9 +62,6 @@ export class KibanaBackendFrameworkAdapter implements BackendFrameworkAdapter {
} }
public exposeStaticDir(urlPath: string, dir: string): void { public exposeStaticDir(urlPath: string, dir: string): void {
if (!this.isSecurityEnabled()) {
return;
}
this.server.route({ this.server.route({
handler: { handler: {
directory: { directory: {
@ -101,7 +98,7 @@ export class KibanaBackendFrameworkAdapter implements BackendFrameworkAdapter {
if ( if (
wrappedRequest.user.kind === 'authenticated' && wrappedRequest.user.kind === 'authenticated' &&
!wrappedRequest.user.roles.includes('superuser') && (!wrappedRequest.user.roles.includes('superuser') || !wrappedRequest.user.roles) &&
difference(requiredRoles, wrappedRequest.user.roles).length !== 0 difference(requiredRoles, wrappedRequest.user.roles).length !== 0
) { ) {
return h.response().code(403); return h.response().code(403);
@ -126,13 +123,6 @@ export class KibanaBackendFrameworkAdapter implements BackendFrameworkAdapter {
} }
} }
private isSecurityEnabled = () => {
return (
this.server.plugins.xpack_main.info.isAvailable() &&
this.server.plugins.xpack_main.info.feature('security').isEnabled()
);
};
// TODO make key a param // TODO make key a param
private validateConfig() { private validateConfig() {
// @ts-ignore // @ts-ignore
@ -172,6 +162,7 @@ export class KibanaBackendFrameworkAdapter implements BackendFrameworkAdapter {
return { return {
securityEnabled: true, securityEnabled: true,
licenseValid: false, licenseValid: false,
licenseExpired: false,
message: `Your ${licenseType} license does not support Beats central management features. Please upgrade your license.`, message: `Your ${licenseType} license does not support Beats central management features. Please upgrade your license.`,
}; };
} }
@ -180,7 +171,8 @@ export class KibanaBackendFrameworkAdapter implements BackendFrameworkAdapter {
if (!isLicenseActive) { if (!isLicenseActive) {
return { return {
securityEnabled: true, securityEnabled: true,
licenseValid: false, licenseValid: true,
licenseExpired: true,
message: `You cannot edit, create, or delete your Beats central management configurations because your ${licenseType} license has expired.`, message: `You cannot edit, create, or delete your Beats central management configurations because your ${licenseType} license has expired.`,
}; };
} }
@ -193,6 +185,8 @@ export class KibanaBackendFrameworkAdapter implements BackendFrameworkAdapter {
return { return {
securityEnabled: false, securityEnabled: false,
licenseValid: true, licenseValid: true,
licenseExpired: false,
message, message,
}; };
} }
@ -201,6 +195,7 @@ export class KibanaBackendFrameworkAdapter implements BackendFrameworkAdapter {
return { return {
securityEnabled: true, securityEnabled: true,
licenseValid: true, licenseValid: true,
licenseExpired: false,
}; };
} }
} }