Skip to content

Commit

Permalink
[RBAC Phase 1] - Update application privileges when XPack license cha…
Browse files Browse the repository at this point in the history
…nges (#19839)

* Adding start to supporting basic license and switching to plat/gold

* Initialize application privilages on XPack license change

* restore mirror_status_and_initialize

* additional tests and peer review updates

* Introducing watchStatusAndLicenseToInitialize

* Adding some tests

* One more test

* Even better tests

* Removing unused mirrorStatusAndInitialize

* Throwing an error if the wrong status function is called
  • Loading branch information
legrego authored Jun 13, 2018
1 parent e887076 commit a76e4a5
Show file tree
Hide file tree
Showing 8 changed files with 429 additions and 227 deletions.
45 changes: 30 additions & 15 deletions x-pack/plugins/security/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ import { validateConfig } from './server/lib/validate_config';
import { authenticateFactory } from './server/lib/auth_redirect';
import { checkLicense } from './server/lib/check_license';
import { initAuthenticator } from './server/lib/authentication/authenticator';
import { mirrorStatusAndInitialize } from './server/lib/mirror_status_and_initialize';
import { registerPrivilegesWithCluster } from './server/lib/privileges';
import { initPrivilegesApi } from './server/routes/api/v1/privileges';
import { hasPrivilegesWithServer } from './server/lib/authorization/has_privileges';
import { SecurityAuditLogger } from './server/lib/audit_logger';
import { AuditLogger } from '../../server/lib/audit_logger';
import { SecureSavedObjectsClient } from './server/lib/saved_objects_client/secure_saved_objects_client';
import { registerPrivilegesWithCluster } from './server/lib/privileges';
import { watchStatusAndLicenseToInitialize } from './server/lib/watch_status_and_license_to_initialize';

export const security = (kibana) => new kibana.Plugin({
id: 'security',
Expand Down Expand Up @@ -88,20 +88,23 @@ export const security = (kibana) => new kibana.Plugin({
},

async init(server) {
const plugin = this;

const config = server.config();
const xpackMainPlugin = server.plugins.xpack_main;
const xpackInfo = xpackMainPlugin.info;

mirrorStatusAndInitialize(xpackMainPlugin.status, this.status, async () => {
if (!config.get('xpack.security.rbac.enabled')) {
return;
}

await registerPrivilegesWithCluster(server);
});
const xpackInfoFeature = xpackInfo.feature(plugin.id);

// Register a function that is called whenever the xpack info changes,
// to re-compute the license check results for this plugin
xpackMainPlugin.info.feature(this.id).registerLicenseCheckResultsGenerator(checkLicense);
xpackInfoFeature.registerLicenseCheckResultsGenerator(checkLicense);

watchStatusAndLicenseToInitialize(xpackMainPlugin, plugin, async (license) => {
if (license.allowRbac) {
await registerPrivilegesWithCluster(server);
}
});

validateConfig(config, message => server.log(['security', 'warning'], message));

Expand All @@ -123,9 +126,23 @@ export const security = (kibana) => new kibana.Plugin({
mappings,
onBeforeWrite
}) => {
const hasPrivileges = hasPrivilegesWithRequest(request);

const adminCluster = server.plugins.elasticsearch.getCluster('admin');

if (!xpackInfoFeature.getLicenseCheckResults().allowRbac) {
const { callWithRequest } = adminCluster;
const callCluster = (...args) => callWithRequest(request, ...args);

const repository = new savedObjects.SavedObjectsRepository({
index,
mappings,
onBeforeWrite,
callCluster,
});

return new savedObjects.SavedObjectsClient(repository);
}

const hasPrivileges = hasPrivilegesWithRequest(request);
const { callWithInternalUser } = adminCluster;

const repository = new savedObjects.SavedObjectsRepository({
Expand Down Expand Up @@ -156,9 +173,7 @@ export const security = (kibana) => new kibana.Plugin({
initLogoutView(server);

server.injectUiAppVars('login', () => {
const pluginId = 'security';
const xpackInfo = server.plugins.xpack_main.info;
const { showLogin, loginMessage, allowLogin } = xpackInfo.feature(pluginId).getLicenseCheckResults() || {};
const { showLogin, loginMessage, allowLogin } = xpackInfo.feature(plugin.id).getLicenseCheckResults() || {};

return {
loginState: {
Expand Down
5 changes: 4 additions & 1 deletion x-pack/plugins/security/server/lib/check_license.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export function checkLicense(xPackInfo) {
showLinks: false,
allowRoleDocumentLevelSecurity: false,
allowRoleFieldLevelSecurity: false,
allowRbac: false,
loginMessage: 'Login is currently disabled. Administrators should consult the Kibana logs for more details.'
};
}
Expand All @@ -46,6 +47,7 @@ export function checkLicense(xPackInfo) {
showLinks: false,
allowRoleDocumentLevelSecurity: false,
allowRoleFieldLevelSecurity: false,
allowRbac: false,
linksMessage: isLicenseBasic
? 'Your Basic license does not support Security. Please upgrade your license.'
: 'Access is denied because Security is disabled in Elasticsearch.'
Expand All @@ -60,6 +62,7 @@ export function checkLicense(xPackInfo) {
showLinks: true,
// Only platinum and trial licenses are compliant with field- and document-level security.
allowRoleDocumentLevelSecurity: isLicensePlatinumOrTrial,
allowRoleFieldLevelSecurity: isLicensePlatinumOrTrial
allowRoleFieldLevelSecurity: isLicensePlatinumOrTrial,
allowRbac: true,
};
}
63 changes: 0 additions & 63 deletions x-pack/plugins/security/server/lib/mirror_status_and_initialize.js

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* 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 } from 'rxjs';

export function watchStatusAndLicenseToInitialize(xpackMainPlugin, downstreamPlugin, initialize) {
const xpackInfo = xpackMainPlugin.info;
const xpackInfoFeature = xpackInfo.feature(downstreamPlugin.id);

const upstreamStatus = xpackMainPlugin.status;
const currentStatus$ = Observable
.of({
state: upstreamStatus.state,
message: upstreamStatus.message,
});
const newStatus$ = Observable
.fromEvent(upstreamStatus, 'change', null, (previousState, previousMsg, state, message) => {
return {
state,
message,
};
});
const status$ = Observable.merge(currentStatus$, newStatus$);

const currentLicense$ = Observable
.of(xpackInfoFeature.getLicenseCheckResults());
const newLicense$ = Observable
.fromEventPattern(xpackInfoFeature.registerLicenseChangeCallback)
.map(() => xpackInfoFeature.getLicenseCheckResults());
const license$ = Observable.merge(currentLicense$, newLicense$);

Observable.combineLatest(status$, license$)
.map(([status, license]) => ({ status, license }))
.switchMap(({ status, license }) => {
if (status.state !== 'green') {
return Observable.of({ state: status.state, message: status.message });
}

return initialize(license)
.then(() => ({
state: 'green',
message: 'Ready',
}))
.catch((err) => ({
state: 'red',
message: err.message
}));
})
.do(({ state, message }) => {
downstreamPlugin.status[state](message);
})
.subscribe();
}
Loading

0 comments on commit a76e4a5

Please sign in to comment.