License Management to New Platform (#51886)

* License Management public -> NP

* - Server to NP
- Slight update to filepicker style (center it)

* Fix snapshots and types

* Server-side: separate new and legacy dependencies [skip ci]

* Fix license upload route after refactor

* Client side: separate new from legacy dependencies

* xpackInfo -> xPackInfo

* Fix types [skip ci]

* Remove kbnUrl, autoLogout. Add history and update paths.

* Update upload license test

* Remove use of legacy chrome, remove use of k7breadcrumbs, replace some common strings with variable and use NP i18n
This commit is contained in:
Jean-Louis Leysens 2019-12-09 14:54:23 +01:00 committed by GitHub
parent 3d2db42b6e
commit 3d36356cab
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
93 changed files with 1349 additions and 907 deletions

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { AddLicense } from '../public/sections/license_dashboard/add_license';
import { AddLicense } from '../public/np_ready/application/sections/license_dashboard/add_license';
import { createMockLicense, getComponent } from './util';
jest.mock(`@elastic/eui/lib/components/form/form_row/make_id`, () => () => `generated-id`);

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { LicenseStatus } from '../public/sections/license_dashboard/license_status';
import { LicenseStatus } from '../public/np_ready/application/sections/license_dashboard/license_status';
import { createMockLicense, getComponent } from './util';
describe('LicenseStatus component', () => {

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { RequestTrialExtension } from '../public/sections/license_dashboard/request_trial_extension';
import { RequestTrialExtension } from '../public/np_ready/application/sections/license_dashboard/request_trial_extension';
import { createMockLicense, getComponent } from './util';
jest.mock(`@elastic/eui/lib/components/form/form_row/make_id`, () => () => `generated-id`);

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { RevertToBasic } from '../public/sections/license_dashboard/revert_to_basic';
import { RevertToBasic } from '../public/np_ready/application/sections/license_dashboard/revert_to_basic';
import { createMockLicense, getComponent } from './util';
jest.mock(`@elastic/eui/lib/components/form/form_row/make_id`, () => () => `generated-id`);

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { StartTrial } from '../public/sections/license_dashboard/start_trial';
import { StartTrial } from '../public/np_ready/application/sections/license_dashboard/start_trial';
import { createMockLicense, getComponent } from './util';
jest.mock(`@elastic/eui/lib/components/form/form_row/make_id`, () => () => `generated-id`);

View file

@ -4,8 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import { setTelemetryEnabled, setTelemetryOptInService } from '../public/lib/telemetry';
import { TelemetryOptIn } from '../public/components/telemetry_opt_in';
import { setTelemetryEnabled, setTelemetryOptInService } from '../public/np_ready/application/lib/telemetry';
import { TelemetryOptIn } from '../public/np_ready/application/components/telemetry_opt_in';
import { mountWithIntl } from '../../../../test_utils/enzyme_helpers';
jest.mock('ui/capabilities', () => ({

View file

@ -4,43 +4,52 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { httpServiceMock, chromeServiceMock } from '../../../../../src/core/public/mocks';
import { mountWithIntl } from '../../../../test_utils/enzyme_helpers';
import React from 'react';
import { Provider } from 'react-redux';
import { uploadLicense } from '../public/store/actions/upload_license';
import { licenseManagementStore } from '../public/store/store';
import { UploadLicense } from '../public/sections/upload_license';
import { BASE_PATH } from '../common/constants';
// @ts-ignore
import { uploadLicense } from '../public/np_ready/application/store/actions/upload_license';
// @ts-ignore
import { licenseManagementStore } from '../public/np_ready/application/store/store';
// @ts-ignore
import { UploadLicense } from '../public/np_ready/application/sections/upload_license';
import {
UPLOAD_LICENSE_EXPIRED,
UPLOAD_LICENSE_REQUIRES_ACK,
UPLOAD_LICENSE_SUCCESS,
UPLOAD_LICENSE_TLS_NOT_ENABLED,
UPLOAD_LICENSE_INVALID,
// @ts-ignore
} from './api_responses';
import sinon from 'sinon';
window.location.reload = () => {};
let server = null;
let store = null;
let component = null;
let store: any = null;
let component: any = null;
const services = {
kbnUrl: {
change: jest.fn()
legacy: {
xPackInfo: {
refresh: jest.fn(),
get: () => {
return { license: { type: 'basic' } };
},
},
refreshXpack: jest.fn(),
},
http: httpServiceMock.createSetupContract(),
chrome: chromeServiceMock.createStartContract(),
history: {
replace: jest.fn(),
},
autoLogout: () => {},
xPackInfo: {
refresh: jest.fn(),
get: () => {
return { license: { type: 'basic' } };
}
}
};
describe('UploadLicense', () => {
beforeEach(() => {
server = sinon.fakeServer.create();
server.respondImmediately = true;
store = licenseManagementStore({}, services);
component = (
<Provider store={store}>
@ -48,57 +57,60 @@ describe('UploadLicense', () => {
</Provider>
);
});
afterEach(() => {
server.restore();
services.xPackInfo.refresh.mockReset();
services.kbnUrl.change.mockReset();
services.legacy.xPackInfo.refresh.mockReset();
services.history.replace.mockReset();
jest.clearAllMocks();
});
it('should display an error when submitting invalid JSON', async () => {
const rendered = mountWithIntl(component);
store.dispatch(uploadLicense('INVALID', 'trial'));
rendered.update();
expect(rendered).toMatchSnapshot();
});
it('should display an error when ES says license is invalid', async () => {
services.http.put.mockResolvedValue(JSON.parse(UPLOAD_LICENSE_INVALID[2]));
const rendered = mountWithIntl(component);
const invalidLicense = JSON.stringify({ license: { type: 'basic' } });
server.respond(UPLOAD_LICENSE_INVALID);
await uploadLicense(invalidLicense)(store.dispatch, null, services);
rendered.update();
expect(rendered).toMatchSnapshot();
});
it('should display an error when ES says license is expired', async () => {
services.http.put.mockResolvedValue(JSON.parse(UPLOAD_LICENSE_EXPIRED[2]));
const rendered = mountWithIntl(component);
const invalidLicense = JSON.stringify({ license: { type: 'basic' } });
server.respond(UPLOAD_LICENSE_EXPIRED);
await uploadLicense(invalidLicense)(store.dispatch, null, services);
rendered.update();
expect(rendered).toMatchSnapshot();
});
it('should display a modal when license requires acknowledgement', async () => {
services.http.put.mockResolvedValue(JSON.parse(UPLOAD_LICENSE_REQUIRES_ACK[2]));
const unacknowledgedLicense = JSON.stringify({
license: { type: 'basic' }
license: { type: 'basic' },
});
server.respond(UPLOAD_LICENSE_REQUIRES_ACK);
await uploadLicense(unacknowledgedLicense, 'trial')(
store.dispatch,
null,
services
);
await uploadLicense(unacknowledgedLicense, 'trial')(store.dispatch, null, services);
const rendered = mountWithIntl(component);
expect(rendered).toMatchSnapshot();
});
it('should refresh xpack info and navigate to BASE_PATH when ES accepts new license', async () => {
services.http.put.mockResolvedValue(JSON.parse(UPLOAD_LICENSE_SUCCESS[2]));
const validLicense = JSON.stringify({ license: { type: 'basic' } });
server.respond(UPLOAD_LICENSE_SUCCESS);
await uploadLicense(validLicense)(store.dispatch, null, services);
expect(services.xPackInfo.refresh).toHaveBeenCalled();
expect(services.kbnUrl.change).toHaveBeenCalledWith(BASE_PATH);
expect(services.legacy.refreshXpack).toHaveBeenCalled();
expect(services.history.replace).toHaveBeenCalled();
});
it('should display error when ES returns error', async () => {
services.http.put.mockResolvedValue(JSON.parse(UPLOAD_LICENSE_TLS_NOT_ENABLED[2]));
const rendered = mountWithIntl(component);
const license = JSON.stringify({ license: { type: 'basic' } });
server.respond(UPLOAD_LICENSE_TLS_NOT_ENABLED);
await uploadLicense(license)(store.dispatch, null, services);
rendered.update();
expect(rendered).toMatchSnapshot();

View file

@ -5,9 +5,10 @@
*/
import { Provider } from 'react-redux';
import { licenseManagementStore } from '../../public/store/store';
import { licenseManagementStore } from '../../public/np_ready/application/store/store';
import React from 'react';
import { mountWithIntl } from '../../../../../test_utils/enzyme_helpers';
import { httpServiceMock } from '../../../../../../src/core/public/mocks';
const highExpirationMillis = new Date('October 13, 2099 00:00:00Z').getTime();
@ -22,7 +23,10 @@ export const createMockLicense = (
};
};
export const getComponent = (initialState, Component) => {
const store = licenseManagementStore(initialState);
const services = {
http: httpServiceMock.createSetupContract()
};
const store = licenseManagementStore(initialState, services);
return mountWithIntl(
<Provider store={store}>
<Component />

View file

@ -9,5 +9,5 @@ const ELASTIC_BASE_URL = 'https://www.elastic.co/';
export const EXTERNAL_LINKS = {
SUBSCRIPTIONS: `${ELASTIC_BASE_URL}subscriptions`,
TRIAL_EXTENSION: `${ELASTIC_BASE_URL}trialextension`,
TRIAL_LICENSE: `${ELASTIC_BASE_URL}legal/trial_license`
TRIAL_LICENSE: `${ELASTIC_BASE_URL}legal/trial_license`,
};

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 { i18n } from '@kbn/i18n';
export const PLUGIN = {
TITLE: i18n.translate('xpack.licenseMgmt.managementSectionDisplayName', {
defaultMessage: 'License Management',
}),
ID: 'license_management',
};

View file

@ -1,38 +0,0 @@
/*
* 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 { resolve } from 'path';
import { PLUGIN } from './common/constants';
import {
registerLicenseRoute,
registerStartTrialRoutes,
registerStartBasicRoute,
registerPermissionsRoute
} from './server/routes/api/license/';
import { createRouter } from '../../server/lib/create_router';
export function licenseManagement(kibana) {
return new kibana.Plugin({
id: PLUGIN.ID,
configPrefix: 'xpack.license_management',
publicDir: resolve(__dirname, 'public'),
require: ['kibana', 'elasticsearch'],
uiExports: {
styleSheetPaths: resolve(__dirname, 'public/index.scss'),
managementSections: [
'plugins/license_management',
]
},
init: (server) => {
const xpackInfo = server.plugins.xpack_main.info;
const router = createRouter(server, PLUGIN.ID, '/api/license');
registerLicenseRoute(router, xpackInfo);
registerStartTrialRoutes(router, xpackInfo);
registerStartBasicRoute(router, xpackInfo);
registerPermissionsRoute(router, xpackInfo);
}
});
}

View file

@ -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 { resolve } from 'path';
import { PLUGIN } from './common/constants';
import { Legacy } from '../../../../kibana';
import { plugin } from './server/np_ready';
export function licenseManagement(kibana: any) {
return new kibana.Plugin({
id: PLUGIN.ID,
configPrefix: 'xpack.license_management',
publicDir: resolve(__dirname, 'public'),
require: ['kibana', 'elasticsearch'],
uiExports: {
styleSheetPaths: resolve(__dirname, 'public/np_ready/application/index.scss'),
managementSections: ['plugins/license_management/legacy'],
},
init: (server: Legacy.Server) => {
plugin({} as any).setup(server.newPlatform.setup.core, {
...server.newPlatform.setup.plugins,
__LEGACY: {
xpackMain: server.plugins.xpack_main,
elasticsearch: server.plugins.elasticsearch,
},
});
},
});
}

View file

@ -1,71 +0,0 @@
/*
* 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 chrome from 'ui/chrome';
import $ from 'jquery';
export function putLicense(license, acknowledge) {
const options = {
url: `${chrome.addBasePath('/api/license')}${acknowledge ? '?acknowledge=true' : ''}`,
data: license,
contentType: 'application/json',
cache: false,
crossDomain: true,
type: 'PUT',
};
return $.ajax(options);
}
export function startBasic(acknowledge) {
const options = {
url: `${chrome.addBasePath('/api/license/start_basic')}${acknowledge ? '?acknowledge=true' : ''}`,
contentType: 'application/json',
cache: false,
crossDomain: true,
type: 'POST',
};
return $.ajax(options);
}
export function startTrial() {
const options = {
url: chrome.addBasePath('/api/license/start_trial'),
contentType: 'application/json',
cache: false,
crossDomain: true,
type: 'POST',
};
return $.ajax(options);
}
export function canStartTrial() {
const options = {
url: chrome.addBasePath('/api/license/start_trial'),
contentType: 'application/json',
cache: false,
crossDomain: true,
type: 'GET',
};
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);
}

View file

@ -1,3 +0,0 @@
<kbn-management-app section="elasticsearch/license_management">
<div id="licenseReactRoot"></div>
</kbn-management-app>

View file

@ -5,15 +5,11 @@
*/
import { management } from 'ui/management';
import { BASE_PATH } from '../common/constants';
import { i18n } from '@kbn/i18n';
import { BASE_PATH, PLUGIN } from '../common/constants';
management.getSection('elasticsearch').register('license_management', {
visible: true,
display: i18n.translate('xpack.licenseMgmt.managementSectionDisplayName', {
defaultMessage: 'License Management',
}),
display: PLUGIN.TITLE,
order: 99,
url: `#${BASE_PATH}home`
url: `#${BASE_PATH}home`,
});

View file

@ -8,7 +8,7 @@ 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 { getPermission, isPermissionsLoading, getPermissionsError } from './store/reducers/license_management';
import { loadPermissions } from './store/actions/permissions';
const mapStateToProps = state => {

View file

@ -6,9 +6,9 @@
import React, { Component } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import { LicenseDashboard, UploadLicense } from './sections/';
import { LicenseDashboard, UploadLicense } from './sections';
import { Switch, Route } from 'react-router-dom';
import { BASE_PATH, APP_PERMISSION } from '../common/constants';
import { APP_PERMISSION } from '../../../common/constants';
import { EuiPageBody, EuiEmptyPrompt, EuiText, EuiLoadingSpinner, EuiCallOut } from '@elastic/eui';
export class App extends Component {
@ -84,8 +84,10 @@ export class App extends Component {
return (
<EuiPageBody>
<Switch>
<Route path={`${BASE_PATH}upload_license`} component={UploadLicense} />
<Route path={`${BASE_PATH}`} component={LicenseDashboard} />
<Route path={`/upload_license`} component={UploadLicense} />
{/* Match all */}
<Route component={LicenseDashboard} />
</Switch>
</EuiPageBody>
);

View file

@ -0,0 +1,75 @@
/*
* 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 React from 'react';
import { Provider } from 'react-redux';
import { HashRouter } from 'react-router-dom';
import { render, unmountComponentAtNode } from 'react-dom';
import * as history from 'history';
import { DocLinksStart, HttpSetup, ToastsSetup, ChromeStart } from 'src/core/public';
// @ts-ignore
import { App } from './app.container';
// @ts-ignore
import { licenseManagementStore } from './store';
import { setDocLinks } from './lib/docs_links';
import { BASE_PATH } from '../../../common/constants';
import { Breadcrumb } from './breadcrumbs';
interface AppDependencies {
element: HTMLElement;
chrome: ChromeStart;
I18nContext: any;
legacy: {
xpackInfo: any;
refreshXpack: () => void;
MANAGEMENT_BREADCRUMB: Breadcrumb;
};
toasts: ToastsSetup;
docLinks: DocLinksStart;
http: HttpSetup;
}
export const boot = (deps: AppDependencies) => {
const { I18nContext, element, legacy, toasts, docLinks, http, chrome } = deps;
const { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } = docLinks;
const esBase = `${ELASTIC_WEBSITE_URL}guide/en/elasticsearch/reference/${DOC_LINK_VERSION}`;
const securityDocumentationLink = `${esBase}/security-settings.html`;
const initialState = { license: legacy.xpackInfo.get('license') };
setDocLinks({ securityDocumentationLink });
const services = {
legacy: {
refreshXpack: legacy.refreshXpack,
xPackInfo: legacy.xpackInfo,
},
// So we can imperatively control the hash route
history: history.createHashHistory({ basename: BASE_PATH }),
toasts,
http,
chrome,
MANAGEMENT_BREADCRUMB: legacy.MANAGEMENT_BREADCRUMB,
};
const store = licenseManagementStore(initialState, services);
render(
<I18nContext>
<Provider store={store}>
<HashRouter basename={BASE_PATH}>
<App />
</HashRouter>
</Provider>
</I18nContext>,
element
);
return () => unmountComponentAtNode(element);
};

View file

@ -5,28 +5,33 @@
*/
import { i18n } from '@kbn/i18n';
import { MANAGEMENT_BREADCRUMB } from 'ui/management';
import { BASE_PATH } from '../common/constants';
export function getDashboardBreadcrumbs() {
import { BASE_PATH } from '../../../common/constants';
export interface Breadcrumb {
text: string;
href: string;
}
export function getDashboardBreadcrumbs(root: Breadcrumb) {
return [
MANAGEMENT_BREADCRUMB,
root,
{
text: i18n.translate('xpack.licenseMgmt.dashboard.breadcrumb', {
defaultMessage: 'License management'
defaultMessage: 'License management',
}),
href: `#${BASE_PATH}home`
}
href: `#${BASE_PATH}home`,
},
];
}
export function getUploadBreadcrumbs() {
export function getUploadBreadcrumbs(root: Breadcrumb) {
return [
...getDashboardBreadcrumbs(),
...getDashboardBreadcrumbs(root),
{
text: i18n.translate('xpack.licenseMgmt.upload.breadcrumb', {
defaultMessage: 'Upload'
})
}
defaultMessage: 'Upload',
}),
},
];
}

View file

@ -10,4 +10,4 @@
// licChart__legend--small
// licChart__legend-isLoading
@import 'license_management';
@import 'license_management';

View file

@ -4,6 +4,4 @@
* you may not use this file except in compliance with the Elastic License.
*/
export const PLUGIN = {
ID: 'license_management',
};
export * from './boot';

View file

@ -4,10 +4,10 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { putLicense } from '../../../lib/license';
let docLinks: Record<string, string> = {};
export function registerLicenseRoute(router, xpackInfo) {
router.put('', (request) => {
return putLicense(request, xpackInfo);
});
}
export const setDocLinks = (links: Record<string, string>) => {
docLinks = links;
};
export const getDocLinks = () => docLinks;

View file

@ -0,0 +1,62 @@
/*
* 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 { HttpSetup } from 'src/core/public';
const BASE_PATH = '/api/license';
export function putLicense(http: HttpSetup, license: string, acknowledge: boolean) {
return http.put(BASE_PATH, {
query: {
acknowledge: acknowledge ? 'true' : '',
},
body: license,
headers: {
contentType: 'application/json',
},
cache: 'no-cache',
});
}
export function startBasic(http: HttpSetup, acknowledge: boolean) {
return http.post(`${BASE_PATH}/start_basic`, {
query: {
acknowledge: acknowledge ? 'true' : '',
},
headers: {
contentType: 'application/json',
},
body: null,
cache: 'no-cache',
});
}
export function startTrial(http: HttpSetup) {
return http.post(`${BASE_PATH}/start_trial`, {
headers: {
contentType: 'application/json',
},
cache: 'no-cache',
});
}
export function canStartTrial(http: HttpSetup) {
return http.get(`${BASE_PATH}/start_trial`, {
headers: {
contentType: 'application/json',
},
cache: 'no-cache',
});
}
export function getPermissions(http: HttpSetup) {
return http.post(`${BASE_PATH}/permissions`, {
headers: {
contentType: 'application/json',
},
cache: 'no-cache',
});
}

View file

@ -4,10 +4,10 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { fetchTelemetry } from '../../../../../../src/legacy/core_plugins/telemetry/public/hacks/fetch_telemetry';
export { PRIVACY_STATEMENT_URL } from '../../../../../../src/legacy/core_plugins/telemetry/common/constants';
export { TelemetryOptInProvider } from '../../../../../../src/legacy/core_plugins/telemetry/public/services';
export { OptInExampleFlyout } from '../../../../../../src/legacy/core_plugins/telemetry/public/components';
import { fetchTelemetry } from '../../../../../../../../src/legacy/core_plugins/telemetry/public/hacks/fetch_telemetry';
export { PRIVACY_STATEMENT_URL } from '../../../../../../../../src/legacy/core_plugins/telemetry/common/constants';
export { TelemetryOptInProvider } from '../../../../../../../../src/legacy/core_plugins/telemetry/public/services';
export { OptInExampleFlyout } from '../../../../../../../../src/legacy/core_plugins/telemetry/public/components';
let telemetryEnabled;
let httpClient;

View file

@ -5,7 +5,7 @@
*/
import React from 'react';
import { BASE_PATH } from '../../../../common/constants';
import { BASE_PATH } from '../../../../../../common/constants';
import { EuiCard, EuiButton } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';

View file

@ -4,4 +4,4 @@
* you may not use this file except in compliance with the Elastic License.
*/
export { LicenseDashboard } from './license_dashboard';
export { LicenseDashboard } from './license_dashboard.container';

View file

@ -0,0 +1,14 @@
/*
* 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 { LicenseDashboard as PresentationComponent } from './license_dashboard';
import { setBreadcrumb } from '../../store/actions/set_breadcrumb';
const mapDispatchToProps = {
setBreadcrumb,
};
export const LicenseDashboard = connect(null, mapDispatchToProps)(PresentationComponent);

View file

@ -18,7 +18,8 @@ import {
EuiSpacer
} from '@elastic/eui';
export const LicenseDashboard = () => {
export const LicenseDashboard = ({ setBreadcrumb } = { setBreadcrumb: () => {} }) => {
setBreadcrumb('dashboard');
return (
<div>
<LicenseStatus />

View file

@ -6,7 +6,7 @@
import { LicenseStatus as PresentationComponent } from './license_status';
import { connect } from 'react-redux';
import { getLicense, getExpirationDateFormatted, isExpired } from '../../../store/reducers/licenseManagement';
import { getLicense, getExpirationDateFormatted, isExpired } from '../../../store/reducers/license_management';
import { i18n } from '@kbn/i18n';
const mapStateToProps = (state) => {

View file

@ -9,7 +9,7 @@ import { connect } from 'react-redux';
import { RequestTrialExtension as PresentationComponent } from './request_trial_extension';
import {
shouldShowRequestTrialExtension
} from '../../../store/reducers/licenseManagement';
} from '../../../store/reducers/license_management';
const mapStateToProps = state => {

View file

@ -8,7 +8,7 @@ import React from 'react';
import { EuiFlexItem, EuiCard, EuiLink, EuiButton } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { EXTERNAL_LINKS } from '../../../../common/constants';
import { EXTERNAL_LINKS } from '../../../../../../common/constants';
export const RequestTrialExtension = ({ shouldShowRequestTrialExtension }) => {
if (!shouldShowRequestTrialExtension) {

View file

@ -12,7 +12,7 @@ import {
getLicenseType,
shouldShowRevertToBasicLicense,
getStartBasicMessages
} from '../../../store/reducers/licenseManagement';
} from '../../../store/reducers/license_management';
import { startBasicLicense, cancelStartBasicLicense } from '../../../store/actions/start_basic';
const mapStateToProps = state => {

View file

@ -16,7 +16,7 @@ import {
EuiText
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { EXTERNAL_LINKS } from '../../../../common/constants';
import { EXTERNAL_LINKS } from '../../../../../../common/constants';
export class RevertToBasic extends React.PureComponent {
cancel = () => {

View file

@ -8,7 +8,7 @@ import { connect } from 'react-redux';
import { StartTrial as PresentationComponent } from './start_trial';
import { loadTrialStatus, startLicenseTrial } from '../../../store/actions/start_trial';
import { shouldShowStartTrial } from '../../../store/reducers/licenseManagement';
import { shouldShowStartTrial } from '../../../store/reducers/license_management';
const mapStateToProps = (state) => {
return {

View file

@ -22,13 +22,11 @@ import {
EuiModalHeaderTitle
} from '@elastic/eui';
import { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } from 'ui/documentation_links';
import { TelemetryOptIn } from '../../../components/telemetry_opt_in';
import { optInToTelemetry } from '../../../lib/telemetry';
import { FormattedMessage } from '@kbn/i18n/react';
import { EXTERNAL_LINKS } from '../../../../common/constants';
const esBase = `${ELASTIC_WEBSITE_URL}guide/en/elasticsearch/reference/${DOC_LINK_VERSION}`;
const securityDocumentationLink = `${esBase}/security-settings.html`;
import { EXTERNAL_LINKS } from '../../../../../../common/constants';
import { getDocLinks } from '../../../lib/docs_links';
export class StartTrial extends React.PureComponent {
@ -134,7 +132,7 @@ export class StartTrial extends React.PureComponent {
authenticationTypeList: 'AD/LDAP, SAML, PKI, SAML/SSO',
securityDocumentationLinkText: (
<EuiLink
href={securityDocumentationLink}
href={getDocLinks().securityDocumentationLink}
target="_blank"
>
<FormattedMessage

View file

@ -5,6 +5,7 @@
*/
import { connect } from 'react-redux';
import { setBreadcrumb } from '../../store/actions/set_breadcrumb';
import { uploadLicense, uploadLicenseStatus } from '../../store/actions/upload_license';
import { addUploadErrorMessage } from '../../store/actions/add_error_message';
@ -15,7 +16,7 @@ import {
isApplying,
uploadNeedsAcknowledgement,
uploadMessages
} from '../../store/reducers/licenseManagement';
} from '../../store/reducers/license_management';
import { UploadLicense as PresentationComponent } from './upload_license';
@ -33,6 +34,7 @@ const mapDispatchToProps = {
addUploadErrorMessage,
uploadLicense,
uploadLicenseStatus,
setBreadcrumb
};
export const UploadLicense = connect(mapStateToProps, mapDispatchToProps)(PresentationComponent);

View file

@ -5,7 +5,7 @@
*/
import React, { Fragment } from 'react';
import { BASE_PATH } from '../../../common/constants';
import { BASE_PATH } from '../../../../../common/constants';
import {
EuiButton,
EuiButtonEmpty,
@ -27,6 +27,7 @@ import { FormattedMessage } from '@kbn/i18n/react';
export class UploadLicense extends React.PureComponent {
componentDidMount() {
this.props.setBreadcrumb('upload');
this.props.addUploadErrorMessage('');
}
send = acknowledge => {
@ -148,16 +149,20 @@ export class UploadLicense extends React.PureComponent {
</EuiText>
<EuiSpacer />
<EuiForm isInvalid={!!this.errorMessage()} error={this.errorMessage()}>
<EuiText>
<EuiFilePicker
id="licenseFile"
initialPromptText={<FormattedMessage
id="xpack.licenseMgmt.uploadLicense.selectLicenseFileDescription"
defaultMessage="Select or drag your license file"
/>}
onChange={this.handleFile}
/>
</EuiText>
<EuiFlexGroup justifyContent="center">
<EuiFlexItem grow={false}>
<EuiText>
<EuiFilePicker
id="licenseFile"
initialPromptText={<FormattedMessage
id="xpack.licenseMgmt.uploadLicense.selectLicenseFileDescription"
defaultMessage="Select or drag your license file"
/>}
onChange={this.handleFile}
/>
</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer size="m" />
<TelemetryOptIn
ref={ref => {

View file

@ -19,10 +19,10 @@ export const permissionsError = createAction(
'LICENSE_MANAGEMENT_PERMISSIONS_ERROR'
);
export const loadPermissions = () => async dispatch => {
export const loadPermissions = () => async (dispatch, getState, { http }) => {
dispatch(permissionsLoading(true));
try {
const permissions = await getPermissions();
const permissions = await getPermissions(http);
dispatch(permissionsLoading(false));
dispatch(permissionsSuccess(permissions.hasPermission));
} catch (e) {

View file

@ -0,0 +1,22 @@
/*
* 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 { ThunkAction } from 'redux-thunk';
import { ChromeStart } from 'src/core/public';
import { getDashboardBreadcrumbs, getUploadBreadcrumbs, Breadcrumb } from '../../breadcrumbs';
export const setBreadcrumb = (
section: 'dashboard' | 'upload'
): ThunkAction<any, any, { chrome: ChromeStart; MANAGEMENT_BREADCRUMB: Breadcrumb }, any> => (
dispatch,
getState,
{ chrome, MANAGEMENT_BREADCRUMB }
) => {
if (section === 'upload') {
chrome.setBreadcrumbs(getUploadBreadcrumbs(MANAGEMENT_BREADCRUMB));
} else {
chrome.setBreadcrumbs(getDashboardBreadcrumbs(MANAGEMENT_BREADCRUMB));
}
};

View file

@ -4,10 +4,9 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { i18n } from '@kbn/i18n';
import { createAction } from 'redux-actions';
import { startBasic } from '../../lib/es';
import { toastNotifications } from 'ui/notify';
import { i18n } from '@kbn/i18n';
export const startBasicLicenseStatus = createAction(
'LICENSE_MANAGEMENT_START_BASIC_LICENSE_STATUS'
@ -20,17 +19,17 @@ export const cancelStartBasicLicense = createAction(
export const startBasicLicense = (currentLicenseType, ack) => async (
dispatch,
getState,
{ xPackInfo, $injector }
{ legacy: { refreshXpack }, toasts, http }
) => {
/*eslint camelcase: 0*/
const { acknowledged, basic_was_started, error_message, acknowledge } = await startBasic(ack);
const { acknowledged, basic_was_started, error_message, acknowledge } = await startBasic(http, ack);
if (acknowledged) {
if (basic_was_started) {
await xPackInfo.refresh($injector);
await refreshXpack();
// reload necessary to get left nav to refresh with proper links
window.location.reload();
} else {
return toastNotifications.addDanger(error_message);
return toasts.addDanger(error_message);
}
} else {
//messages coming back in arrays

View file

@ -6,29 +6,28 @@
import { createAction } from 'redux-actions';
import { canStartTrial, startTrial } from '../../lib/es';
import { toastNotifications } from 'ui/notify';
export const trialStatusLoaded = createAction(
'LICENSE_MANAGEMENT_TRIAL_STATUS_LOADED'
);
export const loadTrialStatus = () => async dispatch => {
const trialOK = await canStartTrial();
export const loadTrialStatus = () => async (dispatch, getState, { http }) => {
const trialOK = await canStartTrial(http);
dispatch(trialStatusLoaded(trialOK));
};
export const startLicenseTrial = () => async (
dispatch,
getState,
{ xPackInfo, $injector }
{ legacy: { refreshXpack }, toasts, http }
) => {
/*eslint camelcase: 0*/
const { trial_was_started, error_message } = await startTrial();
const { trial_was_started, error_message } = await startTrial(http);
if (trial_was_started) {
await xPackInfo.refresh($injector);
await refreshXpack();
// reload necessary to get left nav to refresh with proper links
window.location.reload();
} else {
return toastNotifications.addDanger(error_message);
return toasts.addDanger(error_message);
}
};

View file

@ -5,8 +5,7 @@
*/
import { createAction } from 'redux-actions';
import { addLicense } from '../actions/add_license';
import { BASE_PATH } from '../../../common/constants/base_path';
import { addLicense } from './add_license';
import { putLicense } from '../../lib/es';
import { addUploadErrorMessage } from './add_error_message';
import { i18n } from '@kbn/i18n';
@ -17,7 +16,19 @@ const genericUploadError = i18n.translate('xpack.licenseMgmt.uploadLicense.gener
defaultMessage: 'Error encountered uploading license:'
});
const dispatchFromResponse = async (response, dispatch, currentLicenseType, newLicenseType, { xPackInfo, kbnUrl, $injector }) => {
const dispatchFromResponse = async (
response,
dispatch,
currentLicenseType,
newLicenseType,
{
history,
legacy: {
xPackInfo,
refreshXpack,
},
},
) => {
const { error, acknowledged, license_status: licenseStatus, acknowledge } = response;
if (error) {
dispatch(uploadLicenseStatus({}));
@ -34,10 +45,10 @@ const dispatchFromResponse = async (response, dispatch, currentLicenseType, newL
defaultMessage: 'The supplied license has expired.'
})));
} else {
await xPackInfo.refresh($injector);
await refreshXpack();
dispatch(addLicense(xPackInfo.get('license')));
dispatch(uploadLicenseStatus({}));
kbnUrl.change(BASE_PATH);
history.replace('/home');
// reload necessary to get left nav to refresh with proper links
window.location.reload();
}
@ -75,7 +86,7 @@ export const uploadLicense = (licenseString, currentLicenseType, acknowledge) =>
));
}
try {
const response = await putLicense(licenseString, acknowledge);
const response = await putLicense(services.http, licenseString, acknowledge);
await dispatchFromResponse(response, dispatch, currentLicenseType, newLicenseType, services);
} catch (err) {
const message = (err.responseJSON && err.responseJSON.error.reason) ? err.responseJSON.error.reason : i18n.translate(

View file

@ -4,4 +4,4 @@
* you may not use this file except in compliance with the Elastic License.
*/
export { licenseManagement } from './licenseManagement';
export { licenseManagement } from './license_management';

View file

@ -7,7 +7,7 @@
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import { licenseManagement } from './reducers/';
import { licenseManagement } from './reducers';
export const licenseManagementStore = (initialState = {}, services = {}) => {
const enhancers = [ applyMiddleware(thunk.withExtraArgument(services)) ];

View file

@ -0,0 +1,9 @@
/*
* 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 { PluginInitializerContext } from 'src/core/public';
import { LicenseManagementUIPlugin } from './plugin';
export const plugin = (ctx: PluginInitializerContext) => new LicenseManagementUIPlugin();

View file

@ -0,0 +1,50 @@
/*
* 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 { CoreSetup, CoreStart, Plugin } from 'src/core/public';
import { XPackMainPlugin } from '../../../xpack_main/xpack_main';
import { PLUGIN } from '../../common/constants';
import { Breadcrumb } from './application/breadcrumbs';
export interface Plugins {
__LEGACY: {
xpackInfo: XPackMainPlugin;
refreshXpack: () => void;
MANAGEMENT_BREADCRUMB: Breadcrumb;
};
}
export class LicenseManagementUIPlugin implements Plugin<void, void, any, any> {
setup({ application, notifications, http }: CoreSetup, { __LEGACY }: Plugins) {
application.register({
id: PLUGIN.ID,
title: PLUGIN.TITLE,
async mount(
{
core: {
docLinks,
i18n: { Context: I18nContext },
chrome,
},
},
{ element }
) {
const { boot } = await import('./application');
return boot({
legacy: { ...__LEGACY },
I18nContext,
toasts: notifications.toasts,
docLinks,
http,
element,
chrome,
});
},
});
}
start(core: CoreStart, plugins: any) {}
stop() {}
}

View file

@ -1,109 +0,0 @@
/*
* 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 React from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
import { Provider } from 'react-redux';
import { HashRouter } from 'react-router-dom';
import { setTelemetryOptInService, setTelemetryEnabled, setHttpClient, TelemetryOptInProvider } from './lib/telemetry';
import { I18nContext } from 'ui/i18n';
import chrome from 'ui/chrome';
import { App } from './app.container';
import { BASE_PATH } from '../common/constants/base_path';
import routes from 'ui/routes';
import { xpackInfo } from 'plugins/xpack_main/services/xpack_info';
import template from './main.html';
import { licenseManagementStore } from './store';
import { getDashboardBreadcrumbs, getUploadBreadcrumbs } from './breadcrumbs';
const renderReact = (elem, store) => {
render(
<I18nContext>
<Provider store={store}>
<HashRouter>
<App />
</HashRouter>
</Provider>
</I18nContext>,
elem
);
};
/*
This method handles the cleanup needed when route is scope is destroyed. It also prevents Angular
from destroying scope when route changes and both old route and new route are this same route.
*/
const manageAngularLifecycle = ($scope, $route, elem) => {
const lastRoute = $route.current;
const deregister = $scope.$on('$locationChangeSuccess', () => {
const currentRoute = $route.current;
// if templates are the same we are on the same route
if (lastRoute.$$route.template === currentRoute.$$route.template) {
// update the breadcrumbs by re-running the k7Breadcrumbs function
chrome.breadcrumbs.set(currentRoute.$$route.k7Breadcrumbs($route));
// this prevents angular from destroying scope
$route.current = lastRoute;
}
});
$scope.$on('$destroy', () => {
deregister && deregister();
// manually unmount component when scope is destroyed
elem && unmountComponentAtNode(elem);
});
};
const initializeTelemetry = ($injector) => {
const telemetryEnabled = $injector.get('telemetryEnabled');
const Private = $injector.get('Private');
const telemetryOptInProvider = Private(TelemetryOptInProvider);
setTelemetryOptInService(telemetryOptInProvider);
setTelemetryEnabled(telemetryEnabled);
setHttpClient($injector.get('$http'));
};
routes
.when(`${BASE_PATH}:view?`, {
template: template,
k7Breadcrumbs($route) {
switch ($route.current.params.view) {
case 'upload_license':
return getUploadBreadcrumbs();
default:
return getDashboardBreadcrumbs();
}
},
controllerAs: 'licenseManagement',
controller: class LicenseManagementController {
constructor($injector, $rootScope, $scope, $route, kbnUrl) {
initializeTelemetry($injector);
let autoLogout = null;
/* if security is disabled, there will be no autoLogout service,
so just substitute noop function in that case */
try {
autoLogout = $injector.get('autoLogout');
} catch (e) {
autoLogout = () => {};
}
$scope.$$postDigest(() => {
const elem = document.getElementById('licenseReactRoot');
const initialState = { license: xpackInfo.get('license') };
const kbnUrlWrapper = {
change(url) {
kbnUrl.change(url);
$rootScope.$digest();
}
};
const services = { autoLogout, xPackInfo: xpackInfo, kbnUrl: kbnUrlWrapper, $injector };
const store = licenseManagementStore(initialState, services);
renderReact(elem, store);
manageAngularLifecycle($scope, $route, elem);
});
}
}
});

View file

@ -0,0 +1,98 @@
/*
* 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 { App } from 'src/core/public';
/* Legacy Imports */
import { npSetup, npStart } from 'ui/new_platform';
import { MANAGEMENT_BREADCRUMB } from 'ui/management';
import routes from 'ui/routes';
// @ts-ignore
import { xpackInfo } from 'plugins/xpack_main/services/xpack_info';
import { plugin } from './np_ready';
import {
setTelemetryOptInService,
setTelemetryEnabled,
setHttpClient,
TelemetryOptInProvider,
// @ts-ignore
} from './np_ready/application/lib/telemetry';
import { BASE_PATH } from '../common/constants';
/*
This method handles the cleanup needed when route is scope is destroyed. It also prevents Angular
from destroying scope when route changes and both old route and new route are this same route.
*/
const manageAngularLifecycle = ($scope: any, $route: any, unmount: () => void) => {
const lastRoute = $route.current;
const deregister = $scope.$on('$locationChangeSuccess', () => {
const currentRoute = $route.current;
// if templates are the same we are on the same route
if (lastRoute.$$route.template === currentRoute.$$route.template) {
// this prevents angular from destroying scope
$route.current = lastRoute;
}
});
$scope.$on('$destroy', () => {
if (deregister) {
deregister();
}
unmount();
});
};
const initializeTelemetry = ($injector: any) => {
const telemetryEnabled = $injector.get('telemetryEnabled');
const Private = $injector.get('Private');
const telemetryOptInProvider = Private(TelemetryOptInProvider);
setTelemetryOptInService(telemetryOptInProvider);
setTelemetryEnabled(telemetryEnabled);
setHttpClient($injector.get('$http'));
};
const template = `<kbn-management-app section="elasticsearch/license_management">
<div id="licenseReactRoot"></div>
</kbn-management-app>`;
routes.when(`${BASE_PATH}:view?`, {
template,
controllerAs: 'licenseManagement',
controller: class LicenseManagementController {
constructor($injector: any, $rootScope: any, $scope: any, $route: any) {
initializeTelemetry($injector);
$scope.$$postDigest(() => {
const element = document.getElementById('licenseReactRoot')!;
const refreshXpack = async () => {
await xpackInfo.refresh($injector);
};
plugin({} as any).setup(
{
...npSetup.core,
application: {
...npSetup.core.application,
async register(app: App) {
const unmountApp = await app.mount({ ...npStart } as any, {
element,
appBasePath: '',
});
manageAngularLifecycle($scope, $route, unmountApp as any);
},
},
},
{
__LEGACY: { xpackInfo, refreshXpack, MANAGEMENT_BREADCRUMB },
}
);
});
}
} as any,
} as any);

View file

@ -1,27 +0,0 @@
/*
* 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.
*/
const getLicensePath = (acknowledge) => `/_license${ acknowledge ? '?acknowledge=true' : ''}`;
export async function putLicense(req, xpackInfo) {
const { acknowledge } = req.query;
const { callWithRequest } = req.server.plugins.elasticsearch.getCluster('admin');
const options = {
method: 'POST',
path: getLicensePath(acknowledge),
body: req.payload
};
try {
const response = await callWithRequest(req, 'transport.request', options);
const { acknowledged, license_status: licenseStatus } = response;
if (acknowledged && licenseStatus === 'valid') {
await xpackInfo.refreshNow();
}
return response;
} catch (error) {
return error.body;
}
}

View file

@ -1,28 +0,0 @@
/*
* 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.
*/
const getStartBasicPath = (acknowledge) => `/_license/start_basic${ acknowledge ? '?acknowledge=true' : ''}`;
export async function startBasic(req, xpackInfo) {
const { acknowledge } = req.query;
const { callWithRequest } = req.server.plugins.elasticsearch.getCluster('admin');
const options = {
method: 'POST',
path: getStartBasicPath(acknowledge)
};
try {
const response = await callWithRequest(req, 'transport.request', options);
/*eslint camelcase: 0*/
const { basic_was_started } = response;
if (basic_was_started) {
await xpackInfo.refreshNow();
}
return response;
} catch (error) {
return error.body;
}
}

View file

@ -1,38 +0,0 @@
/*
* 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 async function canStartTrial(req) {
const { callWithRequest } = req.server.plugins.elasticsearch.getCluster('admin');
const options = {
method: 'GET',
path: '/_license/trial_status'
};
try {
const response = await callWithRequest(req, 'transport.request', options);
const { eligible_to_start_trial } = response;
return eligible_to_start_trial;
} catch (error) {
return error.body;
}
}
export async function startTrial(req, xpackInfo) {
const { callWithRequest } = req.server.plugins.elasticsearch.getCluster('admin');
const options = {
method: 'POST',
path: '/_license/start_trial?acknowledge=true'
};
try {
/*eslint camelcase: 0*/
const response = await callWithRequest(req, 'transport.request', options);
const { trial_was_started } = response;
if (trial_was_started) {
await xpackInfo.refreshNow();
}
return response;
} catch (error) {
return error.body;
}
}

View file

@ -4,10 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { startBasic } from '../../../lib/start_basic';
import { PluginInitializerContext } from 'src/core/server';
import { LicenseManagementServerPlugin } from './plugin';
export function registerStartBasicRoute(router, xpackInfo) {
router.post('/start_basic', (request) => {
return startBasic(request, xpackInfo);
});
}
export const plugin = (ctx: PluginInitializerContext) => new LicenseManagementServerPlugin();

View file

@ -0,0 +1,33 @@
/*
* 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 { KibanaRequest } from 'src/core/server';
import { ElasticsearchPlugin } from '../../../../../../../src/legacy/core_plugins/elasticsearch';
const getLicensePath = (acknowledge: boolean) =>
`/_license${acknowledge ? '?acknowledge=true' : ''}`;
export async function putLicense(
req: KibanaRequest<any, { acknowledge: string }, any>,
elasticsearch: ElasticsearchPlugin,
xpackInfo: any
) {
const { acknowledge } = req.query;
const { callWithRequest } = elasticsearch.getCluster('admin');
const options = {
method: 'POST',
path: getLicensePath(Boolean(acknowledge)),
body: req.body,
};
try {
const response = await callWithRequest(req as any, 'transport.request', options);
const { acknowledged, license_status: licenseStatus } = response;
if (acknowledged && licenseStatus === 'valid') {
await xpackInfo.refreshNow();
}
return response;
} catch (error) {
return error.body;
}
}

View file

@ -4,15 +4,14 @@
* 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);
}
import { KibanaRequest } from 'src/core/server';
import { ElasticsearchPlugin } from '../../../../../../../src/legacy/core_plugins/elasticsearch';
export async function getPermissions(
req: KibanaRequest,
elasticsearch: ElasticsearchPlugin,
xpackInfo: any
) {
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
@ -21,22 +20,21 @@ export async function getPermissions(req, xpackInfo) {
};
}
const { callWithRequest } = req.server.plugins.elasticsearch.getCluster('admin');
const { callWithRequest } = 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);
const response = await callWithRequest(req as any, 'transport.request', options);
return {
hasPermission: response.cluster.manage,
};
} catch (error) {
return error.body;
}
}

View file

@ -0,0 +1,34 @@
/*
* 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 { KibanaRequest } from 'kibana/server';
import { ElasticsearchPlugin } from '../../../../../../../src/legacy/core_plugins/elasticsearch';
const getStartBasicPath = (acknowledge: boolean) =>
`/_license/start_basic${acknowledge ? '?acknowledge=true' : ''}`;
export async function startBasic(
req: KibanaRequest<any, { acknowledge: string }, any>,
elasticsearch: ElasticsearchPlugin,
xpackInfo: any
) {
const { acknowledge } = req.query;
const { callWithRequest } = elasticsearch.getCluster('admin');
const options = {
method: 'POST',
path: getStartBasicPath(Boolean(acknowledge)),
};
try {
const response = await callWithRequest(req as any, 'transport.request', options);
const { basic_was_started: basicWasStarted } = response;
if (basicWasStarted) {
await xpackInfo.refreshNow();
}
return response;
} catch (error) {
return error.body;
}
}

View file

@ -0,0 +1,47 @@
/*
* 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 { KibanaRequest } from 'src/core/server';
import { ElasticsearchPlugin } from '../../../../../../../src/legacy/core_plugins/elasticsearch';
export async function canStartTrial(
req: KibanaRequest<any, any, any>,
elasticsearch: ElasticsearchPlugin
) {
const { callWithRequest } = elasticsearch.getCluster('admin');
const options = {
method: 'GET',
path: '/_license/trial_status',
};
try {
const response = await callWithRequest(req as any, 'transport.request', options);
return response.eligible_to_start_trial;
} catch (error) {
return error.body;
}
}
export async function startTrial(
req: KibanaRequest<any, any, any>,
elasticsearch: ElasticsearchPlugin,
xpackInfo: any
) {
const { callWithRequest } = elasticsearch.getCluster('admin');
const options = {
method: 'POST',
path: '/_license/start_trial?acknowledge=true',
};
try {
const response = await callWithRequest(req as any, 'transport.request', options);
const { trial_was_started: trialWasStarted } = response;
if (trialWasStarted) {
await xpackInfo.refreshNow();
}
return response;
} catch (error) {
return error.body;
}
}

View file

@ -0,0 +1,35 @@
/*
* 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 { Plugin, CoreSetup } from 'src/core/server';
import { Dependencies, Server } from './types';
import {
registerLicenseRoute,
registerStartTrialRoutes,
registerStartBasicRoute,
registerPermissionsRoute,
} from './routes/api/license';
export class LicenseManagementServerPlugin implements Plugin<void, void, any, any> {
setup({ http }: CoreSetup, { __LEGACY }: Dependencies) {
const xpackInfo = __LEGACY.xpackMain.info;
const router = http.createRouter();
const server: Server = {
router,
};
const legacy = { plugins: __LEGACY };
registerLicenseRoute(server, legacy, xpackInfo);
registerStartTrialRoutes(server, legacy, xpackInfo);
registerStartBasicRoute(server, legacy, xpackInfo);
registerPermissionsRoute(server, legacy, xpackInfo);
}
start() {}
stop() {}
}

View file

@ -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 { schema } from '@kbn/config-schema';
import { putLicense } from '../../../lib/license';
import { Legacy, Server } from '../../../types';
export function registerLicenseRoute(server: Server, legacy: Legacy, xpackInfo: any) {
server.router.put(
{
path: '/api/license',
validate: {
query: schema.object({ acknowledge: schema.string() }),
body: schema.object({
license: schema.object({}, { allowUnknowns: true }),
}),
},
},
async (ctx, request, response) => {
try {
return response.ok({
body: await putLicense(request, legacy.plugins.elasticsearch, xpackInfo),
});
} catch (e) {
return response.internalError({ body: e });
}
}
);
}

View file

@ -0,0 +1,29 @@
/*
* 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';
import { Legacy, Server } from '../../../types';
export function registerPermissionsRoute(server: Server, legacy: Legacy, xpackInfo: any) {
server.router.post(
{ path: '/api/license/permissions', validate: false },
async (ctx, request, response) => {
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.
return response.customError({ statusCode: 503, body: 'Security info unavailable' });
}
try {
return response.ok({
body: await getPermissions(request, legacy.plugins.elasticsearch, xpackInfo),
});
} catch (e) {
return response.internalError({ body: e });
}
}
);
}

View file

@ -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 { schema } from '@kbn/config-schema';
import { startBasic } from '../../../lib/start_basic';
import { Legacy, Server } from '../../../types';
export function registerStartBasicRoute(server: Server, legacy: Legacy, xpackInfo: any) {
server.router.post(
{
path: '/api/license/start_basic',
validate: { query: schema.object({ acknowledge: schema.string() }) },
},
async (ctx, request, response) => {
try {
return response.ok({
body: await startBasic(request, legacy.plugins.elasticsearch, xpackInfo),
});
} catch (e) {
return response.internalError({ body: e });
}
}
);
}

View file

@ -0,0 +1,34 @@
/*
* 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 { canStartTrial, startTrial } from '../../../lib/start_trial';
import { Legacy, Server } from '../../../types';
export function registerStartTrialRoutes(server: Server, legacy: Legacy, xpackInfo: any) {
server.router.get(
{ path: '/api/license/start_trial', validate: false },
async (ctx, request, response) => {
try {
return response.ok({ body: await canStartTrial(request, legacy.plugins.elasticsearch) });
} catch (e) {
return response.internalError({ body: e });
}
}
);
server.router.post(
{ path: '/api/license/start_trial', validate: false },
async (ctx, request, response) => {
try {
return response.ok({
body: await startTrial(request, legacy.plugins.elasticsearch, xpackInfo),
});
} catch (e) {
return response.internalError({ body: e });
}
}
);
}

View file

@ -0,0 +1,24 @@
/*
* 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 { IRouter } from 'src/core/server';
import { XPackMainPlugin } from '../../../xpack_main/xpack_main';
import { ElasticsearchPlugin } from '../../../../../../src/legacy/core_plugins/elasticsearch';
export interface Dependencies {
__LEGACY: {
xpackMain: XPackMainPlugin;
elasticsearch: ElasticsearchPlugin;
};
}
export interface Server {
router: IRouter;
}
export interface Legacy {
plugins: Dependencies['__LEGACY'];
}

View file

@ -1,13 +0,0 @@
/*
* 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);
});
}

View file

@ -1,16 +0,0 @@
/*
* 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 { canStartTrial, startTrial } from '../../../lib/start_trial';
export function registerStartTrialRoutes(router, xpackInfo) {
router.get('/start_trial', (request) => {
return canStartTrial(request);
});
router.post('/start_trial', (request) => {
return startTrial(request, xpackInfo);
});
}

View file

@ -4,8 +4,11 @@
* you may not use this file except in compliance with the Elastic License.
*/
export { LicenseStatus } from '../../../license_management/public/sections/license_dashboard/license_status/license_status';
export { AddLicense } from '../../../license_management/public/sections/license_dashboard/add_license/add_license';
export {
LicenseStatus
} from '../../../license_management/public/np_ready/application/sections/license_dashboard/license_status/license_status';
export { AddLicense } from '../../../license_management/public/np_ready/application/sections/license_dashboard/add_license/add_license';
/*
* For to link to management