Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/GitHub action for multi env #4744

Closed
wants to merge 46 commits into from
Closed
Changes from 1 commit
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
918418f
feat(settings): centralize the plugin settings
Desvelao Sep 13, 2022
de48a49
feat(settings): add setting services and replaced the references to c…
Desvelao Sep 13, 2022
c25a1a0
feat(settings): refactor the content of the default configuration file
Desvelao Sep 13, 2022
ffdb2d7
feat(inputs): create new inputs components
Desvelao Sep 13, 2022
e7cd9b0
feat(configuration): refactor the form of Settings/Configuration
Desvelao Sep 13, 2022
5999419
feat(settings): support updating multiple setting at the same time
Desvelao Sep 13, 2022
9b977e6
clean: remove not used code
Desvelao Sep 13, 2022
17ad2e8
fix: fixed category name in `Settings/Configuration`
Desvelao Sep 13, 2022
a454dd8
fix(settings): fixed error due to missing service
Desvelao Sep 13, 2022
5d68615
fix(settings): refactor the form and inputs of `Settings/Configuratio…
Desvelao Sep 16, 2022
cf15ef8
fix: add value transformation for the form inputs and output of field…
Desvelao Sep 19, 2022
0a1ee1d
fix(settings): renamed properties related to transform the value of t…
Desvelao Sep 19, 2022
b4ef2cd
feat(settings): add description to the plugin setting definition prop…
Desvelao Sep 19, 2022
138db83
fix(settings): fix getConfiguration service when the configuration fi…
Desvelao Sep 20, 2022
710800d
fix(settings): Fixed error when do changes of the `useForm` hook an r…
Desvelao Sep 20, 2022
7d7decc
tests(settings): add test related to the plugin settings and its conf…
Desvelao Sep 20, 2022
5f4d0bd
feat(settings): rename plugin setting options of type select to match…
Desvelao Sep 21, 2022
0cb6be5
feat(settings): add plugin settings services and enhance the descript…
Desvelao Sep 21, 2022
34ddc38
tests(input-form): update tests of InputForm component
Desvelao Sep 21, 2022
f74553b
test(configuration-file): add tests of the default configuration file
Desvelao Sep 21, 2022
4bc1972
feat(settings): remove `extensions.mitre` plugin setting
Desvelao Sep 21, 2022
3d2ca47
feat(settings): add documentation to some setting services and test s…
Desvelao Sep 22, 2022
ef631ff
fix: fixed documentation of setting service
Desvelao Sep 22, 2022
1851f9e
doc(settings): move the documentation of the plugin setting properties
Desvelao Sep 22, 2022
d2a8773
fix(settings): rename some plugin setting properties because of reque…
Desvelao Sep 23, 2022
1d34f6c
tests: fix tests of InputForm component
Desvelao Sep 23, 2022
ac3ebc2
fix: response properties when saving the configuration
Desvelao Sep 23, 2022
716a582
fix(settings): fix displaying toast to run the healthcheck when savin…
Desvelao Sep 26, 2022
93157e3
Added category sorting + description + docs link
asteriscos Sep 28, 2022
a007e01
Added settings sorting within their category
asteriscos Sep 28, 2022
0549cb6
Fixed constant types definition
asteriscos Sep 28, 2022
26fa66c
Checks if localCompare exists validation
asteriscos Sep 28, 2022
1f4c01a
fix(settings): fixed plugin setting description doesn't display the m…
Desvelao Oct 3, 2022
33bc999
fix(settings): fix setting type of `wazuh.monitoring.replicas` and li…
Desvelao Oct 3, 2022
ffb1eb9
feat(settins): add plugin settings category description
Desvelao Oct 3, 2022
a935861
fix(settings): fix a problem comparing the initial and current value …
Desvelao Oct 3, 2022
b551b00
fix(settings): fix typo in setting description
Desvelao Oct 3, 2022
624a8f4
fix(tests): format tables of the tests
Desvelao Oct 5, 2022
282b7e9
Fix small typo
asteriscos Oct 5, 2022
04f7457
fix(settings): fix a typo in a toast related to modify the plugin set…
Desvelao Oct 6, 2022
312461c
Changed Custom Branding documentation link
asteriscos Oct 11, 2022
19c7cac
Delete unused imports
asteriscos Oct 20, 2022
dc79490
Merge branch '4.4-7.10' into feat/centralize-plugin-settings
AlexRuiz7 Oct 20, 2022
c7eb96b
Update constants.ts
AlexRuiz7 Oct 20, 2022
db98326
Merge branch '4.4-7.10' into feat/centralize-plugin-settings
AlexRuiz7 Oct 20, 2022
eb57078
Change multienv
Mayons95 Oct 21, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
tests(settings): add test related to the plugin settings and its conf…
…iguration from the UI
Desvelao committed Sep 20, 2022

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
commit 7d7decc9a81556d612a99319d6fbb99fe13e3693
244 changes: 244 additions & 0 deletions public/components/common/form/__snapshots__/index.test.tsx.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`[component] InputForm Renders correctly to match the snapshot: Input: editor 1`] = `
<div>
<div
class="euiCodeEditorWrapper"
data-test-subj="codeEditorContainer"
style="width: 100%; height: 50px;"
>
<div
class="euiCodeEditorKeyboardHint"
data-test-subj="codeEditorHint"
id="ia60ba5b2-38e9-11ed-9933-f589929a8b66_codeEditor"
role="button"
tabindex="0"
>
<p
class="euiText"
>
Press Enter to start editing.
</p>
<p
class="euiText"
>
When you're done, press Escape to stop editing.
</p>
</div>
<div
class=" ace_editor ace-tm"
id="ia60ba5b1-38e9-11ed-9933-f589929a8b66"
style="width: 100%; height: 50px;"
>
<textarea
autocapitalize="off"
autocorrect="off"
class="ace_text-input"
spellcheck="false"
style="opacity: 0;"
tabindex="-1"
wrap="off"
/>
<div
aria-hidden="true"
class="ace_gutter"
>
<div
class="ace_layer ace_gutter-layer ace_folding-enabled"
/>
<div
class="ace_gutter-active-line"
/>
</div>
<div
class="ace_scroller"
>
<div
class="ace_content"
>
<div
class="ace_layer ace_print-margin-layer"
>
<div
class="ace_print-margin"
style="left: 4px; visibility: visible;"
/>
</div>
<div
class="ace_layer ace_marker-layer"
/>
<div
class="ace_layer ace_text-layer"
style="padding: 0px 4px;"
/>
<div
class="ace_layer ace_marker-layer"
/>
<div
class="ace_layer ace_cursor-layer ace_hidden-cursors"
>
<div
class="ace_cursor"
/>
</div>
</div>
</div>
<div
class="ace_scrollbar ace_scrollbar-v"
style="display: none; width: 20px;"
>
<div
class="ace_scrollbar-inner"
style="width: 20px;"
/>
</div>
<div
class="ace_scrollbar ace_scrollbar-h"
style="display: none; height: 20px;"
>
<div
class="ace_scrollbar-inner"
style="height: 20px;"
/>
</div>
<div
style="height: auto; width: auto; top: 0px; left: 0px; visibility: hidden; position: absolute; white-space: pre; overflow: hidden;"
>
<div
style="height: auto; width: auto; top: 0px; left: 0px; visibility: hidden; position: absolute; white-space: pre; overflow: visible;"
/>
<div
style="height: auto; width: auto; top: 0px; left: 0px; visibility: hidden; position: absolute; white-space: pre; overflow: visible;"
>
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
</div>
</div>
</div>
</div>
</div>
`;

exports[`[component] InputForm Renders correctly to match the snapshot: Input: number 1`] = `
<div>
<div
class="euiFormControlLayout euiFormControlLayout--fullWidth"
>
<div
class="euiFormControlLayout__childrenWrapper"
>
<input
class="euiFieldNumber euiFieldNumber--fullWidth"
type="number"
value="4"
/>
</div>
</div>
</div>
`;

exports[`[component] InputForm Renders correctly to match the snapshot: Input: select 1`] = `
<div>
<div
class="euiFormControlLayout"
>
<div
class="euiFormControlLayout__childrenWrapper"
>
<select
class="euiSelect"
/>
<div
class="euiFormControlLayoutIcons euiFormControlLayoutIcons--right"
>
<span
class="euiFormControlLayoutCustomIcon"
>
<svg
aria-hidden="true"
class="euiIcon euiIcon--medium euiIcon-isLoading euiFormControlLayoutCustomIcon__icon"
focusable="false"
height="16"
role="img"
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
/>
</span>
</div>
</div>
</div>
</div>
`;

exports[`[component] InputForm Renders correctly to match the snapshot: Input: switch 1`] = `
<div>
<div
class="euiSwitch"
>
<button
aria-checked="true"
aria-labelledby="ia616c943-38e9-11ed-9933-f589929a8b66"
class="euiSwitch__button"
id="ia616c941-38e9-11ed-9933-f589929a8b66"
role="switch"
type="button"
>
<span
class="euiSwitch__body"
>
<span
class="euiSwitch__thumb"
/>
<span
class="euiSwitch__track"
>
<svg
aria-hidden="true"
class="euiIcon euiIcon--medium euiIcon-isLoading euiSwitch__icon"
focusable="false"
height="16"
role="img"
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
/>
<svg
aria-hidden="true"
class="euiIcon euiIcon--medium euiIcon-isLoading euiSwitch__icon euiSwitch__icon--checked"
focusable="false"
height="16"
role="img"
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
/>
</span>
</span>
</button>
<span
class="euiSwitch__label"
id="ia616c943-38e9-11ed-9933-f589929a8b66"
>
Enabled
</span>
</div>
</div>
`;

exports[`[component] InputForm Renders correctly to match the snapshot: Input: text 1`] = `
<div>
<div
class="euiFormControlLayout euiFormControlLayout--fullWidth"
>
<div
class="euiFormControlLayout__childrenWrapper"
>
<input
class="euiFieldText euiFieldText--fullWidth"
type="text"
value="test"
/>
</div>
</div>
</div>
`;
137 changes: 137 additions & 0 deletions public/components/common/form/hooks.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import { renderHook, act } from '@testing-library/react-hooks';
import { useForm } from './hooks';

describe('[hook] useForm', () => {

it(`[hook] useForm. Verify the initial state`, async () => {

const initialFields = {
text1: {
type: 'text',
initialValue: ''
},
};

const { result } = renderHook(() => useForm(initialFields));

// assert initial state
expect(result.current.fields.text1.changed).toBe(false);
expect(result.current.fields.text1.error).toBeUndefined();
expect(result.current.fields.text1.type).toBe('text');
expect(result.current.fields.text1.value).toBe('');
expect(result.current.fields.text1.initialValue).toBe('');
expect(result.current.fields.text1.onChange).toBeDefined();
});

it(`[hook] useForm. Verify the initial state. Multiple fields.`, async () => {

const initialFields = {
text1: {
type: 'text',
initialValue: ''
},
number1: {
type: 'number',
initialValue: 1
},
};

const { result } = renderHook(() => useForm(initialFields));

// assert initial state
expect(result.current.fields.text1.changed).toBe(false);
expect(result.current.fields.text1.error).toBeUndefined();
expect(result.current.fields.text1.type).toBe('text');
expect(result.current.fields.text1.value).toBe('');
expect(result.current.fields.text1.initialValue).toBe('');
expect(result.current.fields.text1.onChange).toBeDefined();

expect(result.current.fields.number1.changed).toBe(false);
expect(result.current.fields.number1.error).toBeUndefined();
expect(result.current.fields.number1.type).toBe('number');
expect(result.current.fields.number1.value).toBe(1);
expect(result.current.fields.number1.initialValue).toBe(1);
expect(result.current.fields.number1.onChange).toBeDefined();
});

it(`[hook] useForm lifecycle. Set the initial value. Change the field value. Undo changes. Change the field. Do changes.`, async () => {

const initialFieldValue = '';
const fieldType = 'text';

const initialFields = {
text1: {
type: fieldType,
initialValue: initialFieldValue
}
};

const { result } = renderHook(() => useForm(initialFields));

// assert initial state
expect(result.current.fields.text1.changed).toBe(false);
expect(result.current.fields.text1.error).toBeUndefined();
expect(result.current.fields.text1.type).toBe(fieldType);
expect(result.current.fields.text1.value).toBe(initialFieldValue);
expect(result.current.fields.text1.initialValue).toBe(initialFieldValue);
expect(result.current.fields.text1.onChange).toBeDefined();

// change the input
const changedValue = 't';
act(() => {
result.current.fields.text1.onChange({
target: {
value: changedValue
}
});
});

// assert changed state
expect(result.current.fields.text1.changed).toBe(true);
expect(result.current.fields.text1.error).toBeUndefined();
expect(result.current.fields.text1.type).toBe(fieldType);
expect(result.current.fields.text1.value).toBe(changedValue);
expect(result.current.fields.text1.initialValue).toBe(initialFieldValue);

// undone changes
act(() => {
result.current.undoChanges();
});

// assert undo changes state
expect(result.current.fields.text1.changed).toBe(false);
expect(result.current.fields.text1.error).toBeUndefined();
expect(result.current.fields.text1.type).toBe(fieldType);
expect(result.current.fields.text1.value).toBe(initialFieldValue);
expect(result.current.fields.text1.initialValue).toBe(initialFieldValue);

// change the input
const changedValue2 = 'e';
act(() => {
result.current.fields.text1.onChange({
target: {
value: changedValue2
}
});
});

// assert changed state
expect(result.current.fields.text1.changed).toBe(true);
expect(result.current.fields.text1.error).toBeUndefined();
expect(result.current.fields.text1.type).toBe(fieldType);
expect(result.current.fields.text1.value).toBe(changedValue2);
expect(result.current.fields.text1.initialValue).toBe(initialFieldValue);

// done changes
act(() => {
result.current.doChanges()
});

// assert do changes state
expect(result.current.fields.text1.changed).toBe(false);
expect(result.current.fields.text1.error).toBeUndefined();
expect(result.current.fields.text1.type).toBe(fieldType);
expect(result.current.fields.text1.value).toBe(changedValue2);
expect(result.current.fields.text1.initialValue).toBe(changedValue2);
});
});
25 changes: 25 additions & 0 deletions public/components/common/form/index.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React from 'react';
import { render } from '@testing-library/react';
import { InputForm } from './index';

describe('[component] InputForm', () => {
it.each`
inputType | value | options | rest
${'editor'} | ${'{}'} | ${{editor: {language: 'json'}}} | ${{}}
${'number'} | ${4} | ${undefined} | ${{}}
${'select'} | ${'value1'} | ${{select: {choices: [{label: 'Label1', value: 'value1'}, {label: 'Label2', value: 'value2'}]}}} | ${{}}
${'switch'} | ${true} | ${{switch: {values: {enabled: {label: 'Enabled', value: true}, disabled: {label: 'Disabled', value: false}}}}} | ${{}}
${'text'} | ${'test'} | ${undefined} | ${{isInvalid: false}}
`('Renders correctly to match the snapshot: Input: $inputType', ({ inputType, value, options }) => {
const wrapper = render(
<InputForm
type={inputType}
value={value}
onChange={() => {}}
options={options}
/>
);
expect(wrapper.container).toMatchSnapshot();
});
});

236 changes: 167 additions & 69 deletions server/routes/wazuh-utils/wazuh-utils.test.ts
Original file line number Diff line number Diff line change
@@ -1,76 +1,174 @@
// To launch this file
// To run this file:
// yarn test:jest --testEnvironment node --verbose server/routes/wazuh-utils
import axios from 'axios';
import { PLUGIN_PLATFORM_REQUEST_HEADERS } from '../../../common/constants';

function buildAxiosOptions(method: string, path: string, data: any = {}, headers: any = {}) {
return {
method: method,
headers: { ...PLUGIN_PLATFORM_REQUEST_HEADERS, 'content-type': 'application/json', ...headers },
url: `http://localhost:5601${path}`,
data: data,
};
}

