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

Security nav control => NP #52386

Merged
merged 32 commits into from
Dec 18, 2019
Merged
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
28bec5c
migrating nav control to NP
legrego Dec 5, 2019
8869956
move licensing service to common
legrego Dec 5, 2019
d0de6b1
only retrieve user when necessary
legrego Dec 5, 2019
3bf75c8
don't block rendering on user promise
legrego Dec 6, 2019
921cbf8
Merge branch 'master' into security/np-chrome-nav
elasticmachine Dec 6, 2019
5ecea19
Merge branch 'master' of github.com:elastic/kibana into security/np-c…
legrego Dec 9, 2019
8feb6be
testing nav control registration
legrego Dec 9, 2019
6c64e4a
moving logic to nav_control_service
legrego Dec 9, 2019
bc259e7
Merge branch 'master' of github.com:elastic/kibana into security/np-c…
legrego Dec 9, 2019
b1cbcc6
register account management in a hack
legrego Dec 9, 2019
8e2461f
Merge branch 'master' of github.com:elastic/kibana into security/np-c…
legrego Dec 9, 2019
9c1b189
Merge branch 'security/np-chrome-nav' of github.com:legrego/kibana in…
legrego Dec 9, 2019
546f091
update import location
legrego Dec 9, 2019
a353bfb
updating license_service to manage its own subscription to the raw li…
legrego Dec 11, 2019
443607b
Merge branch 'master' of github.com:elastic/kibana into security/np-c…
legrego Dec 11, 2019
a747a97
updating mock
legrego Dec 11, 2019
61d8976
update editProfileUrl to not require full page reload if already with…
legrego Dec 11, 2019
1ac4f1a
alternate security license proposal
legrego Dec 12, 2019
6160ad3
adds popover test.
legrego Dec 12, 2019
9f7a1d4
switchMap -> map
legrego Dec 12, 2019
2bba5e1
additional test case.
legrego Dec 13, 2019
81a80d2
Apply suggestions from code review
legrego Dec 13, 2019
eb9c5c0
additional testing
legrego Dec 13, 2019
64f800c
Merge branch 'master' of github.com:elastic/kibana into security/np-c…
legrego Dec 13, 2019
2a212a2
Merge branch 'master' of github.com:elastic/kibana into security/np-c…
legrego Dec 13, 2019
c5a20b8
fix merge from master
legrego Dec 13, 2019
3d9f278
Merge branch 'master' of github.com:elastic/kibana into security/np-c…
legrego Dec 16, 2019
03a1409
fixing es availability check
legrego Dec 16, 2019
08dd4d9
Merge branch 'master' of github.com:elastic/kibana into security/np-c…
legrego Dec 17, 2019
f08cafb
fix merge from master
legrego Dec 17, 2019
d493ec9
switch from deprecated route
legrego Dec 17, 2019
ed440a8
Merge branch 'master' of github.com:elastic/kibana into security/np-c…
legrego Dec 17, 2019
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
3 changes: 2 additions & 1 deletion x-pack/legacy/plugins/security/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export const security = kibana =>
},

uiExports: {
chromeNavControls: ['plugins/security/views/nav_control'],
chromeNavControls: [],
managementSections: ['plugins/security/views/management'],
styleSheetPaths: resolve(__dirname, 'public/index.scss'),
apps: [
Expand Down Expand Up @@ -88,6 +88,7 @@ export const security = kibana =>
hacks: [
'plugins/security/hacks/on_session_timeout',
'plugins/security/hacks/on_unauthorized_response',
'plugins/security/hacks/register_account_management_app',
],
home: ['plugins/security/register_feature'],
injectDefaultVars: server => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@
* you may not use this file except in compliance with the Elastic License.
*/

import './nav_control';
import '../views/account/account';

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { of } from 'rxjs';
import { SecurityLicense } from '.';

export const licenseMock = {
create: (): jest.Mocked<SecurityLicense> => ({
isEnabled: jest.fn().mockReturnValue(true),
getFeatures: jest.fn(),
features$: of(),
}),
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { ILicense } from '../../../licensing/server';
import { of, BehaviorSubject } from 'rxjs';
import { ILicense } from '../../../licensing/public';
import { SecurityLicenseService } from './license_service';

function getMockRawLicense({ isAvailable = false } = {}) {
Expand All @@ -17,7 +18,9 @@ function getMockRawLicense({ isAvailable = false } = {}) {

describe('license features', function() {
it('should display error when ES is unavailable', () => {
const serviceSetup = new SecurityLicenseService().setup();
const serviceSetup = new SecurityLicenseService().setup({
license$: of((undefined as unknown) as ILicense),
});
expect(serviceSetup.license.getFeatures()).toEqual({
showLogin: true,
allowLogin: false,
Expand All @@ -30,8 +33,9 @@ describe('license features', function() {
});

it('should display error when X-Pack is unavailable', () => {
const serviceSetup = new SecurityLicenseService().setup();
serviceSetup.update(getMockRawLicense({ isAvailable: false }));
const serviceSetup = new SecurityLicenseService().setup({
license$: of(getMockRawLicense({ isAvailable: false })),
});
expect(serviceSetup.license.getFeatures()).toEqual({
showLogin: true,
allowLogin: false,
Expand All @@ -43,15 +47,60 @@ describe('license features', function() {
});
});

it('should notify consumers of licensed feature changes', () => {
const rawLicense$ = new BehaviorSubject(getMockRawLicense({ isAvailable: false }));
const serviceSetup = new SecurityLicenseService().setup({
license$: rawLicense$,
});

const subscriptionHandler = jest.fn();
const subscription = serviceSetup.license.features$.subscribe(subscriptionHandler);
try {
expect(subscriptionHandler).toHaveBeenCalledTimes(1);
legrego marked this conversation as resolved.
Show resolved Hide resolved
expect(subscriptionHandler.mock.calls[0]).toMatchInlineSnapshot(`
Array [
Object {
"allowLogin": false,
"allowRbac": false,
"allowRoleDocumentLevelSecurity": false,
"allowRoleFieldLevelSecurity": false,
"layout": "error-xpack-unavailable",
"showLinks": false,
"showLogin": true,
},
]
`);

rawLicense$.next(getMockRawLicense({ isAvailable: true }));
expect(subscriptionHandler).toHaveBeenCalledTimes(2);
expect(subscriptionHandler.mock.calls[1]).toMatchInlineSnapshot(`
Array [
Object {
"allowLogin": false,
"allowRbac": false,
"allowRoleDocumentLevelSecurity": false,
"allowRoleFieldLevelSecurity": false,
"linksMessage": "Access is denied because Security is disabled in Elasticsearch.",
"showLinks": false,
"showLogin": false,
},
]
`);
} finally {
subscription.unsubscribe();
}
});

it('should show login page and other security elements, allow RBAC but forbid document level security if license is not platinum or trial.', () => {
const mockRawLicense = getMockRawLicense({ isAvailable: true });
mockRawLicense.isOneOf.mockImplementation(licenses =>
Array.isArray(licenses) ? licenses.includes('basic') : licenses === 'basic'
);
mockRawLicense.getFeature.mockReturnValue({ isEnabled: true, isAvailable: true });

const serviceSetup = new SecurityLicenseService().setup();
serviceSetup.update(mockRawLicense);
const serviceSetup = new SecurityLicenseService().setup({
license$: of(mockRawLicense),
});
expect(serviceSetup.license.getFeatures()).toEqual({
showLogin: true,
allowLogin: true,
Expand All @@ -69,8 +118,9 @@ describe('license features', function() {
mockRawLicense.isOneOf.mockReturnValue(false);
mockRawLicense.getFeature.mockReturnValue({ isEnabled: false, isAvailable: true });

const serviceSetup = new SecurityLicenseService().setup();
serviceSetup.update(mockRawLicense);
const serviceSetup = new SecurityLicenseService().setup({
license$: of(mockRawLicense),
});
expect(serviceSetup.license.getFeatures()).toEqual({
showLogin: false,
allowLogin: false,
Expand All @@ -94,8 +144,9 @@ describe('license features', function() {
});
mockRawLicense.getFeature.mockReturnValue({ isEnabled: true, isAvailable: true });

const serviceSetup = new SecurityLicenseService().setup();
serviceSetup.update(mockRawLicense);
const serviceSetup = new SecurityLicenseService().setup({
license$: of(mockRawLicense),
});
expect(serviceSetup.license.getFeatures()).toEqual({
showLogin: true,
allowLogin: true,
Expand Down
106 changes: 106 additions & 0 deletions x-pack/plugins/security/common/licensing/license_service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* 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 { Observable, Subscription } from 'rxjs';
import { map } from 'rxjs/operators';
import { ILicense } from '../../../licensing/common/types';
import { SecurityLicenseFeatures } from './license_features';

export interface SecurityLicense {
isEnabled(): boolean;
getFeatures(): SecurityLicenseFeatures;
features$: Observable<SecurityLicenseFeatures>;
}

interface SetupDeps {
license$: Observable<ILicense>;
}

export class SecurityLicenseService {
private licenseSubscription?: Subscription;

public setup({ license$ }: SetupDeps) {
let rawLicense: Readonly<ILicense> | undefined;

this.licenseSubscription = license$.subscribe(nextRawLicense => {
rawLicense = nextRawLicense;
});

return {
license: Object.freeze({
isEnabled: () => this.isSecurityEnabledFromRawLicense(rawLicense),

getFeatures: () => this.calculateFeaturesFromRawLicense(rawLicense),

features$: license$.pipe(
map(nextRawLicense => this.calculateFeaturesFromRawLicense(nextRawLicense))
),
}),
};
}

public stop() {
if (this.licenseSubscription) {
this.licenseSubscription.unsubscribe();
this.licenseSubscription = undefined;
}
}

private isSecurityEnabledFromRawLicense(rawLicense: Readonly<ILicense> | undefined) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: was trying to use new and shiny optional chaining here, but couldn't come up with anything elegant 😢 The closest I have is:

const securityFeature = rawLicense?.getFeature('security');
return !!(securityFeature?.isAvailable && securityFeature.isEnabled);

if (!rawLicense) {
return false;
}

const securityFeature = rawLicense.getFeature('security');
return (
securityFeature !== undefined && securityFeature.isAvailable && securityFeature.isEnabled
);
}

private calculateFeaturesFromRawLicense(
rawLicense: Readonly<ILicense> | undefined
): SecurityLicenseFeatures {
// If, for some reason, we cannot get license information from Elasticsearch,
// assume worst-case and lock user at login screen.
if (!rawLicense?.isAvailable) {
return {
showLogin: true,
allowLogin: false,
showLinks: false,
allowRoleDocumentLevelSecurity: false,
allowRoleFieldLevelSecurity: false,
allowRbac: false,
layout:
legrego marked this conversation as resolved.
Show resolved Hide resolved
rawLicense !== undefined && !rawLicense?.isAvailable
? 'error-xpack-unavailable'
: 'error-es-unavailable',
};
}

if (!this.isSecurityEnabledFromRawLicense(rawLicense)) {
return {
showLogin: false,
allowLogin: false,
showLinks: false,
allowRoleDocumentLevelSecurity: false,
allowRoleFieldLevelSecurity: false,
allowRbac: false,
linksMessage: 'Access is denied because Security is disabled in Elasticsearch.',
};
}

const isLicensePlatinumOrBetter = rawLicense.isOneOf(['enterprise', 'platinum', 'trial']);
return {
showLogin: true,
allowLogin: true,
showLinks: true,
// Only platinum and trial licenses are compliant with field- and document-level security.
allowRoleDocumentLevelSecurity: isLicensePlatinumOrBetter,
allowRoleFieldLevelSecurity: isLicensePlatinumOrBetter,
allowRbac: true,
};
}
}
7 changes: 7 additions & 0 deletions x-pack/plugins/security/public/nav_control/index.ts
Original file line number Diff line number Diff line change
@@ -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 { SecurityNavControlService } from './nav_control_service';
Loading