Skip to content

Commit

Permalink
Add centralized request service (#4758)
Browse files Browse the repository at this point in the history
* 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)
  • Loading branch information
Tostti committed Nov 8, 2022
1 parent 2b0318c commit 518894c
Show file tree
Hide file tree
Showing 8 changed files with 203 additions and 44 deletions.
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ All notable changes to the Wazuh app project will be documented in this file.
- 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)
- 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)
- Add a new plugin setting to enable or disable the customization [#4507](https://github.com/wazuh/wazuh-kibana-app/pull/4507)
- Added a centralized service to handle the requestrs [#4758](https://github.com/wazuh/wazuh-kibana-app/pull/4758)

### Changed

Expand Down Expand Up @@ -41,9 +42,11 @@ commands of Start the agent in the deploy new agent section [#4458](https://gith
- Fixed nested field rendering in security alerts table details [#4428](https://github.com/wazuh/wazuh-kibana-app/pull/4428)
- Fixed a bug where the Wazuh logo was used instead of the custom one [#4539](https://github.com/wazuh/wazuh-kibana-app/pull/4539)
- Fixed rendering problems of the `Agent Overview` section in low resolutions [#4516](https://github.com/wazuh/wazuh-kibana-app/pull/4516)
- Fixed issue when logging out from Wazuh when SAML is enabled [#4595](https://github.com/wazuh/wazuh-kibana-app/issues/4595)
- Fixed issue when logging out from Wazuh when SAML is enabled [#4664](https://github.com/wazuh/wazuh-kibana-app/pull/4664)
- 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)
- Fixed pagination to SCA table [#4653](https://github.com/wazuh/wazuh-kibana-app/issues/4653)
- Fixed a bug that caused the flyouts to close when clicking inside them [#4638](https://github.com/wazuh/wazuh-kibana-app/pull/4638)
- 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)

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

Expand Down
2 changes: 2 additions & 0 deletions public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { getThemeAssetURL, getAssetURL } from './utils/assets';
import { WzRequest } from './react-services/wz-request';
import store from './redux/store';
import { updateAppConfig } from './redux/actions/appConfigActions';
import { initializeInterceptor } from './services/request-handler';

const SIDEBAR_LOGO = 'customization.logo.sidebar';
const innerAngularName = 'app/wazuh';
Expand Down Expand Up @@ -164,6 +165,7 @@ export class WazuhPlugin implements Plugin<WazuhSetup, WazuhStart, WazuhSetupPlu
setSavedObjects(core.savedObjects);
setOverlays(core.overlays);
setErrorOrchestrator(ErrorOrchestratorService);
initializeInterceptor();
return {};
}
}
26 changes: 11 additions & 15 deletions public/react-services/generic-request.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@
* Find more information about this on the LICENSE file.
*/

import axios from 'axios';
import { AppState } from './app-state';
import { WazuhConfig } from './wazuh-config';
import { ApiCheck } from './wz-api-check';
import { WzMisc } from '../factories/misc';
import { OdfeUtils } from '../utils';
import { getHttp, getDataPlugin } from '../kibana-services';
import { PLUGIN_PLATFORM_REQUEST_HEADERS } from '../../common/constants';
import { request } from '../services/request-handler';

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

try{
try {
requestHeaders.pattern = (await getDataPlugin().indexPatterns.get(AppState.getCurrentPattern())).title;
}catch(error){};
} catch (error) { };

try {
requestHeaders.id = JSON.parse(AppState.getCurrentAPI()).id;
Expand All @@ -48,40 +48,36 @@ export class GenericRequest {
if (method === 'GET') {
options = {
method: method,
headers: requestHeaders,
url: tmpUrl,
path: path,
timeout: timeout || 20000
};
}
if (method === 'PUT') {
options = {
method: method,
headers: requestHeaders,
data: payload,
url: tmpUrl,
path: path,
timeout: timeout || 20000
};
}
if (method === 'POST') {
options = {
method: method,
headers: requestHeaders,
data: payload,
url: tmpUrl,
path: path,
timeout: timeout || 20000
};
}
if (method === 'DELETE') {
options = {
method: method,
headers: requestHeaders,
data: payload,
url: tmpUrl,
path: path,
timeout: timeout || 20000
};
}

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

if (!window.location.hash.includes('#/settings') &&
!window.location.hash.includes('#/health-check') &&
!window.location.hash.includes('#/blank-screen')) {
if (!window.location.hash.includes('#/settings') &&
!window.location.hash.includes('#/health-check') &&
!window.location.hash.includes('#/blank-screen')) {
window.location.href = getHttp().basePath.prepend('/app/wazuh#/health-check');
}
}
Expand Down
17 changes: 7 additions & 10 deletions public/react-services/wz-api-check.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,10 @@
* Find more information about this on the LICENSE file.
*/
import { WazuhConfig } from './wazuh-config';
import axios from 'axios';
import { AppState } from './app-state';
import { WzMisc } from '../factories/misc';
import { getHttp } from '../kibana-services';
import { PLUGIN_PLATFORM_REQUEST_HEADERS } from '../../common/constants';
import { request } from '../services/request-handler';

export class ApiCheck {
static async checkStored(data, idChanged = false) {
Expand All @@ -30,8 +29,7 @@ export class ApiCheck {
const url = getHttp().basePath.prepend('/api/check-stored-api');
const options = {
method: 'POST',
headers: { ...PLUGIN_PLATFORM_REQUEST_HEADERS, 'content-type': 'application/json' },
url: url,
path: '/api/check-stored-api',
data: payload,
timeout: timeout || 20000
};
Expand All @@ -40,7 +38,7 @@ export class ApiCheck {
AppState.setPatternSelector(configuration['ip.selector']);
}

const response = await axios(options);
const response = await request(options);

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

const options = {
method: 'POST',
headers: { ...PLUGIN_PLATFORM_REQUEST_HEADERS, 'content-type': 'application/json' },
url: url,
data: {...apiEntry, forceRefresh},
path: '/api/check-api',
data: { ...apiEntry, forceRefresh },
timeout: timeout || 20000
};

const response = await axios(options);
const response = await request(options);

if (response.error) {
return Promise.reject(response);
Expand Down
32 changes: 14 additions & 18 deletions public/react-services/wz-request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
*
* Find more information about this on the LICENSE file.
*/
import axios from 'axios';
import { AppState } from './app-state';
import { ApiCheck } from './wz-api-check';
import { WzAuthentication } from './wz-authentication';
Expand All @@ -18,7 +17,7 @@ import { WazuhConfig } from './wazuh-config';
import { OdfeUtils } from '../utils';
import IApiResponse from './interfaces/api-response.interface';
import { getHttp } from '../kibana-services';
import { PLUGIN_PLATFORM_REQUEST_HEADERS } from '../../common/constants';
import { request } from '../services/request-handler';
export class WzRequest {
static wazuhConfig: any;

Expand All @@ -32,15 +31,13 @@ export class WzRequest {
method,
path,
payload: any = null,
extraOptions: { shouldRetry?: boolean, checkCurrentApiIsUp?: boolean, overwriteHeaders?: any } = {
shouldRetry: true,
checkCurrentApiIsUp: true,
overwriteHeaders: {}
extraOptions: { shouldRetry?: boolean, checkCurrentApiIsUp?: boolean } = {
shouldRetry: true,
checkCurrentApiIsUp: true
}
) {
const shouldRetry = typeof extraOptions.shouldRetry === 'boolean' ? extraOptions.shouldRetry : true;
const shouldRetry = typeof extraOptions.shouldRetry === 'boolean' ? extraOptions.shouldRetry : true;
const checkCurrentApiIsUp = typeof extraOptions.checkCurrentApiIsUp === 'boolean' ? extraOptions.checkCurrentApiIsUp : true;
const overwriteHeaders = typeof extraOptions.overwriteHeaders === 'object' ? extraOptions.overwriteHeaders : {};
try {
if (!method || !path) {
throw new Error('Missing parameters');
Expand All @@ -52,13 +49,12 @@ export class WzRequest {
const url = getHttp().basePath.prepend(path);
const options = {
method: method,
headers: { ...PLUGIN_PLATFORM_REQUEST_HEADERS, 'content-type': 'application/json', ...overwriteHeaders },
url: url,
path: path,
data: payload,
timeout: timeout,
};

const data = await axios(options);
const data = await request(options);

if (data['error']) {
throw new Error(data['error']);
Expand All @@ -68,7 +64,7 @@ export class WzRequest {
} catch (error) {
OdfeUtils.checkOdfeSessionExpired(error);
//if the requests fails, we need to check if the API is down
if(checkCurrentApiIsUp){
if (checkCurrentApiIsUp) {
const currentApi = JSON.parse(AppState.getCurrentAPI() || '{}');
if (currentApi && currentApi.id) {
try {
Expand Down Expand Up @@ -102,7 +98,7 @@ export class WzRequest {
}
return errorMessage
? Promise.reject(this.returnErrorInstance(error, errorMessage))
: Promise.reject(this.returnErrorInstance(error,'Server did not respond'));
: Promise.reject(this.returnErrorInstance(error, 'Server did not respond'));
}
}

Expand All @@ -113,9 +109,9 @@ export class WzRequest {
* @param {Object} body Request body
*/
static async apiReq(
method,
path,
body,
method,
path,
body,
options: { checkCurrentApiIsUp?: boolean } = { checkCurrentApiIsUp: true }
): Promise<IApiResponse<any>> {
try {
Expand Down Expand Up @@ -172,8 +168,8 @@ export class WzRequest {
* @param message
* @returns error
*/
static returnErrorInstance(error, message){
if(!error || typeof error === 'string'){
static returnErrorInstance(error, message) {
if (!error || typeof error === 'string') {
return new Error(message || error);
}
error.message = message
Expand Down
89 changes: 89 additions & 0 deletions public/services/request-handler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { getCore } from '../kibana-services';

let allow = true;
let aborts = [];
let currentid = 0;

const removeController = (id) => {
const index = aborts.findIndex(object => {
return object.id === id;
});
if (!id) {
return;
}
aborts.splice(index);
return;
}

export const disableRequests = () => {
allow = false;
aborts.forEach(item => {
item.controller.abort();
})
return;
}

export const initializeInterceptor = () => {
const core = getCore();
core.http.intercept({
responseError: (httpErrorResponse, controller) => {
if (
httpErrorResponse.response?.status === 401
) {
disableRequests();
setTimeout(() => window.location.reload(), 1000);
}
},
});
}

export const request = async (info = '') => {
if (!allow) {
return Promise.reject('Requests are disabled');
}


if (!info.method | !info.path) {
return Promise.reject("Missing parameters")
}

let { method, path, headers, data, timeout } = info;
const core = getCore();
const url = path.split('?')[0]

const query = Object.fromEntries([... new URLSearchParams(path.split('?')[1])])
const abort = new AbortController();
let options = {
method: method,
headers: headers,
query: query,
signal: abort.signal,
id: currentid
}
currentid++;

if (method !== 'GET') {
options = { ...options, body: JSON.stringify(data) }
}

if (allow) {
try {
aborts.push({ id: options.id, controller: abort })
if (timeout && timeout !== 0) {
const id = setTimeout(() => abort.abort(), timeout);
const requestData = await core.http.fetch(url, options);
clearTimeout(id);
removeController(options.id);
return Promise.resolve({ data: requestData, timeout: timeout });
}
else {
const requestData = await core.http.fetch(url, options);
removeController(options.id);
return Promise.resolve({ data: requestData });
}
}
catch (e) {
return Promise.reject(e);
}
}
}
Loading

0 comments on commit 518894c

Please sign in to comment.