mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
This commit is contained in:
parent
0468537fd0
commit
cacd96c01d
13 changed files with 271 additions and 14 deletions
|
@ -7,3 +7,4 @@
|
|||
export { PLUGIN } from './plugin';
|
||||
export { BASE_PATH } from './base_path';
|
||||
export { EXTERNAL_LINKS } from './external_links';
|
||||
export { APP_PERMISSION } from './permissions';
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export const APP_PERMISSION = 'cluster:manage';
|
|
@ -6,7 +6,12 @@
|
|||
|
||||
import { resolve } from 'path';
|
||||
import { PLUGIN } from './common/constants';
|
||||
import { registerLicenseRoute, registerStartTrialRoutes, registerStartBasicRoute } from './server/routes/api/license/';
|
||||
import {
|
||||
registerLicenseRoute,
|
||||
registerStartTrialRoutes,
|
||||
registerStartBasicRoute,
|
||||
registerPermissionsRoute
|
||||
} from './server/routes/api/license/';
|
||||
import { createRouter } from '../../server/lib/create_router';
|
||||
|
||||
export function licenseManagement(kibana) {
|
||||
|
@ -27,6 +32,7 @@ export function licenseManagement(kibana) {
|
|||
registerLicenseRoute(router, xpackInfo);
|
||||
registerStartTrialRoutes(router, xpackInfo);
|
||||
registerStartBasicRoute(router, xpackInfo);
|
||||
registerPermissionsRoute(router, xpackInfo);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
28
x-pack/plugins/license_management/public/app.container.js
Normal file
28
x-pack/plugins/license_management/public/app.container.js
Normal file
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* 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 { connect } from 'react-redux';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
|
||||
import { App as PresentationComponent } from './app';
|
||||
import { getPermission, isPermissionsLoading, getPermissionsError } from './store/reducers/licenseManagement';
|
||||
import { loadPermissions } from './store/actions/permissions';
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
hasPermission: getPermission(state),
|
||||
permissionsLoading: isPermissionsLoading(state),
|
||||
permissionsError: getPermissionsError(state),
|
||||
};
|
||||
};
|
||||
|
||||
const mapDispatchToProps = {
|
||||
loadPermissions,
|
||||
};
|
||||
|
||||
export const App = withRouter(connect(mapStateToProps, mapDispatchToProps)(
|
||||
PresentationComponent
|
||||
));
|
|
@ -4,17 +4,90 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React, { Component } from 'react';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { LicenseDashboard, UploadLicense } from './sections/';
|
||||
import { Switch, Route } from 'react-router-dom';
|
||||
import { BASE_PATH } from '../common/constants';
|
||||
import { EuiPageBody } from '@elastic/eui';
|
||||
import { BASE_PATH, APP_PERMISSION } from '../common/constants';
|
||||
import { EuiPageBody, EuiEmptyPrompt, EuiText, EuiLoadingSpinner, EuiCallOut } from '@elastic/eui';
|
||||
|
||||
export default () => (
|
||||
<EuiPageBody>
|
||||
<Switch>
|
||||
<Route path={`${BASE_PATH}upload_license`} component={UploadLicense} />
|
||||
<Route path={`${BASE_PATH}`} component={LicenseDashboard} />
|
||||
</Switch>
|
||||
</EuiPageBody>
|
||||
);
|
||||
export class App extends Component {
|
||||
componentDidMount() {
|
||||
const { loadPermissions } = this.props;
|
||||
loadPermissions();
|
||||
}
|
||||
|
||||
render() {
|
||||
const { hasPermission, permissionsLoading, permissionsError } = this.props;
|
||||
|
||||
if (permissionsLoading) {
|
||||
return (
|
||||
<EuiEmptyPrompt
|
||||
title={<EuiLoadingSpinner size="xl" />}
|
||||
body={(
|
||||
<EuiText color="subdued">
|
||||
<FormattedMessage
|
||||
id="xpack.licenseMgmt.app.loadingPermissionsDescription"
|
||||
defaultMessage="Checking permissions…"
|
||||
/>
|
||||
</EuiText>
|
||||
)}
|
||||
data-test-subj="sectionLoading"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (permissionsError) {
|
||||
return (
|
||||
<EuiCallOut
|
||||
title={<FormattedMessage
|
||||
id="xpack.licenseMgmt.app.checkingPermissionsErrorMessage"
|
||||
defaultMessage="Error checking permissions"
|
||||
/>}
|
||||
color="danger"
|
||||
iconType="alert"
|
||||
>
|
||||
{permissionsError.data && permissionsError.data.message ? <div>{permissionsError.data.message}</div> : null}
|
||||
</EuiCallOut>
|
||||
);
|
||||
}
|
||||
|
||||
if (!hasPermission) {
|
||||
return (
|
||||
<EuiPageBody>
|
||||
<EuiEmptyPrompt
|
||||
iconType="securityApp"
|
||||
title={
|
||||
<h2>
|
||||
<FormattedMessage
|
||||
id="xpack.licenseMgmt.app.deniedPermissionTitle"
|
||||
defaultMessage="You're missing cluster privileges"
|
||||
/>
|
||||
</h2>
|
||||
}
|
||||
body={
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.licenseMgmt.app.deniedPermissionDescription"
|
||||
defaultMessage="To use License Management, you must have {permissionType} privileges."
|
||||
values={{
|
||||
permissionType: <strong>{APP_PERMISSION}</strong>
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
}
|
||||
/>
|
||||
</EuiPageBody>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<EuiPageBody>
|
||||
<Switch>
|
||||
<Route path={`${BASE_PATH}upload_license`} component={UploadLicense} />
|
||||
<Route path={`${BASE_PATH}`} component={LicenseDashboard} />
|
||||
</Switch>
|
||||
</EuiPageBody>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,3 +57,15 @@ export function canStartTrial() {
|
|||
return $.ajax(options);
|
||||
}
|
||||
|
||||
export function getPermissions() {
|
||||
const options = {
|
||||
url: chrome.addBasePath('/api/license/permissions'),
|
||||
contentType: 'application/json',
|
||||
cache: false,
|
||||
crossDomain: true,
|
||||
type: 'POST',
|
||||
};
|
||||
|
||||
return $.ajax(options);
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ import { setTelemetryOptInService, setTelemetryEnabled, setHttpClient, Telemetry
|
|||
import { I18nContext } from 'ui/i18n';
|
||||
import chrome from 'ui/chrome';
|
||||
|
||||
import App from './app';
|
||||
import { App } from './app.container';
|
||||
import { BASE_PATH } from '../common/constants/base_path';
|
||||
|
||||
import routes from 'ui/routes';
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* 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 { createAction } from 'redux-actions';
|
||||
import { getPermissions } from '../../lib/es';
|
||||
|
||||
export const permissionsLoading = createAction(
|
||||
'LICENSE_MANAGEMENT_PERMISSIONS_LOADING'
|
||||
);
|
||||
|
||||
export const permissionsSuccess = createAction(
|
||||
'LICENSE_MANAGEMENT_PERMISSIONS_SUCCESS'
|
||||
);
|
||||
|
||||
export const permissionsError = createAction(
|
||||
'LICENSE_MANAGEMENT_PERMISSIONS_ERROR'
|
||||
);
|
||||
|
||||
export const loadPermissions = () => async dispatch => {
|
||||
dispatch(permissionsLoading(true));
|
||||
try {
|
||||
const permissions = await getPermissions();
|
||||
dispatch(permissionsLoading(false));
|
||||
dispatch(permissionsSuccess(permissions.hasPermission));
|
||||
} catch (e) {
|
||||
dispatch(permissionsLoading(false));
|
||||
dispatch(permissionsError(e));
|
||||
}
|
||||
};
|
|
@ -10,6 +10,7 @@ import { uploadStatus } from './upload_status';
|
|||
import { startBasicStatus } from './start_basic_license_status';
|
||||
import { uploadErrorMessage } from './upload_error_message';
|
||||
import { trialStatus } from './trial_status';
|
||||
import { permissions } from './permissions';
|
||||
import moment from 'moment-timezone';
|
||||
|
||||
export const WARNING_THRESHOLD_IN_DAYS = 25;
|
||||
|
@ -19,9 +20,23 @@ export const licenseManagement = combineReducers({
|
|||
uploadStatus,
|
||||
uploadErrorMessage,
|
||||
trialStatus,
|
||||
startBasicStatus
|
||||
startBasicStatus,
|
||||
permissions,
|
||||
});
|
||||
|
||||
export const getPermission = state => {
|
||||
return state.permissions.hasPermission;
|
||||
};
|
||||
|
||||
export const isPermissionsLoading = state => {
|
||||
return state.permissions.loading;
|
||||
};
|
||||
|
||||
|
||||
export const getPermissionsError = state => {
|
||||
return state.permissions.error;
|
||||
};
|
||||
|
||||
export const getLicense = state => {
|
||||
return state.license;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* 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 { handleActions } from 'redux-actions';
|
||||
|
||||
import { permissionsSuccess, permissionsError, permissionsLoading } from '../actions/permissions';
|
||||
|
||||
export const permissions = handleActions({
|
||||
[permissionsLoading](state, { payload }) {
|
||||
return {
|
||||
loading: payload,
|
||||
};
|
||||
},
|
||||
[permissionsSuccess](state, { payload }) {
|
||||
return {
|
||||
hasPermission: payload,
|
||||
};
|
||||
},
|
||||
[permissionsError](state, { payload }) {
|
||||
return {
|
||||
error: payload,
|
||||
};
|
||||
},
|
||||
}, {});
|
42
x-pack/plugins/license_management/server/lib/permissions.js
Normal file
42
x-pack/plugins/license_management/server/lib/permissions.js
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* 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 { wrapCustomError } from '../../../../server/lib/create_router/error_wrappers';
|
||||
|
||||
export async function getPermissions(req, xpackInfo) {
|
||||
if (!xpackInfo) {
|
||||
// xpackInfo is updated via poll, so it may not be available until polling has begun.
|
||||
// In this rare situation, tell the client the service is temporarily unavailable.
|
||||
throw wrapCustomError(new Error('Security info unavailable'), 503);
|
||||
}
|
||||
|
||||
const securityInfo = xpackInfo && xpackInfo.isAvailable() && xpackInfo.feature('security');
|
||||
if (!securityInfo || !securityInfo.isAvailable() || !securityInfo.isEnabled()) {
|
||||
// If security isn't enabled, let the user use license management
|
||||
return {
|
||||
hasPermission: true,
|
||||
};
|
||||
}
|
||||
|
||||
const { callWithRequest } = req.server.plugins.elasticsearch.getCluster('admin');
|
||||
const options = {
|
||||
method: 'POST',
|
||||
path: '/_security/user/_has_privileges',
|
||||
body: {
|
||||
cluster: ['manage'], // License management requires "manage" cluster privileges
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await callWithRequest(req, 'transport.request', options);
|
||||
return {
|
||||
hasPermission: response.cluster.manage,
|
||||
};
|
||||
} catch (error) {
|
||||
return error.body;
|
||||
}
|
||||
|
||||
}
|
|
@ -7,3 +7,4 @@
|
|||
export { registerLicenseRoute } from './register_license_route';
|
||||
export { registerStartBasicRoute } from './register_start_basic_route';
|
||||
export { registerStartTrialRoutes } from './register_start_trial_routes';
|
||||
export { registerPermissionsRoute } from './register_permissions_route';
|
||||
|
|
|
@ -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 { getPermissions } from '../../../lib/permissions';
|
||||
|
||||
export function registerPermissionsRoute(router, xpackInfo) {
|
||||
router.post('/permissions', (request) => {
|
||||
return getPermissions(request, xpackInfo);
|
||||
});
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue