diff --git a/x-pack/plugins/aiops/public/components/change_point_detection/change_point_detection_context.tsx b/x-pack/plugins/aiops/public/components/change_point_detection/change_point_detection_context.tsx index 600a68ff1a498..8d67a28c3055f 100644 --- a/x-pack/plugins/aiops/public/components/change_point_detection/change_point_detection_context.tsx +++ b/x-pack/plugins/aiops/public/components/change_point_detection/change_point_detection_context.tsx @@ -16,7 +16,6 @@ import React, { } from 'react'; import { type DataViewField } from '@kbn/data-views-plugin/public'; import { startWith } from 'rxjs'; -import useMount from 'react-use/lib/useMount'; import type { Query, Filter } from '@kbn/es-query'; import { usePageUrlState } from '@kbn/ml-url-state'; import { @@ -129,7 +128,7 @@ export const ChangePointDetectionContextProvider: FC = ({ children }) => { const timeRange = useTimeRangeUpdates(); - useMount(function updateIntervalOnTimeBoundsChange() { + useEffect(function updateIntervalOnTimeBoundsChange() { const timeUpdateSubscription = timefilter .getTimeUpdate$() .pipe(startWith(timefilter.getTime())) @@ -145,7 +144,8 @@ export const ChangePointDetectionContextProvider: FC = ({ children }) => { return () => { timeUpdateSubscription.unsubscribe(); }; - }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); const metricFieldOptions = useMemo(() => { return dataView.fields.filter(({ aggregatable, type }) => aggregatable && type === 'number'); @@ -195,7 +195,7 @@ export const ChangePointDetectionContextProvider: FC = ({ children }) => { [filterManager] ); - useMount(() => { + useEffect(() => { setResultFilter(filterManager.getFilters()); const sub = filterManager.getUpdates$().subscribe(() => { setResultFilter(filterManager.getFilters()); @@ -203,7 +203,8 @@ export const ChangePointDetectionContextProvider: FC = ({ children }) => { return () => { sub.unsubscribe(); }; - }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); useEffect( function syncFilters() { diff --git a/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/endpoint/endpoint-8.6.1.zip b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/endpoint/endpoint-8.6.1.zip new file mode 100644 index 0000000000000..4c20854aee729 Binary files /dev/null and b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/endpoint/endpoint-8.6.1.zip differ diff --git a/x-pack/test/fleet_api_integration/apis/package_policy/get.ts b/x-pack/test/fleet_api_integration/apis/package_policy/get.ts index d46aea0dbbd1b..5e3978dc50597 100644 --- a/x-pack/test/fleet_api_integration/apis/package_policy/get.ts +++ b/x-pack/test/fleet_api_integration/apis/package_policy/get.ts @@ -8,11 +8,15 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../../api_integration/ftr_provider_context'; import { skipIfNoDockerRegistry } from '../../helpers'; +import { testUsers } from '../test_users'; export default function (providerContext: FtrProviderContext) { const { getService } = providerContext; const supertest = getService('supertest'); + const superTestWithoutAuth = getService('supertestWithoutAuth'); const dockerServers = getService('dockerServers'); + const esArchiver = getService('esArchiver'); + const kibanaServer = getService('kibanaServer'); const server = dockerServers.get('registry'); // use function () {} and not () => {} here @@ -23,23 +27,19 @@ export default function (providerContext: FtrProviderContext) { skipIfNoDockerRegistry(providerContext); before(async () => { - await getService('kibanaServer').savedObjects.cleanStandardList(); - - await getService('esArchiver').load( - 'x-pack/test/functional/es_archives/fleet/empty_fleet_server' - ); + await kibanaServer.savedObjects.cleanStandardList(); + await esArchiver.load('x-pack/test/functional/es_archives/fleet/empty_fleet_server'); }); after(async () => { - await getService('esArchiver').unload( - 'x-pack/test/functional/es_archives/fleet/empty_fleet_server' - ); - await getService('kibanaServer').savedObjects.cleanStandardList(); + await esArchiver.unload('x-pack/test/functional/es_archives/fleet/empty_fleet_server'); + await kibanaServer.savedObjects.cleanStandardList(); }); describe('get by id', async function () { let agentPolicyId: string; let packagePolicyId: string; + let endpointPackagePolicyId: string; before(async function () { if (!server.enabled) { @@ -72,6 +72,25 @@ export default function (providerContext: FtrProviderContext) { }, }); packagePolicyId = packagePolicyResponse.item.id; + + const { body: endpointPackagePolicyResponse } = await supertest + .post(`/api/fleet/package_policies`) + .set('kbn-xsrf', 'xxxx') + .send({ + name: 'endpoint-1', + description: '', + namespace: 'default', + policy_id: agentPolicyId, + enabled: true, + inputs: [], + force: true, + package: { + name: 'endpoint', + title: 'Elastic Defend', + version: '8.6.1', + }, + }); + endpointPackagePolicyId = endpointPackagePolicyResponse.item.id; }); after(async function () { @@ -88,7 +107,14 @@ export default function (providerContext: FtrProviderContext) { await supertest .post(`/api/fleet/package_policies/delete`) .set('kbn-xsrf', 'xxxx') - .send({ packagePolicyIds: [packagePolicyId] }) + .send({ packagePolicyIds: [packagePolicyId, endpointPackagePolicyId] }) + .expect(200); + + // uninstall endpoint package + await supertest + .delete(`/api/fleet/epm/packages/endpoint-8.6.1`) + .set('kbn-xsrf', 'xxxx') + .send({ force: true }) .expect(200); }); @@ -96,6 +122,33 @@ export default function (providerContext: FtrProviderContext) { await supertest.get(`/api/fleet/package_policies/${packagePolicyId}`).expect(200); }); + it('should succeed when requesting with policy ids that match package names allowed by package privileges', async function () { + await superTestWithoutAuth + .get(`/api/fleet/package_policies/${endpointPackagePolicyId}`) + .set('kbn-xsrf', 'xxxx') + .auth( + testUsers.endpoint_integr_read_policy.username, + testUsers.endpoint_integr_read_policy.password + ) + .expect(200); + }); + + it('should return 403 for requests with authenticated role but not allowed packages', async function () { + await superTestWithoutAuth + .get(`/api/fleet/package_policies/${packagePolicyId}`) + .set('kbn-xsrf', 'xxxx') + .auth( + testUsers.endpoint_integr_read_policy.username, + testUsers.endpoint_integr_read_policy.password + ) + .expect(403, { + statusCode: 403, + error: 'Forbidden', + message: + "Authorization denied to [package.name=filetest]. Allowed package.name's: endpoint", + }); + }); + it('should return a 404 with an invalid id', async function () { await supertest.get(`/api/fleet/package_policies/IS_NOT_PRESENT`).expect(404); }); @@ -104,6 +157,7 @@ export default function (providerContext: FtrProviderContext) { describe('POST /api/fleet/package_policies/_bulk_get', async function () { let agentPolicyId: string; let packagePolicyId: string; + let endpointPackagePolicyId: string; before(async function () { if (!server.enabled) { @@ -136,6 +190,25 @@ export default function (providerContext: FtrProviderContext) { }, }); packagePolicyId = packagePolicyResponse.item.id; + + const { body: endpointPackagePolicyResponse } = await supertest + .post(`/api/fleet/package_policies`) + .set('kbn-xsrf', 'xxxx') + .send({ + name: 'endpoint-1', + description: '', + namespace: 'default', + policy_id: agentPolicyId, + enabled: true, + inputs: [], + force: true, + package: { + name: 'endpoint', + title: 'Elastic Defend', + version: '8.6.1', + }, + }); + endpointPackagePolicyId = endpointPackagePolicyResponse.item.id; }); after(async function () { @@ -152,7 +225,14 @@ export default function (providerContext: FtrProviderContext) { await supertest .post(`/api/fleet/package_policies/delete`) .set('kbn-xsrf', 'xxxx') - .send({ packagePolicyIds: [packagePolicyId] }) + .send({ packagePolicyIds: [packagePolicyId, endpointPackagePolicyId] }) + .expect(200); + + // uninstall endpoint package + await supertest + .delete(`/api/fleet/epm/packages/endpoint-8.6.1`) + .set('kbn-xsrf', 'xxxx') + .send({ force: true }) .expect(200); }); @@ -176,6 +256,35 @@ export default function (providerContext: FtrProviderContext) { .expect(404); }); + it('should return 403 without allowed package names', async function () { + await superTestWithoutAuth + .post(`/api/fleet/package_policies/_bulk_get`) + .set('kbn-xsrf', 'xxxx') + .auth( + testUsers.endpoint_integr_read_policy.username, + testUsers.endpoint_integr_read_policy.password + ) + .send({ ids: [packagePolicyId] }) + .expect(403, { + error: 'Forbidden', + message: + "Authorization denied to [package.name=filetest]. Allowed package.name's: endpoint", + statusCode: 403, + }); + }); + + it('should succeed when bulk requesting with policy ids that match package names allowed by package privileges', async function () { + await superTestWithoutAuth + .post(`/api/fleet/package_policies/_bulk_get`) + .set('kbn-xsrf', 'xxxx') + .auth( + testUsers.endpoint_integr_read_policy.username, + testUsers.endpoint_integr_read_policy.password + ) + .send({ ids: [endpointPackagePolicyId] }) + .expect(200); + }); + it('should succeed with mixed valid ids and invalid ids and ignoreMissing flag ', async function () { const { body: { items }, diff --git a/x-pack/test/fleet_api_integration/apis/package_policy/update.ts b/x-pack/test/fleet_api_integration/apis/package_policy/update.ts index 8d565728c2d4c..3a931ca09ca3d 100644 --- a/x-pack/test/fleet_api_integration/apis/package_policy/update.ts +++ b/x-pack/test/fleet_api_integration/apis/package_policy/update.ts @@ -5,14 +5,18 @@ * 2.0. */ import expect from '@kbn/expect'; +import { policyFactory } from '@kbn/security-solution-plugin/common/endpoint/models/policy_config'; import { FtrProviderContext } from '../../../api_integration/ftr_provider_context'; import { skipIfNoDockerRegistry } from '../../helpers'; +import { testUsers } from '../test_users'; export default function (providerContext: FtrProviderContext) { const { getService } = providerContext; const supertest = getService('supertest'); + const superTestWithoutAuth = getService('supertestWithoutAuth'); const dockerServers = getService('dockerServers'); const kibanaServer = getService('kibanaServer'); + const esArchiver = getService('esArchiver'); const getPackagePolicyById = async (id: string) => { const { body } = await supertest.get(`/api/fleet/package_policies/${id}`); @@ -31,11 +35,10 @@ export default function (providerContext: FtrProviderContext) { let packagePolicyId: string; let packagePolicyId2: string; let packagePolicyId3: string; + let endpointPackagePolicyId: string; before(async () => { await kibanaServer.savedObjects.cleanStandardList(); - await getService('esArchiver').load( - 'x-pack/test/functional/es_archives/fleet/empty_fleet_server' - ); + await esArchiver.load('x-pack/test/functional/es_archives/fleet/empty_fleet_server'); }); before(async function () { @@ -132,6 +135,31 @@ export default function (providerContext: FtrProviderContext) { }, }); packagePolicyId3 = packagePolicyResponse3.item.id; + + const { body: endpointPackagePolicyResponse } = await supertest + .post(`/api/fleet/package_policies`) + .set('kbn-xsrf', 'xxxx') + .send({ + name: 'endpoint-1', + description: '', + namespace: 'default', + policy_id: agentPolicyId, + enabled: true, + inputs: [ + { + enabled: true, + streams: [], + type: 'endpoint', + }, + ], + force: true, + package: { + name: 'endpoint', + title: 'Elastic Defend', + version: '8.6.1', + }, + }); + endpointPackagePolicyId = endpointPackagePolicyResponse.item.id; }); after(async function () { @@ -139,12 +167,17 @@ export default function (providerContext: FtrProviderContext) { .post(`/api/fleet/agent_policies/delete`) .set('kbn-xsrf', 'xxxx') .send({ agentPolicyId }); + + // uninstall endpoint package + await supertest + .delete(`/api/fleet/epm/packages/endpoint-8.6.1`) + .set('kbn-xsrf', 'xxxx') + .send({ force: true }) + .expect(200); }); after(async () => { - await getService('esArchiver').unload( - 'x-pack/test/functional/es_archives/fleet/empty_fleet_server' - ); + await esArchiver.unload('x-pack/test/functional/es_archives/fleet/empty_fleet_server'); await kibanaServer.savedObjects.cleanStandardList(); }); @@ -209,6 +242,70 @@ export default function (providerContext: FtrProviderContext) { }); }); + it('should succeed when updating packages that are allowed with package privileges', async function () { + await superTestWithoutAuth + .put(`/api/fleet/package_policies/${endpointPackagePolicyId}`) + .set('kbn-xsrf', 'xxxx') + .auth( + testUsers.endpoint_integr_write_policy.username, + testUsers.endpoint_integr_write_policy.password + ) + .send({ + name: 'endpoint-1', + description: '', + namespace: 'updated_namespace', + policy_id: agentPolicyId, + enabled: true, + inputs: [ + { + enabled: true, + streams: [], + config: { + policy: { + value: policyFactory(), + }, + }, + type: 'endpoint', + }, + ], + force: true, + package: { + name: 'endpoint', + title: 'Elastic Defend', + version: '8.6.1', + }, + }) + .expect(200); + }); + + it('should return a 403 with package names that are not allowed', async function () { + await superTestWithoutAuth + .put(`/api/fleet/package_policies/${packagePolicyId}`) + .set('kbn-xsrf', 'xxxx') + .auth( + testUsers.endpoint_integr_write_policy.username, + testUsers.endpoint_integr_write_policy.password + ) + .send({ + name: 'updated_name', + description: '', + namespace: 'updated_namespace', + policy_id: agentPolicyId, + enabled: true, + inputs: [], + package: { + name: 'filetest', + title: 'For File Tests', + version: '0.1.0', + }, + }) + .expect(403, { + error: 'Forbidden', + message: 'Update for package name filetest is not authorized.', + statusCode: 403, + }); + }); + it('should return a 400 if there is another package policy with the same name', async function () { await supertest .put(`/api/fleet/package_policies/${packagePolicyId2}`) diff --git a/x-pack/test/fleet_api_integration/apis/test_users.ts b/x-pack/test/fleet_api_integration/apis/test_users.ts index d27ae1c4c29ae..2b55facefbc75 100644 --- a/x-pack/test/fleet_api_integration/apis/test_users.ts +++ b/x-pack/test/fleet_api_integration/apis/test_users.ts @@ -73,6 +73,37 @@ export const testUsers: { username: 'integr_all', password: 'changeme', }, + // for package_policy get one, bulk get with ids, get list + endpoint_integr_read_policy: { + permissions: { + feature: { + fleet: ['read'], + siem: [ + 'minimal_all', + 'trusted_applications_read', + 'host_isolation_exceptions_read', + 'blocklist_read', + 'event_filters_read', + 'policy_management_read', + ], + }, + spaces: ['*'], + }, + username: 'endpoint_integr_read_policy', + password: 'changeme', + }, + // for package_policy update API + endpoint_integr_write_policy: { + permissions: { + feature: { + fleet: ['all'], + siem: ['minimal_all', 'policy_management_all'], + }, + spaces: ['*'], + }, + username: 'endpoint_integr_write_policy', + password: 'changeme', + }, }; export const setupTestUsers = async (security: SecurityService) => { diff --git a/x-pack/test/fleet_api_integration/config.ts b/x-pack/test/fleet_api_integration/config.ts index a0882254d53e3..ed7c90f96bc38 100644 --- a/x-pack/test/fleet_api_integration/config.ts +++ b/x-pack/test/fleet_api_integration/config.ts @@ -72,6 +72,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { `--xpack.fleet.packageVerification.gpgKeyPath=${getFullPath( './apis/fixtures/package_verification/signatures/fleet_test_key_public.asc' )}`, + `--xpack.securitySolution.enableExperimental=${JSON.stringify(['endpointRbacEnabled'])}`, `--logging.loggers=${JSON.stringify([ ...getKibanaCliLoggers(xPackAPITestsConfig.get('kbnTestServer.serverArgs')),