kibana/x-pack/plugins/security/index.js

210 lines
7.9 KiB
JavaScript

/*
* 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 { has } from 'lodash';
import { getUserProvider } from './server/lib/get_user';
import { initAuthenticateApi } from './server/routes/api/v1/authenticate';
import { initUsersApi } from './server/routes/api/v1/users';
import { initPublicRolesApi } from './server/routes/api/public/roles';
import { initIndicesApi } from './server/routes/api/v1/indices';
import { initLoginView } from './server/routes/views/login';
import { initLogoutView } from './server/routes/views/logout';
import { initLoggedOutView } from './server/routes/views/logged_out';
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 { SecurityAuditLogger } from './server/lib/audit_logger';
import { AuditLogger } from '../../server/lib/audit_logger';
import { createAuthorizationService, registerPrivilegesWithCluster } from './server/lib/authorization';
import { watchStatusAndLicenseToInitialize } from '../../server/lib/watch_status_and_license_to_initialize';
import { SecureSavedObjectsClientWrapper } from './server/lib/saved_objects_client/secure_saved_objects_client_wrapper';
import { deepFreeze } from './server/lib/deep_freeze';
export const security = (kibana) => new kibana.Plugin({
id: 'security',
configPrefix: 'xpack.security',
publicDir: resolve(__dirname, 'public'),
require: ['kibana', 'elasticsearch', 'xpack_main'],
config(Joi) {
return Joi.object({
authProviders: Joi.array().items(Joi.string()).default(['basic']),
enabled: Joi.boolean().default(true),
cookieName: Joi.string().default('sid'),
encryptionKey: Joi.string(),
sessionTimeout: Joi.number().allow(null).default(null),
secureCookies: Joi.boolean().default(false),
sameSiteCookies: Joi.string().valid(['Strict', 'Lax', 'None']).optional(),
loginAssistanceMessage: Joi.string(),
public: Joi.object({
protocol: Joi.string().valid(['http', 'https']),
hostname: Joi.string().hostname(),
port: Joi.number().integer().min(0).max(65535)
}).default(),
authc: Joi.object({})
.when('authProviders', {
is: Joi.array().items(Joi.string().valid('saml').required(), Joi.string()),
then: Joi.object({ saml: Joi.object({ useRelayStateDeepLink: Joi.boolean().default(false) }) }).default(),
otherwise: Joi.any().forbidden(),
}),
authorization: Joi.object({
legacyFallback: Joi.object({
enabled: Joi.boolean().default(true)
}).default()
}).default(),
audit: Joi.object({
enabled: Joi.boolean().default(false)
}).default(),
}).default();
},
deprecations() {
return [
(settings, log) => {
if (has(settings, 'authc.saml.useRelayStateDeepLink')) {
log('Config key "authc.saml.useRelayStateDeepLink" is deprecated and will be removed in the next major version.');
}
}
];
},
uiExports: {
chromeNavControls: ['plugins/security/views/nav_control'],
managementSections: ['plugins/security/views/management'],
styleSheetPaths: resolve(__dirname, 'public/index.scss'),
apps: [{
id: 'login',
title: 'Login',
main: 'plugins/security/views/login',
hidden: true,
}, {
id: 'logout',
title: 'Logout',
main: 'plugins/security/views/logout',
hidden: true
}, {
id: 'logged_out',
title: 'Logged out',
main: 'plugins/security/views/logged_out',
hidden: true
}],
hacks: [
'plugins/security/hacks/on_session_timeout',
'plugins/security/hacks/on_unauthorized_response'
],
home: ['plugins/security/register_feature'],
injectDefaultVars: function (server) {
const config = server.config();
return {
secureCookies: config.get('xpack.security.secureCookies'),
sessionTimeout: config.get('xpack.security.sessionTimeout'),
enableSpaceAwarePrivileges: config.get('xpack.spaces.enabled'),
};
}
},
async init(server) {
const plugin = this;
const config = server.config();
const xpackMainPlugin = server.plugins.xpack_main;
const xpackInfo = xpackMainPlugin.info;
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
xpackInfoFeature.registerLicenseCheckResultsGenerator(checkLicense);
validateConfig(config, message => server.log(['security', 'warning'], message));
// Create a Hapi auth scheme that should be applied to each request.
server.auth.scheme('login', () => ({ authenticate: authenticateFactory(server) }));
server.auth.strategy('session', 'login');
// The default means that the `session` strategy that is based on `login` schema defined above will be
// automatically assigned to all routes that don't contain an auth config.
server.auth.default('session');
// exposes server.plugins.security.authorization
const authorization = createAuthorizationService(server, xpackInfoFeature);
server.expose('authorization', deepFreeze(authorization));
watchStatusAndLicenseToInitialize(xpackMainPlugin, plugin, async (license) => {
if (license.allowRbac) {
await registerPrivilegesWithCluster(server);
}
});
const auditLogger = new SecurityAuditLogger(new AuditLogger(server, 'security', server.config(), xpackInfo));
const { savedObjects } = server;
savedObjects.setScopedSavedObjectsClientFactory(({
request,
}) => {
const adminCluster = server.plugins.elasticsearch.getCluster('admin');
const { callWithRequest, callWithInternalUser } = adminCluster;
const callCluster = (...args) => callWithRequest(request, ...args);
if (authorization.mode.useRbacForRequest(request)) {
const internalRepository = savedObjects.getSavedObjectsRepository(callWithInternalUser);
return new savedObjects.SavedObjectsClient(internalRepository);
}
const callWithRequestRepository = savedObjects.getSavedObjectsRepository(callCluster);
return new savedObjects.SavedObjectsClient(callWithRequestRepository);
});
savedObjects.addScopedSavedObjectsClientWrapperFactory(Number.MIN_VALUE, ({ client, request }) => {
if (authorization.mode.useRbacForRequest(request)) {
const { spaces } = server.plugins;
return new SecureSavedObjectsClientWrapper({
actions: authorization.actions,
auditLogger,
baseClient: client,
checkPrivilegesWithRequest: authorization.checkPrivilegesWithRequest,
errors: savedObjects.SavedObjectsClient.errors,
request,
savedObjectTypes: savedObjects.types,
spaces,
});
}
return client;
});
getUserProvider(server);
await initAuthenticator(server, authorization.mode);
initAuthenticateApi(server);
initUsersApi(server);
initPublicRolesApi(server);
initIndicesApi(server);
initLoginView(server, xpackMainPlugin);
initLogoutView(server);
initLoggedOutView(server);
server.injectUiAppVars('login', () => {
const { showLogin, loginMessage, allowLogin, layout = 'form' } = xpackInfo.feature(plugin.id).getLicenseCheckResults() || {};
return {
loginAssistanceMessage: config.get('xpack.security.loginAssistanceMessage') || '',
loginState: {
showLogin,
allowLogin,
loginMessage,
layout,
}
};
});
}
});