mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[Fleet] Additional datastreams permissions API (#210452)
This commit is contained in:
parent
f5182586cd
commit
6ecb66df7f
24 changed files with 2020 additions and 255 deletions
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -1737,6 +1737,7 @@
|
|||
"properties": {}
|
||||
},
|
||||
"fleet-package-policies": {
|
||||
"dynamic": false,
|
||||
"properties": {
|
||||
"bump_agent_policy_revision": {
|
||||
"type": "boolean"
|
||||
|
@ -2334,6 +2335,7 @@
|
|||
}
|
||||
},
|
||||
"ingest-package-policies": {
|
||||
"dynamic": false,
|
||||
"properties": {
|
||||
"bump_agent_policy_revision": {
|
||||
"type": "boolean"
|
||||
|
|
|
@ -111,7 +111,7 @@ describe('checking migration metadata changes on all registered SO types', () =>
|
|||
"fleet-agent-policies": "4a5c6477d2a61121e95ea9865ed1403a28c38706",
|
||||
"fleet-fleet-server-host": "69be15f6b6f2a2875ad3c7050ddea7a87f505417",
|
||||
"fleet-message-signing-keys": "93421f43fed2526b59092a4e3c65d64bc2266c0f",
|
||||
"fleet-package-policies": "8173220091e28ff4afa8238bb37749599378f9e5",
|
||||
"fleet-package-policies": "b1ded996118af658bc420a737ff3c4d784641fc7",
|
||||
"fleet-preconfiguration-deletion-record": "c52ea1e13c919afe8a5e8e3adbb7080980ecc08e",
|
||||
"fleet-proxy": "6cb688f0d2dd856400c1dbc998b28704ff70363d",
|
||||
"fleet-setup-lock": "0dc784792c79b5af5a6e6b5dcac06b0dbaa90bde",
|
||||
|
@ -127,7 +127,7 @@ describe('checking migration metadata changes on all registered SO types', () =>
|
|||
"ingest-agent-policies": "57ebfb047cf0b81c6fa0ceed8586fa7199c7c5e2",
|
||||
"ingest-download-sources": "279a68147e62e4d8858c09ad1cf03bd5551ce58d",
|
||||
"ingest-outputs": "6743521f501bd77b1523dbb1df48d7c47fdad529",
|
||||
"ingest-package-policies": "870f8c21fe3602f31075430a1fdfb052c62d4a14",
|
||||
"ingest-package-policies": "6a80000fdf2544f2485b0c6a51ecc434b6a12987",
|
||||
"ingest_manager_settings": "111a616eb72627c002029c19feb9e6c439a10505",
|
||||
"inventory-view": "fd2b7fe713956f261018dded00d8f8c986417763",
|
||||
"kql-telemetry": "93c1d16c1a0dfca9c8842062cf5ef8f62ae401ad",
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
exports[`Fleet - validatePackagePolicy() works for packages with multiple policy templates (aka integrations) returns errors for invalid package policy 1`] = `
|
||||
Object {
|
||||
"additional_datastreams_permissions": null,
|
||||
"description": null,
|
||||
"inputs": Object {
|
||||
"billing-aws/metrics": Object {
|
||||
|
|
|
@ -44,6 +44,10 @@ export const epmRouteService = {
|
|||
return EPM_API_ROUTES.LIMITED_LIST_PATTERN;
|
||||
},
|
||||
|
||||
getDatastreamsPath: () => {
|
||||
return EPM_API_ROUTES.DATA_STREAMS_PATTERN;
|
||||
},
|
||||
|
||||
getInfoPath: (pkgName: string, pkgVersion?: string) => {
|
||||
if (pkgVersion) {
|
||||
return EPM_API_ROUTES.INFO_PATTERN.replace('{pkgName}', pkgName).replace(
|
||||
|
|
|
@ -158,5 +158,21 @@ describe('toPackagePolicy', () => {
|
|||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should to pass additional_datastreams_permissions', () => {
|
||||
const res = simplifiedPackagePolicytoNewPackagePolicy(
|
||||
{
|
||||
name: 'nginx-1',
|
||||
namespace: 'default',
|
||||
policy_id: 'policy123',
|
||||
policy_ids: ['policy123'],
|
||||
description: 'Test description',
|
||||
additional_datastreams_permissions: ['logs-test-123'],
|
||||
},
|
||||
nginxPackageInfo as unknown as PackageInfo
|
||||
);
|
||||
|
||||
expect(res.additional_datastreams_permissions).toEqual(['logs-test-123']);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -51,6 +51,7 @@ export interface SimplifiedPackagePolicy {
|
|||
vars?: SimplifiedVars;
|
||||
inputs?: SimplifiedInputs;
|
||||
supports_agentless?: boolean | null;
|
||||
additional_datastreams_permissions?: string[];
|
||||
}
|
||||
|
||||
export interface FormattedPackagePolicy extends Omit<PackagePolicy, 'inputs' | 'vars'> {
|
||||
|
@ -161,6 +162,7 @@ export function simplifiedPackagePolicytoNewPackagePolicy(
|
|||
inputs = {},
|
||||
vars: packageLevelVars,
|
||||
supports_agentless: supportsAgentless,
|
||||
additional_datastreams_permissions: additionalDatastreamsPermissions,
|
||||
} = data;
|
||||
const packagePolicy = {
|
||||
...packageToPackagePolicy(
|
||||
|
@ -174,6 +176,10 @@ export function simplifiedPackagePolicytoNewPackagePolicy(
|
|||
output_id: outputId,
|
||||
};
|
||||
|
||||
if (additionalDatastreamsPermissions) {
|
||||
packagePolicy.additional_datastreams_permissions = additionalDatastreamsPermissions;
|
||||
}
|
||||
|
||||
if (packagePolicy.package && options?.experimental_data_stream_features) {
|
||||
packagePolicy.package.experimental_data_stream_features =
|
||||
options.experimental_data_stream_features;
|
||||
|
|
|
@ -347,6 +347,7 @@ describe('Fleet - validatePackagePolicy()', () => {
|
|||
|
||||
const noErrorsValidationResults = {
|
||||
name: null,
|
||||
additional_datastreams_permissions: null,
|
||||
description: null,
|
||||
namespace: null,
|
||||
inputs: {
|
||||
|
@ -395,6 +396,7 @@ describe('Fleet - validatePackagePolicy()', () => {
|
|||
name: ['Name is required'],
|
||||
description: null,
|
||||
namespace: null,
|
||||
additional_datastreams_permissions: null,
|
||||
inputs: {
|
||||
foo: {
|
||||
vars: {
|
||||
|
@ -461,6 +463,7 @@ describe('Fleet - validatePackagePolicy()', () => {
|
|||
name: ['Name is required'],
|
||||
description: null,
|
||||
namespace: null,
|
||||
additional_datastreams_permissions: null,
|
||||
inputs: {
|
||||
foo: {
|
||||
vars: {
|
||||
|
@ -514,6 +517,7 @@ describe('Fleet - validatePackagePolicy()', () => {
|
|||
name: null,
|
||||
description: null,
|
||||
namespace: null,
|
||||
additional_datastreams_permissions: null,
|
||||
inputs: {},
|
||||
vars: {},
|
||||
});
|
||||
|
@ -530,6 +534,7 @@ describe('Fleet - validatePackagePolicy()', () => {
|
|||
name: null,
|
||||
description: null,
|
||||
namespace: null,
|
||||
additional_datastreams_permissions: null,
|
||||
inputs: {},
|
||||
vars: {},
|
||||
});
|
||||
|
@ -549,6 +554,7 @@ describe('Fleet - validatePackagePolicy()', () => {
|
|||
name: null,
|
||||
description: null,
|
||||
namespace: null,
|
||||
additional_datastreams_permissions: null,
|
||||
inputs: {},
|
||||
vars: {},
|
||||
});
|
||||
|
@ -564,6 +570,7 @@ describe('Fleet - validatePackagePolicy()', () => {
|
|||
).toEqual({
|
||||
name: null,
|
||||
description: null,
|
||||
additional_datastreams_permissions: null,
|
||||
namespace: null,
|
||||
inputs: {},
|
||||
vars: {},
|
||||
|
@ -602,6 +609,7 @@ describe('Fleet - validatePackagePolicy()', () => {
|
|||
name: null,
|
||||
description: null,
|
||||
namespace: null,
|
||||
additional_datastreams_permissions: null,
|
||||
inputs: {
|
||||
foo: {
|
||||
streams: {
|
||||
|
@ -730,6 +738,7 @@ describe('Fleet - validatePackagePolicy()', () => {
|
|||
)
|
||||
).toEqual({
|
||||
description: null,
|
||||
additional_datastreams_permissions: null,
|
||||
inputs: {
|
||||
'linux/metrics': {
|
||||
streams: {
|
||||
|
@ -1150,6 +1159,7 @@ describe('Fleet - validationHasErrors()', () => {
|
|||
validationHasErrors({
|
||||
name: ['name error'],
|
||||
description: null,
|
||||
additional_datastreams_permissions: null,
|
||||
namespace: null,
|
||||
inputs: {
|
||||
input1: {
|
||||
|
@ -1159,10 +1169,12 @@ describe('Fleet - validationHasErrors()', () => {
|
|||
},
|
||||
})
|
||||
).toBe(true);
|
||||
|
||||
expect(
|
||||
validationHasErrors({
|
||||
name: null,
|
||||
description: null,
|
||||
additional_datastreams_permissions: null,
|
||||
namespace: null,
|
||||
inputs: {
|
||||
input1: {
|
||||
|
@ -1176,6 +1188,7 @@ describe('Fleet - validationHasErrors()', () => {
|
|||
validationHasErrors({
|
||||
name: null,
|
||||
description: null,
|
||||
additional_datastreams_permissions: null,
|
||||
namespace: null,
|
||||
inputs: {
|
||||
input1: {
|
||||
|
@ -1193,6 +1206,7 @@ describe('Fleet - validationHasErrors()', () => {
|
|||
name: null,
|
||||
description: null,
|
||||
namespace: null,
|
||||
additional_datastreams_permissions: null,
|
||||
inputs: {
|
||||
input1: {
|
||||
vars: { foo: null, bar: null },
|
||||
|
|
|
@ -54,6 +54,7 @@ export type PackagePolicyValidationResults = {
|
|||
name: Errors;
|
||||
description: Errors;
|
||||
namespace: Errors;
|
||||
additional_datastreams_permissions: Errors;
|
||||
inputs: Record<PackagePolicyInput['type'], PackagePolicyInputValidationResults> | null;
|
||||
} & PackagePolicyConfigValidationResults;
|
||||
|
||||
|
@ -109,6 +110,9 @@ const validatePackageRequiredVars = (
|
|||
return hasMetRequiredCriteria ? null : evaluatedRequiredVars;
|
||||
};
|
||||
|
||||
const VALIDATE_DATASTREAMS_PERMISSION_REGEX =
|
||||
/^(logs)|(metrics)|(traces)|(synthetics)|(profiling)-(.*)$/;
|
||||
|
||||
/*
|
||||
* Returns validation information for a given package policy and package info
|
||||
* Note: this method assumes that `packagePolicy` is correctly structured for the given package
|
||||
|
@ -124,6 +128,7 @@ export const validatePackagePolicy = (
|
|||
name: null,
|
||||
description: null,
|
||||
namespace: null,
|
||||
additional_datastreams_permissions: null,
|
||||
inputs: {},
|
||||
vars: {},
|
||||
};
|
||||
|
@ -146,6 +151,24 @@ export const validatePackagePolicy = (
|
|||
}
|
||||
}
|
||||
|
||||
if (packagePolicy?.additional_datastreams_permissions) {
|
||||
validationResults.additional_datastreams_permissions =
|
||||
packagePolicy?.additional_datastreams_permissions.reduce<null | string[]>(
|
||||
(acc, additionalDatastreamsPermission) => {
|
||||
if (!additionalDatastreamsPermission.match(VALIDATE_DATASTREAMS_PERMISSION_REGEX)) {
|
||||
if (!acc) {
|
||||
acc = [];
|
||||
}
|
||||
acc.push(
|
||||
`${additionalDatastreamsPermission} is not valid, should match ${VALIDATE_DATASTREAMS_PERMISSION_REGEX.toString()}`
|
||||
);
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
// Validate package-level vars
|
||||
const packageVarsByName = keyBy(packageInfo.vars || [], 'name');
|
||||
const packageVars = Object.entries(packagePolicy.vars || {});
|
||||
|
|
|
@ -94,6 +94,7 @@ export interface NewPackagePolicy {
|
|||
};
|
||||
overrides?: { inputs?: { [key: string]: any } } | null;
|
||||
supports_agentless?: boolean | null;
|
||||
additional_datastreams_permissions?: string[];
|
||||
}
|
||||
|
||||
export interface UpdatePackagePolicy extends NewPackagePolicy {
|
||||
|
|
|
@ -74,6 +74,7 @@ describe('StepDefinePackagePolicy', () => {
|
|||
const validationResults = {
|
||||
name: null,
|
||||
description: null,
|
||||
additional_datastreams_permissions: null,
|
||||
namespace: null,
|
||||
inputs: {},
|
||||
vars: {
|
||||
|
|
|
@ -30,7 +30,11 @@ import type {
|
|||
GetInputsTemplatesRequest,
|
||||
GetInputsTemplatesResponse,
|
||||
} from '../../types';
|
||||
import type { FleetErrorResponse, GetStatsResponse } from '../../../common/types';
|
||||
import type {
|
||||
FleetErrorResponse,
|
||||
GetEpmDataStreamsResponse,
|
||||
GetStatsResponse,
|
||||
} from '../../../common/types';
|
||||
import { API_VERSIONS } from '../../../common/constants';
|
||||
|
||||
import { getCustomIntegrations } from '../../services/custom_integrations';
|
||||
|
@ -240,6 +244,16 @@ export const useGetFileByPathQuery = (filePath: string) => {
|
|||
);
|
||||
};
|
||||
|
||||
export const useGetEpmDatastreams = () => {
|
||||
return useQuery<GetEpmDataStreamsResponse, RequestError>(['get-epm-datastreams'], () =>
|
||||
sendRequestForRq<GetEpmDataStreamsResponse>({
|
||||
path: epmRouteService.getDatastreamsPath(),
|
||||
method: 'get',
|
||||
version: API_VERSIONS.public.v1,
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
export const sendGetFileByPath = (filePath: string) => {
|
||||
return sendRequest<string>({
|
||||
path: epmRouteService.getFilePath(filePath),
|
||||
|
|
|
@ -13,6 +13,7 @@ export const genericErrorResponse = () =>
|
|||
statusCode: schema.maybe(schema.number()),
|
||||
error: schema.maybe(schema.string()),
|
||||
message: schema.string(),
|
||||
attributes: schema.maybe(schema.any()),
|
||||
},
|
||||
{
|
||||
meta: { description: 'Generic Error' },
|
||||
|
|
|
@ -637,6 +637,7 @@ export const getSavedObjectTypes = (
|
|||
importableAndExportable: false,
|
||||
},
|
||||
mappings: {
|
||||
dynamic: false,
|
||||
properties: {
|
||||
name: { type: 'keyword' },
|
||||
description: { type: 'text' },
|
||||
|
@ -844,6 +845,14 @@ export const getSavedObjectTypes = (
|
|||
},
|
||||
],
|
||||
},
|
||||
'18': {
|
||||
changes: [
|
||||
{
|
||||
type: 'mappings_addition',
|
||||
addedMappings: {}, // Empty to add dynamic:false
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
migrations: {
|
||||
'7.10.0': migratePackagePolicyToV7100,
|
||||
|
@ -871,6 +880,7 @@ export const getSavedObjectTypes = (
|
|||
importableAndExportable: false,
|
||||
},
|
||||
mappings: {
|
||||
dynamic: false,
|
||||
properties: {
|
||||
name: { type: 'keyword' },
|
||||
description: { type: 'text' },
|
||||
|
@ -937,6 +947,14 @@ export const getSavedObjectTypes = (
|
|||
},
|
||||
],
|
||||
},
|
||||
'4': {
|
||||
changes: [
|
||||
{
|
||||
type: 'mappings_addition',
|
||||
addedMappings: {}, // Empty to add dynamic:false
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
[PACKAGES_SAVED_OBJECT_TYPE]: {
|
||||
|
|
|
@ -409,6 +409,70 @@ describe('storedPackagePoliciesToAgentPermissions()', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('Add additional_datastream_permissions', async () => {
|
||||
const packagePolicies: PackagePolicy[] = [
|
||||
{
|
||||
id: 'package-policy-uuid-test-123',
|
||||
name: 'test-policy',
|
||||
namespace: 'test',
|
||||
enabled: true,
|
||||
package: { name: 'test_package', version: '0.0.0', title: 'Test Package' },
|
||||
inputs: [
|
||||
{
|
||||
type: 'test-logs',
|
||||
enabled: true,
|
||||
streams: [
|
||||
{
|
||||
id: 'test-logs',
|
||||
enabled: true,
|
||||
data_stream: { type: 'logs', dataset: 'some-logs' },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'test-metrics',
|
||||
enabled: false,
|
||||
streams: [
|
||||
{
|
||||
id: 'test-logs',
|
||||
enabled: false,
|
||||
data_stream: { type: 'metrics', dataset: 'some-metrics' },
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
additional_datastreams_permissions: ['logs-test-default', 'metrics-test-default'],
|
||||
created_at: '',
|
||||
updated_at: '',
|
||||
created_by: '',
|
||||
updated_by: '',
|
||||
revision: 1,
|
||||
policy_id: '',
|
||||
policy_ids: [''],
|
||||
},
|
||||
];
|
||||
|
||||
const permissions = await storedPackagePoliciesToAgentPermissions(
|
||||
packageInfoCache,
|
||||
'test',
|
||||
packagePolicies
|
||||
);
|
||||
expect(permissions).toMatchObject({
|
||||
'package-policy-uuid-test-123': {
|
||||
indices: [
|
||||
{
|
||||
names: ['logs-some-logs-test'],
|
||||
privileges: ['auto_configure', 'create_doc'],
|
||||
},
|
||||
{
|
||||
names: ['logs-test-default', 'metrics-test-default'],
|
||||
privileges: ['auto_configure', 'create_doc'],
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('Returns the dataset for the compiled data_streams', async () => {
|
||||
const packagePolicies: PackagePolicy[] = [
|
||||
{
|
||||
|
|
|
@ -106,7 +106,7 @@ export function storedPackagePoliciesToAgentPermissions(
|
|||
|
||||
const dataStreams = getNormalizedDataStreams(pkg);
|
||||
if (!dataStreams || dataStreams.length === 0) {
|
||||
return [packagePolicy.name, undefined];
|
||||
return [packagePolicy.id, maybeAddAdditionalPackagePoliciesPermissions(packagePolicy)];
|
||||
}
|
||||
|
||||
let dataStreamsForPermissions: DataStreamMeta[];
|
||||
|
@ -183,10 +183,17 @@ export function storedPackagePoliciesToAgentPermissions(
|
|||
}
|
||||
// namespace is either the package policy's or the agent policy one
|
||||
const namespace = packagePolicy?.namespace || agentPolicyNamespace;
|
||||
return maybeAddAgentlessPermissions(packagePolicy, {
|
||||
indices: dataStreamsForPermissions.map((ds) => getDataStreamPrivileges(ds, namespace)),
|
||||
...clusterRoleDescriptor,
|
||||
});
|
||||
|
||||
return [
|
||||
packagePolicy.id,
|
||||
maybeAddAdditionalPackagePoliciesPermissions(
|
||||
packagePolicy,
|
||||
maybeAddAgentlessPermissions(packagePolicy, {
|
||||
indices: dataStreamsForPermissions.map((ds) => getDataStreamPrivileges(ds, namespace)),
|
||||
...clusterRoleDescriptor,
|
||||
})
|
||||
),
|
||||
];
|
||||
});
|
||||
|
||||
return Object.fromEntries(permissionEntries);
|
||||
|
@ -254,15 +261,41 @@ function universalProfilingPermissions(packagePolicyId: string): [string, Securi
|
|||
function maybeAddAgentlessPermissions(
|
||||
packagePolicy: PackagePolicy,
|
||||
existing: SecurityRoleDescriptor
|
||||
): [string, SecurityRoleDescriptor] {
|
||||
): SecurityRoleDescriptor {
|
||||
if (!packagePolicy.supports_agentless) {
|
||||
return [packagePolicy.id, existing];
|
||||
return existing;
|
||||
}
|
||||
existing.indices!.push({
|
||||
names: ['agentless-*'],
|
||||
privileges: AGENTLESS_INDEX_PERMISSIONS,
|
||||
});
|
||||
return [packagePolicy.id, existing];
|
||||
return existing;
|
||||
}
|
||||
|
||||
function maybeAddAdditionalPackagePoliciesPermissions(
|
||||
packagePolicy: PackagePolicy,
|
||||
existing?: SecurityRoleDescriptor
|
||||
): SecurityRoleDescriptor | undefined {
|
||||
if (
|
||||
!packagePolicy.additional_datastreams_permissions ||
|
||||
!packagePolicy.additional_datastreams_permissions.length
|
||||
) {
|
||||
return existing;
|
||||
}
|
||||
|
||||
if (!existing) {
|
||||
existing = {};
|
||||
}
|
||||
|
||||
if (!existing.indices) {
|
||||
existing.indices = [];
|
||||
}
|
||||
|
||||
existing.indices!.push({
|
||||
names: packagePolicy.additional_datastreams_permissions,
|
||||
privileges: PACKAGE_POLICY_DEFAULT_INDEX_PRIVILEGES,
|
||||
});
|
||||
return existing;
|
||||
}
|
||||
|
||||
function apmPermissions(packagePolicyId: string): [string, SecurityRoleDescriptor] {
|
||||
|
|
|
@ -151,7 +151,10 @@ import {
|
|||
import { getPackageAssetsMap } from './epm/packages/get';
|
||||
import { validateAgentPolicyOutputForIntegration } from './agent_policies/outputs_helpers';
|
||||
import type { PackagePolicyClientFetchAllItemIdsOptions } from './package_policy_service';
|
||||
import { validatePolicyNamespaceForSpace } from './spaces/policy_namespaces';
|
||||
import {
|
||||
validateAdditionalDatastreamsPermissionsForSpace,
|
||||
validatePolicyNamespaceForSpace,
|
||||
} from './spaces/policy_namespaces';
|
||||
import { isSpaceAwarenessEnabled, isSpaceAwarenessMigrationPending } from './spaces/helpers';
|
||||
import { updatePackagePolicySpaces } from './spaces/package_policy';
|
||||
import { runWithCache } from './epm/packages/cache';
|
||||
|
@ -319,6 +322,10 @@ class PackagePolicyClientImpl implements PackagePolicyClient {
|
|||
spaceId: soClient.getCurrentNamespace(),
|
||||
});
|
||||
}
|
||||
await validateAdditionalDatastreamsPermissionsForSpace({
|
||||
additionalDatastreamsPermissions: enrichedPackagePolicy.additional_datastreams_permissions,
|
||||
spaceId: soClient.getCurrentNamespace(),
|
||||
});
|
||||
|
||||
let elasticsearchPrivileges: NonNullable<PackagePolicy['elasticsearch']>['privileges'];
|
||||
let inputs = getInputsWithStreamIds(enrichedPackagePolicy, packagePolicyId);
|
||||
|
@ -991,6 +998,10 @@ class PackagePolicyClientImpl implements PackagePolicyClient {
|
|||
spaceId: soClient.getCurrentNamespace(),
|
||||
});
|
||||
}
|
||||
await validateAdditionalDatastreamsPermissionsForSpace({
|
||||
additionalDatastreamsPermissions: enrichedPackagePolicy.additional_datastreams_permissions,
|
||||
spaceId: soClient.getCurrentNamespace(),
|
||||
});
|
||||
|
||||
// eslint-disable-next-line prefer-const
|
||||
let { version, ...restOfPackagePolicy } = packagePolicy;
|
||||
|
@ -1948,6 +1959,7 @@ class PackagePolicyClientImpl implements PackagePolicyClient {
|
|||
inputs: newPolicy.inputs[0]?.streams ? newPolicy.inputs : inputs,
|
||||
vars: newPolicy.vars || newPP.vars,
|
||||
supports_agentless: newPolicy.supports_agentless,
|
||||
additional_datastreams_permissions: newPolicy.additional_datastreams_permissions,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,3 +42,43 @@ export async function validatePolicyNamespaceForSpace({
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
export async function validateAdditionalDatastreamsPermissionsForSpace({
|
||||
additionalDatastreamsPermissions,
|
||||
spaceId,
|
||||
}: {
|
||||
additionalDatastreamsPermissions?: string[];
|
||||
spaceId?: string;
|
||||
}) {
|
||||
const experimentalFeature = appContextService.getExperimentalFeatures();
|
||||
if (!experimentalFeature.useSpaceAwareness) {
|
||||
return;
|
||||
}
|
||||
const settings = await getSpaceSettings(spaceId);
|
||||
if (
|
||||
!settings.allowed_namespace_prefixes ||
|
||||
settings.allowed_namespace_prefixes.length === 0 ||
|
||||
!additionalDatastreamsPermissions ||
|
||||
!additionalDatastreamsPermissions.length
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const additionalDatastreamsPermission of additionalDatastreamsPermissions) {
|
||||
let valid = false;
|
||||
for (const allowedNamespacePrefix of settings.allowed_namespace_prefixes) {
|
||||
if (additionalDatastreamsPermission.startsWith(allowedNamespacePrefix)) {
|
||||
valid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!valid) {
|
||||
throw new PolicyNamespaceValidationError(
|
||||
`Invalid additionalDatastreamsPermission, supported namespace prefixes: ${settings.allowed_namespace_prefixes.join(
|
||||
', '
|
||||
)}`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -182,6 +182,17 @@ export const PackagePolicyBaseSchema = {
|
|||
})
|
||||
)
|
||||
),
|
||||
additional_datastreams_permissions: schema.maybe(
|
||||
schema.oneOf([
|
||||
schema.literal(null),
|
||||
schema.arrayOf(schema.string(), {
|
||||
validate: validateAdditionalDatastreamsPermissions,
|
||||
meta: {
|
||||
description: 'Additional datastream permissions, that will be added to the agent policy.',
|
||||
},
|
||||
}),
|
||||
])
|
||||
),
|
||||
};
|
||||
|
||||
export const NewPackagePolicySchema = schema.object({
|
||||
|
@ -288,6 +299,17 @@ export const SimplifiedPackagePolicyInputsSchema = schema.maybe(
|
|||
)
|
||||
);
|
||||
|
||||
const VALIDATE_DATASTREAMS_PERMISSION_REGEX =
|
||||
/^(logs)|(metrics)|(traces)|(synthetics)|(profiling)-(.*)$/;
|
||||
|
||||
function validateAdditionalDatastreamsPermissions(values: string[]) {
|
||||
for (const val of values) {
|
||||
if (!val.match(VALIDATE_DATASTREAMS_PERMISSION_REGEX)) {
|
||||
return `${val} is not a valid datastream permissions, it should match logs|metrics|traces|synthetics|profiling)-*`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const SimplifiedPackagePolicyBaseSchema = schema.object({
|
||||
id: schema.maybe(schema.string()),
|
||||
name: schema.string(),
|
||||
|
@ -306,6 +328,17 @@ export const SimplifiedPackagePolicyBaseSchema = schema.object({
|
|||
})
|
||||
)
|
||||
),
|
||||
additional_datastreams_permissions: schema.maybe(
|
||||
schema.oneOf([
|
||||
schema.literal(null),
|
||||
schema.arrayOf(schema.string(), {
|
||||
validate: validateAdditionalDatastreamsPermissions,
|
||||
meta: {
|
||||
description: 'Additional datastream permissions, that will be added to the agent policy.',
|
||||
},
|
||||
}),
|
||||
])
|
||||
),
|
||||
});
|
||||
|
||||
export const SimplifiedPackagePolicyPreconfiguredSchema = SimplifiedPackagePolicyBaseSchema.extends(
|
||||
|
|
|
@ -12,6 +12,8 @@ import { v4 as uuidv4 } from 'uuid';
|
|||
|
||||
import { FtrProviderContext } from '../../../api_integration/ftr_provider_context';
|
||||
import { skipIfNoDockerRegistry } from '../../helpers';
|
||||
import { SpaceTestApiClient } from '../space_awareness/api_helper';
|
||||
import { cleanFleetIndices, expectToRejectWithError } from '../space_awareness/helpers';
|
||||
|
||||
export default function (providerContext: FtrProviderContext) {
|
||||
const { getService } = providerContext;
|
||||
|
@ -19,6 +21,8 @@ export default function (providerContext: FtrProviderContext) {
|
|||
const es: Client = getService('es');
|
||||
const supertest = getService('supertest');
|
||||
const kibanaServer = getService('kibanaServer');
|
||||
const fleetAndAgents = getService('fleetAndAgents');
|
||||
const apiClient = new SpaceTestApiClient(supertest);
|
||||
|
||||
const getPackagePolicyById = async (id: string) => {
|
||||
const { body } = await supertest.get(`/api/fleet/package_policies/${id}`);
|
||||
|
@ -30,34 +34,24 @@ export default function (providerContext: FtrProviderContext) {
|
|||
let agentPolicyId2: string;
|
||||
before(async () => {
|
||||
await kibanaServer.savedObjects.cleanStandardList();
|
||||
await getService('esArchiver').load(
|
||||
'x-pack/test/functional/es_archives/fleet/empty_fleet_server'
|
||||
);
|
||||
const { body: agentPolicyResponse } = await supertest
|
||||
.post(`/api/fleet/agent_policies`)
|
||||
.set('kbn-xsrf', 'xxxx')
|
||||
.send({
|
||||
name: `Test policy ${uuidv4()}`,
|
||||
namespace: 'default',
|
||||
})
|
||||
.expect(200);
|
||||
agentPolicyId = agentPolicyResponse.item.id;
|
||||
await cleanFleetIndices(es);
|
||||
await fleetAndAgents.setup();
|
||||
|
||||
const { body: agentPolicyResponse2 } = await supertest
|
||||
.post(`/api/fleet/agent_policies`)
|
||||
.set('kbn-xsrf', 'xxxx')
|
||||
.send({
|
||||
const [agentPolicyResponse, agentPolicyResponse2] = await Promise.all([
|
||||
apiClient.createAgentPolicy(undefined, {
|
||||
name: `Test policy ${uuidv4()}`,
|
||||
namespace: 'default',
|
||||
})
|
||||
.expect(200);
|
||||
}),
|
||||
apiClient.createAgentPolicy(undefined, {
|
||||
name: `Test policy ${uuidv4()}`,
|
||||
namespace: 'default',
|
||||
}),
|
||||
]);
|
||||
|
||||
agentPolicyId = agentPolicyResponse.item.id;
|
||||
agentPolicyId2 = agentPolicyResponse2.item.id;
|
||||
});
|
||||
after(async () => {
|
||||
await kibanaServer.savedObjects.cleanStandardList();
|
||||
await getService('esArchiver').unload(
|
||||
'x-pack/test/functional/es_archives/fleet/empty_fleet_server'
|
||||
);
|
||||
await supertest
|
||||
.post(`/api/fleet/agent_policies/delete`)
|
||||
.set('kbn-xsrf', 'xxxx')
|
||||
|
@ -66,6 +60,9 @@ export default function (providerContext: FtrProviderContext) {
|
|||
.post(`/api/fleet/agent_policies/delete`)
|
||||
.set('kbn-xsrf', 'xxxx')
|
||||
.send({ agentPolicyId: agentPolicyId2 });
|
||||
|
||||
await kibanaServer.savedObjects.cleanStandardList();
|
||||
await cleanFleetIndices(es);
|
||||
});
|
||||
|
||||
it('can only add to hosted agent policies using the force parameter', async function () {
|
||||
|
@ -261,6 +258,11 @@ export default function (providerContext: FtrProviderContext) {
|
|||
});
|
||||
|
||||
it('should not allow multiple limited packages on the same agent policy', async function () {
|
||||
await apiClient.installPackage({
|
||||
pkgName: 'endpoint',
|
||||
pkgVersion: '8.5.0',
|
||||
force: true,
|
||||
});
|
||||
await supertest
|
||||
.post(`/api/fleet/package_policies`)
|
||||
.set('kbn-xsrf', 'xxxx')
|
||||
|
@ -274,7 +276,7 @@ export default function (providerContext: FtrProviderContext) {
|
|||
package: {
|
||||
name: 'endpoint',
|
||||
title: 'Endpoint',
|
||||
version: '8.4.0',
|
||||
version: '8.5.0',
|
||||
},
|
||||
force: true,
|
||||
})
|
||||
|
@ -524,6 +526,85 @@ export default function (providerContext: FtrProviderContext) {
|
|||
.expect(200);
|
||||
});
|
||||
|
||||
it('should support additional_datastreams_permissions', async () => {
|
||||
const createPackagePolicyRes = await apiClient.createPackagePolicy(undefined, {
|
||||
name: 'filetest-3-' + Date.now(),
|
||||
description: '',
|
||||
namespace: 'default',
|
||||
policy_ids: [agentPolicyId],
|
||||
enabled: true,
|
||||
inputs: [
|
||||
{
|
||||
enabled: true,
|
||||
streams: [],
|
||||
type: 'single_input',
|
||||
},
|
||||
],
|
||||
package: {
|
||||
name: 'filetest',
|
||||
title: 'For File Tests',
|
||||
version: '0.1.0',
|
||||
},
|
||||
additional_datastreams_permissions: ['logs-tata-default', 'metrics-tata-default'],
|
||||
} as any);
|
||||
|
||||
const getPackagePolicyRes = await apiClient.getPackagePolicy(createPackagePolicyRes.item.id);
|
||||
|
||||
expect(getPackagePolicyRes.item.additional_datastreams_permissions).to.eql([
|
||||
'logs-tata-default',
|
||||
'metrics-tata-default',
|
||||
]);
|
||||
|
||||
const policyDocRes = await es.search({
|
||||
index: '.fleet-policies',
|
||||
sort: [{ '@timestamp': 'desc' }],
|
||||
query: {
|
||||
term: {
|
||||
policy_id: agentPolicyId,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const packagePolicyPermission = (policyDocRes.hits?.hits[0]._source as any).data
|
||||
?.output_permissions?.default?.[createPackagePolicyRes.item.id];
|
||||
|
||||
expect(
|
||||
packagePolicyPermission.indices.find((permissions: any) =>
|
||||
permissions.names.includes('logs-tata-default')
|
||||
)
|
||||
).to.eql({
|
||||
names: ['logs-tata-default', 'metrics-tata-default'],
|
||||
privileges: ['auto_configure', 'create_doc'],
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw with invalid additional_datastreams_permissions', async () => {
|
||||
await expectToRejectWithError(
|
||||
() =>
|
||||
apiClient.createPackagePolicy(undefined, {
|
||||
name: 'filetest-3-' + Date.now(),
|
||||
description: '',
|
||||
namespace: 'default',
|
||||
policy_ids: [agentPolicyId],
|
||||
enabled: true,
|
||||
inputs: [
|
||||
{
|
||||
enabled: true,
|
||||
streams: [],
|
||||
type: 'single_input',
|
||||
},
|
||||
],
|
||||
package: {
|
||||
name: 'filetest',
|
||||
title: 'For File Tests',
|
||||
version: '0.1.0',
|
||||
},
|
||||
additional_datastreams_permissions: ['invalid-tata-default', 'metrics-tata-default'],
|
||||
} as any),
|
||||
/400 "Bad Request"/
|
||||
);
|
||||
});
|
||||
|
||||
it('should return 200 and formatted inputs when the format=simplified query param is passed', async function () {
|
||||
const { body } = await supertest
|
||||
.post(`/api/fleet/package_policies?format=simplified`)
|
||||
|
@ -989,6 +1070,60 @@ export default function (providerContext: FtrProviderContext) {
|
|||
})
|
||||
.expect(400);
|
||||
});
|
||||
|
||||
it('should support additional_datastreams_permissions', async () => {
|
||||
const createPackagePolicyRes = await apiClient.createPackagePolicy(undefined, {
|
||||
name: `create-simplified-package-policy-required-variables-${Date.now()}`,
|
||||
description: '',
|
||||
namespace: 'default',
|
||||
policy_ids: [agentPolicyId],
|
||||
inputs: {
|
||||
'with_required_variables-test_input': {
|
||||
streams: {
|
||||
'with_required_variables.log': {
|
||||
vars: { test_var_required: 'I am required' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
package: {
|
||||
name: 'with_required_variables',
|
||||
version: '0.1.0',
|
||||
},
|
||||
additional_datastreams_permissions: ['logs-test-default', 'metrics-test-default'],
|
||||
});
|
||||
|
||||
const getPackagePolicyRes = await apiClient.getPackagePolicy(
|
||||
createPackagePolicyRes.item.id
|
||||
);
|
||||
|
||||
expect(getPackagePolicyRes.item.additional_datastreams_permissions).to.eql([
|
||||
'logs-test-default',
|
||||
'metrics-test-default',
|
||||
]);
|
||||
|
||||
const policyDocRes = await es.search({
|
||||
index: '.fleet-policies',
|
||||
sort: [{ '@timestamp': 'desc' }],
|
||||
query: {
|
||||
term: {
|
||||
policy_id: agentPolicyId,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const packagePolicyPermission = (policyDocRes.hits?.hits[0]._source as any).data
|
||||
?.output_permissions?.default?.[createPackagePolicyRes.item.id];
|
||||
|
||||
expect(
|
||||
packagePolicyPermission.indices.find((permissions: any) =>
|
||||
permissions.names.includes('logs-test-default')
|
||||
)
|
||||
).to.eql({
|
||||
names: ['logs-test-default', 'metrics-test-default'],
|
||||
privileges: ['auto_configure', 'create_doc'],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Package verification', () => {
|
||||
|
|
|
@ -97,13 +97,20 @@ export class SpaceTestApiClient {
|
|||
spaceId?: string,
|
||||
data: Partial<SimplifiedPackagePolicy & { package: { name: string; version: string } }> = {}
|
||||
): Promise<CreatePackagePolicyResponse> {
|
||||
const { body: res } = await this.supertest
|
||||
const { body: res, statusCode } = await this.supertest
|
||||
.post(`${this.getBaseUrl(spaceId)}/api/fleet/package_policies`)
|
||||
.set('kbn-xsrf', 'xxxx')
|
||||
.send(data)
|
||||
.expect(200);
|
||||
.send(data);
|
||||
|
||||
return res;
|
||||
if (statusCode === 200) {
|
||||
return res;
|
||||
}
|
||||
|
||||
if (statusCode === 404) {
|
||||
throw new Error('404 "Not Found"');
|
||||
} else {
|
||||
throw new Error(`${statusCode} "${res?.error}" ${res.message}`);
|
||||
}
|
||||
}
|
||||
async getPackagePolicy(
|
||||
packagePolicyId: string,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue