diff --git a/package.json b/package.json index 36398fae84..007d64bed1 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "ansicolors": "^0.3.2", "install": "^0.10.1", "js-yaml": "3.10.0", + "json2csv": "^4.1.2", "lodash": "3.10.1", "needle": "^2.0.1", "node-cron": "^1.1.2", diff --git a/public/app.js b/public/app.js index 7bf02e61dd..0ee8c3855e 100644 --- a/public/app.js +++ b/public/app.js @@ -56,6 +56,7 @@ import 'plugins/wazuh/services/data-handler.js'; import 'plugins/wazuh/services/app-state.js'; import 'plugins/wazuh/services/api-tester.js'; import 'plugins/wazuh/services/pattern-handler.js'; +import 'plugins/wazuh/services/csv-request.js'; // Set up routes and views import 'plugins/wazuh/services/routes.js'; diff --git a/public/controllers/agents-preview.js b/public/controllers/agents-preview.js index f7f39c28bd..e4c1d36cf6 100644 --- a/public/controllers/agents-preview.js +++ b/public/controllers/agents-preview.js @@ -10,10 +10,11 @@ * Find more information about this on the LICENSE file. */ import * as modules from 'ui/modules' +import CsvGenerator from './csv-generator' const app = modules.get('app/wazuh', []); -app.controller('agentsPreviewController', function ($scope, $rootScope, $routeParams, genericReq, apiReq, appState, Agents, $location, errorHandler) { +app.controller('agentsPreviewController', function ($scope, $rootScope, $routeParams, genericReq, apiReq, appState, Agents, $location, errorHandler, csvReq) { $scope.loading = true; $scope.agents = Agents; $scope.status = 'all'; @@ -80,6 +81,18 @@ app.controller('agentsPreviewController', function ($scope, $rootScope, $routePa } } + $scope.downloadCsv = async () => { + try { + const currentApi = JSON.parse(appState.getCurrentAPI()).id; + const output = await csvReq.fetch('/agents', currentApi, $scope.agents ? $scope.agents.filters : null); + const csvGenerator = new CsvGenerator(output.csv, 'agents.csv'); + csvGenerator.download(true); + } catch (error) { + errorHandler.handle(error,'Download CSV'); + if(!$rootScope.$$phase) $rootScope.$digest(); + } + } + const load = async () => { try{ const data = await Promise.all([ diff --git a/public/controllers/csv-generator.js b/public/controllers/csv-generator.js new file mode 100644 index 0000000000..681ccb7ff3 --- /dev/null +++ b/public/controllers/csv-generator.js @@ -0,0 +1,34 @@ +/* + * Wazuh app - CSV file generator + * 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. + */ +export default class CsvGenerator { + + constructor (dataArray, fileName) { + this.dataArray = dataArray; + this.fileName = fileName; + } + + getLinkElement (linkText) { + return this.linkElement = this.linkElement || $('' + (linkText || '') + '', { + href: 'data:attachment/csv;base64,' + encodeURI(btoa(this.dataArray)), + target: '_blank', + download: this.fileName + }); + } + + download (removeAfterDownload) { + this.getLinkElement().css('display', 'none').appendTo('body'); + this.getLinkElement()[0].click(); + if (removeAfterDownload) { + this.getLinkElement().remove(); + } + } +} \ No newline at end of file diff --git a/public/controllers/groups.js b/public/controllers/groups.js index bb6545f9e4..1c34641d9f 100644 --- a/public/controllers/groups.js +++ b/public/controllers/groups.js @@ -11,12 +11,13 @@ */ import beautifier from 'plugins/wazuh/utils/json-beautifier'; import * as modules from 'ui/modules' +import CsvGenerator from './csv-generator' const app = modules.get('app/wazuh', []); // Groups preview controller app.controller('groupsPreviewController', -function ($scope, $rootScope, $location, apiReq, Groups, GroupFiles, GroupAgents, errorHandler) { +function ($scope, $rootScope, $location, apiReq, Groups, GroupFiles, GroupAgents, errorHandler, csvReq, appState) { const reloadWatcher = $rootScope.$watch('groupsIsReloaded',() => { delete $rootScope.groupsIsReloaded; $scope.lookingGroup = false; @@ -31,6 +32,19 @@ function ($scope, $rootScope, $location, apiReq, Groups, GroupFiles, GroupAgents $scope.groupAgents = GroupAgents; $scope.groupFiles = GroupFiles; + $scope.downloadCsv = async dataProvider => { + try { + const path = $scope[dataProvider] ? $scope[dataProvider].path : null; + const currentApi = JSON.parse(appState.getCurrentAPI()).id; + const output = await csvReq.fetch(path, currentApi, $scope[dataProvider] ? $scope[dataProvider].filters : null); + const csvGenerator = new CsvGenerator(output.csv, 'groups.csv'); + csvGenerator.download(true); + } catch (error) { + errorHandler.handle(error,'Download CSV'); + if(!$rootScope.$$phase) $rootScope.$digest(); + } + } + // Store a boolean variable to check if come from agents const fromAgents = ('comeFrom' in $rootScope) && ('globalAgent' in $rootScope) && $rootScope.comeFrom === 'agents'; diff --git a/public/controllers/osseclog.js b/public/controllers/osseclog.js index df37436aaf..c83f83f854 100644 --- a/public/controllers/osseclog.js +++ b/public/controllers/osseclog.js @@ -10,11 +10,12 @@ * Find more information about this on the LICENSE file. */ import * as modules from 'ui/modules' +import CsvGenerator from './csv-generator' const app = modules.get('app/wazuh', []); // Logs controller -app.controller('managerLogController', function ($scope, $rootScope, Logs, apiReq, errorHandler) { +app.controller('managerLogController', function ($scope, $rootScope, Logs, apiReq, errorHandler, csvReq, appState) { $scope.searchTerm = ''; $scope.loading = true; $scope.logs = Logs; @@ -48,8 +49,20 @@ app.controller('managerLogController', function ($scope, $rootScope, Logs, apiRe if(!$scope.$$phase) $scope.$digest(); } + $scope.downloadCsv = async () => { + try { + const currentApi = JSON.parse(appState.getCurrentAPI()).id; + const output = await csvReq.fetch('/manager/logs', currentApi, $scope.logs ? $scope.logs.filters : null); + const csvGenerator = new CsvGenerator(output.csv, 'logs.csv'); + csvGenerator.download(true); + } catch (error) { + errorHandler.handle(error,'Download CSV'); + if(!$rootScope.$$phase) $rootScope.$digest(); + } + } + const initialize = async () => { - try{ + try{ await $scope.logs.nextPage(); const data = await apiReq.request('GET', '/manager/logs/summary', {}); $scope.summary = data.data.data; diff --git a/public/controllers/ruleset.js b/public/controllers/ruleset.js index ec539cc230..4f4eacb20a 100644 --- a/public/controllers/ruleset.js +++ b/public/controllers/ruleset.js @@ -10,10 +10,11 @@ * Find more information about this on the LICENSE file. */ import * as modules from 'ui/modules' +import CsvGenerator from './csv-generator' const app = modules.get('app/wazuh', []); -app.controller('rulesController', function ($scope, $rootScope, Rules, RulesRelated, RulesAutoComplete, errorHandler, genericReq, appState) { +app.controller('rulesController', function ($scope, $rootScope, Rules, RulesRelated, RulesAutoComplete, errorHandler, genericReq, appState, csvReq) { $scope.setRulesTab = tab => $rootScope.globalsubmenuNavItem2 = tab; @@ -66,6 +67,18 @@ app.controller('rulesController', function ($scope, $rootScope, Rules, RulesRela } }; + $scope.downloadCsv = async () => { + try { + const currentApi = JSON.parse(appState.getCurrentAPI()).id; + const output = await csvReq.fetch('/rules', currentApi, $scope.rules ? $scope.rules.filters : null); + const csvGenerator = new CsvGenerator(output.csv, 'rules.csv'); + csvGenerator.download(true); + } catch (error) { + errorHandler.handle(error,'Download CSV'); + if(!$rootScope.$$phase) $rootScope.$digest(); + } + } + /** * This function takes back to the list but adding a group filter */ @@ -169,7 +182,7 @@ app.controller('rulesController', function ($scope, $rootScope, Rules, RulesRela }); }); -app.controller('decodersController', function ($scope, $rootScope, $sce, Decoders, DecodersRelated, DecodersAutoComplete, errorHandler, genericReq, appState) { +app.controller('decodersController', function ($scope, $rootScope, $sce, Decoders, DecodersRelated, DecodersAutoComplete, errorHandler, genericReq, appState, csvReq) { $scope.setRulesTab = tab => $rootScope.globalsubmenuNavItem2 = tab; //Initialization @@ -257,6 +270,18 @@ app.controller('decodersController', function ($scope, $rootScope, $sce, Decoder } } + $scope.downloadCsv = async () => { + try { + const currentApi = JSON.parse(appState.getCurrentAPI()).id; + const output = await csvReq.fetch('/decoders', currentApi, $scope.decoders ? $scope.decoders.filters : null); + const csvGenerator = new CsvGenerator(output.csv, 'decoders.csv'); + csvGenerator.download(true); + } catch (error) { + errorHandler.handle(error,'Download CSV'); + if(!$rootScope.$$phase) $rootScope.$digest(); + } + } + /** * This function changes to the decoder detail view */ diff --git a/public/services/csv-request.js b/public/services/csv-request.js new file mode 100644 index 0000000000..43db0c850e --- /dev/null +++ b/public/services/csv-request.js @@ -0,0 +1,27 @@ +/* + * Wazuh app - API request service + * 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. + */ +import * as modules from 'ui/modules' + +const app = modules.get('app/wazuh', []); + +app.service('csvReq', function (genericReq) { + return { + fetch: async (path, id, filters = null) => { + try { + const output = await genericReq.request('POST','/api/wazuh-api/csv',{ path, id, filters }); + return output.data; + } catch (error) { + return Promise.reject(error); + } + } + } +}); \ No newline at end of file diff --git a/public/services/error-handler.js b/public/services/error-handler.js index da809bd181..df05746c6a 100644 --- a/public/services/error-handler.js +++ b/public/services/error-handler.js @@ -20,6 +20,7 @@ app.service('errorHandler', function ( Notifier, appState, $location) { if(error.data && error.data.errorData && error.data.errorData.message) return error.data.errorData.message; if(error.errorData && error.errorData.message) return error.errorData.message; if(error.data && typeof error.data === 'string') return error.data; + if(error.data && error.data.error && typeof error.data.error === 'string') return error.data.error; if(error.data && error.data.message && typeof error.data.message === 'string') return error.data.message; if(error.data && error.data.message && error.data.message.msg && typeof error.data.message.msg === 'string') return error.data.message.msg; if(error.data && error.data.data && typeof error.data.data === 'string') return error.data.data; @@ -119,7 +120,7 @@ app.service('errorHandler', function ( Notifier, appState, $location) { if(error.extraMessage) text = error.extraMessage; text = location ? location + '. ' + text : text; if(!silent){ - if(isWarning) notify.warning(text); + if(isWarning || (text && typeof text === 'string' && text.toLowerCase().includes('no results'))) notify.warning(text); else notify.error(text); } if(goSettings) $location.path('/settings'); diff --git a/public/templates/agents-prev/agents-prev.html b/public/templates/agents-prev/agents-prev.html index b327adc397..75aef324ec 100644 --- a/public/templates/agents-prev/agents-prev.html +++ b/public/templates/agents-prev/agents-prev.html @@ -109,4 +109,8 @@ class="no-lateral-padding" full="'agent'"> +