diff --git a/docs/changelog/118058.yaml b/docs/changelog/118058.yaml new file mode 100644 index 0000000000000..d5fad346d4d85 --- /dev/null +++ b/docs/changelog/118058.yaml @@ -0,0 +1,5 @@ +pr: 118058 +summary: Grant necessary Kibana application privileges to `reporting_user` role +area: Authorization +type: enhancement +issues: [] diff --git a/docs/reference/security/authorization/built-in-roles.asciidoc b/docs/reference/security/authorization/built-in-roles.asciidoc index d730587e7db17..13812b915dc5e 100644 --- a/docs/reference/security/authorization/built-in-roles.asciidoc +++ b/docs/reference/security/authorization/built-in-roles.asciidoc @@ -161,12 +161,11 @@ Grants the minimum privileges required to write data into the monitoring indices Grants the minimum privileges required to collect monitoring data for the {stack}. [[built-in-roles-reporting-user]] `reporting_user`:: -Grants the specific privileges required for users of {reporting} other than those -required to use {kib}. This role grants access to the reporting indices; each -user has access to only their own reports. -Reporting users should also be assigned additional roles that grant -{kibana-ref}/xpack-security-authorization.html[access to {kib}] as well as read -access to the <> that will be used to generate reports. +Grants the necessary privileges required to use {reporting} features in {kib}, +including generating and downloading reports. This role implicitly grants access +to all Kibana reporting features, with each user having access only to their own reports. +Note that reporting users should also be assigned additional roles that grant read access +to the <> that will be used to generate reports. [[built-in-roles-rollup-admin]] `rollup_admin`:: Grants `manage_rollup` cluster privileges, which enable you to manage and execute all rollup actions. diff --git a/muted-tests.yml b/muted-tests.yml index 36dfc306b0147..e0d011c1bf239 100644 --- a/muted-tests.yml +++ b/muted-tests.yml @@ -317,9 +317,6 @@ tests: - class: org.elasticsearch.reservedstate.service.FileSettingsServiceTests method: testInvalidJSON issue: https://github.com/elastic/elasticsearch/issues/116521 -- class: org.elasticsearch.xpack.esql.optimizer.LogicalPlanOptimizerTests - method: testPlanSanityCheckWithBinaryPlans - issue: https://github.com/elastic/elasticsearch/issues/118656 # Examples: # diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStore.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStore.java index fc14ec6811014..bdaf75203ee5d 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStore.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStore.java @@ -301,25 +301,40 @@ private static Map initializeReservedRoles() { "Grants access to manage all index templates and all ingest pipeline configurations." ) ), - // reporting_user doesn't have any privileges in Elasticsearch, and Kibana authorizes privileges based on this role entry( "reporting_user", new RoleDescriptor( "reporting_user", null, null, + new RoleDescriptor.ApplicationResourcePrivileges[] { + RoleDescriptor.ApplicationResourcePrivileges.builder() + .application("kibana-.kibana") + .resources("*") + .privileges( + "feature_discover.minimal_read", + "feature_discover.generate_report", + "feature_dashboard.minimal_read", + "feature_dashboard.generate_report", + "feature_dashboard.download_csv_report", + "feature_canvas.minimal_read", + "feature_canvas.generate_report", + "feature_visualize.minimal_read", + "feature_visualize.generate_report" + ) + .build() }, null, null, - null, - MetadataUtils.getDeprecatedReservedMetadata("Please use Kibana feature privileges instead"), + MetadataUtils.DEFAULT_RESERVED_METADATA, null, null, null, null, - "Grants the specific privileges required for users of X-Pack reporting other than those required to use Kibana. " - + "This role grants access to the reporting indices; each user has access to only their own reports. " - + "Reporting users should also be assigned additional roles that grant access to Kibana as well as read access " - + "to the indices that will be used to generate reports." + "Grants the necessary privileges required to use reporting features in Kibana, " + + "including generating and downloading reports. " + + "This role implicitly grants access to all Kibana reporting features, " + + "with each user having access only to their own reports. Note that reporting users should also be assigned " + + "additional roles that grant read access to the indices that will be used to generate reports." ) ), entry(KibanaSystemUser.ROLE_NAME, kibanaSystemRoleDescriptor(KibanaSystemUser.ROLE_NAME)), diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStoreTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStoreTests.java index b69b0ece89960..1b9a65d12d8d9 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStoreTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/store/ReservedRolesStoreTests.java @@ -2646,12 +2646,57 @@ public void testReportingUserRole() { RoleDescriptor roleDescriptor = ReservedRolesStore.roleDescriptor("reporting_user"); assertNotNull(roleDescriptor); assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true)); - assertThat(roleDescriptor.getMetadata(), hasEntry("_deprecated", true)); + + final String applicationName = "kibana-.kibana"; + + final Set applicationPrivilegeNames = Set.of( + "feature_discover.minimal_read", + "feature_discover.generate_report", + "feature_dashboard.minimal_read", + "feature_dashboard.generate_report", + "feature_dashboard.download_csv_report", + "feature_canvas.minimal_read", + "feature_canvas.generate_report", + "feature_visualize.minimal_read", + "feature_visualize.generate_report" + ); + + final Set allowedApplicationActionPatterns = Set.of( + "login:", + "app:discover", + "app:canvas", + "app:kibana", + "ui:catalogue/canvas", + "ui:navLinks/canvas", + "ui:catalogue/discover", + "ui:navLinks/discover", + "ui:navLinks/kibana", + "saved_object:index-pattern/*", + "saved_object:search/*", + "saved_object:query/*", + "saved_object:config/*", + "saved_object:config/get", + "saved_object:config/find", + "saved_object:config-global/*", + "saved_object:telemetry/*", + "saved_object:canvas-workpad/*", + "saved_object:canvas-element/*", + "saved_object:url/*", + "ui:discover/show" + ); + + final List applicationPrivilegeDescriptors = new ArrayList<>(); + for (String appPrivilegeName : applicationPrivilegeNames) { + applicationPrivilegeDescriptors.add( + new ApplicationPrivilegeDescriptor(applicationName, appPrivilegeName, allowedApplicationActionPatterns, Map.of()) + ); + } Role reportingUserRole = Role.buildFromRoleDescriptor( roleDescriptor, new FieldPermissionsCache(Settings.EMPTY), - RESTRICTED_INDICES + RESTRICTED_INDICES, + applicationPrivilegeDescriptors ); assertThat(reportingUserRole.cluster().check(TransportClusterHealthAction.NAME, request, authentication), is(false)); assertThat(reportingUserRole.cluster().check(ClusterStateAction.NAME, request, authentication), is(false)); @@ -2723,6 +2768,33 @@ public void testReportingUserRole() { assertNoAccessAllowed(reportingUserRole, TestRestrictedIndices.SAMPLE_RESTRICTED_NAMES); assertNoAccessAllowed(reportingUserRole, XPackPlugin.ASYNC_RESULTS_INDEX + randomAlphaOfLengthBetween(0, 2)); + + applicationPrivilegeNames.forEach(appPrivilege -> { + assertThat( + reportingUserRole.application() + .grants( + ApplicationPrivilegeTests.createPrivilege( + applicationName, + appPrivilege, + allowedApplicationActionPatterns.toArray(new String[0]) + ), + "*" + ), + is(true) + ); + }); + assertThat( + reportingUserRole.application() + .grants( + ApplicationPrivilegeTests.createPrivilege( + "kibana-.*", + "feature_random.minimal_read", + allowedApplicationActionPatterns.toArray(new String[0]) + ), + "*" + ), + is(false) + ); } public void testSuperuserRole() { diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java index 7e498eb6654b9..d97a8bb2bc27f 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java @@ -4911,8 +4911,7 @@ public void testPlanSanityCheckWithBinaryPlans() throws Exception { """); var project = as(plan, Project.class); - var limit = as(project.child(), Limit.class); - var join = as(limit.child(), Join.class); + var join = as(project.child(), Join.class); var joinWithInvalidLeftPlan = join.replaceChildren(join.right(), join.right()); IllegalStateException e = expectThrows(IllegalStateException.class, () -> logicalOptimizer.optimize(joinWithInvalidLeftPlan));