mirror of
https://github.com/elastic/kibana.git
synced 2025-06-28 03:01:21 -04:00
## Summary This PR adds the ability to upload a CSV file with privileged users from the Entity Analytics pages ## Changes ### Backend - Added (or completed) the upload CSV route: `/api/entity_analytics/monitoring/users/_csv` - Added shared utilities for batching with Node streams - Added bulk processing actions for the upload - Parsing users from CSV - Soft delete for omitted users - Batch upsert via the bulk API - Added a check for installing all required privmon resources ### Frontend - File uploader components - File validation logic - Updated EA privmon page to account for the new flow - Added managing users panels - open upload flow (same as asset criticality) ## Screen recording https://github.com/user-attachments/assets/7956f1cf-49e0-4430-8c23-7d6178a15342 ## How to test #### Prerequisite Make sure you have a CSV file with usernames Check [here](https://gist.github.com/tiansivive/0be2f09e1bb380fdde6609a131e929ed) for a little helper script Create a few copies where some of the users are deleted, in order to test soft delete 1. Start up kibana and ES 2. Navigate to Security > Entity Analytics > Privilege User Monitoring 3. Select the `File` option to add data 4. Add one of the CSV files to the open modal and upload 5. Repeat but now upload one of files with the omitted users Alternatively, testing only the backend only is possible by directly hitting the API wit curl ``` curl -u elastic:changeme \ -X POST "http://localhost:5601/api/entity_analytics/monitoring/users/_csv" \ -H "kbn-xsrf: true" \ -F "file=@test.csv;type=text/csv" ``` #### Verifying Easiest way is to use the dev tools to `_search` the privmon users index with: ``` GET .entity_analytics.monitoring.users-default/_search ``` Look for number of hits and/or use `query` to search for omitted users. ## Remaining work - [x] API integration tests - [ ] Batching logic unit tests - [ ] E2E tests? --------- Co-authored-by: machadoum <pablo.nevesmachado@elastic.co> Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
84 lines
2.6 KiB
TypeScript
84 lines
2.6 KiB
TypeScript
/*
|
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
* or more contributor license agreements. Licensed under the Elastic License
|
|
* 2.0; you may not use this file except in compliance with the Elastic License
|
|
* 2.0.
|
|
*/
|
|
|
|
import { DEFAULT_SIGNALS_INDEX, SIGNALS_INDEX_KEY } from '../common/constants';
|
|
import type { ExperimentalFeatures } from '../common/experimental_features';
|
|
import { parseExperimentalConfigValue } from '../common/experimental_features';
|
|
import { getDefaultConfigSettings } from '../common/config_settings';
|
|
import type { ConfigType } from './config';
|
|
import { duration } from 'moment';
|
|
|
|
export const createMockConfig = (): ConfigType => {
|
|
const enableExperimental: Array<keyof ExperimentalFeatures> = ['responseActionUploadEnabled'];
|
|
|
|
return {
|
|
[SIGNALS_INDEX_KEY]: DEFAULT_SIGNALS_INDEX,
|
|
maxRuleImportExportSize: 10000,
|
|
maxRuleImportPayloadBytes: 10485760,
|
|
maxTimelineImportExportSize: 10000,
|
|
maxTimelineImportPayloadBytes: 10485760,
|
|
enableExperimental,
|
|
packagerTaskInterval: '60s',
|
|
packagerTaskTimeout: '5m',
|
|
packagerTaskPackagePolicyUpdateBatchSize: 10,
|
|
completeExternalResponseActionsTaskInterval: '60s',
|
|
completeExternalResponseActionsTaskTimeout: '20m',
|
|
prebuiltRulesPackageVersion: '',
|
|
alertMergeStrategy: 'missingFields',
|
|
alertIgnoreFields: [],
|
|
maxUploadResponseActionFileBytes: 26214400,
|
|
settings: getDefaultConfigSettings(),
|
|
experimentalFeatures: parseExperimentalConfigValue(enableExperimental).features,
|
|
enabled: true,
|
|
enableUiSettingsValidations: false,
|
|
entityAnalytics: {
|
|
riskEngine: {
|
|
alertSampleSizePerShard: 10_000,
|
|
},
|
|
assetCriticality: {
|
|
csvUpload: {
|
|
errorRetries: 3,
|
|
maxBulkRequestBodySizeBytes: 10_485_760,
|
|
},
|
|
},
|
|
entityStore: {
|
|
frequency: duration('1m'),
|
|
syncDelay: duration('5m'),
|
|
developer: {
|
|
pipelineDebugMode: false,
|
|
},
|
|
},
|
|
monitoring: {
|
|
privileges: {
|
|
users: {
|
|
csvUpload: {
|
|
errorRetries: 3,
|
|
maxBulkRequestBodySizeBytes: 10_485_760,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
};
|
|
};
|
|
|
|
const withExperimentalFeature = (
|
|
config: ConfigType,
|
|
feature: keyof ExperimentalFeatures
|
|
): ConfigType => {
|
|
const enableExperimental = config.enableExperimental.concat(feature);
|
|
return {
|
|
...config,
|
|
enableExperimental,
|
|
experimentalFeatures: parseExperimentalConfigValue(enableExperimental).features,
|
|
};
|
|
};
|
|
|
|
export const configMock = {
|
|
createDefault: createMockConfig,
|
|
withExperimentalFeature,
|
|
};
|