mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
# Backport This will backport the following commits from `main` to `8.16`: - [Kibana space scoped component-template for risk engine (#197170)](https://github.com/elastic/kibana/pull/197170) <!--- Backport version: 8.9.8 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sqren/backport) <!--BACKPORT [{"author":{"name":"Abhishek Bhatia","email":"117628830+abhishekbhatia1710@users.noreply.github.com"},"sourceCommit":{"committedDate":"2024-10-22T15:45:52Z","message":"Kibana space scoped component-template for risk engine (#197170)\n\n## Summary\r\n\r\nThe component template used when enabling the risk engine is not Kibana\r\nspace-agnostic, as the component template in every space is named\r\n`.risk-score-mappings`. This caused issues during the cleanup process,\r\nwhere it attempted to delete the same component template from each space\r\nbut failed because other spaces' index templates were still referencing\r\nit.\r\n\r\n\r\n\r\n### Checklist\r\n\r\n- [x] [Unit or functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere updated or added to match the most common scenarios\r\n- [x] [Flaky Test\r\nRunner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was\r\nused on any tests changed\r\n\r\n\r\n\r\n### For maintainers\r\n\r\n- [ ] This was checked for breaking API changes and was [labeled\r\nappropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#_add_your_labels)\r\n- [ ] This will appear in the **Release Notes** and follow the\r\n[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>","sha":"b562288289f326084511c68759db84e20cee2791","branchLabelMapping":{"^v9.0.0$":"main","^v8.17.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:fix","backport missing","v9.0.0","Team:Entity Analytics","backport:version"],"number":197170,"url":"https://github.com/elastic/kibana/pull/197170","mergeCommit":{"message":"Kibana space scoped component-template for risk engine (#197170)\n\n## Summary\r\n\r\nThe component template used when enabling the risk engine is not Kibana\r\nspace-agnostic, as the component template in every space is named\r\n`.risk-score-mappings`. This caused issues during the cleanup process,\r\nwhere it attempted to delete the same component template from each space\r\nbut failed because other spaces' index templates were still referencing\r\nit.\r\n\r\n\r\n\r\n### Checklist\r\n\r\n- [x] [Unit or functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere updated or added to match the most common scenarios\r\n- [x] [Flaky Test\r\nRunner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was\r\nused on any tests changed\r\n\r\n\r\n\r\n### For maintainers\r\n\r\n- [ ] This was checked for breaking API changes and was [labeled\r\nappropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#_add_your_labels)\r\n- [ ] This will appear in the **Release Notes** and follow the\r\n[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>","sha":"b562288289f326084511c68759db84e20cee2791"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","labelRegex":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/197170","number":197170,"mergeCommit":{"message":"Kibana space scoped component-template for risk engine (#197170)\n\n## Summary\r\n\r\nThe component template used when enabling the risk engine is not Kibana\r\nspace-agnostic, as the component template in every space is named\r\n`.risk-score-mappings`. This caused issues during the cleanup process,\r\nwhere it attempted to delete the same component template from each space\r\nbut failed because other spaces' index templates were still referencing\r\nit.\r\n\r\n\r\n\r\n### Checklist\r\n\r\n- [x] [Unit or functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere updated or added to match the most common scenarios\r\n- [x] [Flaky Test\r\nRunner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was\r\nused on any tests changed\r\n\r\n\r\n\r\n### For maintainers\r\n\r\n- [ ] This was checked for breaking API changes and was [labeled\r\nappropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#_add_your_labels)\r\n- [ ] This will appear in the **Release Notes** and follow the\r\n[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>","sha":"b562288289f326084511c68759db84e20cee2791"}}]}] BACKPORT-->
This commit is contained in:
parent
4c242c6b5b
commit
6f333b8ae0
4 changed files with 666 additions and 355 deletions
|
@ -129,6 +129,9 @@ export const riskScoreFieldMap: FieldMap = {
|
|||
} as const;
|
||||
|
||||
export const mappingComponentName = '.risk-score-mappings';
|
||||
export const nameSpaceAwareMappingsComponentName = (namespace: string): string => {
|
||||
return `${mappingComponentName}-${namespace}`;
|
||||
};
|
||||
export const totalFieldsLimit = 1000;
|
||||
|
||||
export const getIndexPatternDataStream = (namespace: string): IIndexPatternString => ({
|
||||
|
|
|
@ -40,6 +40,7 @@ jest.spyOn(transforms, 'scheduleTransformNow').mockResolvedValue(Promise.resolve
|
|||
|
||||
describe('RiskScoreDataClient', () => {
|
||||
let riskScoreDataClient: RiskScoreDataClient;
|
||||
let riskScoreDataClientWithNameSpace: RiskScoreDataClient;
|
||||
let mockSavedObjectClient: ReturnType<typeof savedObjectsClientMock.create>;
|
||||
let logger: ReturnType<typeof loggingSystemMock.createLogger>;
|
||||
const esClient = elasticsearchServiceMock.createScopedClusterClient().asCurrentUser;
|
||||
|
@ -56,6 +57,8 @@ describe('RiskScoreDataClient', () => {
|
|||
namespace: 'default',
|
||||
};
|
||||
riskScoreDataClient = new RiskScoreDataClient(options);
|
||||
const optionsWithNamespace = { ...options, namespace: 'space-1' };
|
||||
riskScoreDataClientWithNameSpace = new RiskScoreDataClient(optionsWithNamespace);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
@ -80,369 +83,396 @@ describe('RiskScoreDataClient', () => {
|
|||
});
|
||||
|
||||
describe('init success', () => {
|
||||
it('should initialize risk engine resources', async () => {
|
||||
await riskScoreDataClient.init();
|
||||
it('should initialize risk engine resources in the appropriate space', async () => {
|
||||
const assertComponentTemplate = (namespace: string) => {
|
||||
expect(createOrUpdateComponentTemplate).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
logger,
|
||||
esClient,
|
||||
template: expect.objectContaining({
|
||||
name: `.risk-score-mappings-${namespace}`,
|
||||
_meta: {
|
||||
managed: true,
|
||||
},
|
||||
}),
|
||||
totalFieldsLimit: 1000,
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
expect(createOrUpdateComponentTemplate).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
const assertIndexTemplate = (namespace: string) => {
|
||||
expect(createOrUpdateIndexTemplate).toHaveBeenCalledWith({
|
||||
logger,
|
||||
esClient,
|
||||
template: expect.objectContaining({
|
||||
name: '.risk-score-mappings',
|
||||
_meta: {
|
||||
managed: true,
|
||||
template: {
|
||||
name: `.risk-score.risk-score-${namespace}-index-template`,
|
||||
body: {
|
||||
data_stream: { hidden: true },
|
||||
index_patterns: [`risk-score.risk-score-${namespace}`],
|
||||
composed_of: [`.risk-score-mappings-${namespace}`],
|
||||
template: {
|
||||
lifecycle: {},
|
||||
settings: {
|
||||
'index.mapping.total_fields.limit': totalFieldsLimit,
|
||||
},
|
||||
mappings: {
|
||||
dynamic: false,
|
||||
_meta: {
|
||||
kibana: {
|
||||
version: '8.9.0',
|
||||
},
|
||||
managed: true,
|
||||
namespace,
|
||||
},
|
||||
},
|
||||
},
|
||||
_meta: {
|
||||
kibana: {
|
||||
version: '8.9.0',
|
||||
},
|
||||
managed: true,
|
||||
namespace,
|
||||
},
|
||||
},
|
||||
}),
|
||||
totalFieldsLimit: 1000,
|
||||
})
|
||||
);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const assertDataStream = (namespace: string) => {
|
||||
expect(createDataStream).toHaveBeenCalledWith({
|
||||
logger,
|
||||
esClient,
|
||||
totalFieldsLimit,
|
||||
indexPatterns: {
|
||||
template: `.risk-score.risk-score-${namespace}-index-template`,
|
||||
alias: `risk-score.risk-score-${namespace}`,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const assertIndex = (namespace: string) => {
|
||||
expect(createOrUpdateIndex).toHaveBeenCalledWith({
|
||||
logger,
|
||||
esClient,
|
||||
options: {
|
||||
index: `risk-score.risk-score-latest-${namespace}`,
|
||||
mappings: {
|
||||
dynamic: false,
|
||||
properties: {
|
||||
'@timestamp': {
|
||||
ignore_malformed: false,
|
||||
type: 'date',
|
||||
},
|
||||
host: {
|
||||
properties: {
|
||||
name: {
|
||||
type: 'keyword',
|
||||
},
|
||||
risk: {
|
||||
properties: {
|
||||
calculated_level: {
|
||||
type: 'keyword',
|
||||
},
|
||||
calculated_score: {
|
||||
type: 'float',
|
||||
},
|
||||
calculated_score_norm: {
|
||||
type: 'float',
|
||||
},
|
||||
category_1_count: {
|
||||
type: 'long',
|
||||
},
|
||||
category_1_score: {
|
||||
type: 'float',
|
||||
},
|
||||
id_field: {
|
||||
type: 'keyword',
|
||||
},
|
||||
id_value: {
|
||||
type: 'keyword',
|
||||
},
|
||||
inputs: {
|
||||
properties: {
|
||||
category: {
|
||||
type: 'keyword',
|
||||
},
|
||||
description: {
|
||||
type: 'keyword',
|
||||
},
|
||||
id: {
|
||||
type: 'keyword',
|
||||
},
|
||||
index: {
|
||||
type: 'keyword',
|
||||
},
|
||||
risk_score: {
|
||||
type: 'float',
|
||||
},
|
||||
timestamp: {
|
||||
type: 'date',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
notes: {
|
||||
type: 'keyword',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
},
|
||||
},
|
||||
user: {
|
||||
properties: {
|
||||
name: {
|
||||
type: 'keyword',
|
||||
},
|
||||
risk: {
|
||||
properties: {
|
||||
calculated_level: {
|
||||
type: 'keyword',
|
||||
},
|
||||
calculated_score: {
|
||||
type: 'float',
|
||||
},
|
||||
calculated_score_norm: {
|
||||
type: 'float',
|
||||
},
|
||||
category_1_count: {
|
||||
type: 'long',
|
||||
},
|
||||
category_1_score: {
|
||||
type: 'float',
|
||||
},
|
||||
id_field: {
|
||||
type: 'keyword',
|
||||
},
|
||||
id_value: {
|
||||
type: 'keyword',
|
||||
},
|
||||
inputs: {
|
||||
properties: {
|
||||
category: {
|
||||
type: 'keyword',
|
||||
},
|
||||
description: {
|
||||
type: 'keyword',
|
||||
},
|
||||
id: {
|
||||
type: 'keyword',
|
||||
},
|
||||
index: {
|
||||
type: 'keyword',
|
||||
},
|
||||
risk_score: {
|
||||
type: 'float',
|
||||
},
|
||||
timestamp: {
|
||||
type: 'date',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
notes: {
|
||||
type: 'keyword',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const assertTransform = (namespace: string) => {
|
||||
expect(transforms.createTransform).toHaveBeenCalledWith({
|
||||
logger,
|
||||
esClient,
|
||||
transform: {
|
||||
dest: {
|
||||
index: `risk-score.risk-score-latest-${namespace}`,
|
||||
},
|
||||
frequency: '1h',
|
||||
latest: {
|
||||
sort: '@timestamp',
|
||||
unique_key: ['host.name', 'user.name'],
|
||||
},
|
||||
source: {
|
||||
index: [`risk-score.risk-score-${namespace}`],
|
||||
},
|
||||
sync: {
|
||||
time: {
|
||||
delay: '0s',
|
||||
field: '@timestamp',
|
||||
},
|
||||
},
|
||||
transform_id: `risk_score_latest_transform_${namespace}`,
|
||||
settings: {
|
||||
unattended: true,
|
||||
},
|
||||
_meta: {
|
||||
version: 2,
|
||||
managed: true,
|
||||
managed_by: 'security-entity-analytics',
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// Default namespace
|
||||
esClient.cluster.existsComponentTemplate.mockResolvedValue(false);
|
||||
await riskScoreDataClient.init();
|
||||
assertComponentTemplate('default');
|
||||
assertIndexTemplate('default');
|
||||
assertDataStream('default');
|
||||
assertIndex('default');
|
||||
assertTransform('default');
|
||||
|
||||
// Space-1 namespace
|
||||
esClient.cluster.existsComponentTemplate.mockResolvedValue(false);
|
||||
await riskScoreDataClientWithNameSpace.init();
|
||||
assertComponentTemplate('space-1');
|
||||
assertIndexTemplate('space-1');
|
||||
assertDataStream('space-1');
|
||||
assertIndex('space-1');
|
||||
assertTransform('space-1');
|
||||
|
||||
expect((createOrUpdateComponentTemplate as jest.Mock).mock.lastCall[0].template.template)
|
||||
.toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"mappings": Object {
|
||||
"dynamic": "strict",
|
||||
"properties": Object {
|
||||
"@timestamp": Object {
|
||||
"ignore_malformed": false,
|
||||
"type": "date",
|
||||
},
|
||||
"host": Object {
|
||||
"properties": Object {
|
||||
"name": Object {
|
||||
"type": "keyword",
|
||||
},
|
||||
"risk": Object {
|
||||
"properties": Object {
|
||||
"calculated_level": Object {
|
||||
"type": "keyword",
|
||||
},
|
||||
"calculated_score": Object {
|
||||
"type": "float",
|
||||
},
|
||||
"calculated_score_norm": Object {
|
||||
"type": "float",
|
||||
},
|
||||
"category_1_count": Object {
|
||||
"type": "long",
|
||||
},
|
||||
"category_1_score": Object {
|
||||
"type": "float",
|
||||
},
|
||||
"id_field": Object {
|
||||
"type": "keyword",
|
||||
},
|
||||
"id_value": Object {
|
||||
"type": "keyword",
|
||||
},
|
||||
"inputs": Object {
|
||||
"properties": Object {
|
||||
"category": Object {
|
||||
"type": "keyword",
|
||||
},
|
||||
"description": Object {
|
||||
"type": "keyword",
|
||||
},
|
||||
"id": Object {
|
||||
"type": "keyword",
|
||||
},
|
||||
"index": Object {
|
||||
"type": "keyword",
|
||||
},
|
||||
"risk_score": Object {
|
||||
"type": "float",
|
||||
},
|
||||
"timestamp": Object {
|
||||
"type": "date",
|
||||
},
|
||||
Object {
|
||||
"mappings": Object {
|
||||
"dynamic": "strict",
|
||||
"properties": Object {
|
||||
"@timestamp": Object {
|
||||
"ignore_malformed": false,
|
||||
"type": "date",
|
||||
},
|
||||
"host": Object {
|
||||
"properties": Object {
|
||||
"name": Object {
|
||||
"type": "keyword",
|
||||
},
|
||||
"risk": Object {
|
||||
"properties": Object {
|
||||
"calculated_level": Object {
|
||||
"type": "keyword",
|
||||
},
|
||||
"calculated_score": Object {
|
||||
"type": "float",
|
||||
},
|
||||
"calculated_score_norm": Object {
|
||||
"type": "float",
|
||||
},
|
||||
"category_1_count": Object {
|
||||
"type": "long",
|
||||
},
|
||||
"category_1_score": Object {
|
||||
"type": "float",
|
||||
},
|
||||
"id_field": Object {
|
||||
"type": "keyword",
|
||||
},
|
||||
"id_value": Object {
|
||||
"type": "keyword",
|
||||
},
|
||||
"inputs": Object {
|
||||
"properties": Object {
|
||||
"category": Object {
|
||||
"type": "keyword",
|
||||
},
|
||||
"type": "object",
|
||||
},
|
||||
"notes": Object {
|
||||
"type": "keyword",
|
||||
},
|
||||
},
|
||||
"type": "object",
|
||||
},
|
||||
},
|
||||
},
|
||||
"user": Object {
|
||||
"properties": Object {
|
||||
"name": Object {
|
||||
"type": "keyword",
|
||||
},
|
||||
"risk": Object {
|
||||
"properties": Object {
|
||||
"calculated_level": Object {
|
||||
"type": "keyword",
|
||||
},
|
||||
"calculated_score": Object {
|
||||
"type": "float",
|
||||
},
|
||||
"calculated_score_norm": Object {
|
||||
"type": "float",
|
||||
},
|
||||
"category_1_count": Object {
|
||||
"type": "long",
|
||||
},
|
||||
"category_1_score": Object {
|
||||
"type": "float",
|
||||
},
|
||||
"id_field": Object {
|
||||
"type": "keyword",
|
||||
},
|
||||
"id_value": Object {
|
||||
"type": "keyword",
|
||||
},
|
||||
"inputs": Object {
|
||||
"properties": Object {
|
||||
"category": Object {
|
||||
"type": "keyword",
|
||||
},
|
||||
"description": Object {
|
||||
"type": "keyword",
|
||||
},
|
||||
"id": Object {
|
||||
"type": "keyword",
|
||||
},
|
||||
"index": Object {
|
||||
"type": "keyword",
|
||||
},
|
||||
"risk_score": Object {
|
||||
"type": "float",
|
||||
},
|
||||
"timestamp": Object {
|
||||
"type": "date",
|
||||
},
|
||||
"description": Object {
|
||||
"type": "keyword",
|
||||
},
|
||||
"id": Object {
|
||||
"type": "keyword",
|
||||
},
|
||||
"index": Object {
|
||||
"type": "keyword",
|
||||
},
|
||||
"risk_score": Object {
|
||||
"type": "float",
|
||||
},
|
||||
"timestamp": Object {
|
||||
"type": "date",
|
||||
},
|
||||
"type": "object",
|
||||
},
|
||||
"notes": Object {
|
||||
"type": "keyword",
|
||||
},
|
||||
"type": "object",
|
||||
},
|
||||
"notes": Object {
|
||||
"type": "keyword",
|
||||
},
|
||||
"type": "object",
|
||||
},
|
||||
"type": "object",
|
||||
},
|
||||
},
|
||||
},
|
||||
"user": Object {
|
||||
"properties": Object {
|
||||
"name": Object {
|
||||
"type": "keyword",
|
||||
},
|
||||
"risk": Object {
|
||||
"properties": Object {
|
||||
"calculated_level": Object {
|
||||
"type": "keyword",
|
||||
},
|
||||
"calculated_score": Object {
|
||||
"type": "float",
|
||||
},
|
||||
"calculated_score_norm": Object {
|
||||
"type": "float",
|
||||
},
|
||||
"category_1_count": Object {
|
||||
"type": "long",
|
||||
},
|
||||
"category_1_score": Object {
|
||||
"type": "float",
|
||||
},
|
||||
"id_field": Object {
|
||||
"type": "keyword",
|
||||
},
|
||||
"id_value": Object {
|
||||
"type": "keyword",
|
||||
},
|
||||
"inputs": Object {
|
||||
"properties": Object {
|
||||
"category": Object {
|
||||
"type": "keyword",
|
||||
},
|
||||
"description": Object {
|
||||
"type": "keyword",
|
||||
},
|
||||
"id": Object {
|
||||
"type": "keyword",
|
||||
},
|
||||
"index": Object {
|
||||
"type": "keyword",
|
||||
},
|
||||
"risk_score": Object {
|
||||
"type": "float",
|
||||
},
|
||||
"timestamp": Object {
|
||||
"type": "date",
|
||||
},
|
||||
},
|
||||
"type": "object",
|
||||
},
|
||||
"notes": Object {
|
||||
"type": "keyword",
|
||||
},
|
||||
},
|
||||
"type": "object",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"settings": Object {},
|
||||
}
|
||||
`);
|
||||
|
||||
expect(createOrUpdateIndexTemplate).toHaveBeenCalledWith({
|
||||
logger,
|
||||
esClient,
|
||||
template: {
|
||||
name: '.risk-score.risk-score-default-index-template',
|
||||
body: {
|
||||
data_stream: { hidden: true },
|
||||
index_patterns: ['risk-score.risk-score-default'],
|
||||
composed_of: ['.risk-score-mappings'],
|
||||
template: {
|
||||
lifecycle: {},
|
||||
settings: {
|
||||
'index.mapping.total_fields.limit': totalFieldsLimit,
|
||||
},
|
||||
mappings: {
|
||||
dynamic: false,
|
||||
_meta: {
|
||||
kibana: {
|
||||
version: '8.9.0',
|
||||
},
|
||||
managed: true,
|
||||
namespace: 'default',
|
||||
},
|
||||
},
|
||||
},
|
||||
_meta: {
|
||||
kibana: {
|
||||
version: '8.9.0',
|
||||
},
|
||||
managed: true,
|
||||
namespace: 'default',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(createDataStream).toHaveBeenCalledWith({
|
||||
logger,
|
||||
esClient,
|
||||
totalFieldsLimit,
|
||||
indexPatterns: {
|
||||
template: `.risk-score.risk-score-default-index-template`,
|
||||
alias: `risk-score.risk-score-default`,
|
||||
},
|
||||
});
|
||||
|
||||
expect(createOrUpdateIndex).toHaveBeenCalledWith({
|
||||
logger,
|
||||
esClient,
|
||||
options: {
|
||||
index: `risk-score.risk-score-latest-default`,
|
||||
mappings: {
|
||||
dynamic: false,
|
||||
properties: {
|
||||
'@timestamp': {
|
||||
ignore_malformed: false,
|
||||
type: 'date',
|
||||
},
|
||||
host: {
|
||||
properties: {
|
||||
name: {
|
||||
type: 'keyword',
|
||||
},
|
||||
risk: {
|
||||
properties: {
|
||||
calculated_level: {
|
||||
type: 'keyword',
|
||||
},
|
||||
calculated_score: {
|
||||
type: 'float',
|
||||
},
|
||||
calculated_score_norm: {
|
||||
type: 'float',
|
||||
},
|
||||
category_1_count: {
|
||||
type: 'long',
|
||||
},
|
||||
category_1_score: {
|
||||
type: 'float',
|
||||
},
|
||||
id_field: {
|
||||
type: 'keyword',
|
||||
},
|
||||
id_value: {
|
||||
type: 'keyword',
|
||||
},
|
||||
inputs: {
|
||||
properties: {
|
||||
category: {
|
||||
type: 'keyword',
|
||||
},
|
||||
description: {
|
||||
type: 'keyword',
|
||||
},
|
||||
id: {
|
||||
type: 'keyword',
|
||||
},
|
||||
index: {
|
||||
type: 'keyword',
|
||||
},
|
||||
risk_score: {
|
||||
type: 'float',
|
||||
},
|
||||
timestamp: {
|
||||
type: 'date',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
notes: {
|
||||
type: 'keyword',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
},
|
||||
},
|
||||
user: {
|
||||
properties: {
|
||||
name: {
|
||||
type: 'keyword',
|
||||
},
|
||||
risk: {
|
||||
properties: {
|
||||
calculated_level: {
|
||||
type: 'keyword',
|
||||
},
|
||||
calculated_score: {
|
||||
type: 'float',
|
||||
},
|
||||
calculated_score_norm: {
|
||||
type: 'float',
|
||||
},
|
||||
category_1_count: {
|
||||
type: 'long',
|
||||
},
|
||||
category_1_score: {
|
||||
type: 'float',
|
||||
},
|
||||
id_field: {
|
||||
type: 'keyword',
|
||||
},
|
||||
id_value: {
|
||||
type: 'keyword',
|
||||
},
|
||||
inputs: {
|
||||
properties: {
|
||||
category: {
|
||||
type: 'keyword',
|
||||
},
|
||||
description: {
|
||||
type: 'keyword',
|
||||
},
|
||||
id: {
|
||||
type: 'keyword',
|
||||
},
|
||||
index: {
|
||||
type: 'keyword',
|
||||
},
|
||||
risk_score: {
|
||||
type: 'float',
|
||||
},
|
||||
timestamp: {
|
||||
type: 'date',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
notes: {
|
||||
type: 'keyword',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(transforms.createTransform).toHaveBeenCalledWith({
|
||||
logger,
|
||||
esClient,
|
||||
transform: {
|
||||
dest: {
|
||||
index: 'risk-score.risk-score-latest-default',
|
||||
},
|
||||
frequency: '1h',
|
||||
latest: {
|
||||
sort: '@timestamp',
|
||||
unique_key: ['host.name', 'user.name'],
|
||||
},
|
||||
source: {
|
||||
index: ['risk-score.risk-score-default'],
|
||||
},
|
||||
sync: {
|
||||
time: {
|
||||
delay: '0s',
|
||||
field: '@timestamp',
|
||||
},
|
||||
},
|
||||
transform_id: 'risk_score_latest_transform_default',
|
||||
settings: {
|
||||
unattended: true,
|
||||
},
|
||||
_meta: {
|
||||
version: 2,
|
||||
managed: true,
|
||||
managed_by: 'security-entity-analytics',
|
||||
},
|
||||
},
|
||||
});
|
||||
"settings": Object {},
|
||||
}
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -479,7 +509,7 @@ describe('RiskScoreDataClient', () => {
|
|||
expect(esClient.transform.deleteTransform).toHaveBeenCalledTimes(1);
|
||||
expect(esClient.indices.deleteDataStream).toHaveBeenCalledTimes(1);
|
||||
expect(esClient.indices.deleteIndexTemplate).toHaveBeenCalledTimes(1);
|
||||
expect(esClient.cluster.deleteComponentTemplate).toHaveBeenCalledTimes(1);
|
||||
expect(esClient.cluster.deleteComponentTemplate).toHaveBeenCalledTimes(2);
|
||||
expect(errors).toEqual([]);
|
||||
});
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ import {
|
|||
getIndexPatternDataStream,
|
||||
getTransformOptions,
|
||||
mappingComponentName,
|
||||
nameSpaceAwareMappingsComponentName,
|
||||
riskScoreFieldMap,
|
||||
totalFieldsLimit,
|
||||
} from './configurations';
|
||||
|
@ -114,12 +115,42 @@ export class RiskScoreDataClient {
|
|||
namespace,
|
||||
};
|
||||
|
||||
// Check if there are any existing component templates with the namespace in the name
|
||||
|
||||
const oldComponentTemplateExists = await esClient.cluster.existsComponentTemplate({
|
||||
name: mappingComponentName,
|
||||
});
|
||||
// If present then copy the contents to a new component template with the namespace in the name
|
||||
if (oldComponentTemplateExists) {
|
||||
const oldComponentTemplateResponse = await esClient.cluster.getComponentTemplate(
|
||||
{
|
||||
name: mappingComponentName,
|
||||
},
|
||||
{ ignore: [404] }
|
||||
);
|
||||
const oldComponentTemplate = oldComponentTemplateResponse?.component_templates[0];
|
||||
const newComponentTemplateName = nameSpaceAwareMappingsComponentName(namespace);
|
||||
await esClient.cluster.putComponentTemplate({
|
||||
name: newComponentTemplateName,
|
||||
body: oldComponentTemplate.component_template,
|
||||
});
|
||||
}
|
||||
|
||||
// Delete the component template without the namespace in the name
|
||||
await esClient.cluster.deleteComponentTemplate(
|
||||
{
|
||||
name: mappingComponentName,
|
||||
},
|
||||
{ ignore: [404] }
|
||||
);
|
||||
|
||||
// Update the new component template with the required data
|
||||
await Promise.all([
|
||||
createOrUpdateComponentTemplate({
|
||||
logger: this.options.logger,
|
||||
esClient,
|
||||
template: {
|
||||
name: mappingComponentName,
|
||||
name: nameSpaceAwareMappingsComponentName(namespace),
|
||||
_meta: {
|
||||
managed: true,
|
||||
},
|
||||
|
@ -132,6 +163,7 @@ export class RiskScoreDataClient {
|
|||
}),
|
||||
]);
|
||||
|
||||
// Reference the new component template in the index template
|
||||
await createOrUpdateIndexTemplate({
|
||||
logger: this.options.logger,
|
||||
esClient,
|
||||
|
@ -140,7 +172,7 @@ export class RiskScoreDataClient {
|
|||
body: {
|
||||
data_stream: { hidden: true },
|
||||
index_patterns: [indexPatterns.alias],
|
||||
composed_of: [mappingComponentName],
|
||||
composed_of: [nameSpaceAwareMappingsComponentName(namespace)],
|
||||
template: {
|
||||
lifecycle: {},
|
||||
settings: {
|
||||
|
@ -235,6 +267,15 @@ export class RiskScoreDataClient {
|
|||
)
|
||||
.catch(addError);
|
||||
|
||||
await esClient.cluster
|
||||
.deleteComponentTemplate(
|
||||
{
|
||||
name: nameSpaceAwareMappingsComponentName(namespace),
|
||||
},
|
||||
{ ignore: [404] }
|
||||
)
|
||||
.catch(addError);
|
||||
|
||||
await esClient.cluster
|
||||
.deleteComponentTemplate(
|
||||
{
|
||||
|
|
|
@ -27,19 +27,30 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
const es = getService('es');
|
||||
const supertest = getService('supertest');
|
||||
const kibanaServer = getService('kibanaServer');
|
||||
const spaces = getService('spaces');
|
||||
const customSpaceName = 'ea-customspace-it';
|
||||
const riskEngineRoutes = riskEngineRouteHelpersFactory(supertest);
|
||||
const riskEngineRoutesWithNamespace = riskEngineRouteHelpersFactory(supertest, customSpaceName);
|
||||
const log = getService('log');
|
||||
|
||||
// Failing: See https://github.com/elastic/kibana/issues/191637
|
||||
describe.skip('@ess @serverless @serverlessQA init_and_status_apis', () => {
|
||||
describe('@ess @serverless @serverlessQA init_and_status_apis', () => {
|
||||
before(async () => {
|
||||
await spaces.create({
|
||||
id: customSpaceName,
|
||||
name: customSpaceName,
|
||||
description: 'Space for ${customSpaceName}',
|
||||
disabledFeatures: [],
|
||||
});
|
||||
await riskEngineRoutes.cleanUp();
|
||||
await riskEngineRoutesWithNamespace.cleanUp();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await riskEngineRoutes.cleanUp();
|
||||
await riskEngineRoutesWithNamespace.cleanUp();
|
||||
await clearLegacyTransforms({ es, log });
|
||||
await clearLegacyDashboards({ supertest, log });
|
||||
await spaces.delete(customSpaceName);
|
||||
});
|
||||
|
||||
describe('init api', () => {
|
||||
|
@ -54,10 +65,21 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
risk_engine_resources_installed: true,
|
||||
},
|
||||
});
|
||||
|
||||
const customNamespaceResponse = await riskEngineRoutesWithNamespace.init();
|
||||
expect(customNamespaceResponse.body).to.eql({
|
||||
result: {
|
||||
errors: [],
|
||||
legacy_risk_engine_disabled: true,
|
||||
risk_engine_configuration_created: true,
|
||||
risk_engine_enabled: true,
|
||||
risk_engine_resources_installed: true,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should install resources on init call', async () => {
|
||||
const componentTemplateName = '.risk-score-mappings';
|
||||
it('should install resources on init call in the default namespace', async () => {
|
||||
const componentTemplateName = '.risk-score-mappings-default';
|
||||
const indexTemplateName = '.risk-score.risk-score-default-index-template';
|
||||
const dataStreamName = 'risk-score.risk-score-default';
|
||||
const latestIndexName = 'risk-score.risk-score-latest-default';
|
||||
|
@ -210,7 +232,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
expect(indexTemplate.index_template.index_patterns).to.eql([
|
||||
'risk-score.risk-score-default',
|
||||
]);
|
||||
expect(indexTemplate.index_template.composed_of).to.eql(['.risk-score-mappings']);
|
||||
expect(indexTemplate.index_template.composed_of).to.eql(['.risk-score-mappings-default']);
|
||||
expect(indexTemplate.index_template.template!.mappings?.dynamic).to.eql(false);
|
||||
expect(indexTemplate.index_template.template!.mappings?._meta?.managed).to.eql(true);
|
||||
expect(indexTemplate.index_template.template!.mappings?._meta?.namespace).to.eql('default');
|
||||
|
@ -267,6 +289,221 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
expect(transformStats.transforms[0].state).to.eql('stopped');
|
||||
});
|
||||
|
||||
it('should install resources on init call in the custom namespace', async () => {
|
||||
const componentTemplateName = `.risk-score-mappings-${customSpaceName}`;
|
||||
const indexTemplateName = `.risk-score.risk-score-${customSpaceName}-index-template`;
|
||||
const dataStreamName = `risk-score.risk-score-${customSpaceName}`;
|
||||
const latestIndexName = `risk-score.risk-score-latest-${customSpaceName}`;
|
||||
const transformId = `risk_score_latest_transform_${customSpaceName}`;
|
||||
|
||||
await riskEngineRoutesWithNamespace.init();
|
||||
|
||||
const { component_templates: componentTemplates1 } = await es.cluster.getComponentTemplate({
|
||||
name: componentTemplateName,
|
||||
});
|
||||
|
||||
expect(componentTemplates1.length).to.eql(1);
|
||||
const componentTemplate = componentTemplates1[0];
|
||||
|
||||
expect(componentTemplate.name).to.eql(componentTemplateName);
|
||||
expect(componentTemplate.component_template.template.mappings).to.eql({
|
||||
dynamic: 'strict',
|
||||
properties: {
|
||||
'@timestamp': {
|
||||
ignore_malformed: false,
|
||||
type: 'date',
|
||||
},
|
||||
host: {
|
||||
properties: {
|
||||
name: {
|
||||
type: 'keyword',
|
||||
},
|
||||
risk: {
|
||||
properties: {
|
||||
calculated_level: {
|
||||
type: 'keyword',
|
||||
},
|
||||
calculated_score: {
|
||||
type: 'float',
|
||||
},
|
||||
calculated_score_norm: {
|
||||
type: 'float',
|
||||
},
|
||||
category_1_count: {
|
||||
type: 'long',
|
||||
},
|
||||
category_1_score: {
|
||||
type: 'float',
|
||||
},
|
||||
id_field: {
|
||||
type: 'keyword',
|
||||
},
|
||||
id_value: {
|
||||
type: 'keyword',
|
||||
},
|
||||
notes: {
|
||||
type: 'keyword',
|
||||
},
|
||||
inputs: {
|
||||
properties: {
|
||||
id: {
|
||||
type: 'keyword',
|
||||
},
|
||||
index: {
|
||||
type: 'keyword',
|
||||
},
|
||||
category: {
|
||||
type: 'keyword',
|
||||
},
|
||||
description: {
|
||||
type: 'keyword',
|
||||
},
|
||||
risk_score: {
|
||||
type: 'float',
|
||||
},
|
||||
timestamp: {
|
||||
type: 'date',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
},
|
||||
},
|
||||
user: {
|
||||
properties: {
|
||||
name: {
|
||||
type: 'keyword',
|
||||
},
|
||||
risk: {
|
||||
properties: {
|
||||
calculated_level: {
|
||||
type: 'keyword',
|
||||
},
|
||||
calculated_score: {
|
||||
type: 'float',
|
||||
},
|
||||
calculated_score_norm: {
|
||||
type: 'float',
|
||||
},
|
||||
category_1_count: {
|
||||
type: 'long',
|
||||
},
|
||||
category_1_score: {
|
||||
type: 'float',
|
||||
},
|
||||
id_field: {
|
||||
type: 'keyword',
|
||||
},
|
||||
id_value: {
|
||||
type: 'keyword',
|
||||
},
|
||||
notes: {
|
||||
type: 'keyword',
|
||||
},
|
||||
inputs: {
|
||||
properties: {
|
||||
id: {
|
||||
type: 'keyword',
|
||||
},
|
||||
index: {
|
||||
type: 'keyword',
|
||||
},
|
||||
category: {
|
||||
type: 'keyword',
|
||||
},
|
||||
description: {
|
||||
type: 'keyword',
|
||||
},
|
||||
risk_score: {
|
||||
type: 'float',
|
||||
},
|
||||
timestamp: {
|
||||
type: 'date',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
},
|
||||
type: 'object',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const { index_templates: indexTemplates } = await es.indices.getIndexTemplate({
|
||||
name: indexTemplateName,
|
||||
});
|
||||
expect(indexTemplates.length).to.eql(1);
|
||||
const indexTemplate = indexTemplates[0];
|
||||
expect(indexTemplate.name).to.eql(indexTemplateName);
|
||||
expect(indexTemplate.index_template.index_patterns).to.eql([
|
||||
`risk-score.risk-score-${customSpaceName}`,
|
||||
]);
|
||||
expect(indexTemplate.index_template.composed_of).to.eql([
|
||||
`.risk-score-mappings-${customSpaceName}`,
|
||||
]);
|
||||
expect(indexTemplate.index_template.template!.mappings?.dynamic).to.eql(false);
|
||||
expect(indexTemplate.index_template.template!.mappings?._meta?.managed).to.eql(true);
|
||||
expect(indexTemplate.index_template.template!.mappings?._meta?.namespace).to.eql(
|
||||
customSpaceName
|
||||
);
|
||||
expect(indexTemplate.index_template.template!.mappings?._meta?.kibana?.version).to.be.a(
|
||||
'string'
|
||||
);
|
||||
|
||||
expect(indexTemplate.index_template.template!.settings).to.eql({
|
||||
index: {
|
||||
mapping: {
|
||||
total_fields: {
|
||||
limit: '1000',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(indexTemplate.index_template.template!.lifecycle).to.eql({
|
||||
enabled: true,
|
||||
});
|
||||
|
||||
const dsResponse = await es.indices.get({
|
||||
index: dataStreamName,
|
||||
});
|
||||
|
||||
const dataStream = Object.values(dsResponse).find(
|
||||
(ds) => ds.data_stream === dataStreamName
|
||||
);
|
||||
|
||||
expect(dataStream?.mappings?._meta?.managed).to.eql(true);
|
||||
expect(dataStream?.mappings?._meta?.namespace).to.eql(customSpaceName);
|
||||
expect(dataStream?.mappings?._meta?.kibana?.version).to.be.a('string');
|
||||
expect(dataStream?.mappings?.dynamic).to.eql('false');
|
||||
|
||||
expect(dataStream?.settings?.index?.mapping).to.eql({
|
||||
total_fields: {
|
||||
limit: '1000',
|
||||
},
|
||||
});
|
||||
|
||||
expect(dataStream?.settings?.index?.hidden).to.eql('true');
|
||||
expect(dataStream?.settings?.index?.number_of_shards).to.eql(1);
|
||||
|
||||
const indexExist = await es.indices.exists({
|
||||
index: latestIndexName,
|
||||
});
|
||||
|
||||
expect(indexExist).to.eql(true);
|
||||
|
||||
const transformStats = await es.transform.getTransformStats({
|
||||
transform_id: transformId,
|
||||
});
|
||||
|
||||
expect(transformStats.transforms[0].state).to.eql('stopped');
|
||||
});
|
||||
|
||||
it('should create configuration saved object', async () => {
|
||||
await riskEngineRoutes.init();
|
||||
const response = await kibanaServer.savedObjects.find({
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue