Skip to content

Commit 9c26205

Browse files
committed
Add centralized request service (#4758)
* create service * Improve service functionality * add timeout handling * Improve code quality * Fix issue that caused infinite loop * separate interceptor initialization * add interceptor to plugin start * fix double reload and add abort * Add function to disable requests externally * Add auth validation on server to disable requests * Update changelog * Add missing parameters verification * Add missing parameter validation * Add reload to interceptor * Change structure of code * Implement new service * Fix bug * Fix errors * Create unit tests * Fix bad reference to previous PR in changelog * Add missing previous change in changelog (cherry picked from commit 34c3fae)
1 parent cfaf613 commit 9c26205

File tree

8 files changed

+203
-44
lines changed

8 files changed

+203
-44
lines changed

CHANGELOG.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ All notable changes to the Wazuh app project will be documented in this file.
1414
- Added validation to the plugin settings in the form of `Settings/Configuration` and the endpoint to update the plugin configuration [#4503](https://github.com/wazuh/wazuh-kibana-app/pull/4503)[#4785](https://github.com/wazuh/wazuh-kibana-app/pull/4785)
1515
- Added new plugin settings to customize the header and footer on the PDF reports [#4505](https://github.com/wazuh/wazuh-kibana-app/pull/4505)[#4798](https://github.com/wazuh/wazuh-kibana-app/pull/4798)[#4805](https://github.com/wazuh/wazuh-kibana-app/pull/4805)
1616
- Add a new plugin setting to enable or disable the customization [#4507](https://github.com/wazuh/wazuh-kibana-app/pull/4507)
17+
- Added a centralized service to handle the requestrs [#4758](https://github.com/wazuh/wazuh-kibana-app/pull/4758)
1718

1819
### Changed
1920

@@ -41,11 +42,13 @@ commands of Start the agent in the deploy new agent section [#4458](https://gith
4142
- Improved Agents Overview performance [#4363](https://github.com/wazuh/wazuh-kibana-app/pull/4363)
4243
- Fixed a bug where the Wazuh logo was used instead of the custom one [#4539](https://github.com/wazuh/wazuh-kibana-app/pull/4539)
4344
- Fixed rendering problems of the `Agent Overview` section in low resolutions [#4516](https://github.com/wazuh/wazuh-kibana-app/pull/4516)
44-
- Fixed issue when logging out from Wazuh when SAML is enabled [#4595](https://github.com/wazuh/wazuh-kibana-app/issues/4595)
45+
- Fixed issue when logging out from Wazuh when SAML is enabled [#4664](https://github.com/wazuh/wazuh-kibana-app/pull/4664)
4546
- Fixed server errors with code 500 when the Wazuh API is not reachable / up. [#4710](https://github.com/wazuh/wazuh-kibana-app/pull/4710) [#4728](https://github.com/wazuh/wazuh-kibana-app/pull/4728)
4647

4748

4849
- Fixed pagination to SCA table [#4653](https://github.com/wazuh/wazuh-kibana-app/issues/4653)
50+
- Fixed a bug that caused the flyouts to close when clicking inside them [#4638](https://github.com/wazuh/wazuh-kibana-app/pull/4638)
51+
- Fixed a bug that caused the main Office 365 dashboard to display an incorrect Max rule level [#4508](https://github.com/wazuh/wazuh-kibana-app/pull/4508)
4952

5053
## Wazuh v4.3.9 - Kibana 7.10.2, 7.16.x, 7.17.x - Revision 4310
5154

public/plugin.ts

+2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import { getThemeAssetURL, getAssetURL } from './utils/assets';
3131
import { WzRequest } from './react-services/wz-request';
3232
import store from './redux/store';
3333
import { updateAppConfig } from './redux/actions/appConfigActions';
34+
import { initializeInterceptor } from './services/request-handler';
3435

3536
const SIDEBAR_LOGO = 'customization.logo.sidebar';
3637
const innerAngularName = 'app/wazuh';
@@ -164,6 +165,7 @@ export class WazuhPlugin implements Plugin<WazuhSetup, WazuhStart, WazuhSetupPlu
164165
setSavedObjects(core.savedObjects);
165166
setOverlays(core.overlays);
166167
setErrorOrchestrator(ErrorOrchestratorService);
168+
initializeInterceptor();
167169
return {};
168170
}
169171
}

public/react-services/generic-request.js

+11-15
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,14 @@
1010
* Find more information about this on the LICENSE file.
1111
*/
1212

13-
import axios from 'axios';
1413
import { AppState } from './app-state';
1514
import { WazuhConfig } from './wazuh-config';
1615
import { ApiCheck } from './wz-api-check';
1716
import { WzMisc } from '../factories/misc';
1817
import { OdfeUtils } from '../utils';
1918
import { getHttp, getDataPlugin } from '../kibana-services';
2019
import { PLUGIN_PLATFORM_REQUEST_HEADERS } from '../../common/constants';
20+
import { request } from '../services/request-handler';
2121

2222
export class GenericRequest {
2323
static async request(method, path, payload = null, returnError = false) {
@@ -33,9 +33,9 @@ export class GenericRequest {
3333
};
3434
const tmpUrl = getHttp().basePath.prepend(path);
3535

36-
try{
36+
try {
3737
requestHeaders.pattern = (await getDataPlugin().indexPatterns.get(AppState.getCurrentPattern())).title;
38-
}catch(error){};
38+
} catch (error) { };
3939

4040
try {
4141
requestHeaders.id = JSON.parse(AppState.getCurrentAPI()).id;
@@ -48,40 +48,36 @@ export class GenericRequest {
4848
if (method === 'GET') {
4949
options = {
5050
method: method,
51-
headers: requestHeaders,
52-
url: tmpUrl,
51+
path: path,
5352
timeout: timeout || 20000
5453
};
5554
}
5655
if (method === 'PUT') {
5756
options = {
5857
method: method,
59-
headers: requestHeaders,
6058
data: payload,
61-
url: tmpUrl,
59+
path: path,
6260
timeout: timeout || 20000
6361
};
6462
}
6563
if (method === 'POST') {
6664
options = {
6765
method: method,
68-
headers: requestHeaders,
6966
data: payload,
70-
url: tmpUrl,
67+
path: path,
7168
timeout: timeout || 20000
7269
};
7370
}
7471
if (method === 'DELETE') {
7572
options = {
7673
method: method,
77-
headers: requestHeaders,
7874
data: payload,
79-
url: tmpUrl,
75+
path: path,
8076
timeout: timeout || 20000
8177
};
8278
}
8379

84-
Object.assign(data, await axios(options));
80+
Object.assign(data, await request(options));
8581
if (!data) {
8682
throw new Error(
8783
`Error doing a request to ${tmpUrl}, method: ${method}.`
@@ -100,9 +96,9 @@ export class GenericRequest {
10096
const wzMisc = new WzMisc();
10197
wzMisc.setApiIsDown(true);
10298

103-
if (!window.location.hash.includes('#/settings') &&
104-
!window.location.hash.includes('#/health-check') &&
105-
!window.location.hash.includes('#/blank-screen')) {
99+
if (!window.location.hash.includes('#/settings') &&
100+
!window.location.hash.includes('#/health-check') &&
101+
!window.location.hash.includes('#/blank-screen')) {
106102
window.location.href = getHttp().basePath.prepend('/app/wazuh#/health-check');
107103
}
108104
}

public/react-services/wz-api-check.js

+7-10
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,10 @@
1010
* Find more information about this on the LICENSE file.
1111
*/
1212
import { WazuhConfig } from './wazuh-config';
13-
import axios from 'axios';
1413
import { AppState } from './app-state';
1514
import { WzMisc } from '../factories/misc';
1615
import { getHttp } from '../kibana-services';
17-
import { PLUGIN_PLATFORM_REQUEST_HEADERS } from '../../common/constants';
16+
import { request } from '../services/request-handler';
1817

1918
export class ApiCheck {
2019
static async checkStored(data, idChanged = false) {
@@ -30,8 +29,7 @@ export class ApiCheck {
3029
const url = getHttp().basePath.prepend('/api/check-stored-api');
3130
const options = {
3231
method: 'POST',
33-
headers: { ...PLUGIN_PLATFORM_REQUEST_HEADERS, 'content-type': 'application/json' },
34-
url: url,
32+
path: '/api/check-stored-api',
3533
data: payload,
3634
timeout: timeout || 20000
3735
};
@@ -40,7 +38,7 @@ export class ApiCheck {
4038
AppState.setPatternSelector(configuration['ip.selector']);
4139
}
4240

43-
const response = await axios(options);
41+
const response = await request(options);
4442

4543
if (response.error) {
4644
return Promise.reject(response);
@@ -65,21 +63,20 @@ export class ApiCheck {
6563
* Check the status of an API entry
6664
* @param {String} apiObject
6765
*/
68-
static async checkApi(apiEntry, forceRefresh=false) {
66+
static async checkApi(apiEntry, forceRefresh = false) {
6967
try {
7068
const wazuhConfig = new WazuhConfig();
7169
const { timeout } = wazuhConfig.getConfig();
7270
const url = getHttp().basePath.prepend('/api/check-api');
7371

7472
const options = {
7573
method: 'POST',
76-
headers: { ...PLUGIN_PLATFORM_REQUEST_HEADERS, 'content-type': 'application/json' },
77-
url: url,
78-
data: {...apiEntry, forceRefresh},
74+
path: '/api/check-api',
75+
data: { ...apiEntry, forceRefresh },
7976
timeout: timeout || 20000
8077
};
8178

82-
const response = await axios(options);
79+
const response = await request(options);
8380

8481
if (response.error) {
8582
return Promise.reject(response);

public/react-services/wz-request.ts

+14-18
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
*
1010
* Find more information about this on the LICENSE file.
1111
*/
12-
import axios from 'axios';
1312
import { AppState } from './app-state';
1413
import { ApiCheck } from './wz-api-check';
1514
import { WzAuthentication } from './wz-authentication';
@@ -18,7 +17,7 @@ import { WazuhConfig } from './wazuh-config';
1817
import { OdfeUtils } from '../utils';
1918
import IApiResponse from './interfaces/api-response.interface';
2019
import { getHttp } from '../kibana-services';
21-
import { PLUGIN_PLATFORM_REQUEST_HEADERS } from '../../common/constants';
20+
import { request } from '../services/request-handler';
2221
export class WzRequest {
2322
static wazuhConfig: any;
2423

@@ -32,15 +31,13 @@ export class WzRequest {
3231
method,
3332
path,
3433
payload: any = null,
35-
extraOptions: { shouldRetry?: boolean, checkCurrentApiIsUp?: boolean, overwriteHeaders?: any } = {
36-
shouldRetry: true,
37-
checkCurrentApiIsUp: true,
38-
overwriteHeaders: {}
34+
extraOptions: { shouldRetry?: boolean, checkCurrentApiIsUp?: boolean } = {
35+
shouldRetry: true,
36+
checkCurrentApiIsUp: true
3937
}
4038
) {
41-
const shouldRetry = typeof extraOptions.shouldRetry === 'boolean' ? extraOptions.shouldRetry : true;
39+
const shouldRetry = typeof extraOptions.shouldRetry === 'boolean' ? extraOptions.shouldRetry : true;
4240
const checkCurrentApiIsUp = typeof extraOptions.checkCurrentApiIsUp === 'boolean' ? extraOptions.checkCurrentApiIsUp : true;
43-
const overwriteHeaders = typeof extraOptions.overwriteHeaders === 'object' ? extraOptions.overwriteHeaders : {};
4441
try {
4542
if (!method || !path) {
4643
throw new Error('Missing parameters');
@@ -52,13 +49,12 @@ export class WzRequest {
5249
const url = getHttp().basePath.prepend(path);
5350
const options = {
5451
method: method,
55-
headers: { ...PLUGIN_PLATFORM_REQUEST_HEADERS, 'content-type': 'application/json', ...overwriteHeaders },
56-
url: url,
52+
path: path,
5753
data: payload,
5854
timeout: timeout,
5955
};
6056

61-
const data = await axios(options);
57+
const data = await request(options);
6258

6359
if (data['error']) {
6460
throw new Error(data['error']);
@@ -68,7 +64,7 @@ export class WzRequest {
6864
} catch (error) {
6965
OdfeUtils.checkOdfeSessionExpired(error);
7066
//if the requests fails, we need to check if the API is down
71-
if(checkCurrentApiIsUp){
67+
if (checkCurrentApiIsUp) {
7268
const currentApi = JSON.parse(AppState.getCurrentAPI() || '{}');
7369
if (currentApi && currentApi.id) {
7470
try {
@@ -102,7 +98,7 @@ export class WzRequest {
10298
}
10399
return errorMessage
104100
? Promise.reject(this.returnErrorInstance(error, errorMessage))
105-
: Promise.reject(this.returnErrorInstance(error,'Server did not respond'));
101+
: Promise.reject(this.returnErrorInstance(error, 'Server did not respond'));
106102
}
107103
}
108104

@@ -113,9 +109,9 @@ export class WzRequest {
113109
* @param {Object} body Request body
114110
*/
115111
static async apiReq(
116-
method,
117-
path,
118-
body,
112+
method,
113+
path,
114+
body,
119115
options: { checkCurrentApiIsUp?: boolean } = { checkCurrentApiIsUp: true }
120116
): Promise<IApiResponse<any>> {
121117
try {
@@ -172,8 +168,8 @@ export class WzRequest {
172168
* @param message
173169
* @returns error
174170
*/
175-
static returnErrorInstance(error, message){
176-
if(!error || typeof error === 'string'){
171+
static returnErrorInstance(error, message) {
172+
if (!error || typeof error === 'string') {
177173
return new Error(message || error);
178174
}
179175
error.message = message

public/services/request-handler.js

+89
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import { getCore } from '../kibana-services';
2+
3+
let allow = true;
4+
let aborts = [];
5+
let currentid = 0;
6+
7+
const removeController = (id) => {
8+
const index = aborts.findIndex(object => {
9+
return object.id === id;
10+
});
11+
if (!id) {
12+
return;
13+
}
14+
aborts.splice(index);
15+
return;
16+
}
17+
18+
export const disableRequests = () => {
19+
allow = false;
20+
aborts.forEach(item => {
21+
item.controller.abort();
22+
})
23+
return;
24+
}
25+
26+
export const initializeInterceptor = () => {
27+
const core = getCore();
28+
core.http.intercept({
29+
responseError: (httpErrorResponse, controller) => {
30+
if (
31+
httpErrorResponse.response?.status === 401
32+
) {
33+
disableRequests();
34+
setTimeout(() => window.location.reload(), 1000);
35+
}
36+
},
37+
});
38+
}
39+
40+
export const request = async (info = '') => {
41+
if (!allow) {
42+
return Promise.reject('Requests are disabled');
43+
}
44+
45+
46+
if (!info.method | !info.path) {
47+
return Promise.reject("Missing parameters")
48+
}
49+
50+
let { method, path, headers, data, timeout } = info;
51+
const core = getCore();
52+
const url = path.split('?')[0]
53+
54+
const query = Object.fromEntries([... new URLSearchParams(path.split('?')[1])])
55+
const abort = new AbortController();
56+
let options = {
57+
method: method,
58+
headers: headers,
59+
query: query,
60+
signal: abort.signal,
61+
id: currentid
62+
}
63+
currentid++;
64+
65+
if (method !== 'GET') {
66+
options = { ...options, body: JSON.stringify(data) }
67+
}
68+
69+
if (allow) {
70+
try {
71+
aborts.push({ id: options.id, controller: abort })
72+
if (timeout && timeout !== 0) {
73+
const id = setTimeout(() => abort.abort(), timeout);
74+
const requestData = await core.http.fetch(url, options);
75+
clearTimeout(id);
76+
removeController(options.id);
77+
return Promise.resolve({ data: requestData, timeout: timeout });
78+
}
79+
else {
80+
const requestData = await core.http.fetch(url, options);
81+
removeController(options.id);
82+
return Promise.resolve({ data: requestData });
83+
}
84+
}
85+
catch (e) {
86+
return Promise.reject(e);
87+
}
88+
}
89+
}

0 commit comments

Comments
 (0)