diff --git a/src/server/saved_objects/service/create_saved_objects_service.js b/src/server/saved_objects/service/create_saved_objects_service.js index ff68b5eaee2833..fd471cad2dd23d 100644 --- a/src/server/saved_objects/service/create_saved_objects_service.js +++ b/src/server/saved_objects/service/create_saved_objects_service.js @@ -17,6 +17,7 @@ * under the License. */ +import { getRootPropertiesObjects } from '../../mappings'; import { SavedObjectsRepository, ScopedSavedObjectsClientProvider, SavedObjectsRepositoryProvider } from './lib'; import { SavedObjectsClient } from './saved_objects_client'; @@ -58,15 +59,16 @@ export function createSavedObjectsService(server) { } }; + const mappings = server.getKibanaIndexMappingsDsl(); const repositoryProvider = new SavedObjectsRepositoryProvider({ index: server.config().get('kibana.index'), - mappings: server.getKibanaIndexMappingsDsl(), + mappings, onBeforeWrite, }); const scopedClientProvider = new ScopedSavedObjectsClientProvider({ index: server.config().get('kibana.index'), - mappings: server.getKibanaIndexMappingsDsl(), + mappings, onBeforeWrite, defaultClientFactory({ request, @@ -81,6 +83,7 @@ export function createSavedObjectsService(server) { }); return { + types: Object.keys(getRootPropertiesObjects(mappings)), SavedObjectsClient, SavedObjectsRepository, getSavedObjectsRepository: (...args) => diff --git a/x-pack/plugins/security/common/constants.js b/x-pack/plugins/security/common/constants.js new file mode 100644 index 00000000000000..1b762d5f15acc6 --- /dev/null +++ b/x-pack/plugins/security/common/constants.js @@ -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 ALL_RESOURCE = '*'; diff --git a/x-pack/plugins/security/index.js b/x-pack/plugins/security/index.js index 5e4602d4480844..b86489171ff269 100644 --- a/x-pack/plugins/security/index.js +++ b/x-pack/plugins/security/index.js @@ -8,7 +8,7 @@ import { resolve } from 'path'; import { getUserProvider } from './server/lib/get_user'; import { initAuthenticateApi } from './server/routes/api/v1/authenticate'; import { initUsersApi } from './server/routes/api/v1/users'; -import { initRolesApi } from './server/routes/api/v1/roles'; +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'; @@ -16,7 +16,12 @@ 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 { mirrorPluginStatus } from '../../server/lib/mirror_plugin_status'; +import { initPrivilegesApi } from './server/routes/api/v1/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 { initAuthorizationService, registerPrivilegesWithCluster } from './server/lib/authorization'; +import { watchStatusAndLicenseToInitialize } from './server/lib/watch_status_and_license_to_initialize'; export const security = (kibana) => new kibana.Plugin({ id: 'security', @@ -37,6 +42,14 @@ export const security = (kibana) => new kibana.Plugin({ hostname: Joi.string().hostname(), port: Joi.number().integer().min(0).max(65535) }).default(), + authorization: Joi.object({ + legacyFallback: Joi.object({ + enabled: Joi.boolean().default(true) + }).default() + }).default(), + audit: Joi.object({ + enabled: Joi.boolean().default(false) + }).default(), }).default(); }, @@ -64,21 +77,24 @@ export const security = (kibana) => new kibana.Plugin({ return { secureCookies: config.get('xpack.security.secureCookies'), - sessionTimeout: config.get('xpack.security.sessionTimeout') + sessionTimeout: config.get('xpack.security.sessionTimeout'), }; } }, async init(server) { - const thisPlugin = this; + const plugin = this; + + const config = server.config(); const xpackMainPlugin = server.plugins.xpack_main; - mirrorPluginStatus(xpackMainPlugin, thisPlugin); + 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 - xpackMainPlugin.info.feature(thisPlugin.id).registerLicenseCheckResultsGenerator(checkLicense); + xpackInfoFeature.registerLicenseCheckResultsGenerator(checkLicense); - const config = server.config(); validateConfig(config, message => server.log(['security', 'warning'], message)); // Create a Hapi auth scheme that should be applied to each request. @@ -88,20 +104,59 @@ export const security = (kibana) => new kibana.Plugin({ // automatically assigned to all routes that don't contain an auth config. server.auth.strategy('session', 'login', 'required'); + // exposes server.plugins.security.authorization + initAuthorizationService(server); + + watchStatusAndLicenseToInitialize(xpackMainPlugin, plugin, async (license) => { + if (license.allowRbac) { + await registerPrivilegesWithCluster(server); + } + }); + + const auditLogger = new SecurityAuditLogger(server.config(), new AuditLogger(server, 'security')); + + const { savedObjects } = server; + savedObjects.setScopedSavedObjectsClientFactory(({ + request, + }) => { + const adminCluster = server.plugins.elasticsearch.getCluster('admin'); + const { callWithRequest, callWithInternalUser } = adminCluster; + const callCluster = (...args) => callWithRequest(request, ...args); + + const callWithRequestRepository = savedObjects.getSavedObjectsRepository(callCluster); + + if (!xpackInfoFeature.getLicenseCheckResults().allowRbac) { + return new savedObjects.SavedObjectsClient(callWithRequestRepository); + } + + const { authorization } = server.plugins.security; + const checkPrivileges = authorization.checkPrivilegesWithRequest(request); + const internalRepository = savedObjects.getSavedObjectsRepository(callWithInternalUser); + + return new SecureSavedObjectsClient({ + internalRepository, + callWithRequestRepository, + errors: savedObjects.SavedObjectsClient.errors, + checkPrivileges, + auditLogger, + savedObjectTypes: savedObjects.types, + actions: authorization.actions, + }); + }); + getUserProvider(server); await initAuthenticator(server); initAuthenticateApi(server); initUsersApi(server); - initRolesApi(server); + initPublicRolesApi(server); initIndicesApi(server); + initPrivilegesApi(server); initLoginView(server, xpackMainPlugin); 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: { diff --git a/x-pack/plugins/security/public/lib/api.js b/x-pack/plugins/security/public/lib/api.js index 908a0ceaf31035..a41afcc0d4a3f8 100644 --- a/x-pack/plugins/security/public/lib/api.js +++ b/x-pack/plugins/security/public/lib/api.js @@ -6,7 +6,7 @@ import chrome from 'ui/chrome'; const usersUrl = chrome.addBasePath('/api/security/v1/users'); -const rolesUrl = chrome.addBasePath('/api/security/v1/roles'); +const rolesUrl = chrome.addBasePath('/api/security/role'); export const createApiClient = (httpClient) => { return { diff --git a/x-pack/plugins/security/public/services/application_privilege.js b/x-pack/plugins/security/public/services/application_privilege.js new file mode 100644 index 00000000000000..615188cad33d7f --- /dev/null +++ b/x-pack/plugins/security/public/services/application_privilege.js @@ -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 'angular-resource'; +import { uiModules } from 'ui/modules'; + +const module = uiModules.get('security', ['ngResource']); +module.service('ApplicationPrivileges', ($resource, chrome) => { + const baseUrl = chrome.addBasePath('/api/security/v1/privileges'); + return $resource(baseUrl); +}); diff --git a/x-pack/plugins/security/public/services/shield_privileges.js b/x-pack/plugins/security/public/services/shield_privileges.js index aabd0a95b26cab..a00b326ab82da3 100644 --- a/x-pack/plugins/security/public/services/shield_privileges.js +++ b/x-pack/plugins/security/public/services/shield_privileges.js @@ -35,5 +35,6 @@ module.constant('shieldPrivileges', { 'create_index', 'view_index_metadata', 'read_cross_cluster', - ] + ], + applications: [] }); diff --git a/x-pack/plugins/security/public/services/shield_role.js b/x-pack/plugins/security/public/services/shield_role.js index 1954415f0384bd..3ee73dbad57dbb 100644 --- a/x-pack/plugins/security/public/services/shield_role.js +++ b/x-pack/plugins/security/public/services/shield_role.js @@ -5,11 +5,20 @@ */ import 'angular-resource'; +import { omit } from 'lodash'; +import angular from 'angular'; import { uiModules } from 'ui/modules'; const module = uiModules.get('security', ['ngResource']); module.service('ShieldRole', ($resource, chrome) => { - return $resource(chrome.addBasePath('/api/security/v1/roles/:name'), { + return $resource(chrome.addBasePath('/api/security/role/:name'), { name: '@name' + }, { + save: { + method: 'PUT', + transformRequest(data) { + return angular.toJson(omit(data, 'name', 'transient_metadata', '_unrecognized_applications')); + } + } }); }); diff --git a/x-pack/plugins/security/public/views/management/edit_role.html b/x-pack/plugins/security/public/views/management/edit_role.html index f547788606cc32..490e1c0e8f8c5e 100644 --- a/x-pack/plugins/security/public/views/management/edit_role.html +++ b/x-pack/plugins/security/public/views/management/edit_role.html @@ -1,8 +1,10 @@
+
+

@@ -39,6 +41,18 @@

+
+
+
+ + + This role contains application privileges for the {{ otherApplications.join(', ') }} application(s) that can't be edited. + If they are for other instances of Kibana, you must manage those privileges on that Kibana instance. + +
+
+
+
@@ -56,7 +70,7 @@

ng-model="role.name" required pattern="[a-zA-Z_][a-zA-Z0-9_@\-\$\.]*" - maxlength="30" + maxlength="1024" data-test-subj="roleFormNameInput" /> @@ -87,15 +101,36 @@

{{privilege}}

+ +
+ + +
+ +
+
+