describe.skip('Wazuh API - utils', () => {
describe('Wazuh API - /utils/configuration', () => {
test('[200] Returns the app configuration', () => {
const options = buildAxiosOptions('get', '/utils/configuration');
return axios(options)
.then((response) => {
expect(response.status).toBe(200);
expect(typeof response.data.data).toBe('object');
expect(typeof response.data.data.hosts).toBe('object');
})
.catch((error) => {
throw error;
});
});
import { Router } from '../../../../../src/core/server/http/router/router';
import { HttpServer } from '../../../../../src/core/server/http/http_server';
import { loggingSystemMock } from '../../../../../src/core/server/logging/logging_system.mock';
import { ByteSizeValue } from '@kbn/config-schema';
import supertest from 'supertest';
import { WazuhUtilsRoutes } from './wazuh-utils';
import { WazuhUtilsCtrl } from '../../controllers/wazuh-utils/wazuh-utils';
import md5 from 'md5';
import { createDataDirectoryIfNotExists, createDirectoryIfNotExists } from '../../lib/filesystem';
import { WAZUH_DATA_ABSOLUTE_PATH, WAZUH_DATA_CONFIG_APP_PATH, WAZUH_DATA_CONFIG_DIRECTORY_PATH, WAZUH_DATA_DOWNLOADS_DIRECTORY_PATH, WAZUH_DATA_DOWNLOADS_REPORTS_DIRECTORY_PATH, WAZUH_DATA_LOGS_DIRECTORY_PATH, WAZUH_DATA_LOGS_RAW_PATH } from '../../../common/constants';
import { execSync } from 'child_process';
import path from 'path';
import fs from 'fs';

const loggingService = loggingSystemMock.create();
const logger = loggingService.get();
const context = {
wazuh: {
}
};

const enhanceWithContext = (fn: (...args: any[]) => any) => fn.bind(null, context);
let server, innerServer;

beforeAll(async () => {
// Create <PLUGIN_PLATFORM_PATH>/data/wazuh directory.
createDataDirectoryIfNotExists();
// Create <PLUGIN_PLATFORM_PATH>/data/wazuh/config directory.
createDirectoryIfNotExists(WAZUH_DATA_CONFIG_DIRECTORY_PATH);

// Create <PLUGIN_PLATFORM_PATH>/data/wazuh/logs directory.
createDirectoryIfNotExists(WAZUH_DATA_LOGS_DIRECTORY_PATH);

// Create server
const config = {
name: 'plugin_platform',
host: '127.0.0.1',
maxPayload: new ByteSizeValue(1024),
port: 10002,
ssl: { enabled: false },
compression: { enabled: true },
requestId: {
allowFromAnyIp: true,
ipAllowlist: [],
},
} as any;
server = new HttpServer(loggingService, 'tests');
const router = new Router('', logger, enhanceWithContext);
const { registerRouter, server: innerServerTest, ...rest } = await server.setup(config);
innerServer = innerServerTest;

const spyRouteDecoratorProtectedAdministratorRoleValidToken = jest.spyOn(WazuhUtilsCtrl.prototype as any, 'routeDecoratorProtectedAdministratorRoleValidToken')
.mockImplementation((handler) => async (...args) => handler(...args));

// Register routes
WazuhUtilsRoutes(router);

// Register router
registerRouter(router);

// start server
await server.start();
});

afterAll(async () => {
// Stop server
await server.stop();

// Clear all mocks
jest.clearAllMocks();

// Remove <PLUGIN_PLATFORM_PATH>/data/wazuh directory.
execSync(`rm -rf ${WAZUH_DATA_ABSOLUTE_PATH}`);
});

describe('[endpoint] GET /utils/configuration', () => {
beforeAll(() => {
// Create the configuration file with custom content
const fileContent = `---
pattern: test-alerts-*
hosts:
- default:
url: https://localhost
port: 55000
username: wazuh-wui
password: wazuh-wui
run_as: false
`;

fs.writeFileSync(WAZUH_DATA_CONFIG_APP_PATH, fileContent, 'utf8');
});

afterAll(() => {
// Remove the configuration file
fs.unlinkSync(WAZUH_DATA_CONFIG_APP_PATH);
});

it(`Get plugin configuration GET /utils/configuration - 200`, async () => {
const response = await supertest(innerServer.listener)
.get('/utils/configuration')
.expect(200);
expect(response.body.data).toBeDefined();
expect(response.body.data.pattern).toBeDefined();
expect(response.body.data.hosts).toBeDefined();
});
});


describe('[endpoint] PUT /utils/configuration', () => {
beforeAll(() => {
// Create the configuration file with custom content
const fileContent = `---
pattern: test-alerts-*
hosts:
- default:
url: https://localhost
port: 55000
username: wazuh-wui
password: wazuh-wui
run_as: false
`;

fs.writeFileSync(WAZUH_DATA_CONFIG_APP_PATH, fileContent, 'utf8');
});

describe('Wazuh API - /utils/configuration', () => {
let userToken = null;
beforeAll(() => {
const optionsAuthenticate = buildAxiosOptions('post', '/api/login', {
idHost: 'default',
});
return axios(optionsAuthenticate).then((response) => {
userToken = response.data.token;
return response.data.token;
});
});
test('[200] Updates the app configuration', () => {
const options = buildAxiosOptions(
'put',
'/utils/configuration',
{
key: 'logs.level',
value: 'debug',
},
{
cookie: `wz-token=${userToken};wz-api=default;`,
}
);
return axios(options)
.then((response) => {
expect(response.status).toBe(200);
})
.catch((error) => {
throw error;
});
});
afterAll(() => {
// Remove the configuration file
fs.unlinkSync(WAZUH_DATA_CONFIG_APP_PATH);
});

describe('Wazuh API - /utils/logs', () => {
test('[200] Get the app logs', () => {
const options = buildAxiosOptions('get', '/utils/logs');
return axios(options)
.then((response) => {
expect(response.status).toBe(200);
})
.catch((error) => {
throw error;
});
});
it.each`
settings | responseStatusCode
${{pattern: 'test-alerts-groupA-*'}} | ${200}
${{pattern: 'test-alerts-groupA-*','logs.level': 'debug'}} | ${200}
`(`Update the plugin configuration: $settings. PUT /utils/configuration - $responseStatusCode`, async ({responseStatusCode, settings}) => {
const response = await supertest(innerServer.listener)
.put('/utils/configuration')
.send(settings)
.expect(responseStatusCode);

expect(response.body.data.updatedConfiguration).toEqual(settings);
expect(response.body.data.requireHealthCheck).toBeDefined();
expect(response.body.data.requireReload).toBeDefined();
expect(response.body.data.requireRestart).toBeDefined();
});
});

describe('[endpoint] GET /utils/logs', () => {
beforeAll(() => {
// Create the configuration file with custom content
const fileContent = `---
{"date":"2022-09-20T08:36:16.688Z","level":"info","location":"initialize","message":"Kibana index: .kibana"}
{"date":"2022-09-20T08:36:16.689Z","level":"info","location":"initialize","message":"App revision: 4400"}
`;
fs.writeFileSync(WAZUH_DATA_LOGS_RAW_PATH, fileContent, 'utf8');
});

afterAll(() => {
// Remove the configuration file
fs.unlinkSync(WAZUH_DATA_LOGS_RAW_PATH);
});

it(`Get plugin logs. GET /utils/logs`, async () => {
const response = await supertest(innerServer.listener)
.get('/utils/logs')
.expect(200);

expect(response.body.lastLogs).toBeDefined();
});
});