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

Add centralized request service #4758

Merged
merged 23 commits into from
Nov 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 3 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 @@ -48,10 +49,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 - OpenSearch Dashboards 1.2.0 - 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