2018-04-22 19:10:29 +00:00
|
|
|
/*
|
|
|
|
* Wazuh app - Class for Wazuh-API functions
|
|
|
|
* Copyright (C) 2018 Wazuh, Inc.
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* Find more information about this on the LICENSE file.
|
|
|
|
*/
|
2017-10-27 07:22:10 +00:00
|
|
|
|
2017-10-27 07:34:38 +00:00
|
|
|
// Require some libraries
|
2018-06-28 14:12:16 +00:00
|
|
|
import needle from 'needle';
|
|
|
|
import pciRequirementsFile from '../integration-files/pci-requirements';
|
2018-06-28 07:27:27 +00:00
|
|
|
import gdprRequirementsFile from '../integration-files/gdpr-requirements';
|
2018-06-28 14:12:16 +00:00
|
|
|
import ElasticWrapper from '../lib/elastic-wrapper';
|
|
|
|
import getPath from '../../util/get-path';
|
|
|
|
import packageInfo from '../../package.json';
|
|
|
|
import monitoring from '../monitoring';
|
|
|
|
import ErrorResponse from './error-response';
|
|
|
|
import { Parser } from 'json2csv';
|
|
|
|
import getConfiguration from '../lib/get-configuration';
|
|
|
|
import { totalmem } from 'os';
|
2018-07-17 07:41:53 +00:00
|
|
|
import simpleTail from 'simple-tail';
|
|
|
|
import path from 'path';
|
2017-10-20 18:53:56 +00:00
|
|
|
|
2018-07-16 09:35:46 +00:00
|
|
|
import CsvKeys from '../../util/csv-key-equivalence';
|
|
|
|
|
2018-04-22 13:41:10 +00:00
|
|
|
export default class WazuhApi {
|
2018-04-11 07:58:23 +00:00
|
|
|
constructor(server){
|
|
|
|
this.wzWrapper = new ElasticWrapper(server);
|
2018-04-22 13:41:10 +00:00
|
|
|
this.fetchAgentsExternal = monitoring(server,{disableCron:true})
|
2017-10-27 07:34:38 +00:00
|
|
|
}
|
2017-01-26 15:35:07 +00:00
|
|
|
|
2018-04-11 07:58:23 +00:00
|
|
|
async checkStoredAPI (req, reply) {
|
2018-03-22 16:10:33 +00:00
|
|
|
try{
|
2018-06-28 07:42:55 +00:00
|
|
|
|
2018-03-22 16:10:33 +00:00
|
|
|
// Get config from elasticsearch
|
2018-08-16 15:49:39 +00:00
|
|
|
const wapi_config = await this.wzWrapper.getWazuhConfigurationById(req.payload)
|
2017-10-20 18:53:56 +00:00
|
|
|
if (wapi_config.error_code > 1) {
|
2018-05-04 09:07:57 +00:00
|
|
|
throw new Error(`Could not find Wazuh API entry on Elasticsearch.`)
|
2017-10-20 18:53:56 +00:00
|
|
|
} else if (wapi_config.error_code > 0) {
|
2018-05-04 09:07:57 +00:00
|
|
|
throw new Error('Valid credentials not found in Elasticsearch. It seems the credentials were not saved.')
|
2017-10-20 18:53:56 +00:00
|
|
|
}
|
2017-10-27 07:34:38 +00:00
|
|
|
|
2018-03-22 16:10:33 +00:00
|
|
|
let response = await needle('get', `${wapi_config.url}:${wapi_config.port}/version`, {}, {
|
2018-06-11 11:32:01 +00:00
|
|
|
headers: {
|
|
|
|
'wazuh-app-version': packageInfo.version
|
|
|
|
},
|
2018-03-22 16:10:33 +00:00
|
|
|
username : wapi_config.user,
|
|
|
|
password : wapi_config.password,
|
2017-10-27 07:34:38 +00:00
|
|
|
rejectUnauthorized: !wapi_config.insecure
|
|
|
|
})
|
2018-03-22 16:10:33 +00:00
|
|
|
|
|
|
|
if (parseInt(response.body.error) === 0 && response.body.data) {
|
|
|
|
// Checking the cluster status
|
2018-04-22 19:10:29 +00:00
|
|
|
response = await needle('get', `${wapi_config.url}:${wapi_config.port}/cluster/status`, {}, {
|
2018-06-11 11:32:01 +00:00
|
|
|
headers: {
|
|
|
|
'wazuh-app-version': packageInfo.version
|
|
|
|
},
|
2018-03-22 16:10:33 +00:00
|
|
|
username : wapi_config.user,
|
|
|
|
password : wapi_config.password,
|
|
|
|
rejectUnauthorized: !wapi_config.insecure
|
|
|
|
})
|
|
|
|
|
|
|
|
if (!response.body.error) {
|
|
|
|
// If cluster mode is active
|
2018-04-22 19:10:29 +00:00
|
|
|
if (response.body.data.enabled === 'yes') {
|
2018-03-22 16:10:33 +00:00
|
|
|
response = await needle('get', `${wapi_config.url}:${wapi_config.port}/cluster/node`, {}, {
|
2018-06-11 11:32:01 +00:00
|
|
|
headers: {
|
|
|
|
'wazuh-app-version': packageInfo.version
|
|
|
|
},
|
2018-03-22 16:10:33 +00:00
|
|
|
username : wapi_config.user,
|
|
|
|
password : wapi_config.password,
|
|
|
|
rejectUnauthorized: !wapi_config.insecure
|
|
|
|
})
|
2018-04-22 19:10:29 +00:00
|
|
|
|
2017-12-03 14:30:47 +00:00
|
|
|
if (!response.body.error) {
|
2018-03-22 16:10:33 +00:00
|
|
|
let managerName = wapi_config.cluster_info.manager;
|
|
|
|
delete wapi_config.cluster_info;
|
|
|
|
wapi_config.cluster_info = {};
|
|
|
|
wapi_config.cluster_info.status = 'enabled';
|
|
|
|
wapi_config.cluster_info.manager = managerName;
|
|
|
|
wapi_config.cluster_info.node = response.body.data.node;
|
|
|
|
wapi_config.cluster_info.cluster = response.body.data.cluster;
|
2018-03-24 11:04:20 +00:00
|
|
|
wapi_config.password = '****'
|
2018-03-22 16:10:33 +00:00
|
|
|
return reply({ statusCode: 200, data: wapi_config });
|
|
|
|
|
|
|
|
} else if (response.body.error){
|
2018-05-14 10:56:27 +00:00
|
|
|
const tmpMsg = response && response.body && response.body.message ?
|
|
|
|
response.body.message :
|
2018-04-30 15:12:55 +00:00
|
|
|
'Unexpected error from /cluster/node';
|
2018-05-14 10:56:27 +00:00
|
|
|
|
2018-04-30 15:12:55 +00:00
|
|
|
throw new Error(tmpMsg)
|
2017-12-03 14:30:47 +00:00
|
|
|
}
|
2018-04-22 19:10:29 +00:00
|
|
|
|
2018-03-22 16:10:33 +00:00
|
|
|
} else { // Cluster mode is not active
|
|
|
|
let managerName = wapi_config.cluster_info.manager;
|
|
|
|
delete wapi_config.cluster_info;
|
|
|
|
wapi_config.cluster_info = {};
|
|
|
|
wapi_config.cluster_info.status = 'disabled';
|
|
|
|
wapi_config.cluster_info.cluster = 'Disabled';
|
|
|
|
wapi_config.cluster_info.manager = managerName;
|
2018-03-24 11:04:20 +00:00
|
|
|
wapi_config.password = '****'
|
2018-03-22 16:10:33 +00:00
|
|
|
|
2018-04-30 15:12:55 +00:00
|
|
|
return reply({ statusCode: 200, data: wapi_config });
|
2018-03-22 16:10:33 +00:00
|
|
|
}
|
2018-04-30 15:12:55 +00:00
|
|
|
|
2017-11-30 12:04:48 +00:00
|
|
|
} else {
|
2018-05-14 10:56:27 +00:00
|
|
|
const tmpMsg = response && response.body && response.body.message ?
|
|
|
|
response.body.message :
|
2018-04-30 15:12:55 +00:00
|
|
|
'Unexpected error from /cluster/status';
|
|
|
|
|
|
|
|
throw new Error(tmpMsg)
|
2017-11-30 12:04:48 +00:00
|
|
|
}
|
2018-04-22 19:10:29 +00:00
|
|
|
|
2018-03-22 16:10:33 +00:00
|
|
|
} else {
|
2018-07-23 07:16:12 +00:00
|
|
|
if(response && response.body && response.body.error && response.body.message){
|
|
|
|
throw new Error(response.body.message)
|
|
|
|
}
|
|
|
|
|
2018-04-30 15:12:55 +00:00
|
|
|
throw new Error(`${wapi_config.url}:${wapi_config.port}/version is unreachable`)
|
2018-03-22 16:10:33 +00:00
|
|
|
}
|
|
|
|
} catch(error){
|
|
|
|
if(error.code === 'ECONNREFUSED'){
|
2018-04-20 12:10:46 +00:00
|
|
|
return reply({ statusCode: 200, data: {password: '****', apiIsDown: true } });
|
2018-03-22 16:10:33 +00:00
|
|
|
} else {
|
2018-08-16 15:49:39 +00:00
|
|
|
// Check if we can connect to a different API
|
|
|
|
if(error && error.body && typeof error.body.found !== 'undefined' && !error.body.found){
|
|
|
|
try {
|
|
|
|
const apis = await this.wzWrapper.getWazuhAPIEntries();
|
|
|
|
for(const api of apis.hits.hits){
|
|
|
|
try {
|
|
|
|
const response = await needle('get', `${api._source.url}:${api._source.api_port}/version`, {}, {
|
|
|
|
headers: {
|
|
|
|
'wazuh-app-version': packageInfo.version
|
|
|
|
},
|
|
|
|
username : api._source.api_user,
|
|
|
|
password : Buffer.from(api._source.api_password, 'base64').toString("ascii"),
|
|
|
|
rejectUnauthorized: !api._source.insecure
|
|
|
|
})
|
|
|
|
if(response && response.body && response.body.error === 0 && response.body.data) {
|
|
|
|
req.payload = api._id;
|
|
|
|
return this.checkStoredAPI(req,reply);
|
|
|
|
}
|
|
|
|
} catch(error) { } // eslint-disable-line
|
|
|
|
}
|
|
|
|
} catch (error) {
|
|
|
|
return ErrorResponse(error.message || error, 3020, 500, reply);
|
|
|
|
}
|
|
|
|
}
|
2018-04-30 15:12:55 +00:00
|
|
|
return ErrorResponse(error.message || error, 3002, 500, reply);
|
2018-03-22 16:10:33 +00:00
|
|
|
}
|
|
|
|
}
|
2018-04-11 07:58:23 +00:00
|
|
|
}
|
2017-06-01 15:08:10 +00:00
|
|
|
|
2018-04-11 07:58:23 +00:00
|
|
|
validateCheckApiParams (payload) {
|
2018-03-22 14:50:42 +00:00
|
|
|
if (!('user' in payload)) {
|
2018-04-30 15:12:55 +00:00
|
|
|
return 'Missing param: API USER';
|
2018-04-22 19:10:29 +00:00
|
|
|
}
|
2017-10-27 07:46:52 +00:00
|
|
|
|
2018-05-28 09:57:35 +00:00
|
|
|
if (!('password' in payload) && !('id' in payload)) {
|
2018-04-30 15:12:55 +00:00
|
|
|
return 'Missing param: API PASSWORD';
|
2018-04-22 19:10:29 +00:00
|
|
|
}
|
|
|
|
|
2018-03-22 14:50:42 +00:00
|
|
|
if (!('url' in payload)) {
|
2018-04-30 15:12:55 +00:00
|
|
|
return 'Missing param: API URL';
|
2018-04-22 19:10:29 +00:00
|
|
|
}
|
|
|
|
|
2018-03-22 14:50:42 +00:00
|
|
|
if (!('port' in payload)) {
|
2018-04-30 15:12:55 +00:00
|
|
|
return 'Missing param: API PORT';
|
2018-04-22 19:10:29 +00:00
|
|
|
}
|
|
|
|
|
2018-03-22 14:50:42 +00:00
|
|
|
if (!(payload.url.includes('https://')) && !(payload.url.includes('http://'))) {
|
2018-04-30 15:12:55 +00:00
|
|
|
return 'protocol_error';
|
2018-04-22 19:10:29 +00:00
|
|
|
}
|
2017-10-27 08:05:27 +00:00
|
|
|
|
2018-03-22 14:50:42 +00:00
|
|
|
return false;
|
|
|
|
}
|
2017-12-20 16:48:20 +00:00
|
|
|
|
2018-04-11 07:58:23 +00:00
|
|
|
async checkAPI (req, reply) {
|
2018-03-22 14:50:42 +00:00
|
|
|
try {
|
2018-05-28 09:57:35 +00:00
|
|
|
|
|
|
|
let apiAvailable = null;
|
|
|
|
|
2018-04-11 07:58:23 +00:00
|
|
|
const notValid = this.validateCheckApiParams(req.payload);
|
2018-05-14 10:56:27 +00:00
|
|
|
if(notValid) return ErrorResponse(notValid, 3003, 500, reply);
|
2018-03-22 14:50:42 +00:00
|
|
|
|
2018-05-28 09:59:12 +00:00
|
|
|
// Check if a Wazuh API id is given (already stored API)
|
2018-06-11 09:22:58 +00:00
|
|
|
if(req.payload && req.payload.id && !req.payload.password) {
|
2018-05-28 09:59:12 +00:00
|
|
|
|
2018-05-28 09:57:35 +00:00
|
|
|
const data = await this.wzWrapper.getWazuhConfigurationById(req.payload.id);
|
|
|
|
if(data) apiAvailable = data;
|
|
|
|
else return ErrorResponse(`The API ${req.payload.id} was not found`, 3029, 500, reply);
|
2018-05-28 09:59:12 +00:00
|
|
|
|
2018-06-11 09:22:58 +00:00
|
|
|
// Check if a password is given
|
|
|
|
} else if(req.payload && req.payload.password) {
|
2018-04-22 19:10:29 +00:00
|
|
|
|
2018-05-28 09:57:35 +00:00
|
|
|
apiAvailable = req.payload;
|
|
|
|
apiAvailable.password = Buffer.from(req.payload.password, 'base64').toString('ascii');
|
2018-05-28 09:59:12 +00:00
|
|
|
|
2018-05-28 09:57:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let response = await needle('get', `${apiAvailable.url}:${apiAvailable.port}/version`, {}, {
|
2018-06-11 11:32:01 +00:00
|
|
|
headers: {
|
|
|
|
'wazuh-app-version': packageInfo.version
|
|
|
|
},
|
2018-05-28 09:57:35 +00:00
|
|
|
username : apiAvailable.user,
|
|
|
|
password : apiAvailable.password,
|
|
|
|
rejectUnauthorized: !apiAvailable.insecure
|
2018-03-22 14:50:42 +00:00
|
|
|
})
|
2018-04-22 19:10:29 +00:00
|
|
|
|
|
|
|
|
2017-12-20 16:48:20 +00:00
|
|
|
// Check wrong credentials
|
|
|
|
if(parseInt(response.statusCode) === 401){
|
2018-04-30 15:12:55 +00:00
|
|
|
return ErrorResponse('Wrong credentials', 3004, 500, reply);
|
2017-12-20 16:48:20 +00:00
|
|
|
}
|
2018-04-22 19:10:29 +00:00
|
|
|
|
2017-11-02 18:00:35 +00:00
|
|
|
if (parseInt(response.body.error) === 0 && response.body.data) {
|
2018-04-22 19:10:29 +00:00
|
|
|
|
2018-05-28 09:57:35 +00:00
|
|
|
response = await needle('get', `${apiAvailable.url}:${apiAvailable.port}/agents/000`, {}, {
|
2018-06-11 11:32:01 +00:00
|
|
|
headers: {
|
|
|
|
'wazuh-app-version': packageInfo.version
|
|
|
|
},
|
2018-05-28 09:57:35 +00:00
|
|
|
username : apiAvailable.user,
|
|
|
|
password : apiAvailable.password,
|
|
|
|
rejectUnauthorized: !apiAvailable.insecure
|
2017-10-27 07:46:52 +00:00
|
|
|
})
|
2018-04-22 19:10:29 +00:00
|
|
|
|
2018-03-22 14:50:42 +00:00
|
|
|
if (!response.body.error) {
|
|
|
|
const managerName = response.body.data.name;
|
2018-04-22 19:10:29 +00:00
|
|
|
|
2018-05-28 09:57:35 +00:00
|
|
|
response = await needle('get', `${apiAvailable.url}:${apiAvailable.port}/cluster/status`, {}, { // Checking the cluster status
|
2018-06-11 11:32:01 +00:00
|
|
|
headers: {
|
|
|
|
'wazuh-app-version': packageInfo.version
|
|
|
|
},
|
2018-05-28 09:57:35 +00:00
|
|
|
username : apiAvailable.user,
|
|
|
|
password : apiAvailable.password,
|
|
|
|
rejectUnauthorized: !apiAvailable.insecure
|
2018-03-22 14:50:42 +00:00
|
|
|
})
|
|
|
|
|
2017-10-27 07:46:52 +00:00
|
|
|
if (!response.body.error) {
|
2018-04-22 19:10:29 +00:00
|
|
|
if (response.body.data.enabled === 'yes') {
|
|
|
|
|
2018-03-22 14:50:42 +00:00
|
|
|
// If cluster mode is active
|
2018-05-28 09:57:35 +00:00
|
|
|
response = await needle('get', `${apiAvailable.url}:${apiAvailable.port}/cluster/node`, {}, {
|
2018-06-11 11:32:01 +00:00
|
|
|
headers: {
|
|
|
|
'wazuh-app-version': packageInfo.version
|
|
|
|
},
|
2018-05-28 09:57:35 +00:00
|
|
|
username : apiAvailable.user,
|
|
|
|
password : apiAvailable.password,
|
|
|
|
rejectUnauthorized: !apiAvailable.insecure
|
2018-03-22 14:50:42 +00:00
|
|
|
})
|
|
|
|
|
2017-10-27 07:46:52 +00:00
|
|
|
if (!response.body.error) {
|
2018-03-22 14:50:42 +00:00
|
|
|
return reply({
|
|
|
|
manager: managerName,
|
|
|
|
node : response.body.data.node,
|
|
|
|
cluster: response.body.data.cluster,
|
|
|
|
status : 'enabled'
|
|
|
|
});
|
2018-04-22 19:10:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
2018-03-22 14:50:42 +00:00
|
|
|
// Cluster mode is not active
|
|
|
|
return reply({
|
|
|
|
manager: managerName,
|
|
|
|
cluster: 'Disabled',
|
|
|
|
status : 'disabled'
|
|
|
|
});
|
|
|
|
}
|
2017-10-27 07:46:52 +00:00
|
|
|
}
|
2018-03-22 14:50:42 +00:00
|
|
|
}
|
2018-04-22 19:10:29 +00:00
|
|
|
}
|
2018-05-14 10:56:27 +00:00
|
|
|
const tmpMsg = response.body && response.body.message ?
|
|
|
|
response.body.message :
|
2018-04-30 15:12:55 +00:00
|
|
|
'Unexpected error checking the Wazuh API';
|
2018-04-22 19:10:29 +00:00
|
|
|
|
2018-04-30 15:12:55 +00:00
|
|
|
throw new Error(tmpMsg)
|
2018-03-22 14:50:42 +00:00
|
|
|
|
|
|
|
} catch(error) {
|
2018-04-30 15:12:55 +00:00
|
|
|
return ErrorResponse(error.message || error, 3005, 500, reply);
|
2018-03-22 14:50:42 +00:00
|
|
|
}
|
2018-04-11 07:58:23 +00:00
|
|
|
}
|
2016-10-25 19:54:05 +00:00
|
|
|
|
2018-04-11 07:58:23 +00:00
|
|
|
async getPciRequirement (req, reply) {
|
2017-10-20 18:53:56 +00:00
|
|
|
try {
|
2018-04-22 19:10:29 +00:00
|
|
|
|
2018-01-31 15:08:42 +00:00
|
|
|
let pci_description = '';
|
|
|
|
|
|
|
|
if (req.params.requirement === 'all') {
|
|
|
|
if(!req.headers.id) {
|
2018-04-18 16:07:44 +00:00
|
|
|
return reply(pciRequirementsFile);
|
2018-01-31 15:08:42 +00:00
|
|
|
}
|
2018-04-11 07:58:23 +00:00
|
|
|
let wapi_config = await this.wzWrapper.getWazuhConfigurationById(req.headers.id);
|
2018-03-22 16:10:33 +00:00
|
|
|
|
|
|
|
if (wapi_config.error_code > 1) {
|
|
|
|
// Can not connect to elasticsearch
|
2018-05-14 10:56:27 +00:00
|
|
|
return ErrorResponse('Elasticsearch unexpected error or cannot connect', 3007, 400, reply);
|
2018-03-22 16:10:33 +00:00
|
|
|
} else if (wapi_config.error_code > 0) {
|
|
|
|
// Credentials not found
|
2018-05-14 10:56:27 +00:00
|
|
|
return ErrorResponse('Credentials does not exists', 3008, 400, reply);
|
2018-03-22 16:10:33 +00:00
|
|
|
}
|
2018-04-22 19:10:29 +00:00
|
|
|
|
2018-03-22 16:10:33 +00:00
|
|
|
const response = await needle('get', `${wapi_config.url}:${wapi_config.port}/rules/pci`, {}, {
|
2018-06-11 11:32:01 +00:00
|
|
|
headers: {
|
|
|
|
'wazuh-app-version': packageInfo.version
|
|
|
|
},
|
2018-03-22 16:10:33 +00:00
|
|
|
username : wapi_config.user,
|
|
|
|
password : wapi_config.password,
|
|
|
|
rejectUnauthorized: !wapi_config.insecure
|
|
|
|
})
|
2018-04-22 19:10:29 +00:00
|
|
|
|
2018-03-22 16:10:33 +00:00
|
|
|
if(response.body.data && response.body.data.items){
|
|
|
|
let PCIobject = {};
|
|
|
|
for(let item of response.body.data.items){
|
2018-04-18 16:07:44 +00:00
|
|
|
if(typeof pciRequirementsFile[item] !== 'undefined') PCIobject[item] = pciRequirementsFile[item];
|
2018-03-22 16:10:33 +00:00
|
|
|
}
|
|
|
|
return reply(PCIobject);
|
|
|
|
} else {
|
2018-05-14 10:56:27 +00:00
|
|
|
return ErrorResponse('An error occurred trying to parse PCI DSS requirements', 3009, 400, reply);
|
2018-03-22 16:10:33 +00:00
|
|
|
}
|
2018-04-22 19:10:29 +00:00
|
|
|
|
2018-01-31 15:08:42 +00:00
|
|
|
} else {
|
2018-04-18 16:07:44 +00:00
|
|
|
if (typeof pciRequirementsFile[req.params.requirement] !== 'undefined'){
|
|
|
|
pci_description = pciRequirementsFile[req.params.requirement];
|
2018-01-31 15:08:42 +00:00
|
|
|
}
|
2018-04-22 19:10:29 +00:00
|
|
|
|
2018-01-31 15:08:42 +00:00
|
|
|
return reply({
|
|
|
|
pci: {
|
|
|
|
requirement: req.params.requirement,
|
|
|
|
description: pci_description
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2018-03-22 14:50:42 +00:00
|
|
|
} catch (error) {
|
2018-05-14 10:56:27 +00:00
|
|
|
return ErrorResponse(error.message || error, 3010, 400, reply);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
async getGdprRequirement (req, reply) {
|
|
|
|
try {
|
|
|
|
let gdpr_description = '';
|
|
|
|
|
|
|
|
if (req.params.requirement === 'all') {
|
|
|
|
if(!req.headers.id) {
|
|
|
|
return reply(gdprRequirementsFile);
|
|
|
|
}
|
2018-06-08 07:34:26 +00:00
|
|
|
const wapi_config = await this.wzWrapper.getWazuhConfigurationById(req.headers.id);
|
|
|
|
|
|
|
|
// Checking for GDPR
|
|
|
|
const version = await needle('get', `${wapi_config.url}:${wapi_config.port}/version`, {}, {
|
2018-06-11 11:32:01 +00:00
|
|
|
headers: {
|
|
|
|
'wazuh-app-version': packageInfo.version
|
|
|
|
},
|
2018-06-08 07:34:26 +00:00
|
|
|
username : wapi_config.user,
|
|
|
|
password : wapi_config.password,
|
|
|
|
rejectUnauthorized: !wapi_config.insecure
|
|
|
|
});
|
|
|
|
|
|
|
|
const number = version.body.data;
|
|
|
|
|
|
|
|
const major = number.split('v')[1].split('.')[0]
|
|
|
|
const minor = number.split('v')[1].split('.')[1].split('.')[0]
|
|
|
|
const patch = number.split('v')[1].split('.')[1].split('.')[1]
|
|
|
|
|
|
|
|
if((major >= 3 && minor < 2) || (major >= 3 && minor >= 2 && patch < 3)){
|
|
|
|
return reply({});
|
|
|
|
}
|
2018-05-14 10:56:27 +00:00
|
|
|
|
|
|
|
if (wapi_config.error_code > 1) {
|
|
|
|
// Can not connect to elasticsearch
|
2018-05-15 15:42:04 +00:00
|
|
|
return ErrorResponse('Elasticsearch unexpected error or cannot connect', 3024, 400, reply);
|
2018-05-14 10:56:27 +00:00
|
|
|
} else if (wapi_config.error_code > 0) {
|
|
|
|
// Credentials not found
|
2018-05-15 15:42:04 +00:00
|
|
|
return ErrorResponse('Credentials does not exists', 3025, 400, reply);
|
2018-05-14 10:56:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const response = await needle('get', `${wapi_config.url}:${wapi_config.port}/rules/gdpr`, {}, {
|
2018-06-11 11:32:01 +00:00
|
|
|
headers: {
|
|
|
|
'wazuh-app-version': packageInfo.version
|
|
|
|
},
|
2018-05-14 10:56:27 +00:00
|
|
|
username : wapi_config.user,
|
|
|
|
password : wapi_config.password,
|
|
|
|
rejectUnauthorized: !wapi_config.insecure
|
|
|
|
})
|
|
|
|
|
|
|
|
if(response.body.data && response.body.data.items){
|
|
|
|
let GDPRobject = {};
|
|
|
|
for(let item of response.body.data.items){
|
|
|
|
if(typeof gdprRequirementsFile[item] !== 'undefined') GDPRobject[item] = gdprRequirementsFile[item];
|
|
|
|
}
|
|
|
|
return reply(GDPRobject);
|
|
|
|
} else {
|
2018-05-15 15:42:04 +00:00
|
|
|
return ErrorResponse('An error occurred trying to parse GDPR requirements', 3026, 400, reply);
|
2018-05-14 10:56:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
if (typeof gdprRequirementsFile[req.params.requirement] !== 'undefined'){
|
|
|
|
gdpr_description = gdprRequirementsFile[req.params.requirement];
|
|
|
|
}
|
|
|
|
|
|
|
|
return reply({
|
|
|
|
gdpr: {
|
|
|
|
requirement: req.params.requirement,
|
|
|
|
description: gdpr_description
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
} catch (error) {
|
2018-05-15 15:42:04 +00:00
|
|
|
return ErrorResponse(error.message || error, 3027, 400, reply);
|
2017-10-27 08:05:27 +00:00
|
|
|
}
|
2016-10-25 19:54:05 +00:00
|
|
|
|
2018-04-11 07:58:23 +00:00
|
|
|
}
|
2017-08-13 19:54:56 +00:00
|
|
|
|
2018-04-11 07:58:23 +00:00
|
|
|
async makeRequest (method, path, data, id, reply) {
|
2018-03-22 16:10:33 +00:00
|
|
|
try {
|
2018-04-11 07:58:23 +00:00
|
|
|
const wapi_config = await this.wzWrapper.getWazuhConfigurationById(id);
|
2018-03-22 16:10:33 +00:00
|
|
|
|
2016-10-25 19:54:05 +00:00
|
|
|
if (wapi_config.error_code > 1) {
|
|
|
|
//Can not connect to elasticsearch
|
2018-05-14 10:56:27 +00:00
|
|
|
return ErrorResponse('Could not connect with elasticsearch', 3011, 404, reply);
|
2016-10-25 19:54:05 +00:00
|
|
|
} else if (wapi_config.error_code > 0) {
|
|
|
|
//Credentials not found
|
2018-05-14 10:56:27 +00:00
|
|
|
return ErrorResponse('Credentials does not exists', 3012, 404, reply);
|
2016-10-25 19:54:05 +00:00
|
|
|
}
|
|
|
|
|
2017-10-27 07:34:38 +00:00
|
|
|
if (!data) {
|
|
|
|
data = {};
|
|
|
|
}
|
2016-10-25 19:54:05 +00:00
|
|
|
|
2018-03-22 14:50:42 +00:00
|
|
|
const options = {
|
2017-10-27 07:34:38 +00:00
|
|
|
headers: {
|
|
|
|
'wazuh-app-version': packageInfo.version
|
|
|
|
},
|
2018-03-22 16:10:33 +00:00
|
|
|
username : wapi_config.user,
|
|
|
|
password : wapi_config.password,
|
2016-10-25 19:54:05 +00:00
|
|
|
rejectUnauthorized: !wapi_config.insecure
|
|
|
|
};
|
|
|
|
|
2018-03-22 16:10:33 +00:00
|
|
|
const fullUrl = getPath(wapi_config) + path;
|
|
|
|
const response = await needle(method, fullUrl, data, options);
|
2017-10-27 08:05:27 +00:00
|
|
|
|
2018-04-30 14:24:19 +00:00
|
|
|
if(response && response.body && !response.body.error && response.body.data) {
|
|
|
|
return reply(response.body)
|
2018-04-22 19:10:29 +00:00
|
|
|
}
|
2018-03-22 16:10:33 +00:00
|
|
|
|
2018-04-30 14:24:19 +00:00
|
|
|
throw response && response.body && response.body.error && response.body.message ?
|
|
|
|
new Error(response.body.message) :
|
|
|
|
new Error('Unexpected error fetching data from the Wazuh API')
|
2018-05-14 10:56:27 +00:00
|
|
|
|
2018-03-22 16:10:33 +00:00
|
|
|
} catch (error) {
|
2018-05-14 10:56:27 +00:00
|
|
|
return ErrorResponse(error.message || error, 3013, 500, reply);
|
2018-03-22 16:10:33 +00:00
|
|
|
}
|
2018-04-11 07:58:23 +00:00
|
|
|
}
|
2016-10-25 19:54:05 +00:00
|
|
|
|
2018-07-10 14:25:15 +00:00
|
|
|
async makeGenericRequest (method, path, data, id) {
|
|
|
|
try {
|
|
|
|
const wapi_config = await this.wzWrapper.getWazuhConfigurationById(id);
|
|
|
|
|
|
|
|
if (wapi_config.error_code > 1) {
|
|
|
|
//Can not connect to elasticsearch
|
|
|
|
throw new Error('Could not connect with elasticsearch');
|
|
|
|
} else if (wapi_config.error_code > 0) {
|
|
|
|
//Credentials not found
|
|
|
|
throw new Error('Credentials does not exists');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!data) {
|
|
|
|
data = {};
|
|
|
|
}
|
|
|
|
|
|
|
|
const options = {
|
|
|
|
headers: {
|
|
|
|
'wazuh-app-version': packageInfo.version
|
|
|
|
},
|
|
|
|
username : wapi_config.user,
|
|
|
|
password : wapi_config.password,
|
|
|
|
rejectUnauthorized: !wapi_config.insecure
|
|
|
|
};
|
|
|
|
|
|
|
|
const fullUrl = getPath(wapi_config) + path;
|
|
|
|
const response = await needle(method, fullUrl, data, options);
|
|
|
|
|
|
|
|
if(response && response.body && !response.body.error && response.body.data) {
|
|
|
|
return response.body;
|
|
|
|
}
|
|
|
|
|
|
|
|
throw response && response.body && response.body.error && response.body.message ?
|
|
|
|
new Error(response.body.message) :
|
|
|
|
new Error('Unexpected error fetching data from the Wazuh API');
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
return Promise.reject(error);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-11 07:58:23 +00:00
|
|
|
requestApi (req, reply) {
|
2016-10-25 19:54:05 +00:00
|
|
|
if (!req.payload.method) {
|
2018-05-14 10:56:27 +00:00
|
|
|
return ErrorResponse('Missing param: method', 3015, 400, reply);
|
2016-10-25 19:54:05 +00:00
|
|
|
} else if (!req.payload.path) {
|
2018-05-14 10:56:27 +00:00
|
|
|
return ErrorResponse('Missing param: path', 3016, 400, reply);
|
2016-10-25 19:54:05 +00:00
|
|
|
} else {
|
2018-05-11 08:39:37 +00:00
|
|
|
if(req.payload.method !== 'GET' && req.payload.body && req.payload.body.devTools){
|
|
|
|
const configuration = getConfiguration();
|
|
|
|
if(!configuration || (configuration && !configuration['devtools.allowall'])){
|
2018-05-28 09:06:09 +00:00
|
|
|
return ErrorResponse('Allowed method: [GET]', 3029, 400, reply);
|
2018-05-11 08:39:37 +00:00
|
|
|
}
|
|
|
|
}
|
2018-05-18 10:16:00 +00:00
|
|
|
if(req.payload.body.devTools) {
|
|
|
|
delete req.payload.body.devTools;
|
|
|
|
const keyRegex = new RegExp(/.*agents\/\d*\/key.*/)
|
|
|
|
if(typeof req.payload.path === 'string' && keyRegex.test(req.payload.path)){
|
|
|
|
return ErrorResponse('Forbidden route /agents/<id>/key', 3028, 400, reply);
|
|
|
|
}
|
|
|
|
}
|
2018-04-11 07:58:23 +00:00
|
|
|
return this.makeRequest(req.payload.method, req.payload.path, req.payload.body, req.payload.id, reply);
|
2016-10-25 19:54:05 +00:00
|
|
|
}
|
2018-04-11 07:58:23 +00:00
|
|
|
}
|
2016-10-25 19:54:05 +00:00
|
|
|
|
2017-10-27 07:34:38 +00:00
|
|
|
// Fetch agent status and insert it directly on demand
|
2018-04-21 11:31:47 +00:00
|
|
|
async fetchAgents (req, reply) {
|
|
|
|
try{
|
|
|
|
const output = await this.fetchAgentsExternal();
|
|
|
|
return reply({
|
|
|
|
'statusCode': 200,
|
|
|
|
'error': '0',
|
|
|
|
'data': '',
|
|
|
|
output
|
|
|
|
});
|
|
|
|
} catch(error){
|
2018-05-14 10:56:27 +00:00
|
|
|
return ErrorResponse(error.message || error, 3018, 500, reply);
|
2018-04-21 11:31:47 +00:00
|
|
|
}
|
2018-04-11 07:58:23 +00:00
|
|
|
}
|
2017-06-01 15:08:10 +00:00
|
|
|
|
2018-04-11 07:58:23 +00:00
|
|
|
getConfigurationFile (req,reply) {
|
2018-01-24 11:54:32 +00:00
|
|
|
try{
|
2018-05-07 11:32:29 +00:00
|
|
|
const configFile = getConfiguration();
|
2018-04-22 19:10:29 +00:00
|
|
|
|
2018-01-24 11:54:32 +00:00
|
|
|
return reply({
|
|
|
|
statusCode: 200,
|
2018-03-22 14:50:42 +00:00
|
|
|
error : 0,
|
|
|
|
data : configFile || {}
|
2018-01-24 11:54:32 +00:00
|
|
|
});
|
2018-03-22 14:50:42 +00:00
|
|
|
|
2018-01-24 11:54:32 +00:00
|
|
|
} catch (error) {
|
2018-05-14 10:56:27 +00:00
|
|
|
return ErrorResponse(error.message || error, 3019, 500, reply);
|
2018-01-24 11:54:32 +00:00
|
|
|
}
|
2018-01-24 20:33:45 +00:00
|
|
|
}
|
|
|
|
|
2018-04-25 14:24:27 +00:00
|
|
|
/**
|
|
|
|
* Get full data on CSV format from a list Wazuh API endpoint
|
2018-05-14 10:56:27 +00:00
|
|
|
* @param {*} req
|
|
|
|
* @param {*} res
|
2018-04-25 14:24:27 +00:00
|
|
|
*/
|
2018-06-04 09:10:59 +00:00
|
|
|
async csv(req,reply) {
|
2018-04-25 14:24:27 +00:00
|
|
|
try{
|
2018-05-14 10:56:27 +00:00
|
|
|
|
2018-04-25 14:24:27 +00:00
|
|
|
if(!req.payload || !req.payload.path) throw new Error('Field path is required')
|
|
|
|
if(!req.payload.id) throw new Error('Field id is required')
|
|
|
|
|
2018-04-30 11:19:50 +00:00
|
|
|
const filters = req.payload && req.payload.filters && Array.isArray(req.payload.filters) ?
|
|
|
|
req.payload.filters :
|
|
|
|
[];
|
2018-05-14 10:56:27 +00:00
|
|
|
|
2018-04-25 14:24:27 +00:00
|
|
|
const config = await this.wzWrapper.getWazuhConfigurationById(req.payload.id)
|
|
|
|
|
2018-06-04 09:10:59 +00:00
|
|
|
let path_tmp = req.payload.path;
|
2018-04-25 14:24:27 +00:00
|
|
|
|
2018-06-04 09:10:59 +00:00
|
|
|
if(path_tmp && typeof path_tmp === 'string'){
|
|
|
|
path_tmp = path_tmp[0] === '/' ? path_tmp.substr(1) : path_tmp
|
2018-04-25 14:24:27 +00:00
|
|
|
}
|
2018-05-14 10:56:27 +00:00
|
|
|
|
2018-06-04 09:10:59 +00:00
|
|
|
if(!path_tmp) throw new Error('An error occurred parsing path field')
|
2018-05-14 10:56:27 +00:00
|
|
|
|
2018-06-04 09:10:59 +00:00
|
|
|
// Real limit, regardless the user query
|
2018-07-16 09:35:46 +00:00
|
|
|
const params = { limit: 1000 };
|
2018-04-30 11:19:50 +00:00
|
|
|
|
|
|
|
if(filters.length) {
|
|
|
|
for(const filter of filters) {
|
|
|
|
if(!filter.name || !filter.value) continue;
|
|
|
|
params[filter.name] = filter.value;
|
|
|
|
}
|
|
|
|
}
|
2018-04-25 14:24:27 +00:00
|
|
|
|
2018-07-16 09:35:46 +00:00
|
|
|
const cred = {
|
2018-06-11 11:32:01 +00:00
|
|
|
headers: {
|
|
|
|
'wazuh-app-version': packageInfo.version
|
|
|
|
},
|
2018-04-25 14:24:27 +00:00
|
|
|
username : config.user,
|
|
|
|
password : config.password,
|
|
|
|
rejectUnauthorized: !config.insecure
|
2018-07-16 09:35:46 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
const itemsArray = [];
|
|
|
|
const output = await needle('get', `${config.url}:${config.port}/${path_tmp}`, params, cred);
|
|
|
|
if(output && output.body && output.body.data && output.body.data.totalItems) {
|
|
|
|
params.offset = 0;
|
|
|
|
const { totalItems } = output.body.data;
|
|
|
|
itemsArray.push(...output.body.data.items);
|
|
|
|
while(itemsArray.length < totalItems){
|
|
|
|
params.offset += params.limit;
|
|
|
|
const tmpData = await needle('get', `${config.url}:${config.port}/${path_tmp}`, params, cred);
|
|
|
|
itemsArray.push(...tmpData.body.data.items);
|
|
|
|
}
|
|
|
|
}
|
2018-04-25 14:24:27 +00:00
|
|
|
|
|
|
|
if(output && output.body && output.body.data && output.body.data.totalItems) {
|
|
|
|
const fields = Object.keys(output.body.data.items[0]);
|
2018-04-26 11:51:49 +00:00
|
|
|
|
2018-04-25 14:24:27 +00:00
|
|
|
const json2csvParser = new Parser({ fields });
|
2018-07-16 09:35:46 +00:00
|
|
|
let csv = json2csvParser.parse(itemsArray);
|
|
|
|
|
|
|
|
for(const field of fields) {
|
|
|
|
if(csv.includes(field)) {
|
|
|
|
csv = csv.replace(field,CsvKeys[field] || field);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return reply(csv).type('text/csv');
|
2018-05-14 10:56:27 +00:00
|
|
|
|
2018-04-25 14:24:27 +00:00
|
|
|
} else if (output && output.body && output.body.data && !output.body.data.totalItems) {
|
2018-05-14 10:56:27 +00:00
|
|
|
|
2018-07-16 09:35:46 +00:00
|
|
|
throw new Error('No results');
|
2018-05-14 10:56:27 +00:00
|
|
|
|
2018-04-25 14:24:27 +00:00
|
|
|
} else {
|
|
|
|
|
|
|
|
throw new Error('An error occurred fetching data from the Wazuh API')
|
2018-05-14 10:56:27 +00:00
|
|
|
|
2018-04-25 14:24:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
} catch (error) {
|
2018-06-04 09:10:59 +00:00
|
|
|
return ErrorResponse(error.message || error, 3034, 500, reply);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-28 14:12:16 +00:00
|
|
|
|
2018-05-29 11:40:37 +00:00
|
|
|
|
2018-05-28 09:06:09 +00:00
|
|
|
async totalRam(req,reply) {
|
|
|
|
try{
|
|
|
|
// RAM in MB
|
|
|
|
const ram = Math.ceil(totalmem()/1024/1024);
|
|
|
|
return reply({ statusCode: 200, error: 0, ram });
|
|
|
|
} catch (error) {
|
2018-06-01 07:51:24 +00:00
|
|
|
return ErrorResponse(error.message || error, 3033, 500, reply);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-06-19 14:20:00 +00:00
|
|
|
async getAgentsFieldsUniqueCount(req, reply) {
|
|
|
|
try {
|
|
|
|
|
|
|
|
if(!req.params || !req.params.api) throw new Error('Field api is required')
|
|
|
|
|
|
|
|
const config = await this.wzWrapper.getWazuhConfigurationById(req.params.api);
|
|
|
|
|
|
|
|
const headers = {
|
|
|
|
headers: {
|
|
|
|
'wazuh-app-version': packageInfo.version
|
|
|
|
},
|
|
|
|
username : config.user,
|
|
|
|
password : config.password,
|
|
|
|
rejectUnauthorized: !config.insecure
|
|
|
|
};
|
|
|
|
|
2018-07-23 10:25:13 +00:00
|
|
|
const distinctUrl = '/agents/stats/distinct'
|
2018-06-19 14:20:00 +00:00
|
|
|
|
2018-07-23 10:25:13 +00:00
|
|
|
const data = await Promise.all([
|
|
|
|
needle('get', `${config.url}:${config.port}${distinctUrl}`, { fields: 'node_name', select: 'node_name' }, headers),
|
|
|
|
needle('get', `${config.url}:${config.port}${distinctUrl}`, { fields: 'group', select: 'group' }, headers),
|
|
|
|
needle('get', `${config.url}:${config.port}${distinctUrl}`, { fields: 'os.name,os.platform,os.version', select: 'os.name,os.platform,os.version' }, headers),
|
|
|
|
needle('get', `${config.url}:${config.port}${distinctUrl}`, { fields: 'version', select: 'version' }, headers),
|
|
|
|
needle('get', `${config.url}:${config.port}/agents/summary`, { }, headers),
|
|
|
|
needle('get', `${config.url}:${config.port}/agents`, { limit:1, sort: '-dateAdd' }, headers)
|
|
|
|
]);
|
|
|
|
|
2018-06-19 14:20:00 +00:00
|
|
|
const result = {
|
|
|
|
groups : [],
|
|
|
|
nodes : [],
|
|
|
|
versions : [],
|
|
|
|
osPlatforms: [],
|
2018-07-23 10:25:13 +00:00
|
|
|
lastAgent : {},
|
2018-06-19 14:20:00 +00:00
|
|
|
summary: {
|
2018-07-23 10:25:13 +00:00
|
|
|
agentsCountActive : 0,
|
|
|
|
agentsCountDisconnected : 0,
|
|
|
|
agentsCountNeverConnected: 0,
|
|
|
|
agentsCountTotal : 0,
|
|
|
|
agentsCoverity : 0
|
2018-06-19 14:20:00 +00:00
|
|
|
}
|
2018-07-23 10:25:13 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
if(data && data[0] && data[0].body && data[0].body.data && !data[0].body.error && data[0].body.data.items) {
|
|
|
|
result.nodes = data[0].body.data.items.filter(item => !!item.node_name).map(item => item.node_name);
|
2018-06-19 14:20:00 +00:00
|
|
|
}
|
|
|
|
|
2018-07-23 10:25:13 +00:00
|
|
|
if(data && data[1] && data[1].body && data[1].body.data && !data[1].body.error && data[1].body.data.items) {
|
|
|
|
result.groups = data[1].body.data.items.filter(item => !!item.group).map(item => item.group);
|
2018-06-19 14:20:00 +00:00
|
|
|
}
|
|
|
|
|
2018-07-23 10:25:13 +00:00
|
|
|
if(data && data[2] && data[2].body && data[2].body.data && !data[2].body.error && data[1].body.data.items) {
|
|
|
|
result.osPlatforms = data[2].body.data.items.filter(item => !!item.os && item.os.platform && item.os.name && item.os.version).map(item => item.os);
|
|
|
|
}
|
2018-06-19 14:20:00 +00:00
|
|
|
|
2018-07-23 10:25:13 +00:00
|
|
|
if(data && data[3] && data[3].body && data[3].body.data && !data[3].body.error && data[3].body.data.items) {
|
|
|
|
result.versions = data[3].body.data.items.filter(item => !!item.version).map(item => item.version);
|
|
|
|
}
|
2018-06-19 14:20:00 +00:00
|
|
|
|
2018-07-23 10:25:13 +00:00
|
|
|
if(data[4] && data[4].body && !data[4].body.error && data[4].body.data){
|
|
|
|
Object.assign(result.summary,{
|
|
|
|
agentsCountActive : data[4].body.data.Active - 1,
|
|
|
|
agentsCountDisconnected : data[4].body.data.Disconnected,
|
|
|
|
agentsCountNeverConnected: data[4].body.data['Never connected'],
|
|
|
|
agentsCountTotal : data[4].body.data.Total - 1,
|
|
|
|
agentsCoverity : ((data[4].body.data.Active - 1) / (data[4].body.data.Total - 1 )) * 100
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
if(data[5] && data[5].body && !data[5].body.error && data[5].body.data && data[5].body.data.items && data[5].body.data.items.length){
|
|
|
|
Object.assign(result.lastAgent,data[5].body.data.items[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
return reply({error: 0, result });
|
2018-06-19 14:20:00 +00:00
|
|
|
|
|
|
|
} catch (error) {
|
2018-07-23 10:25:13 +00:00
|
|
|
return ErrorResponse(error.message || error, 3035, 500, reply);
|
2018-06-19 14:20:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-17 07:41:53 +00:00
|
|
|
async getAppLogs(req, reply) {
|
|
|
|
try {
|
|
|
|
const lastLogs = await simpleTail(path.join(__dirname, '../../../../optimize/wazuh-logs/wazuhapp.log'),20);
|
|
|
|
return lastLogs && Array.isArray(lastLogs) ?
|
|
|
|
reply({error:0,lastLogs: lastLogs.filter(item => typeof item === 'string' && item.length)}) :
|
|
|
|
reply({error:0,lastLogs:[]});
|
|
|
|
} catch (error) {
|
|
|
|
return ErrorResponse(error.message || error, 3036, 500, reply);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-11 07:58:23 +00:00
|
|
|
}
|