Backports 6.8 from 7.3

This commit is contained in:
JuanCarlos 2019-08-23 09:36:44 +02:00
commit 8eae1c20fc
No known key found for this signature in database
GPG Key ID: B1C4FB733616273A
214 changed files with 16023 additions and 12431 deletions

View File

@ -1,31 +1,35 @@
{
"env": {
"node": true,
"es6": true,
"mocha": true,
"jquery": true
},
"parserOptions": {
"ecmaVersion": 7,
"sourceType": "module",
"ecmaFeatures": {
"jsx": true
}
},
"globals": {
"XPACK_RBAC_ENABLED": true
},
"parser": "babel-eslint",
"rules": {
"node/exports-style": ["error", "module.exports"],
"no-console": "warn",
"semi": "off",
"no-process-exit": "error",
"no-extra-boolean-cast": "off",
"node/no-unpublished-require": 0,
"node/no-unsupported-features": 0,
"node/no-unsupported-features/es-syntax": 0
},
"plugins": ["node", "async-await"],
"extends": ["eslint:recommended", "plugin:node/recommended"]
"env": {
"node": true,
"es6": true,
"mocha": true,
"jquery": true
},
"parserOptions": {
"ecmaVersion": 7,
"sourceType": "module",
"ecmaFeatures": {
"jsx": true
}
},
"globals": {
"XPACK_RBAC_ENABLED": true
},
"parser": "babel-eslint",
"rules": {
"node/exports-style": ["error", "module.exports"],
"no-console": "warn",
"semi": "off",
"no-process-exit": "error",
"no-extra-boolean-cast": "off",
"node/no-unpublished-require": 0,
"node/no-unsupported-features": 0,
"node/no-unsupported-features/es-syntax": 0
},
"plugins": ["node", "async-await"],
"extends": [
"eslint:recommended",
"plugin:node/recommended",
"plugin:react/recommended"
]
}

27
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@ -0,0 +1,27 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
| Wazuh | Elastic | Rev |
| ----- | ------- | --- |
| 3.x | 6.x | 444 |
**Description**
Short description of your issue.
**Steps to reproduce**
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Additional context**
Add any other context about the problem here. Here you can paste log entries too or any other useful information that may help with the issue.

View File

@ -0,0 +1,17 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''
---
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

1
.yarnignore Normal file
View File

@ -0,0 +1 @@
plugins/wazuh/server/wazuh-version.json

View File

@ -1255,4 +1255,4 @@ All notable changes to the Wazuh app project will be documented in this file.
### Fixed
- Search bar across panels now support parenthesis grouping
- Several CSS fixes for IE browser
- Several CSS fixes for IE browser

View File

@ -220,4 +220,4 @@ Copyright © 2019 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](LICENSE) file.
Find more information about this on the [LICENSE](LICENSE) file.

View File

@ -39,6 +39,8 @@
# Values must to be true or false.
#extensions.pci : true
#extensions.gdpr : true
#extensions.hipaa : true
#extensions.nist : true
#extensions.audit : true
#extensions.oscap : false
#extensions.ciscat : false
@ -57,11 +59,9 @@
#
# ------------------------------ Advanced indices ------------------------------
#
# Configure .wazuh and .wazuh-version indices shards and replicas.
# Configure .wazuh indices shards and replicas.
#wazuh.shards : 1
#wazuh.replicas : 0
#wazuh-version.shards : 1
#wazuh-version.replicas: 0
#
# --------------------------- Index pattern selector ---------------------------
#

View File

@ -38,6 +38,19 @@ export default kibana =>
}
},
init(server, options) {
// Kibana spaces locker
const xpackMainPlugin = server.plugins.xpack_main;
if (xpackMainPlugin) {
xpackMainPlugin.registerFeature({
id: 'wazuh',
name: 'Wazuh',
app: ['wazuh', 'kibana', 'elasticsearch'],
navLinkId: 'wazuh',
privileges: {}
});
}
return initApp(server, options);
}
});

View File

@ -1,8 +1,8 @@
{
"name": "wazuh",
"version": "3.9.5",
"revision": "0454",
"code": "0454-0",
"version": "3.10.0",
"revision": "0455",
"code": "0455-0",
"kibana": {
"version": "6.8.2"
},
@ -33,10 +33,10 @@
"test": "_mocha test/**/*"
},
"dependencies": {
"angular-animate": "1.6.5",
"angular-chart.js": "^1.1.1",
"angular-animate": "1.7.8",
"angular-chart.js": "1.1.1",
"angular-cookies": "1.6.5",
"angular-material": "1.1.10",
"angular-material": "1.1.18",
"dom-to-image": "^2.6.0",
"install": "^0.10.1",
"js2xmlparser": "^3.0.0",
@ -48,7 +48,8 @@
"querystring-browser": "1.0.4",
"simple-tail": "^1.1.0",
"timsort": "^0.3.0",
"winston": "3.0.0"
"winston": "3.0.0",
"babel-polyfill": "^6.13.0"
},
"devDependencies": {
"@elastic/plugin-helpers": "^7.1.8",
@ -58,6 +59,7 @@
"eslint-plugin-async-await": "^0.0.0",
"eslint-plugin-import": "^2.14.0",
"eslint-plugin-node": "^7.0.1",
"eslint-plugin-react": "^7.13.0",
"mocha": "^5.2.0",
"prettier": "^1.14.2",
"tslint": "^5.11.0",

View File

@ -25,6 +25,9 @@ import 'uiExports/docViews';
import 'uiExports/embeddableFactories';
import 'uiExports/autocompleteProviders';
// Require babel for Kibana 7.2
import 'babel-polyfill';
// Require CSS
import './less/loader';
import { uiModules } from 'ui/modules';
@ -36,6 +39,32 @@ import './components';
// angular-charts.js
import 'angular-chart.js';
// Font Awesome, Kibana UI framework and others
import './utils/fontawesome/css/font-awesome.min.css';
// Dev tools
import './utils/codemirror';
import './utils/jquery-ui';
// Material
import 'angular-material/angular-material.css';
import 'angular-aria/angular-aria';
import 'angular-animate/angular-animate';
import 'angular-material/angular-material';
// Cookies
import 'angular-cookies/angular-cookies';
import 'ui/autoload/all';
// Wazuh
import './kibana-integrations';
import './services';
import './controllers';
import './factories';
import './directives';
// Set up Wazuh app
const app = uiModules.get('app/wazuh', ['ngCookies', 'ngMaterial', 'chart.js']);
@ -56,6 +85,9 @@ app.config([
]);
app.run(function($rootScope, $route, $location, appState, $window) {
chrome
.setRootTemplate('<wz-menu></wz-menu><div ng-view></div>')
.setRootController(() => require('./app'));
appState.setNavigation({ status: false });
appState.setNavigation({
reloaded: false,
@ -79,6 +111,7 @@ app.run(function($rootScope, $route, $location, appState, $window) {
$rootScope.$on('$locationChangeSuccess', () => {
const navigation = appState.getNavigation();
$rootScope.hideWzMenu = navigation.currLocation === '/health-check';
appState.setNavigation({ currLocation: $location.path() });
if (navigation.currLocation !== navigation.prevLocation) {
if (navigation.discoverSections.includes(navigation.currLocation)) {
@ -187,32 +220,6 @@ app.run(function($rootScope, $route, $location, appState, $window) {
});
});
// Font Awesome, Kibana UI framework and others
import './utils/fontawesome/css/font-awesome.min.css';
// Dev tools
import './utils/codemirror';
import './utils/jquery-ui';
// Material
import 'angular-material/angular-material.css';
import 'angular-aria/angular-aria';
import 'angular-animate/angular-animate';
import 'angular-material/angular-material';
// Cookies
import 'angular-cookies/angular-cookies';
import 'ui/autoload/all';
// Wazuh
import './kibana-integrations';
import './services';
import './controllers';
import './factories';
import './directives';
// Added due to Kibana 6.3.0. Do not modify.
uiModules.get('kibana').provider('dashboardConfig', () => {
let hideWriteControls = false;

View File

@ -17,9 +17,14 @@ import {
EuiLoadingSpinner,
EuiProgress,
EuiBasicTable,
EuiButtonIcon
EuiButtonIcon,
EuiHealth,
EuiCallOut
} from '@elastic/eui';
import { BasicTable } from '../directives/wz-table-eui/components/table';
import { Tabs } from '../directives/wz-tabs-eui/components/tabs';
const app = uiModules.get('app/wazuh', ['react']);
app
@ -28,4 +33,8 @@ app
.value('EuiLoadingSpinner', EuiLoadingSpinner)
.value('EuiProgress', EuiProgress)
.value('EuiButtonIcon', EuiButtonIcon)
.value('EuiBasicTable', EuiBasicTable);
.value('EuiBasicTable', EuiBasicTable)
.value('EuiHealth', EuiHealth)
.value('EuiCallOut', EuiCallOut)
.value('BasicTable', BasicTable)
.value('Tabs', Tabs);

View File

@ -58,7 +58,7 @@ export class AgentsPreviewController {
*/
$onInit() {
this.init = true;
this.api = JSON.parse(this.appState.getCurrentAPI()).id;
this.api = JSON.parse(this.appState.getCurrentAPI()).id;
const loc = this.$location.search();
if ((loc || {}).agent && (loc || {}).agent !== '000') {
this.commonData.setTimefilter(timefilter.getTime());
@ -90,12 +90,14 @@ export class AgentsPreviewController {
this.$location.search('tab', this.submenuNavItem);
});
this.$scope.$on('wazuhFetched', (ev, parameters) => {
this.$scope.$on('wazuhFetched', (ev) => {
ev.stopPropagation();
this.$scope.showNoAgents =
!parameters.items.length > 0 && !parameters.filters.length;
});
this.registerAgentsProps = {
addNewAgent: flag => this.addNewAgent(flag)
};
this.init = false;
//Load
this.load();
@ -201,6 +203,9 @@ export class AgentsPreviewController {
this.osPlatforms = unique.osPlatforms;
this.lastAgent = unique.lastAgent;
this.summary = unique.summary;
if (!this.lastAgent || !this.lastAgent.id) {
this.addNewAgent(true);
}
if (agentsTop.data.data === '') {
this.mostActiveAgent.name = this.appState.getClusterInfo().manager;
@ -225,22 +230,27 @@ export class AgentsPreviewController {
return;
}
registerNewAgent(flag) {
this.$scope.registerNewAgent = flag;
addNewAgent(flag) {
this.addingNewAgent = flag;
}
reloadList() {
this.refreshAgentsStats();
this.refreshAgentsStats();
this.$scope.$broadcast('wazuhSearch', { term: this.prevSearch || '' });
}
async refreshAgentsStats() {
try {
const data = await this.genericReq.request('GET', '/api/agents-unique/' + this.api, {});
this.summary = ((data.data || {}).result || {}).summary || {};
const data = await this.genericReq.request(
'GET',
'/api/agents-unique/' + this.api,
{}
);
this.summary = ((data.data || {}).result || {}).summary || {};
} catch (error) {
this.errorHandler.handle('Error refreshing agents stats');
}
this.$scope.$broadcast('reloadSearchFilterBar', {});
}
openRegistrationDocs() {

View File

@ -14,6 +14,7 @@ import { generateMetric } from '../../utils/generate-metric';
import { TabNames } from '../../utils/tab-names';
import * as FileSaver from '../../services/file-saver';
import { TabDescription } from '../../../server/reporting/tab-description';
import { UnsupportedComponents } from '../../utils/components-os-support';
import {
metricsGeneral,
metricsAudit,
@ -60,7 +61,8 @@ export class AgentsController {
$mdDialog,
groupHandler,
wazuhConfig,
timeService
timeService,
genericReq
) {
this.$scope = $scope;
this.$location = $location;
@ -79,6 +81,7 @@ export class AgentsController {
this.groupHandler = groupHandler;
this.wazuhConfig = wazuhConfig;
this.timeService = timeService;
this.genericReq = genericReq;
// Config on-demand
this.$scope.isArray = Array.isArray;
@ -160,11 +163,6 @@ export class AgentsController {
this.tabVisualizations.assign('agents');
this.$scope.hostMonitoringTabs = ['general', 'fim', 'syscollector'];
this.$scope.systemAuditTabs = ['pm', 'sca', 'audit', 'oscap', 'ciscat'];
this.$scope.securityTabs = ['vuls', 'virustotal', 'osquery', 'docker'];
this.$scope.complianceTabs = ['pci', 'gdpr'];
/**
* This check if given array of items contais a single given item
* @param {Object} item
@ -212,6 +210,9 @@ export class AgentsController {
this.$scope.startVis2Png = () => this.startVis2Png();
this.$scope.shouldShowComponent = component =>
this.shouldShowComponent(component);
this.$scope.$on('$destroy', () => {
this.visFactoryService.clearAll();
});
@ -223,6 +224,14 @@ export class AgentsController {
this.$location.path('/manager/groups');
};
this.$scope.exportConfiguration = enabledComponents => {
this.reportingService.startConfigReport(
this.$scope.agent,
'agentConfig',
enabledComponents
);
};
this.$scope.restartAgent = async agent => {
this.$scope.restartingAgent = true;
try {
@ -368,7 +377,8 @@ export class AgentsController {
this.$scope.showSyscheckFiles = !this.$scope.showSyscheckFiles;
if (!this.$scope.showSyscheckFiles) {
this.$scope.$emit('changeTabView', {
tabView: this.$scope.tabView
tabView: this.$scope.tabView,
sameSection: true
});
}
this.$scope.$applyAsync();
@ -379,7 +389,8 @@ export class AgentsController {
this.$scope.showScaScan = !this.$scope.showScaScan;
if (!this.$scope.showScaScan) {
this.$scope.$emit('changeTabView', {
tabView: this.$scope.tabView
tabView: this.$scope.tabView,
sameSection: true
});
}
this.$scope.$applyAsync();
@ -435,6 +446,7 @@ export class AgentsController {
};
this.$scope.expand = i => this.expand(i);
this.setTabs();
}
/**
* Create metric for given object
@ -542,8 +554,8 @@ export class AgentsController {
}
// Update agent status
try {
if ((this.$scope || {}).agent || false) {
if (!force && ((this.$scope || {}).agent || false)) {
try {
const agentInfo = await this.apiReq.request(
'GET',
`/agents/${this.$scope.agent.id}`,
@ -552,21 +564,29 @@ export class AgentsController {
this.$scope.agent.status =
(((agentInfo || {}).data || {}).data || {}).status ||
this.$scope.agent.status;
}
} catch (error) {} // eslint-disable-line
} catch (error) {} // eslint-disable-line
}
try {
this.$scope.showSyscheckFiles = false;
this.$scope.showScaScan = false;
if (tab === 'pci') {
const pciTabs = await this.commonData.getPCI();
this.$scope.pciTabs = pciTabs;
this.$scope.selectedPciIndex = 0;
this.$scope.pciReqs = {items: pciTabs, reqTitle: 'PCI DSS Requirement'}
}
if (tab === 'gdpr') {
const gdprTabs = await this.commonData.getGDPR();
this.$scope.gdprTabs = gdprTabs;
this.$scope.selectedGdprIndex = 0;
this.$scope.gdprReqs = {items: gdprTabs, reqTitle: 'GDPR Requirement'};
}
if (tab === 'hipaa') {
const hipaaTabs = await this.commonData.getHIPAA();
this.$scope.hipaaReqs = {items: hipaaTabs, reqTitle: 'HIPAA Requirement'};
}
if (tab === 'nist') {
const nistTabs = await this.commonData.getNIST();
this.$scope.nistReqs = {items: nistTabs, reqTitle: 'NIST 800-53 Requirement'};
}
if (tab === 'sca') {
@ -635,6 +655,66 @@ export class AgentsController {
} catch (error) {
return Promise.reject(error);
}
this.$scope.configurationTabsProps = {};
this.$scope.buildProps = tabs => {
const cleanTabs = [];
tabs.forEach(x => {
if (
this.$scope.configurationTab === 'integrity-monitoring' &&
x.id === 'fim-whodata' &&
x.agent &&
x.agent.agentPlatform !== 'linux'
)
return;
cleanTabs.push({
id: x.id,
name: x.name
});
});
this.$scope.configurationTabsProps = {
clickAction: tab => {
this.$scope.switchConfigurationSubTab(tab);
},
selectedTab:
this.$scope.configurationSubTab || (tabs && tabs.length)
? tabs[0].id
: '',
tabs: cleanTabs
};
};
this.setTabs();
}
/**
* Build the current section tabs
*/
setTabs() {
this.$scope.agentsTabsProps = false;
this.currentPanel = this.commonData.getCurrentPanel(this.$scope.tab);
if (!this.currentPanel) return;
const tabs = this.commonData.getTabsFromCurrentPanel(
this.currentPanel,
this.$scope.extensions,
this.$scope.tabNames
);
this.$scope.agentsTabsProps = {
clickAction: tab => {
this.switchTab(tab, true);
},
selectedTab:
this.$scope.tab ||
(this.currentPanel && this.currentPanel.length
? this.currentPanel[0]
: ''),
tabs
};
this.$scope.$applyAsync();
}
goDiscover() {
@ -683,86 +763,12 @@ export class AgentsController {
*/
async loadSyscollector(id) {
try {
// Continue API requests if we do have Syscollector enabled
// Fetch Syscollector data
const data = await Promise.all([
this.apiReq.request('GET', `/syscollector/${id}/hardware`, {}),
this.apiReq.request('GET', `/syscollector/${id}/os`, {}),
this.apiReq.request('GET', `/syscollector/${id}/ports`, { limit: 1 }),
this.apiReq.request('GET', `/syscollector/${id}/packages`, {
limit: 1,
select: 'scan_time'
}),
this.apiReq.request('GET', `/syscollector/${id}/processes`, {
limit: 1,
select: 'scan_time'
})
]);
const syscollectorData = await this.genericReq.request(
'GET',
`/api/syscollector/${id}`
);
const result = data.map(item => ((item || {}).data || {}).data || false);
const [
hardwareResponse,
osResponse,
portsResponse,
packagesDateResponse,
processesDateResponse
] = result;
// This API call may fail so we put it out of Promise.all
let netifaceResponse = false;
try {
const resultNetiface = await this.apiReq.request(
'GET',
`/syscollector/${id}/netiface`,
{}
);
netifaceResponse = ((resultNetiface || {}).data || {}).data || false;
} catch (error) {} // eslint-disable-line
// This API call may fail so we put it out of Promise.all
let netaddrResponse = false;
try {
const resultNetaddrResponse = await this.apiReq.request(
'GET',
`/syscollector/${id}/netaddr`,
{ limit: 1 }
);
netaddrResponse =
((resultNetaddrResponse || {}).data || {}).data || false;
} catch (error) {} // eslint-disable-line
// Before proceeding, syscollector data is an empty object
this.$scope.syscollector = {};
const packagesDate = packagesDateResponse
? { ...packagesDateResponse }
: false;
const processesDate = processesDateResponse
? { ...processesDateResponse }
: false;
// Fill syscollector object
this.$scope.syscollector = {
hardware:
typeof hardwareResponse === 'object' &&
Object.keys(hardwareResponse).length
? { ...hardwareResponse }
: false,
os:
typeof osResponse === 'object' && Object.keys(osResponse).length
? { ...osResponse }
: false,
netiface: netifaceResponse ? { ...netifaceResponse } : false,
ports: portsResponse ? { ...portsResponse } : false,
netaddr: netaddrResponse ? { ...netaddrResponse } : false,
packagesDate: ((packagesDate || {}).items || []).length
? packagesDate.items[0].scan_time
: '-',
processesDate: ((processesDate || {}).items || []).length
? processesDate.items[0].scan_time
: '-'
};
this.$scope.syscollector = (syscollectorData || {}).data || {};
return;
} catch (error) {
@ -785,53 +791,24 @@ export class AgentsController {
const id = this.commonData.checkLocationAgentId(newAgentId, globalAgent);
const data = [false, false, false];
const data = await this.apiReq.request('GET', `/agents/${id}`, {});
try {
data[0] = await this.apiReq.request('GET', `/agents/${id}`, {});
} catch (error) {} //eslint-disable-line
try {
data[1] = await this.apiReq.request(
'GET',
`/syscheck/${id}/last_scan`,
{}
);
} catch (error) {} //eslint-disable-line
try {
data[2] = await this.apiReq.request(
'GET',
`/rootcheck/${id}/last_scan`,
{}
);
} catch (error) {} //eslint-disable-line
const result = data.map(item => ((item || {}).data || {}).data || false);
const [agentInfo, syscheckLastScan, rootcheckLastScan] = result;
const agentInfo = ((data || {}).data || {}).data || false;
// Agent
this.$scope.agent = agentInfo;
if (this.$scope.agent.os) {
if (agentInfo && this.$scope.agent.os) {
this.$scope.agentOS =
this.$scope.agent.os.name + ' ' + this.$scope.agent.os.version;
this.$scope.agent.isLinuxOS = this.$scope.agent.os.uname.includes(
'Linux'
);
const isLinux = this.$scope.agent.os.uname.includes('Linux');
this.$scope.agent.agentPlatform = isLinux
? 'linux'
: this.$scope.agent.os.platform;
} else {
this.$scope.agentOS = '-';
this.$scope.agent.isLinuxOS = false;
this.$scope.agent.agentPlatform = false;
}
// Syscheck
this.$scope.agent.syscheck = syscheckLastScan;
this.validateSysCheck();
// Rootcheck
this.$scope.agent.rootcheck = rootcheckLastScan;
this.validateRootCheck();
await this.$scope.switchTab(this.$scope.tab, true);
const groups = await this.apiReq.request('GET', '/agents/groups', {});
@ -842,37 +819,7 @@ export class AgentsController {
this.$scope.agent.group && !this.$scope.agent.group.includes(item)
);
const outdatedAgents = await this.apiReq.request(
'GET',
'/agents/outdated/',
{}
);
this.$scope.agent.outdated = outdatedAgents.data.data.items
.map(x => x.id)
.find(x => x === this.$scope.agent.id);
if (this.$scope.agent.outdated) {
if (
this.appState.getSessionStorageItem(
`updatingAgent${this.$scope.agent.id}`
)
) {
this.$scope.agent.upgrading = true;
}
} else {
if (
this.appState.getSessionStorageItem(
`updatingAgent${this.$scope.agent.id}`
)
) {
this.appState.removeSessionStorageItem(
`updatingAgent${this.$scope.agent.id}`
);
this.$scope.agent.outdated = false;
}
this.$scope.$applyAsync();
}
this.loadWelcomeCardsProps();
this.$scope.load = false;
this.$scope.$applyAsync();
return;
@ -892,11 +839,48 @@ export class AgentsController {
this.$location.path('/agents-preview');
}
}
this.$scope.load = false;
this.$scope.$applyAsync();
return;
}
shouldShowComponent(component) {
return !(
UnsupportedComponents[this.$scope.agent.agentPlatform] ||
UnsupportedComponents['other']
).includes(component);
}
cleanExtensions(extensions) {
const result = {};
for (const extension in extensions) {
if (
!(
UnsupportedComponents[this.$scope.agent.agentPlatform] ||
UnsupportedComponents['other']
).includes(extension)
) {
result[extension] = extensions[extension];
}
}
return result;
}
/**
* Get available welcome cards after getting the agent
*/
loadWelcomeCardsProps() {
this.$scope.welcomeCardsProps = {
switchTab: tab => this.switchTab(tab),
extensions: this.cleanExtensions(this.$scope.extensions),
agent: this.$scope.agent,
api: this.appState.getCurrentAPI(),
setExtensions: (api, extensions) =>
this.appState.setExtensions(api, extensions)
};
}
switchGroupEdit() {
this.$scope.editGroup = !!!this.$scope.editGroup;
this.$scope.$applyAsync();
@ -907,13 +891,13 @@ export class AgentsController {
* @param {String} binding_text
* @param {String} date
*/
offsetTimestamp = (text, time) => {
offsetTimestamp(text, time) {
try {
return text + this.timeService.offset(time);
} catch (error) {
return time !== '-' ? `${text}${time} (UTC)` : time;
}
};
}
/**
* Navigate to the groups of an agent

View File

@ -0,0 +1,159 @@
import React, { Component } from 'react';
import {
EuiPopover,
EuiButton,
EuiCheckboxGroup,
EuiSpacer,
EuiButtonEmpty
} from '@elastic/eui';
import PropTypes from 'prop-types';
import { UnsupportedComponents } from '../../../utils/components-os-support';
export class ExportConfiguration extends Component {
constructor(props) {
super(props);
this.state = {
buttonDisabled: false,
isPopoverOpen: false
};
const agentOptions = [
'Global configuration',
'Communication',
'Anti-flooding settings',
'Labels',
'Policy monitoring',
{ name: 'oscap', desc: 'OpenSCAP' },
'CIS-CAT',
'Osquery',
'Inventory data',
'Active response',
'Commands',
{ name: 'docker', desc: 'Docker listener' },
'Log collection',
'Integrity monitoring'
];
const groupOptions = ['Configurations', 'Agents in group'];
this.options = [];
const list = this.props.type === 'agent' ? agentOptions : groupOptions;
list.forEach((x, idx) => {
if (
typeof x === 'string' ||
(x.name &&
!(
UnsupportedComponents[this.props.agentPlatform] ||
UnsupportedComponents['other']
).includes(x.name))
) {
this.options.push({ id: `${idx}`, label: x.desc || x });
}
});
let initialChecks = {};
this.options.forEach(x => {
initialChecks[x.id] = true;
});
this.state.checkboxIdToSelectedMap = initialChecks;
}
selectAll(flag) {
let newCheckboxIdToSelectedMap = {};
for (let i = 0; i < this.options.length; i++) {
newCheckboxIdToSelectedMap[`${this.options[i].id}`] = flag;
}
this.setState({
checkboxIdToSelectedMap: newCheckboxIdToSelectedMap,
buttonDisabled: !flag
});
}
exportClick() {
this.setState({
isPopoverOpen: !this.state.isPopoverOpen
});
}
closePopover() {
this.setState({
isPopoverOpen: false
});
}
onChange = optionId => {
const newCheckboxIdToSelectedMap = {
...this.state.checkboxIdToSelectedMap,
...{
[optionId]: !this.state.checkboxIdToSelectedMap[optionId]
}
};
let result = false;
for (let i = 0; i < this.options.length; i++) {
if (newCheckboxIdToSelectedMap[`${this.options[i].id}`] === true) {
result = true;
}
}
this.setState({
checkboxIdToSelectedMap: newCheckboxIdToSelectedMap,
buttonDisabled: !result
});
};
render() {
const button = (
<EuiButton
iconType="importAction"
iconSide="left"
size="s"
onClick={this.exportClick.bind(this)}
>
PDF
</EuiButton>
);
return (
<EuiPopover
id="trapFocus"
ownFocus
button={button}
isOpen={this.state.isPopoverOpen}
closePopover={this.closePopover.bind(this)}
anchorPosition="downRight"
>
<EuiCheckboxGroup
options={this.options}
idToSelectedMap={this.state.checkboxIdToSelectedMap}
onChange={this.onChange}
compressed
/>
<EuiSpacer size="s" />
<EuiButtonEmpty size="xs" onClick={() => this.selectAll(true)}>
Select all
</EuiButtonEmpty>
<EuiSpacer size="s" />
<EuiButtonEmpty size="xs" onClick={() => this.selectAll(false)}>
Unselect all
</EuiButtonEmpty>
<EuiSpacer size="m" />
<EuiButton
isDisabled={this.state.buttonDisabled}
onClick={() => {
this.closePopover();
this.props.exportConfiguration(this.state.checkboxIdToSelectedMap);
}}
fill
>
Generate PDF report
</EuiButton>
</EuiPopover>
);
}
}
ExportConfiguration.propTypes = {
exportConfiguration: PropTypes.func,
type: PropTypes.string,
agentPlatform: PropTypes.string
};

View File

@ -0,0 +1,191 @@
import React, { Component, Fragment } from 'react';
import {
EuiSteps,
EuiFlexGroup,
EuiFlexItem,
EuiPanel,
EuiButtonToggle,
EuiFieldText,
EuiText,
EuiCodeBlock,
EuiTitle,
EuiButtonIcon,
EuiButtonEmpty,
EuiCopy,
EuiPage,
EuiPageBody
} from '@elastic/eui';
import { RegisterGuideDefs } from './register-guide-defs';
import PropTypes from 'prop-types';
export class RegisterAgent extends Component {
constructor(props) {
super(props);
this.state = {
status: 'incomplete',
selectedOS: '',
serverAddress: ''
};
}
selectOS(os) {
this.setState({ selectedOS: os });
}
setServerAddress(event) {
this.setState({ serverAddress: event.target.value });
}
render() {
const rpmButton = (
<EuiButtonToggle
label="Red Hat / CentOS"
onChange={() => this.selectOS('rpm')}
fill={this.state.selectedOS === 'rpm'}
/>
);
const debButton = (
<EuiButtonToggle
label="Debian / Ubuntu"
onChange={() => this.selectOS('deb')}
fill={this.state.selectedOS === 'deb'}
/>
);
const windowsButton = (
<EuiButtonToggle
label="Windows"
onChange={() => this.selectOS('win')}
fill={this.state.selectedOS === 'win'}
/>
);
const macOSButton = (
<EuiButtonToggle
label="MacOS"
onChange={() => this.selectOS('macos')}
fill={this.state.selectedOS === 'macos'}
/>
);
const ipInput = (
<EuiFieldText
placeholder="Server address..."
value={this.state.serverAddress}
onChange={event => this.setServerAddress(event)}
/>
);
const copyButton = {
position: 'relative',
float: 'right',
zIndex: '1000',
right: '8px',
top: '16px'
};
const codeBlock = {
zIndex: '100'
};
const customTexts = {
rpmText: `WAZUH_MANAGER_IP='${this.state.serverAddress}' yum install wazuh-agent`,
debText: `WAZUH_MANAGER_IP='${this.state.serverAddress}' apt-get install wazuh-agent`,
macosText: `launchctl setenv WAZUH_MANAGER_IP '${this.state.serverAddress}' && installer -pkg wazuh-agent-.pkg -target /`,
winText: `wazuh-agent-3.9.1-1.msi /q ADDRESS='${this.state.serverAddress}' AUTHD_SERVER='${this.state.serverAddress}'`
};
const field = `${this.state.selectedOS}Text`;
const text = customTexts[field];
const guide = (
<div>
{this.state.selectedOS && (
<EuiText>
<div style={copyButton}>
<EuiCopy textToCopy={text}>
{copy => (
<EuiButtonIcon
onClick={copy}
iconType="copy"
aria-label="Copy"
/>
)}
</EuiCopy>
</div>
<EuiCodeBlock style={codeBlock} language="js">
{text}
</EuiCodeBlock>
</EuiText>
)}
</div>
);
const steps = [
{
title: 'Choose your OS',
children: (
<Fragment>
{rpmButton} {debButton} {windowsButton} {macOSButton}
</Fragment>
)
},
{
title: 'Wazuh server address',
children: <Fragment>{ipInput}</Fragment>
},
{
title: 'Complete the installation',
children: (
<div>
<Fragment>
<div>{guide}</div>
</Fragment>
</div>
)
}
];
return (
<div>
<EuiPage restrictWidth="1000px">
<EuiPageBody>
<EuiFlexGroup>
<EuiFlexItem>
<EuiTitle>
<h2>Add a new agent</h2>
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonEmpty
size="s"
onClick={() => this.props.addNewAgent(false)}
iconType="cross"
>
close
</EuiButtonEmpty>
</EuiFlexItem>
</EuiFlexGroup>
<EuiFlexGroup>
<EuiFlexItem>
<EuiPanel>
<EuiFlexItem>
<EuiSteps steps={steps} />
</EuiFlexItem>
</EuiPanel>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPageBody>
</EuiPage>
</div>
);
}
}
RegisterAgent.propTypes = {
addNewAgent: PropTypes.func
};

View File

@ -0,0 +1,20 @@
/*
* Wazuh app - Wazuh Register Agents Component steps texts
* Copyright (C) 2015-2019 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 const RegisterGuideDefs = {
rpmText1:
'rpm --import http://packages.wazuh.com/key/GPG-KEY-WAZUH\ncat > /etc/yum.repos.d/wazuh.repo <<EOF\n[wazuh_repo]\ngpgcheck=1\ngpgkey=https://packages.wazuh.com/key/GPG-KEY-WAZUH\nenabled=1\nname=Wazuh repository\nbaseurl=https://packages.wazuh.com/3.x/yum/\nprotect=1\nEOF',
debText1: 'apt-get install curl apt-transport-https lsb-release',
debText2:
'curl -s https://packages.wazuh.com/key/GPG-KEY-WAZUH | apt-key add -',
debText3:
'echo "deb https://packages.wazuh.com/3.x/apt/ stable main" | tee /etc/apt/sources.list.d/wazuh.list\napt-get update'
};

View File

@ -0,0 +1,58 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { EuiStat, EuiFlexItem, EuiFlexGroup, EuiPanel } from '@elastic/eui';
export class Stats extends Component {
constructor(props) {
super(props);
this.state = {};
}
buildStats(items) {
const stats = items.map(item => {
return (
<EuiFlexItem key={item.description} style={item.style || null}>
<EuiStat
title={item.title}
description={item.description}
textAlign="center"
titleSize="s"
reverse
/>
</EuiFlexItem>
);
});
return stats;
}
render() {
const stats = this.buildStats([
{ title: this.props.id, description: 'ID', style: { maxWidth: 100 } },
{ title: this.props.ip, description: 'IP' },
{ title: this.props.version, description: 'Version' },
{
title: this.props.agentOS,
description: 'OS',
style: { minWidth: 400 }
},
{ title: this.props.dateAdd, description: 'Registration date' },
{ title: this.props.lastKeepAlive, description: 'Last keep alive' }
]);
return (
<EuiPanel betaBadgeLabel={this.props.name}>
<EuiFlexGroup>{stats}</EuiFlexGroup>
</EuiPanel>
);
}
}
Stats.propTypes = {
id: PropTypes.string,
ip: PropTypes.string,
version: PropTypes.string,
agentOS: PropTypes.string,
dateAdd: PropTypes.any,
lastKeepAlive: PropTypes.any,
name: PropTypes.string
};

View File

@ -0,0 +1,247 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import {
EuiCard,
EuiIcon,
EuiPanel,
EuiFlexItem,
EuiFlexGroup,
EuiSpacer,
EuiSwitch,
EuiPopover,
EuiButtonIcon,
EuiFormRow,
EuiFlexGrid,
EuiCallOut
} from '@elastic/eui';
import { TabDescription } from '../../../../server/reporting/tab-description';
import { UnsupportedComponents } from '../../../utils/components-os-support';
export class WelcomeScreen extends Component {
constructor(props) {
super(props);
this.state = {
extensions: this.props.extensions
};
}
onButtonClick(btn) {
this.setState({
[btn]: !this.state[btn]
});
}
closePopover(popover) {
this.setState({
[popover]: false
});
}
toggleExtension(extension) {
const extensions = this.state.extensions;
extensions[extension] = !extensions[extension];
this.setState({
extensions
});
try {
const api = JSON.parse(this.props.api).id;
api && this.props.setExtensions(api, extensions);
} catch (error) {} //eslint-disable-line
}
buildTabCard(tab, icon) {
return (
<EuiFlexItem>
<EuiCard
layout="horizontal"
icon={<EuiIcon size="xl" type={icon} />}
title={TabDescription[tab].title}
onClick={() => this.props.switchTab(tab)}
description={TabDescription[tab].description}
/>
</EuiFlexItem>
);
}
buildPopover(popoverName, extensions) {
const switches = extensions
.filter(extension => this.props.extensions[extension] !== undefined)
.map(extension => {
return (
<EuiFormRow key={extension}>
<EuiSwitch
label={`${TabDescription[extension].title} extension`}
checked={this.state.extensions[extension]}
onChange={() => this.toggleExtension(extension)}
/>
</EuiFormRow>
);
});
return (
<EuiPopover
id={popoverName}
button={
<EuiButtonIcon
aria-label="Extensions"
iconType="eye"
onClick={() => this.onButtonClick(popoverName)}
/>
}
isOpen={this.state[popoverName]}
closePopover={() => this.closePopover(popoverName)}
>
{switches}
</EuiPopover>
);
}
render() {
return (
<div>
<EuiFlexGroup>
<EuiFlexItem>
<EuiPanel betaBadgeLabel="Security Information Management">
<EuiSpacer size="l" />
<EuiFlexGrid columns={2}>
{this.buildTabCard('general', 'dashboardApp')}
{this.buildTabCard('fim', 'loggingApp')}
{this.buildTabCard('configuration', 'gear')}
{this.buildTabCard('syscollector', 'notebookApp')}
</EuiFlexGrid>
</EuiPanel>
</EuiFlexItem>
<EuiFlexItem>
<EuiPanel betaBadgeLabel="Auditing and Policy Monitoring">
<EuiFlexGroup gutterSize="xs">
<EuiFlexItem />
<EuiFlexItem grow={false}>
{this.buildPopover('popoverAuditing', [
'audit',
'oscap',
'ciscat'
])}
</EuiFlexItem>
</EuiFlexGroup>
<EuiFlexGrid columns={2}>
{this.buildTabCard('pm', 'advancedSettingsApp')}
{this.buildTabCard('sca', 'securityAnalyticsApp')}
{this.props.extensions.audit &&
this.buildTabCard('audit', 'monitoringApp')}
{this.props.extensions.oscap &&
this.buildTabCard('oscap', 'codeApp')}
{this.props.extensions.ciscat &&
this.buildTabCard('ciscat', 'auditbeatApp')}
</EuiFlexGrid>
</EuiPanel>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer size="xl" />
<EuiFlexGroup>
<EuiFlexItem>
<EuiPanel betaBadgeLabel="Threat Detection and Response">
<EuiFlexGroup gutterSize="xs">
<EuiFlexItem />
<EuiFlexItem grow={false}>
{this.buildPopover('popoverThreat', [
'virustotal',
'osquery',
'docker'
])}
</EuiFlexItem>
</EuiFlexGroup>
{(
UnsupportedComponents[this.props.agent.agentPlatform] ||
UnsupportedComponents['other']
).includes('vuls') &&
!this.props.extensions.virustotal &&
!this.props.extensions.osquery &&
!this.props.extensions.docker && (
<EuiFlexGroup>
<EuiFlexItem>
<EuiCallOut
title={
<p>
Click the <EuiIcon type="eye" /> icon to show thread
detection and response extensions.
</p>
}
color="success"
iconType="help"
/>
</EuiFlexItem>
</EuiFlexGroup>
)}
<EuiFlexGrid columns={2}>
{!(
UnsupportedComponents[this.props.agent.agentPlatform] ||
UnsupportedComponents['other']
).includes('vuls') && this.buildTabCard('vuls', 'securityApp')}
{this.props.extensions.virustotal &&
this.buildTabCard('virustotal', 'savedObjectsApp')}
{this.props.extensions.osquery &&
this.buildTabCard('osquery', 'searchProfilerApp')}
{this.props.extensions.docker &&
this.buildTabCard('docker', 'spacesApp')}
</EuiFlexGrid>
</EuiPanel>
</EuiFlexItem>
<EuiFlexItem>
<EuiPanel betaBadgeLabel="Regulatory Compliance">
<EuiFlexGroup gutterSize="xs">
<EuiFlexItem />
<EuiFlexItem grow={false}>
{this.buildPopover('popoverRegulatory', [
'pci',
'gdpr',
'hipaa',
'nist'
])}
</EuiFlexItem>
</EuiFlexGroup>
{!this.props.extensions.pci && !this.props.extensions.gdpr && (
<EuiFlexGroup>
<EuiFlexItem>
<EuiCallOut
title={
<p>
Click the <EuiIcon type="eye" /> icon to show
regulatory compliance extensions.
</p>
}
color="success"
iconType="help"
/>
</EuiFlexItem>
</EuiFlexGroup>
)}
{(this.props.extensions.pci || this.props.extensions.gdpr) && (
<EuiFlexGrid columns={2}>
{this.props.extensions.pci &&
this.buildTabCard('pci', 'visTagCloud')}
{this.props.extensions.gdpr &&
this.buildTabCard('gdpr', 'visBarVertical')}
{this.props.extensions.hipaa &&
this.buildTabCard('hipaa', 'emsApp')}
{this.props.extensions.nist &&
this.buildTabCard('nist', 'apmApp')}
</EuiFlexGrid>
)}
</EuiPanel>
</EuiFlexItem>
</EuiFlexGroup>
</div>
);
}
}
WelcomeScreen.propTypes = {
extensions: PropTypes.object,
agent: PropTypes.object,
setExtensions: PropTypes.func,
switchTab: PropTypes.func,
api: PropTypes.string
};

View File

@ -12,9 +12,17 @@
import { uiModules } from 'ui/modules';
import { AgentsPreviewController } from './agents-preview';
import { AgentsController } from './agents';
import { RegisterAgent } from './components/register-agent';
import { ExportConfiguration } from './components/export-configuration';
import { WelcomeScreen } from './components/welcome';
import { Stats } from './components/stats';
const app = uiModules.get('app/wazuh', []);
app
.controller('agentsController', AgentsController)
.controller('agentsPreviewController', AgentsPreviewController);
.controller('agentsPreviewController', AgentsPreviewController)
.value('RegisterAgent', RegisterAgent)
.value('ExportConfiguration', ExportConfiguration)
.value('WelcomeScreenAgent', WelcomeScreen)
.value('StatsAgent', Stats);

View File

@ -15,6 +15,7 @@ import { ExcludedIntelliSenseTriggerKeys } from '../../../util/excluded-devtools
import queryString from 'querystring-browser';
import $ from 'jquery';
import * as FileSaver from '../../services/file-saver';
export class DevToolsController {
/**
* Constructor
@ -328,6 +329,19 @@ export class DevToolsController {
}
}
getPosition(element) {
var xPosition = 0;
var yPosition = 0;
while (element) {
xPosition += element.offsetLeft - element.scrollLeft + element.clientLeft;
yPosition += element.offsetTop - element.scrollTop + element.clientTop;
element = element.offsetParent;
}
return { x: xPosition, y: yPosition };
}
/**
* This set some required settings at init
*/
@ -427,7 +441,7 @@ export class DevToolsController {
const leftOrigWidth = $('#wz-dev-left-column').width();
const rightOrigWidth = $('#wz-dev-right-column').width();
$(evtDocument).mousemove(function(e) {
const leftWidth = e.pageX - 215 + 14;
const leftWidth = e.pageX - 85 + 14;
let rightWidth = leftOrigWidth - leftWidth;
$('#wz-dev-left-column').css('width', leftWidth);
$('#wz-dev-right-column').css('width', rightOrigWidth + rightWidth);
@ -471,6 +485,7 @@ export class DevToolsController {
);
}, 1);
};
dynamicHeight();
}
/**
@ -602,7 +617,7 @@ export class DevToolsController {
} catch (error) {
if ((error || {}).status === -1) {
return this.apiOutputBox.setValue(
"Wazuh API don't reachable. Reason: timeout."
'Wazuh API is not reachable. Reason: timeout.'
);
} else {
const parsedError = this.errorHandler.handle(error, null, null, true);
@ -628,17 +643,4 @@ export class DevToolsController {
this.errorHandler.handle(error, 'Export JSON');
}
}
getPosition(element) {
var xPosition = 0;
var yPosition = 0;
while (element) {
xPosition += element.offsetLeft - element.scrollLeft + element.clientLeft;
yPosition += element.offsetTop - element.scrollTop + element.clientTop;
element = element.offsetParent;
}
return { x: xPosition, y: yPosition };
}
}

View File

@ -0,0 +1,123 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { EuiBasicTable, EuiButtonIcon } from '@elastic/eui';
export class ReportingTable extends Component {
constructor(props) {
super(props);
this.state = {
items: this.props.items,
pageIndex: 0,
pageSize: 10,
showPerPageOptions: true
};
}
UNSAFE_componentWillReceiveProps(nextProps) {
this.setState({
items: nextProps.items
});
}
onTableChange({ page = {} }) {
const { index: pageIndex, size: pageSize } = page;
this.setState({
pageIndex,
pageSize
});
}
filterItems(pageIndex, pageSize) {
const { items } = this.state;
const pages = [];
const len = (items || []).length;
for (let i = 0; i < len; i += pageSize) {
pages.push(items.slice(i, i + pageSize));
}
return { pageOfItems: pages[pageIndex] || [], totalItemCount: len };
}
render() {
const { pageIndex, pageSize } = this.state;
const { pageOfItems, totalItemCount } = this.filterItems(
pageIndex,
pageSize
);
const columns = [
{
field: 'name',
name: 'File',
sortable: true
},
{
field: 'size',
name: 'Size',
sortable: true,
render: size => {
const fixedSize = size / 1024;
return `${fixedSize.toFixed(2)}KB`;
}
},
{
field: 'date',
name: 'Created',
sortable: true,
render: value => this.props.offsetTimestamp(value)
},
{
name: 'Actions',
render: item => {
return (
<div>
<EuiButtonIcon
aria-label="Download report"
onClick={() => this.props.goReport(item.name)}
iconType="importAction"
/>
<EuiButtonIcon
aria-label="Delete report"
onClick={() =>
this.props
.deleteReport(item.name)
.then(items => this.setState({ items }))
}
iconType="trash"
color="danger"
/>
</div>
);
}
}
];
const pagination = {
pageIndex,
pageSize,
totalItemCount,
pageSizeOptions: [10, 20, 50]
};
return (
<div>
<EuiBasicTable
items={pageOfItems}
columns={columns}
pagination={pagination}
onChange={obj => this.onTableChange(obj)}
/>
</div>
);
}
}
ReportingTable.propTypes = {
items: PropTypes.array,
goReport: PropTypes.func,
deleteReport: PropTypes.func
};

View File

@ -0,0 +1,113 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import {
EuiCard,
EuiIcon,
EuiPanel,
EuiFlexItem,
EuiFlexGroup,
EuiSpacer
} from '@elastic/eui';
export class WelcomeScreen extends Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
return (
<div>
<EuiFlexGroup>
<EuiFlexItem>
<EuiPanel betaBadgeLabel="Administration">
<EuiSpacer size="m" />
<EuiFlexGroup>
<EuiFlexItem>
<EuiCard
layout="horizontal"
icon={<EuiIcon size="xl" type="indexRollupApp" />}
title="Ruleset"
onClick={() => this.props.switchTab('ruleset', true)}
description="Manage your Wazuh cluster ruleset."
/>
</EuiFlexItem>
<EuiFlexItem>
<EuiCard
layout="horizontal"
icon={<EuiIcon size="xl" type="usersRolesApp" />}
title="Groups"
onClick={() => this.props.switchTab('groups', true)}
description="Manage your agent groups."
/>
</EuiFlexItem>
</EuiFlexGroup>
<EuiFlexGroup>
<EuiFlexItem>
<EuiCard
layout="horizontal"
icon={<EuiIcon size="xl" type="devToolsApp" />}
title="Configuration"
onClick={() => this.props.switchTab('configuration', true)}
description="Manage your Wazuh cluster configuration."
/>
</EuiFlexItem>
<EuiFlexItem />
</EuiFlexGroup>
</EuiPanel>
</EuiFlexItem>
<EuiFlexItem>
<EuiPanel betaBadgeLabel="Status and reports">
<EuiSpacer size="m" />
<EuiFlexGroup>
<EuiFlexItem>
<EuiCard
layout="horizontal"
icon={<EuiIcon size="xl" type="uptimeApp" />}
title="Status"
onClick={() => this.props.switchTab('status', true)}
description="Manage your Wazuh cluster status."
/>
</EuiFlexItem>
<EuiFlexItem>
<EuiCard
layout="horizontal"
icon={<EuiIcon size="xl" type="indexPatternApp" />}
title="Cluster"
onClick={() => this.props.switchTab('monitoring', true)}
description="Visualize your Wazuh cluster."
/>
</EuiFlexItem>
</EuiFlexGroup>
<EuiFlexGroup>
<EuiFlexItem>
<EuiCard
layout="horizontal"
icon={<EuiIcon size="xl" type="filebeatApp" />}
title="Logs"
onClick={() => this.props.switchTab('logs', true)}
description="Logs from your Wazuh cluster."
/>
</EuiFlexItem>
<EuiFlexItem>
<EuiCard
layout="horizontal"
icon={<EuiIcon size="xl" type="reportingApp" />}
title="Reporting"
onClick={() => this.props.switchTab('reporting', true)}
description="Check your stored Wazuh reports."
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPanel>
</EuiFlexItem>
</EuiFlexGroup>
</div>
);
}
}
WelcomeScreen.propTypes = {
switchTab: PropTypes.func
};

View File

@ -82,6 +82,20 @@ export class ConfigurationController {
}
this.switchConfigurationTab(this.configurationTab, false, true);
});
this.$scope.configurationTabsProps = {};
this.$scope.buildProps = tabs => {
this.$scope.configurationTabsProps = {
clickAction: tab => {
this.$scope.switchConfigurationSubTab(tab);
},
selectedTab:
this.$scope.configurationSubTab || (tabs && tabs.length)
? tabs[0].id
: '',
tabs
};
};
}
switchConfigurationSubTab(configurationSubTab) {

View File

@ -46,7 +46,6 @@ export class FilesController {
this.$scope.viewingDetail = this.$scope.mctrl.showFile.parameters.viewingDetail;
}
this.$scope.mctrl.showFile = false;
this.$scope.$on('editFile', (ev, params) => {
this.$scope.editorReadOnly = false;
this.editFile(params);

View File

@ -22,7 +22,8 @@ export function GroupsController(
shareAgent,
groupHandler,
wzTableFilter,
wazuhConfig
wazuhConfig,
reportingService
) {
$scope.addingGroup = false;
$scope.$on('groupsIsReloaded', () => {
@ -238,6 +239,14 @@ export function GroupsController(
$scope.$applyAsync();
};
$scope.exportConfiguration = enabledComponents => {
reportingService.startConfigReport(
$scope.currentGroup,
'groupConfig',
enabledComponents
);
};
/**
* This show us a group file, for a given group and file
*/
@ -605,6 +614,18 @@ export function GroupsController(
$scope.$broadcast('wazuhSearch', {});
};
$scope.groupsTabsProps = {
clickAction: tab => {
if (tab === 'agents') {
$scope.goBackToAgents();
} else if (tab === 'content') {
$scope.goBackFiles();
}
},
selectedTab: $scope.groupsSelectedTab || 'agents',
tabs: [{ id: 'agents', name: 'Agents' }, { id: 'content', name: 'Content' }]
};
// Come from the pencil icon on the groups table
$scope.$on('openGroupFromList', (ev, parameters) => {
$scope.editingFile = true;

View File

@ -24,6 +24,8 @@ import { ConfigurationRulesetController } from './config-ruleset';
import { ConfigurationGroupsController } from './config-groups';
import { EditionController } from './edition';
import { FilesController } from './files';
import { WelcomeScreen } from './components/welcome';
import { ReportingTable } from './components/reporting-table';
const app = uiModules.get('app/wazuh', []);
@ -40,4 +42,6 @@ app
.controller('configurationRulesetController', ConfigurationRulesetController)
.controller('configurationGroupsController', ConfigurationGroupsController)
.controller('editionController', EditionController)
.controller('filesController', FilesController);
.controller('filesController', FilesController)
.value('WelcomeScreenManagement', WelcomeScreen)
.value('ReportingTable', ReportingTable);

View File

@ -51,42 +51,51 @@ export class ManagementController {
this.$scope.$on('setCurrentGroup', (ev, params) => {
this.currentGroup = (params || {}).currentGroup || false;
});
this.$scope.$on('removeCurrentGroup', () => {
this.currentGroup = false;
this.appState.setNavigation({ status: true });
});
this.$scope.$on('setCurrentRule', (ev, params) => {
this.setCurrentRule(params);
});
this.$scope.$on('removeCurrentRule', () => {
this.currentRule = false;
this.appState.setNavigation({ status: true });
this.$location.search('currentRule', null);
});
this.$scope.$on('setCurrentDecoder', (ev, params) => {
this.currentDecoder = (params || {}).currentDecoder || false;
this.$location.search('currentDecoder', true);
this.appState.setNavigation({ status: true });
});
this.$scope.$on('removeCurrentDecoder', () => {
this.currentDecoder = false;
this.appState.setNavigation({ status: true });
this.$location.search('currentDecoder', null);
});
this.$scope.$on('setCurrentList', (ev, params) => {
this.currentList = (params || {}).currentList || false;
this.$location.search('currentList', true);
this.appState.setNavigation({ status: true });
this.$scope.$applyAsync();
});
this.$scope.$on('removeCurrentList', () => {
this.currentList = false;
this.appState.setNavigation({ status: true });
this.$location.search('currentList', null);
});
this.$scope.$on('setCurrentConfiguration', (ev, params) => {
this.currentConfiguration = (params || {}).currentConfiguration || false;
});
this.$scope.$on('removeCurrentConfiguration', () => {
this.currentConfiguration = false;
});
@ -137,6 +146,31 @@ export class ManagementController {
: this.restartManager();
});
this.appState = appState;
this.welcomeCardsProps = {
switchTab: (tab, setNav) => this.switchTab(tab, setNav)
};
this.rulesetTabsProps = {
clickAction: tab => this.setRulesTab(tab),
selectedTab: this.rulesetTab || 'rules',
tabs: [
{ id: 'rules', name: 'Rules' },
{ id: 'decoders', name: 'Decoders' },
{ id: 'cdblists', name: 'Lists' }
]
};
this.managementTabsProps = {
clickAction: tab => this.switchTab(tab, true),
selectedTab: this.tab,
tabs: [
{ id: 'status', name: 'Status' },
{ id: 'logs', name: 'Logs' },
{ id: 'monitoring', name: 'Cluster' },
{ id: 'reporting', name: 'Reporting' }
]
};
}
/**
@ -263,6 +297,7 @@ export class ManagementController {
this.currentRule = false;
this.currentDecoder = false;
this.currentList = false;
this.managementTabsProps.selectedTab = this.tab;
}
this.$location.search('tab', this.tab);

View File

@ -303,7 +303,6 @@ export function ClusterController(
//listeners
$scope.$on('$destroy', () => {
$location.search('tabView', null);
discoverPendingUpdates.removeAll();
tabVisualizations.removeAll();
rawVisualizations.removeAll();

View File

@ -80,6 +80,33 @@ export function RulesController(
);
$scope.appliedFilters.push(filter);
$scope.$broadcast('wazuhFilter', { filter });
} else if (
term &&
term.startsWith('hipaa:') &&
term.split('hipaa:')[1].trim()
) {
$scope.custom_search = '';
const filter = { name: 'hipaa', value: term.split('hipaa:')[1].trim() };
$scope.appliedFilters = $scope.appliedFilters.filter(
item => item.name !== 'hipaa'
);
$scope.appliedFilters.push(filter);
$scope.$broadcast('wazuhFilter', { filter });
} else if (
term &&
term.startsWith('nist-800-53:') &&
term.split('nist-800-53:')[1].trim()
) {
$scope.custom_search = '';
const filter = {
name: 'nist-800-53',
value: term.split('nist-800-53:')[1].trim()
};
$scope.appliedFilters = $scope.appliedFilters.filter(
item => item.name !== 'nist-800-53'
);
$scope.appliedFilters.push(filter);
$scope.$broadcast('wazuhFilter', { filter });
} else if (
term &&
term.startsWith('file:') &&

View File

@ -68,6 +68,7 @@ export class HealthCheck {
this.errors = [];
this.processedChecks = 0;
this.totalChecks = 0;
this.$rootScope.hideWzMenu = true;
}
/**
@ -172,7 +173,7 @@ export class HealthCheck {
const apiVersion = versionData.data.data;
const setupData = await this.genericReq.request(
'GET',
'/elastic/setup'
'/api/setup'
);
if (!setupData.data.data['app-version'] || !apiVersion) {
this.errorHandler.handle(
@ -266,7 +267,7 @@ export class HealthCheck {
}
if (!this.errors || !this.errors.length) {
await this.$timeout(800);
await this.$timeout(300);
this.$window.location.assign(
chrome.addBasePath('wazuh#' + this.$rootScope.previousLocation || '')
);

View File

@ -11,24 +11,17 @@
*/
import chrome from 'ui/chrome';
export class ReportingController {
/**
* Class controller
* @param {*} $scope
* @param {*} errorHandler
* @param {*} genericReq
*/
constructor($scope, errorHandler, genericReq, $window, timeService) {
// Services
this.$scope = $scope;
this.$window = $window;
this.errorHandler = errorHandler;
this.genericReq = genericReq;
this.loading = true;
this.itemsPerPage = 15;
this.pagedItems = [];
this.currentPage = 0;
this.items = [];
this.gap = 0;
this.timeService = timeService;
// Variables
this.loading = true;
this.items = [];
}
/**
@ -39,14 +32,9 @@ export class ReportingController {
}
/**
* This performs a search
* Transform a give date applying the browser's offset
* @param {*} time
*/
search() {
this.filteredItems = this.items;
this.currentPage = 0;
this.groupToPages();
}
offsetTimestamp(time) {
try {
return this.timeService.offset(time);
@ -56,86 +44,28 @@ export class ReportingController {
}
/**
* This delete a report with a given name
* Deletes a report
* @param {*} name The name of the report
*/
async deleteReport(name) {
try {
this.loading = true;
await this.genericReq.request('DELETE', '/reports/' + name, {});
await this.load();
this.errorHandler.info('Success', 'Reporting');
this.items = this.items.filter(item => item.name !== name);
this.errorHandler.info('Success');
} catch (error) {
this.errorHandler.handle(error.message || error);
}
}
// calculate page in place
groupToPages() {
this.pagedItems = [];
for (let i = 0; i < this.filteredItems.length; i++) {
if (i % this.itemsPerPage === 0) {
this.pagedItems[Math.floor(i / this.itemsPerPage)] = [
this.filteredItems[i]
];
} else {
this.pagedItems[Math.floor(i / this.itemsPerPage)].push(
this.filteredItems[i]
);
}
}
return this.items;
}
/**
* This organize in pages of given size the items
* @param {*} size
* @param {*} start
* @param {*} end
* Downloads the report
* @param {*} name The name of the report
*/
range(size, start, end) {
const ret = [];
if (size < end) {
end = size;
start = size - this.gap;
}
for (let i = start; i < end; i++) {
ret.push(i);
}
return ret;
goReport(name) {
this.$window.open(chrome.addBasePath(`/reports/${name}`), '_blank');
}
/**
* This navigates to the prevoous page
*/
prevPage() {
if (this.currentPage > 0) {
this.currentPage--;
}
}
/**
* This navigates to the next page
*/
nextPage(n) {
if (!n && n !== 0 && this.currentPage < this.pagedItems.length - 1) {
this.currentPage++;
}
}
/**
* This navigates to a given page
*/
setPage(n) {
this.currentPage = n;
this.nextPage(n);
}
goReport = item => {
this.$window.open(chrome.addBasePath(`/reports/${item}`), '_blank');
};
/**
* On controller loads
*/
@ -143,19 +73,19 @@ export class ReportingController {
try {
this.loading = true;
const data = await this.genericReq.request('GET', '/reports', {});
this.items = data.data.list;
const gap = this.items.length / 15;
const gapInteger = parseInt(this.items.length / 15);
this.gap =
gap - parseInt(this.items.length / 15) > 0
? gapInteger + 1
: gapInteger;
if (this.gap > 5) this.gap = 5;
this.search();
this.loading = false;
this.$scope.$applyAsync();
this.items = ((data || {}).data || {}).list || [];
this.reportingTableProps = {
deleteReport: name => this.deleteReport(name),
goReport: name => this.goReport(name),
offsetTimestamp: time => this.offsetTimestamp(time),
load: () => this.load(true),
items: this.items
};
} catch (error) {
this.errorHandler.handle(error.message || error);
}
this.loading = false;
this.$scope.$applyAsync();
return this.items;
}
}

View File

@ -0,0 +1,45 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { EuiStat, EuiFlexItem, EuiFlexGroup } from '@elastic/eui';
export class AlertsStats extends Component {
constructor(props) {
super(props);
this.state = {};
}
buildStats() {
const stats = this.props.items.map(item => {
const title = typeof item.value !== 'undefined' ? item.value : '-';
return (
<EuiFlexItem key={`${item.description}${title}`}>
<EuiStat
title={title}
description={item.description}
titleColor={item.color || 'primary'}
textAlign="center"
/>
</EuiFlexItem>
);
});
return stats;
}
render() {
const items = this.buildStats();
return (
<div>
<EuiFlexGroup>
<EuiFlexItem />
{items}
<EuiFlexItem />
</EuiFlexGroup>
</div>
);
}
}
AlertsStats.propTypes = {
items: PropTypes.array
};

View File

@ -0,0 +1,154 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { EuiButtonEmpty, EuiButtonIcon, EuiCard, EuiFlexItem, EuiFlexGroup } from '@elastic/eui';
export class RequirementCard extends Component {
constructor(props) {
super(props);
this.state = {
position: 0,
slider: [],
sliderLength: 0
};
this.chunkSize = 4;
this.chartNum = 250;
this.expanded = false;
}
buildSlider() {
const items = this.props.items.map((req, index) => {
const title = `${this.props.reqTitle}: ${req.title}`;
const expandMessage = this.expanded ? 'Show less' : 'More info'
const cardFooterContent = (
<EuiButtonEmpty
iconType="iInCircle"
size="xs"
className="footer-req wz-margin--10"
onClick={() => this.expand()}>
{expandMessage}
</EuiButtonEmpty>
);
if (req.content.length >= this.chartNum) {
const content = this.expanded ? req.content : `${req.content.substring(0, this.chartNum - 5)}...`
return (
<EuiFlexItem key={index}>
<EuiCard
title={title}
description={content}
textAlign="left"
className="wz-padding-bt-5 reqCard"
footer={cardFooterContent}
/>
</EuiFlexItem>
);
} else {
return (
<EuiFlexItem key={index}>
<EuiCard
title={title}
description={req.content}
textAlign="left"
className="wz-padding-bt-5 reqCard"
/>
</EuiFlexItem>
)
}
});
const slider = this.chunk(items, this.chunkSize);
const lastArr = slider.length - 1;
const last = slider[lastArr];
const rest = this.chunkSize - last.length;
if (last.length < this.chunkSize) {
for (let i = 0; i < rest; i++) {
slider[lastArr].push(
<EuiFlexItem key={`hidden${i}`}>
<EuiCard
title='Title'
className='hiddenCard'
description='Description'
textAlign='left'
/>
</EuiFlexItem>
)
}
}
this.setState({ slider: slider, sliderLength: slider.length });
}
/**
* Expands the card to show all info
*/
expand() {
this.expanded = !this.expanded;
this.buildSlider()
}
/**
* Slides to the right the slider
*/
slideRight() {
const newPos = this.state.position + 1;
this.setState({ position: newPos });
}
/**
* Slides to the left the slider
*/
slideLeft() {
const newPos = this.state.position - 1;
this.setState({ position: newPos });
}
/**
* Split an array into smallers array
* @param {Array} array
* @param {Number} size
*/
chunk = (array, size) => {
const chunked = [];
for (const item of array) {
const last = chunked[chunked.length - 1];
if (!last || last.length === size) {
chunked.push([item]);
} else {
last.push(item);
}
}
return chunked;
}
render() {
if (!this.state.slider.length) this.buildSlider();
const cards = this.state.slider[this.state.position];
return (
<div>
<EuiFlexGroup gutterSize="l">
{(this.state.sliderLength > 1 && this.state.position > 0) && (
<EuiButtonIcon
className="wz-margin-left-10"
iconType="arrowLeft"
aria-label="Previous"
onClick={() => this.slideLeft()}
/>
)}
{cards}
{(this.state.sliderLength > 1 && this.state.position < this.state.sliderLength - 1) && (
<EuiButtonIcon
className="wz-margin-right-10"
iconType="arrowRight"
aria-label="Next"
onClick={() => this.slideRight()}
/>
)}
</EuiFlexGroup>
</div >
);
}
}
RequirementCard.propTypes = {
items: PropTypes.array,
reqTitle: PropTypes.string
};

View File

@ -0,0 +1,61 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { EuiStat, EuiFlexItem, EuiFlexGroup } from '@elastic/eui';
export class Stats extends Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
return (
<div>
<EuiFlexGroup>
<EuiFlexItem />
<EuiFlexItem>
<EuiStat
title={this.props.total}
description="Total agents"
titleColor="primary"
textAlign="center"
/>
</EuiFlexItem>
<EuiFlexItem>
<EuiStat
title={this.props.active}
description="Active agents"
titleColor="secondary"
textAlign="center"
/>
</EuiFlexItem>
<EuiFlexItem>
<EuiStat
title={this.props.disconnected}
description="Disconnected agents"
titleColor="danger"
textAlign="center"
/>
</EuiFlexItem>
<EuiFlexItem>
<EuiStat
title={this.props.neverConnected}
description="Never connected agents"
titleColor="subdued"
textAlign="center"
/>
</EuiFlexItem>
<EuiFlexItem />
</EuiFlexGroup>
</div>
);
}
}
Stats.propTypes = {
total: PropTypes.any,
active: PropTypes.any,
disconnected: PropTypes.any,
neverConnected: PropTypes.any
};

View File

@ -0,0 +1,223 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import {
EuiCard,
EuiIcon,
EuiPanel,
EuiFlexItem,
EuiFlexGroup,
EuiSpacer,
EuiSwitch,
EuiPopover,
EuiButtonIcon,
EuiFormRow,
EuiFlexGrid,
EuiCallOut
} from '@elastic/eui';
import { TabDescription } from '../../../../server/reporting/tab-description';
export class WelcomeScreen extends Component {
constructor(props) {
super(props);
this.state = {
extensions: this.props.extensions
};
}
onButtonClick(btn) {
this.setState({
[btn]: !this.state[btn]
});
}
closePopover(popover) {
this.setState({
[popover]: false
});
}
toggleExtension(extension) {
const extensions = this.state.extensions;
extensions[extension] = !extensions[extension];
this.setState({
extensions
});
try {
const api = JSON.parse(this.props.api).id;
api && this.props.setExtensions(api, extensions);
} catch (error) {} //eslint-disable-line
}
buildTabCard(tab, icon) {
return (
<EuiFlexItem>
<EuiCard
layout="horizontal"
icon={<EuiIcon size="xl" type={icon} />}
title={TabDescription[tab].title}
onClick={() => this.props.switchTab(tab)}
description={TabDescription[tab].description}
/>
</EuiFlexItem>
);
}
buildPopover(popoverName, extensions) {
const switches = extensions.map(extension => {
return (
<EuiFormRow key={extension}>
<EuiSwitch
label={`${TabDescription[extension].title} extension`}
checked={this.state.extensions[extension]}
onChange={() => this.toggleExtension(extension)}
/>
</EuiFormRow>
);
});
return (
<EuiPopover
id={popoverName}
button={
<EuiButtonIcon
aria-label="Extensions"
iconType="eye"
onClick={() => this.onButtonClick(popoverName)}
/>
}
isOpen={this.state[popoverName]}
closePopover={() => this.closePopover(popoverName)}
>
{switches}
</EuiPopover>
);
}
render() {
return (
<div>
<EuiFlexGroup>
<EuiFlexItem>
<EuiPanel betaBadgeLabel="Security Information Management">
<EuiFlexGroup gutterSize="xs">
<EuiFlexItem />
<EuiFlexItem grow={false}>
{this.buildPopover('popoverSecurity', ['aws'])}
</EuiFlexItem>
</EuiFlexGroup>
<EuiFlexGrid columns={2}>
{this.buildTabCard('general', 'dashboardApp')}
{this.buildTabCard('fim', 'loggingApp')}
{this.props.extensions.aws &&
this.buildTabCard('aws', 'logoAWSMono')}
</EuiFlexGrid>
</EuiPanel>
</EuiFlexItem>
<EuiFlexItem>
<EuiPanel betaBadgeLabel="Auditing and Policy Monitoring">
<EuiFlexGroup gutterSize="xs">
<EuiFlexItem />
<EuiFlexItem grow={false}>
{this.buildPopover('popoverAuditing', [
'audit',
'oscap',
'ciscat'
])}
</EuiFlexItem>
</EuiFlexGroup>
<EuiFlexGrid columns={2}>
{this.buildTabCard('pm', 'advancedSettingsApp')}
{this.props.extensions.audit &&
this.buildTabCard('audit', 'monitoringApp')}
{this.props.extensions.oscap &&
this.buildTabCard('oscap', 'codeApp')}
{this.props.extensions.ciscat &&
this.buildTabCard('ciscat', 'auditbeatApp')}
</EuiFlexGrid>
</EuiPanel>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer size="xl" />
<EuiFlexGroup>
<EuiFlexItem>
<EuiPanel betaBadgeLabel="Threat Detection and Response">
<EuiFlexGroup gutterSize="xs">
<EuiFlexItem />
<EuiFlexItem grow={false}>
{this.buildPopover('popoverThreat', [
'virustotal',
'osquery',
'docker'
])}
</EuiFlexItem>
</EuiFlexGroup>
<EuiFlexGrid columns={2}>
{this.buildTabCard('vuls', 'securityApp')}
{this.props.extensions.virustotal &&
this.buildTabCard('virustotal', 'savedObjectsApp')}
{this.props.extensions.osquery &&
this.buildTabCard('osquery', 'searchProfilerApp')}
{this.props.extensions.docker &&
this.buildTabCard('docker', 'spacesApp')}
</EuiFlexGrid>
</EuiPanel>
</EuiFlexItem>
<EuiFlexItem>
<EuiPanel betaBadgeLabel="Regulatory Compliance">
<EuiFlexGroup gutterSize="xs">
<EuiFlexItem />
<EuiFlexItem grow={false}>
{this.buildPopover('popoverRegulatory', [
'pci',
'gdpr',
'hipaa',
'nist'
])}
</EuiFlexItem>
</EuiFlexGroup>
{!this.props.extensions.pci && !this.props.extensions.gdpr && (
<EuiFlexGroup>
<EuiFlexItem>
<EuiCallOut
title={
<p>
Click the <EuiIcon type="eye" /> icon to show
regulatory compliance extensions.
</p>
}
color="success"
iconType="help"
/>
</EuiFlexItem>
</EuiFlexGroup>
)}
{(this.props.extensions.pci || this.props.extensions.gdpr) && (
<EuiFlexGrid columns={2}>
{this.props.extensions.pci &&
this.buildTabCard('pci', 'visTagCloud')}
{this.props.extensions.gdpr &&
this.buildTabCard('gdpr', 'visBarVertical')}
{this.props.extensions.hipaa &&
this.buildTabCard('hipaa', 'emsApp')}
{this.props.extensions.nist &&
this.buildTabCard('nist', 'apmApp')}
</EuiFlexGrid>
)}
</EuiPanel>
</EuiFlexItem>
</EuiFlexGroup>
</div>
);
}
}
WelcomeScreen.propTypes = {
extensions: PropTypes.object,
switchTab: PropTypes.func,
setExtensions: PropTypes.func,
api: PropTypes.string
};

View File

@ -11,7 +11,16 @@
*/
import { uiModules } from 'ui/modules';
import { OverviewController } from './overview';
import { WelcomeScreen } from './components/welcome';
import { Stats } from './components/stats';
import { AlertsStats } from './components/alerts-stats';
import { RequirementCard } from './components/requirement-card';
const app = uiModules.get('app/wazuh', []);
const app = uiModules.get('app/wazuh', ['react']);
app.controller('overviewController', OverviewController);
app
.controller('overviewController', OverviewController)
.value('WelcomeScreenOverview', WelcomeScreen)
.value('StatsOverview', Stats)
.value('AlertsStats', AlertsStats)
.value('RequirementCard', RequirementCard);

View File

@ -114,15 +114,20 @@ export class OverviewController {
// This object represents the number of visualizations per tab; used to show a progress bar
this.tabVisualizations.assign('overview');
this.hostMonitoringTabs = ['general', 'fim', 'aws'];
this.systemAuditTabs = ['pm', 'audit', 'oscap', 'ciscat', 'sca'];
this.securityTabs = ['vuls', 'virustotal', 'osquery', 'docker'];
this.complianceTabs = ['pci', 'gdpr'];
this.wodlesConfiguration = null;
this.init();
this.welcomeCardsProps = {
api: this.appState.getCurrentAPI(),
switchTab: tab => this.switchTab(tab),
extensions: this.extensions,
setExtensions: (api, extensions) =>
this.appState.setExtensions(api, extensions)
};
this.setTabs();
this.$scope.$on('$destroy', () => {
this.visFactoryService.clearAll();
});
@ -177,6 +182,35 @@ export class OverviewController {
}
}
/**
* Build the current section tabs
*/
setTabs() {
this.overviewTabsProps = false;
this.currentPanel = this.commonData.getCurrentPanel(this.tab);
if (!this.currentPanel) return;
const tabs = this.commonData.getTabsFromCurrentPanel(
this.currentPanel,
this.extensions,
this.tabNames
);
this.overviewTabsProps = {
clickAction: tab => {
this.switchTab(tab, true);
},
selectedTab:
this.tab ||
(this.currentPanel && this.currentPanel.length
? this.currentPanel[0]
: ''),
tabs
};
this.$scope.$applyAsync();
}
// Switch subtab
async switchSubtab(
subtab,
@ -201,7 +235,9 @@ export class OverviewController {
localChange || preserveDiscover
);
} else {
this.$scope.$emit('changeTabView', { tabView: this.tabView });
this.$scope.$emit('changeTabView', {
tabView: this.tabView
});
}
this.checkMetrics(this.tab, subtab);
@ -238,14 +274,22 @@ export class OverviewController {
if (newTab === 'pci') {
const pciTabs = await this.commonData.getPCI();
this.pciTabs = pciTabs;
this.selectedPciIndex = 0;
this.pciReqs = {items: pciTabs, reqTitle: 'PCI DSS Requirement'};
}
if (newTab === 'gdpr') {
const gdprTabs = await this.commonData.getGDPR();
this.gdprTabs = gdprTabs;
this.selectedGdprIndex = 0;
this.gdprReqs = {items: gdprTabs, reqTitle: 'GDPR Requirement'};
}
if (newTab === 'hipaa') {
const hipaaTabs = await this.commonData.getHIPAA();
this.hipaaReqs = {items: hipaaTabs, reqTitle: 'HIPAA Requirement'};
}
if (newTab === 'nist') {
const nistTabs = await this.commonData.getNIST();
this.nistReqs = {items: nistTabs, reqTitle: 'NIST 800-53 Requirement'};
}
if (newTab !== 'welcome') this.tabHistory.push(newTab);
@ -278,6 +322,7 @@ export class OverviewController {
} catch (error) {
this.errorHandler.handle(error.message || error);
}
this.setTabs();
this.$scope.$applyAsync();
return;
}

View File

@ -0,0 +1,90 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import {
EuiFlexGroup,
EuiFlexItem,
EuiFormRow,
EuiFieldText,
EuiFieldPassword,
EuiFieldNumber,
EuiButton,
EuiPanel
} from '@elastic/eui';
export class AddApi extends Component {
constructor(props) {
super(props);
this.state = {
user: '',
password: '',
url: '',
port: 55000
};
}
onChangeEdit(e, field) {
this.setState({
[field]: e.target.value
});
}
render() {
return (
<EuiPanel>
<EuiFlexGroup>
<EuiFlexItem>
<EuiFormRow label="Username">
<EuiFieldText
onChange={e => this.onChangeEdit(e, 'user')}
placeholder="foo"
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem>
<EuiFormRow label="Password">
<EuiFieldPassword
onChange={e => this.onChangeEdit(e, 'password')}
placeholder="bar"
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem>
<EuiFormRow label="Host">
<EuiFieldText
onChange={e => this.onChangeEdit(e, 'url')}
placeholder="http://localhost"
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem>
<EuiFormRow label="Port">
<EuiFieldNumber
max={99999}
onChange={e => this.onChangeEdit(e, 'port')}
placeholder={55000}
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFormRow label="Actions">
<EuiButton
aria-label="Save"
iconType="save"
color="primary"
onClick={() => this.props.saveSettings({ ...this.state })}
>
Save
</EuiButton>
</EuiFormRow>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPanel>
);
}
}
AddApi.propTypes = {
saveSettings: PropTypes.func
};

View File

@ -0,0 +1,256 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { RIGHT_ALIGNMENT } from '@elastic/eui/lib/services';
import {
EuiFlexGroup,
EuiFlexItem,
EuiBasicTable,
EuiPanel,
EuiButtonIcon,
EuiToolTip,
EuiFormRow,
EuiFieldText,
EuiFieldPassword,
EuiFieldNumber,
EuiButton,
EuiSpacer,
EuiButtonEmpty
} from '@elastic/eui';
export class ApiTable extends Component {
constructor(props) {
super(props);
this.state = {
itemIdToExpandedRowMap: {},
user: '',
password: '',
url: '',
port: 55000,
apiEntries: [],
currentDefault: 0
};
}
componentDidMount() {
this.setState({
apiEntries: [...this.props.apiEntries],
currentDefault: this.props.currentDefault
});
}
onChangeEdit(e, field) {
this.setState({
[field]: e.target.value
});
}
toggleDetails(item) {
const itemIdToExpandedRowMap = { ...this.state.itemIdToExpandedRowMap };
if (itemIdToExpandedRowMap[item._id]) {
delete itemIdToExpandedRowMap[item._id];
} else {
itemIdToExpandedRowMap[item._id] = (
<EuiFlexGroup>
<EuiFlexItem>
<EuiFormRow label="Username">
<EuiFieldText
onChange={e => this.onChangeEdit(e, 'user')}
placeholder="foo"
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem>
<EuiFormRow label="Password">
<EuiFieldPassword
onChange={e => this.onChangeEdit(e, 'password')}
placeholder="bar"
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem>
<EuiFormRow label="Host">
<EuiFieldText
onChange={e => this.onChangeEdit(e, 'url')}
placeholder="http://localhost"
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem>
<EuiFormRow label="Port">
<EuiFieldNumber
max={99999}
onChange={e => this.onChangeEdit(e, 'port')}
placeholder={55000}
/>
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFormRow label="Actions">
<EuiButton
aria-label="Update"
iconType="save"
color="primary"
onClick={() =>
this.props
.updateSettings({ ...this.state, _id: item._id }, true)
.then(result => result !== -1 && this.toggleDetails(item))
}
>
Save
</EuiButton>
</EuiFormRow>
</EuiFlexItem>
</EuiFlexGroup>
);
}
this.setState({ itemIdToExpandedRowMap });
}
UNSAFE_componentWillReceiveProps(nextProps) {
this.setState({
apiEntries: nextProps.apiEntries,
currentDefault: nextProps.currentDefault
});
}
render() {
const { itemIdToExpandedRowMap } = this.state;
const items = [...this.state.apiEntries];
const columns = [
{
field: '_source.cluster_info.cluster',
name: 'Cluster',
align: 'left'
},
{
field: '_source.cluster_info.manager',
name: 'Manager',
align: 'left'
},
{
field: '_source.url',
name: 'Host',
align: 'left'
},
{
field: '_source.api_port',
name: 'Port',
align: 'left'
},
{
field: '_source.api_user',
name: 'User',
align: 'left'
},
{
name: 'Actions',
render: item => (
<EuiFlexGroup>
<EuiFlexItem grow={false}>
<EuiToolTip position="bottom" content={<p>Set as default</p>}>
<EuiButtonIcon
iconType={
item._id === this.state.currentDefault
? 'starFilled'
: 'starEmpty'
}
aria-label="Set as default"
onClick={() => {
const currentDefault = this.props.setDefault(item);
this.setState({
currentDefault
});
}}
/>
</EuiToolTip>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiToolTip position="bottom" content={<p>Check connection</p>}>
<EuiButtonIcon
aria-label="Check connection"
iconType="refresh"
onClick={() => this.props.checkManager(item)}
color="success"
/>
</EuiToolTip>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiToolTip position="bottom" content={<p>Remove</p>}>
<EuiButtonIcon
aria-label="Remove manager"
iconType="trash"
onClick={() =>
this.props.removeManager(item).then(apiEntries =>
this.setState({
apiEntries
})
)
}
color="danger"
/>
</EuiToolTip>
</EuiFlexItem>
</EuiFlexGroup>
)
},
{
align: RIGHT_ALIGNMENT,
width: '40px',
isExpander: true,
render: item => (
<EuiToolTip position="bottom" content={<p>Edit</p>}>
<EuiButtonIcon
onClick={() => this.toggleDetails(item)}
aria-label={
itemIdToExpandedRowMap[item.id]
? 'Collapse edition'
: 'Expand edition'
}
iconType={
itemIdToExpandedRowMap[item.id] ? 'arrowUp' : 'arrowDown'
}
/>
</EuiToolTip>
)
}
];
return (
<EuiPanel>
<EuiBasicTable
itemId="_id"
items={items}
columns={columns}
itemIdToExpandedRowMap={this.state.itemIdToExpandedRowMap}
isExpandable={true}
/>
<EuiSpacer size="m" />
<EuiFlexGroup>
<EuiFlexItem />
<EuiFlexItem grow={false}>
<EuiButtonEmpty
aria-label="Add"
iconType="plusInCircle"
onClick={() => this.props.switch()}
>
Add new
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem />
</EuiFlexGroup>
</EuiPanel>
);
}
}
ApiTable.propTypes = {
apiEntries: PropTypes.array,
currentDefault: PropTypes.string,
updateSettings: PropTypes.func,
setDefault: PropTypes.func,
checkManager: PropTypes.func,
removeManager: PropTypes.func,
switch: PropTypes.func
};

View File

@ -14,4 +14,10 @@ import { SettingsController } from './settings';
const app = uiModules.get('app/wazuh', []);
app.controller('settingsController', SettingsController);
import { ApiTable } from './components/api-table';
import { AddApi } from './components/add-api';
app
.controller('settingsController', SettingsController)
.value('ApiTable', ApiTable)
.value('AddApi', AddApi);

View File

@ -95,6 +95,7 @@ export class SettingsController {
const location = this.$location.search();
if (location && location.tab) {
this.tab = location.tab;
this.settingsTabsProps.selectedTab = this.tab;
}
this.savingApi = false;
@ -105,7 +106,23 @@ export class SettingsController {
*/
$onInit() {
// Loading data
this.getSettings().then(() => this.getAppInfo());
this.getSettings().then(() => {
this.apiTableProps = {
currentDefault: this.currentDefault,
apiEntries: this.apiEntries,
compressed: true,
setDefault: entry => this.setDefault(entry),
checkManager: entry => this.checkManager(entry),
removeManager: entry => this.removeManager(entry),
updateSettings: (entry, useItem = false) =>
this.updateSettings(entry, useItem),
switch: () => this.switch()
};
this.addApiProps = {
saveSettings: entry => this.saveSettings(entry)
};
return this.getAppInfo();
});
}
/**
@ -127,7 +144,7 @@ export class SettingsController {
async removeManager(item) {
try {
const currentApi = this.appState.getCurrentAPI();
let index = this.apiEntries.indexOf(item);
let index = this.apiEntries.map(item => item._id).indexOf(item._id);
if (currentApi) {
if (this.apiEntries[index]._id === JSON.parse(currentApi).id) {
// We are trying to remove the one selected as default
@ -146,12 +163,12 @@ export class SettingsController {
for (const key in this.showEditForm) this.showEditForm[key] = false;
this.$scope.$emit('updateAPI', {});
this.errorHandler.info('The API was removed successfully', 'Settings');
this.apiTableProps.apiEntries = this.apiEntries;
this.$scope.$applyAsync();
return;
} catch (error) {
this.errorHandler.handle('Could not remove the API', 'Settings');
}
return;
return this.apiEntries;
}
// Get current API index
@ -175,7 +192,7 @@ export class SettingsController {
// Set default API
setDefault(item) {
const index = this.apiEntries.indexOf(item);
const index = this.apiEntries.map(item => item._id).indexOf(item._id);
this.appState.setClusterInfo(this.apiEntries[index]._source.cluster_info);
@ -199,6 +216,8 @@ export class SettingsController {
const currentApi = this.appState.getCurrentAPI();
this.currentDefault = JSON.parse(currentApi).id;
this.apiTableProps.currentDefault = this.currentDefault;
this.$scope.$applyAsync();
this.errorHandler.info(
`API ${this.apiEntries[index]._source.cluster_info.manager} set as default`,
@ -214,10 +233,8 @@ export class SettingsController {
);
}
this.extensions = this.appState.getExtensions(JSON.parse(currentApi).id);
this.$scope.$applyAsync();
return;
return this.currentDefault;
}
// Get settings function
@ -262,8 +279,6 @@ export class SettingsController {
);
}
this.extensions = this.appState.getExtensions(JSON.parse(currentApi).id);
this.$scope.$applyAsync();
return;
} catch (error) {
@ -327,12 +342,22 @@ export class SettingsController {
}
// Save settings function
async saveSettings() {
async saveSettings(entry) {
try {
if (this.savingApi) {
this.errorHandler.info('Please, wait for success message', 'Settings');
return;
return this.apiEntries;
}
if (entry) {
this.formData = {
user: entry.user,
password: entry.password,
url: entry.url,
port: entry.port
};
}
this.savingApi = true;
this.messageError = '';
this.isEditing = false;
@ -342,7 +367,7 @@ export class SettingsController {
this.messageError = invalid;
this.errorHandler.handle(invalid, 'Settings');
this.savingApi = false;
return;
return this.apiEntries;
}
const tmpData = {
@ -362,6 +387,8 @@ export class SettingsController {
tmpData.extensions.audit = config['extensions.audit'];
tmpData.extensions.pci = config['extensions.pci'];
tmpData.extensions.gdpr = config['extensions.gdpr'];
tmpData.extensions.hipaa = config['extensions.hipaa'];
tmpData.extensions.nist = config['extensions.nist'];
tmpData.extensions.oscap = config['extensions.oscap'];
tmpData.extensions.ciscat = config['extensions.ciscat'];
tmpData.extensions.aws = config['extensions.aws'];
@ -385,7 +412,6 @@ export class SettingsController {
_id: data.data.response._id,
_source: {
cluster_info: tmpData.cluster_info,
active: tmpData.active,
url: tmpData.url,
api_user: tmpData.user,
api_port: tmpData.port,
@ -394,6 +420,11 @@ export class SettingsController {
};
this.apiEntries.push(newEntry);
this.apiEntries = this.apiEntries.sort(this.sortByTimestamp);
this.apiTableProps.apiEntries = this.apiEntries;
if (this.apiEntries.length === 1) {
this.setDefault(newEntry);
}
this.$scope.$applyAsync();
this.errorHandler.info('Wazuh API successfully added', 'Settings');
this.addManagerContainer = false;
@ -451,7 +482,7 @@ export class SettingsController {
}
this.savingApi = false;
this.$scope.$applyAsync();
return;
return this.apiEntries;
}
/**
@ -465,24 +496,32 @@ export class SettingsController {
}
// Update settings function
async updateSettings(item) {
async updateSettings(item, useItem = false) {
try {
if (this.savingApi) {
this.errorHandler.info('Please, wait for success message', 'Settings');
return;
return this.apiEntries;
}
this.savingApi = true;
this.messageErrorUpdate = '';
if (useItem) {
this.formUpdate = {
user: item.user,
password: item.password,
url: item.url,
port: item.port
};
}
const invalid = this.validator('formUpdate');
if (invalid) {
this.messageErrorUpdate = invalid;
this.errorHandler.handle(invalid, 'Settings');
this.savingApi = false;
return;
return this.apiEntries;
}
const index = this.apiEntries.indexOf(item);
const index = this.apiEntries.map(item => item._id).indexOf(item._id);
const tmpData = {
user: this.formUpdate.user,
@ -521,7 +560,7 @@ export class SettingsController {
}
this.savingApi = false;
this.$scope.$applyAsync();
return;
return this.apiEntries;
}
/**
@ -534,7 +573,9 @@ export class SettingsController {
// Check manager connectivity
async checkManager(item, isIndex, silent = false) {
try {
const index = isIndex ? item : this.apiEntries.indexOf(item);
const index = isIndex
? item
: this.apiEntries.map(item => item._id).indexOf(item._id);
const tmpData = {
user: this.apiEntries[index]._source.api_user,
@ -570,23 +611,6 @@ export class SettingsController {
}
}
// Toggle extension
toggleExtension(extension, state) {
try {
const api = JSON.parse(this.appState.getCurrentAPI()).id;
const currentExtensions = this.appState.getExtensions(api);
currentExtensions[extension] = state;
this.appState.setExtensions(api, currentExtensions);
this.getCurrentAPIIndex();
this.apiEntries[
this.currentApiEntryIndex
]._source.extensions = currentExtensions;
this.$scope.$applyAsync();
} catch (error) {
this.errorHandler.handle(error, 'Settings');
}
}
/**
* This change to a given index pattern
* @param {} newIndexPattern
@ -653,7 +677,7 @@ export class SettingsController {
*/
async getAppInfo() {
try {
const data = await this.genericReq.request('GET', '/elastic/setup');
const data = await this.genericReq.request('GET', '/api/setup');
this.appInfo = {};
this.appInfo['app-version'] = data.data.data['app-version'];
this.appInfo['installationDate'] = data.data.data['installationDate'];
@ -672,23 +696,6 @@ export class SettingsController {
this.selectedIndexPattern = config['pattern'];
}
if (!this.appState.getCurrentAPI()) {
this.extensions = {};
this.extensions.audit = config['extensions.audit'];
this.extensions.pci = config['extensions.pci'];
this.extensions.gdpr = config['extensions.gdpr'];
this.extensions.oscap = config['extensions.oscap'];
this.extensions.ciscat = config['extensions.ciscat'];
this.extensions.aws = config['extensions.aws'];
this.extensions.virustotal = config['extensions.virustotal'];
this.extensions.osquery = config['extensions.osquery'];
this.extensions.docker = config['extensions.docker'];
} else {
this.extensions = this.appState.getExtensions(
JSON.parse(this.appState.getCurrentAPI()).id
);
}
if (this.tab === 'logs') {
this.getAppLogs();
}
@ -782,4 +789,21 @@ export class SettingsController {
this.errorHandler.handle(error);
}
}
settingsTabsProps = {
clickAction: tab => {
this.switchTab(tab, true);
if (tab === 'logs') {
this.refreshLogs();
}
},
selectedTab: this.tab || 'api',
tabs: [
{ id: 'api', name: 'API' },
{ id: 'pattern', name: 'Pattern' },
{ id: 'configuration', name: 'Configuration' },
{ id: 'logs', name: 'Logs' },
{ id: 'about', name: 'About' }
]
};
}

View File

@ -14,7 +14,7 @@ import './wz-enter/wz-enter';
import './wz-menu/wz-menu';
import './wz-menu/wz-menu.less';
import './wz-table';
import './wz-data-table';
import './wz-table-eui';
import './wz-welcome-card/wz-welcome-card';
import './wz-no-config/wz-no-config';
import './wz-config-item/wz-config-item';
@ -29,7 +29,6 @@ import './wz-multiple-selector/wz-multiple-selector';
import './wz-multiple-selector/wz-multiple-selector.less';
import './wz-list-manage/wz-list-manage';
import './wz-kbn-switch/wz-kbn-switch';
import './wz-register-agents/wz-register-agents';
import './wz-register-agents/wz-register-agents.less';
import './wz-add-filter-chip/wz-add-filter-chip';
import './wz-add-filter-chip/wz-add-filter-chip.less';
import './wz-src/wz-src';

View File

@ -1,6 +1,6 @@
<div class="open-chips">
<i class="btn-as-i cursor-pointer fa" ng-class="!showDropdown ? 'fa-filter' : 'fa-times'"
ng-click="showDropdown = !showDropdown" aria-hidden="true"></i>
<i class="btn-as-i cursor-pointer fa" ng-class="!showDropdown ? 'fa-filter' : 'fa-times'" ng-click="showDropdown = !showDropdown"
aria-hidden="true"></i>
</div>
<div ng-show="showDropdown" class="chips-dropdown">
<ul class="uiSelectChoices--autoWidth ui-select-choices ui-select-choices-content ui-select-dropdown dropdown-menu">

View File

@ -30,6 +30,7 @@
.chips-dropdown .dropdown-menu{
display: block !important;
right: 0;
}
.ui-select-choices-row-inner {

View File

@ -79,10 +79,17 @@ class WzConfigViewer {
const windows = $(window).height();
const offsetTop = getPosition(editorContainer[0]).y;
const bottom = $scope.isLogs ? 75 : 20;
const headerContainer = $('.wzXmlEditorHeader')
const headerContainerHeight = headerContainer.height() + 30 ? headerContainer.height() + 30 : $scope.isLogs ? 0 : 80;
const headerContainer = $('.wzXmlEditorHeader');
const headerContainerHeight =
headerContainer.height() + 30
? headerContainer.height() + 30
: $scope.isLogs
? 0
: 80;
editorContainer.height(windows - (offsetTop + bottom));
$('.wzXmlEditorBody .CodeMirror').height(windows - (offsetTop + bottom + headerContainerHeight));
$('.wzXmlEditorBody .CodeMirror').height(
windows - (offsetTop + bottom + headerContainerHeight)
);
}, 1);
};

View File

@ -1,139 +0,0 @@
/*
* Wazuh app - Wazuh table with data as input parameter directive
* Copyright (C) 2015-2019 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 template from './wz-data-table.html';
import { uiModules } from 'ui/modules';
import { KeyEquivalenece } from '../../../util/csv-key-equivalence';
import { calcTableRows } from '../wz-table/lib/rows';
import * as pagination from '../wz-table/lib/pagination';
import { checkGap } from '../wz-table/lib/check-gap';
const app = uiModules.get('app/wazuh', []);
app.directive('wzDataTable', function() {
return {
restrict: 'E',
scope: {
rowSizes: '=rowSizes',
data: '=',
keys: '='
},
controller($scope, $filter, errorHandler, $window) {
/**
* Init variables
*/
$scope.keyEquivalence = KeyEquivalenece;
$scope.totalItems = 0;
$scope.wazuh_table_loading = true;
$scope.items = [];
/**
* Resizing. Calculate number of table rows depending on the screen height
*/
const rowSizes = $scope.rowSizes || [15, 13, 11];
let doit;
let resizing = false;
$window.onresize = () => {
if (resizing) return;
resizing = true;
clearTimeout(doit);
doit = setTimeout(() => {
$scope.rowsPerPage = calcTableRows($window.innerHeight, rowSizes);
$scope.itemsPerPage = $scope.rowsPerPage;
init()
.then(() => (resizing = false))
.catch(() => (resizing = false));
}, 150);
};
$scope.rowsPerPage = calcTableRows($window.innerHeight, rowSizes);
/**
* This loads data for table, that has been provided by parameter
*/
const fetch = () => {
try {
$scope.filterTable();
} catch (error) {
errorHandler.handle(error.message || error);
}
return;
};
$scope.sortValue = '';
$scope.sortReverse = false;
$scope.searchTerm = '';
$scope.sort = key => {
if (key !== $scope.sortValue) {
$scope.sortReverse = false;
}
$scope.sortValue = key;
$scope.sortReverse = !$scope.sortReverse;
$scope.filterTable();
};
/**
* This apply filter and sorting to table data
*/
$scope.filterTable = () => {
items = $filter('orderBy')(
$filter('filter')($scope.data, $scope.searchTerm),
$scope.sortValue,
$scope.sortReverse
);
$scope.totalItems = items.length;
$scope.items = items;
checkGap($scope, items);
$scope.searchTable();
};
/**
* On controller loads
*/
const init = async () => {
$scope.error = false;
$scope.wazuh_table_loading = true;
await fetch();
$scope.wazuh_table_loading = false;
};
/**
* Pagination variables and functions
*/
$scope.itemsPerPage = $scope.rowsPerPage || 10;
$scope.pagedItems = [];
$scope.currentPage = 0;
let items = [];
$scope.gap = 0;
$scope.searchTable = () => pagination.searchTable($scope, items);
$scope.groupToPages = () => pagination.groupToPages($scope);
$scope.range = (size, start, end) =>
pagination.range(size, start, end, $scope.gap);
$scope.prevPage = () => pagination.prevPage($scope);
$scope.nextPage = async currentPage =>
pagination.nextPage(currentPage, $scope, errorHandler, fetch);
$scope.setPage = function() {
$scope.currentPage = this.n;
$scope.nextPage(this.n);
};
/**
* Event listeners
*/
$scope.$on('$destroy', () => {
$window.onresize = null;
});
init();
},
template
};
});

View File

@ -1,75 +0,0 @@
<div ng-if="wazuh_table_loading" class="md-padding wz-margin-top-16">
<react-component name="EuiProgress" props="{size: 'xs', color: 'primary'}" />
</div>
<div ng-show="!error && !wazuh_table_loading" layout="row" class="wz-margin-top-17">
<input placeholder="Filter..." ng-model="searchTerm" ng-change="filterTable()" type="text"
class="kuiLocalSearchInput ng-empty ng-pristine ng-scope ng-touched ng-valid">
<button class="kuiLocalSearchButton height-40"><span class="fa fa-search" aria-hidden="true"></span> </button>
</div>
<div ng-show="!error && !wazuh_table_loading && items.length">
<table class="table table-striped table-condensed wz-margin-top-17" style="table-layout: fixed !important"
id="wz_table">
<thead class="wz-text-bold">
<th ng-repeat="key in keys" class="wz-text-left"
ng-class="{ 'cursor-pointer' : !key.nosortable, 'col-lg-1' : !key.size, 'col-lg-{{key.size}}' : key.size }"
ng-click="!key.nosortable && sort(key)">
{{ keyEquivalence[key.value || key] || key.value || key }}
<i ng-if="!key.nosortable" class="fa wz-theader-sort-icon"
ng-class="sortValue === (key.value || key) ? (!sortReverse ? 'fa-sort-asc' : 'fa-sort-desc') : 'fa-sort'"
aria-hidden="true"></i>
</th>
</thead>
<tbody>
<tr ng-class="allowClick ? 'cursor-pointer' : ''" class="wz-word-wrap"
ng-repeat="item in pagedItems[currentPage] | filter:{item:'!'}" ng-click="clickAction(item)">
<td ng-repeat="key in keys">
{{item[key] | limitTo: 25}}
</td>
</tr>
</tbody>
<tfoot>
<td colspan="{{keys.length}}">
<span ng-show="!wazuh_table_loading" class="color-grey">{{ totalItems }} items</span>
<div ng-show="items.length > itemsPerPage" class="pagination pull-right" style="margin:0 !important">
<ul layout="row">
<li ng-show="currentPage" class="md-padding">
<a href ng-click="prevPage()">« Prev</a>
</li>
<li ng-repeat="n in range(pagedItems.length, currentPage, currentPage + gap) "
ng-class="{'wz-text-active': n == currentPage}" ng-click="setPage()" class="md-padding">
<a href ng-bind="n + 1">1</a>
</li>
<li ng-show="currentPage < pagedItems.length - 1" class="md-padding">
<a href ng-click="nextPage()">Next »</a>
</li>
</ul>
</div>
</td>
</tfoot>
</table>
</div>
<div layout="row" ng-if="!error && !wazuh_table_loading && !totalItems">
<div flex class="euiCallOut euiCallOut--warning wz-margin-top-17">
<div class="euiCallOutHeader">
<react-component name="EuiIcon" props="{type:'help',className:'euiCallOutHeader__title'}" />
<span class="euiCallOutHeader__title">No results match your search criteria</span>
</div>
</div>
</div>
<div layout="row" ng-if="error" class="wz-margin-bottom-45">
<div flex class="euiCallOut euiCallOut--warning wz-margin-top-17">
<div class="euiCallOutHeader">
<react-component name="EuiIcon" props="{type:'help',className:'euiCallOutHeader__title'}" />
<span class="euiCallOutHeader__title">{{error}}</span>
</div>
</div>
</div>
<div layout="row" ng-if="!wazuh_table_loading && !totalItems" class="wz-margin-top-10">
<span class="color-grey">0 items</span>
</div>

View File

@ -86,6 +86,7 @@ app.directive('wzListManage', function() {
const fetch = () => {
try {
$scope.filterTable();
$scope.$applyAsync();
return;
} catch (error) {
errorHandler.handle(error.message || error);
@ -228,7 +229,7 @@ app.directive('wzListManage', function() {
$scope.removingEntry = false;
fetch();
$scope.setPage(
$scope.pagedItems.length - 1 >= $scope.n ? page : page - 1
$scope.pagedItems.length - 1 < $scope.n ? page - 1 : page
);
};

View File

@ -7,48 +7,41 @@
<div id='wzMultipleSelector' ng-show='!loading'>
<div class='wzMultipleSelectorLeft'>
<div class='panel panel-primary'>
<div class='panel-heading text-center'>{{titleAvailableItems}}<span
ng-click='doReload("left", availableFilter, true)' class='pull-right cursor-pointer'><span
<div class='panel-heading text-center'>{{titleAvailableItems}}<span ng-click='doReload("left", availableFilter, true)'
class='pull-right cursor-pointer' ng-hide="$parent.$parent.$parent.selectedAgents.loadedAll"><span
class='fa fa-refresh' aria-hidden='true'></span></span></div>
<div class='panel-body'>
<div layout="row">
<input placeholder='Filter...' ng-model='availableFilter' type='text'
class='kuiLocalSearchInput' wz-enter='doReload("left", availableFilter, true)'
ng-change='availableItem=null'>
<button type='submit' class='kuiLocalSearchButton height-40'
ng-click='doReload("left", availableFilter, true)'>
<input placeholder='Filter...' ng-model='availableFilter' type='text' class='kuiLocalSearchInput'
wz-enter='doReload("left", availableFilter, true)' ng-change='availableItem=null'>
<button type='submit' class='kuiLocalSearchButton height-40' ng-click='doReload("left", availableFilter, true)'>
<span class='fa fa-search' aria-hidden='true'></span>
</button>
</div>
<select size='10' multiple ng-model='availableItem' ng-change='selectedElement=null;doCheckLimit()'
class='width-100'
ng-dblclick='moveItem(availableItem, availableItems, selectedItems);availableItem=null'>
<option ng-repeat='item in availableItems | orderBy:sort'
ng-style='{"background-color":(item.type === "a" ? "#c6ffc6": item.type === "r" ? "#ffe7e7" : "")}'
class='width-100' ng-dblclick='moveItem(availableItem, availableItems, selectedItems);availableItem=null'>
<option ng-repeat='item in availableItems | orderBy:sort' ng-style='{"background-color":(item.type === "a" ? "#c6ffc6": item.type === "r" ? "#ffe7e7" : "")}'
ng-value="{{item}}">{{item.key + " - " + item.value}}</option>
</select>
</div>
</div>
</div>
<div class='wzMultipleSelectorButtons'>
<button ng-disabled='availableItems.length === 0 || availableItems.length > 500' type='button'
class='btn wz-button-groups' tooltip='Add all items' tooltip-placement='top' ng-click='moveAll(availableItems, selectedItems, "a");
<button ng-disabled='availableItems.length === 0 || availableItems.length > 500' type='button' class='btn wz-button-groups'
tooltip='Add all items' tooltip-placement='top' ng-click='moveAll(availableItems, selectedItems, "a");
availableItem=null;availableFilter="" ;doReload("left", availableFilter, true)'>
<span><i class='fa fa-forward'></i></span>
</button>
<button ng-disabled='!availableItem || availableItem.length > 500' type='button'
class='btn wz-button-groups' tooltip='Add selected items' tooltip-placement='top'
ng-click='moveItem(availableItem, availableItems, selectedItems, "a");availableItem=null;availableFilter=""'>
<button ng-disabled='!availableItem || availableItem.length > 500' type='button' class='btn wz-button-groups'
tooltip='Add selected items' tooltip-placement='top' ng-click='moveItem(availableItem, availableItems, selectedItems, "a");availableItem=null;availableFilter=""'>
<span><i class='fa fa-arrow-right'></i></span>
</button>
<button ng-disabled='!selectedElement || selectedElement.length > 500' type='button'
class='btn wz-button-groups' tooltip='Remove selected items' tooltip-placement='top'
ng-click='moveItem(selectedElement, selectedItems, availableItems, "r");selectedFilter="";selectedElement=null'>
<button ng-disabled='!selectedElement || selectedElement.length > 500' type='button' class='btn wz-button-groups'
tooltip='Remove selected items' tooltip-placement='top' ng-click='moveItem(selectedElement, selectedItems, availableItems, "r");selectedFilter="";selectedElement=null'>
<span><i class='fa fa-arrow-left'></i></span>
</button>
<button ng-disabled='selectedItems.length === 0 || selectedItems.length > 500' type='button'
class='btn wz-button-groups' tooltip='Remove all items' tooltip-placement='top'
ng-click='moveAll(selectedItems, availableItems, "r");selectedElement=null;selectedFilter="";doReload("right")'>
<button ng-disabled='selectedItems.length === 0 || selectedItems.length > 500' type='button' class='btn wz-button-groups'
tooltip='Remove all items' tooltip-placement='top' ng-click='moveAll(selectedItems, availableItems, "r");selectedElement=null;selectedFilter="";doReload("right")'>
<span><i class='fa fa-backward'></i></span>
</button>
</div>
@ -59,10 +52,8 @@
<input placeholder='Filter...' ng-model='selectedFilter' type='text' class='kuiLocalSearchInput'
ng-change='selectedElement=null' style='padding: 8px 15px'>
<select size='10' multiple ng-model='selectedElement' ng-change='availableItem=null;doCheckLimit()'
class='width-100'
ng-dblclick='moveItem(selectedElement, selectedItems, availableItems);selectedElement=null'>
<option ng-repeat='item in selectedItems | filter: selectedFilter | orderBy:sort'
ng-style='{"background-color":(item.type === "a" ? "#c6ffc6": item.type === "r" ? "#ffe7e7" : "")}'
class='width-100' ng-dblclick='moveItem(selectedElement, selectedItems, availableItems);selectedElement=null'>
<option ng-repeat='item in selectedItems | filter: selectedFilter | orderBy:sort' ng-style='{"background-color":(item.type === "a" ? "#c6ffc6": item.type === "r" ? "#ffe7e7" : "")}'
ng-value="{{item}}">{{item.key + " - " + item.value}}</option>
</select>
</div>

View File

@ -1,221 +0,0 @@
<div class="tutorial">
<md-nav-bar class="wz-nav-bar nav-bar-white-bg tutorial-nav-bar" md-selected-nav-item="registerObj.selectedSystemTab"
nav-bar-aria-label="System" class="wz-margin-bottom-25">
<md-nav-item class="wz-nav-item" md-nav-click="setSystem('linux')" name="linux"><i class="fa fa-linux"></i>
Linux</md-nav-item>
<md-nav-item class="wz-nav-item" md-nav-click="setSystem('windows')" name="windows"><i class="fa fa-windows"></i>
Windows</md-nav-item>
<md-nav-item class="wz-nav-item" md-nav-click="setSystem('osx')" name="osx"><i class="fa fa-apple"></i>
OSX</md-nav-item>
</md-nav-bar>
<!-- Linux -->
<div ng-if="registerObj.selectedSystem === 0">
<div class="tutorial-step" ng-class="{'active' : registerObj.currentStep === 0, 'completed' : registerObj.currentStep > 0 }">
<div layout="row" class="tutorial-header">
<div class="step-number">
<p>1</p>
</div>
<h3 class="step-title">{{registerObj.systems[0].steps[0].title}}</h3>
</div>
<div class="tutorial-body">
<div layout="row">
<div class="wz-padding-right-16">
<label class="euiFormLabel">Agent name</label>
<div class="euiFormControlLayout wz-margin-top-4">
<div class="euiFormControlLayout__childrenWrapper">
<input type="text" class="euiFieldText" aria-label="default columns" ng-model="registerObj.systems[0].steps[0].agentName">
</div>
</div>
</div>
<div class="wz-padding-right-16">
<label class="euiFormLabel">Agent IP</label>
<div class="euiFormControlLayout wz-margin-top-4">
<div class="euiFormControlLayout__childrenWrapper">
<input type="text" class="euiFieldText" aria-label="default columns" ng-model="registerObj.systems[0].steps[0].agentIP">
</div>
</div>
</div>
<md-button ng-disabled="!registerObj.systems[0].steps[0].agentName || !registerObj.systems[0].steps[0].agentIP || addingAgent"
class="wz-button btn height-40" ng-click="addAgent()"><i class="fa fa-plus" ng-class="addingAgent ? 'fa-spin fa-spinner' : ''"></i>
Add agent
</md-button>
</div>
</div>
</div>
<div class="tutorial-step" ng-class="{'active' : registerObj.currentStep === 1, 'completed' : registerObj.currentStep > 1, 'disabled' : registerObj.currentStep < 1 }">
<div layout="row" class="tutorial-header">
<div class="step-number">
<p>2</p>
</div>
<h3 class="step-title">{{registerObj.systems[0].steps[1].title}}</h3>
</div>
<div class="tutorial-body">
<p class="tutorial-exit">Please execute the following command:</p>
<div class="wz-code-viewer">{{registerObj.systems[0].steps[1].code}}</div>
<md-button class="wz-button btn height-40 next-btn" ng-click="nextStep()"><i class="fa fa-success"></i>
Next
</md-button>
</div>
</div>
<div class="tutorial-step" ng-class="{'active' : registerObj.currentStep === 2, 'completed' : registerObj.currentStep > 2, 'disabled' : registerObj.currentStep < 2 }">
<div layout="row" class="tutorial-header">
<div class="step-number">
<p>3</p>
</div>
<h3 class="step-title">{{registerObj.systems[0].steps[2].title}}</h3>
</div>
<div class="tutorial-body">
<div class="wz-margin-bottom-10">
<label class="euiFormLabel">Manager IP</label>
<div class="euiFormControlLayout wz-margin-top-4">
<div class="euiFormControlLayout__childrenWrapper">
<input type="text" class="euiFieldText" aria-label="default columns" ng-model="registerObj.systems[0].steps[2].managerIp">
</div>
</div>
</div>
<p class="tutorial-exit" ng-show="registerObj.systems[0].steps[2].managerIp">Please execute the
following
command:</p>
<div ng-show="registerObj.systems[0].steps[2].managerIp" class="wz-code-viewer">
sed -i 's/MANAGER_IP/{{registerObj.systems[0].steps[2].managerIp}}/g' /var/ossec/etc/ossec.conf
</div>
<md-button ng-show="registerObj.systems[0].steps[2].managerIp" class="wz-button btn height-40 next-btn"
ng-click="nextStep()"><i class="fa fa-success"></i> Next
</md-button>
</div>
</div>
<div class="tutorial-step" ng-class="{'active' : registerObj.currentStep === 3, 'completed' : registerObj.currentStep > 3, 'disabled' : registerObj.currentStep < 3 }">
<div layout="row" class="tutorial-header">
<div class="step-number">
<p>4</p>
</div>
<h3 class="step-title">{{registerObj.systems[0].steps[3].title}}</h3>
</div>
<div class="tutorial-body">
<md-button class="wz-button wz-no-margin height-40" ng-click="restartAgent()"><i class="fa fa-refresh"
ng-class="restartingAgent ? 'fa-spin fa-spinner' : ''"></i>
Restart agent
</md-button>
</div>
</div>
</div>
<!-- Windows -->
<div ng-if="registerObj.selectedSystem === 1">
<div class="tutorial-step" ng-class="{'active' : registerObj.currentStep === 0, 'completed' : registerObj.currentStep > 0, 'disabled' : registerObj.currentStep < 0 }">
<div layout="row" class="tutorial-header">
<div class="step-number">
<p>1</p>
</div>
<h3 class="step-title">{{registerObj.systems[1].steps[0].title}}</h3>
</div>
<div class="tutorial-body">
<div layout="row" class="wz-margin-bottom-10">
<div class="wz-padding-right-16">
<label class="euiFormLabel">Agent name</label>
<div class="euiFormControlLayout wz-margin-top-4">
<div class="euiFormControlLayout__childrenWrapper">
<input type="text" class="euiFieldText" aria-label="default columns" ng-model="registerObj.systems[1].steps[0].agentName">
</div>
</div>
</div>
<div class="wz-padding-right-16">
<label class="euiFormLabel">Manager IP</label>
<div class="euiFormControlLayout wz-margin-top-4">
<div class="euiFormControlLayout__childrenWrapper">
<input type="text" class="euiFieldText" aria-label="default columns" ng-model="registerObj.systems[1].steps[0].managerIP">
</div>
</div>
</div>
<div class="wz-padding-right-16">
<label class="euiFormLabel">Authd service IP</label>
<div class="euiFormControlLayout wz-margin-top-4">
<div class="euiFormControlLayout__childrenWrapper">
<input type="text" class="euiFieldText" aria-label="default columns" ng-model="registerObj.systems[1].steps[0].authdIP">
</div>
</div>
</div>
</div>
<p class="tutorial-exit" ng-show="registerObj.systems[1].steps[0].agentName && registerObj.systems[1].steps[0].managerIP &&
registerObj.systems[1].steps[0].authdIP">Please
execute the following command:</p>
<div ng-show="registerObj.systems[1].steps[0].agentName && registerObj.systems[1].steps[0].managerIP &&
registerObj.systems[1].steps[0].authdIP"
class="wz-code-viewer">
wazuh-agent-3.8.2-1.msi /q ADDRESS="{{registerObj.systems[1].steps[0].managerIP}}"
AUTHD_SERVER="{{registerObj.systems[1].steps[0].authdIP}}"
AGENT_NAME="{{registerObj.systems[1].steps[0].agentName}}"
</div>
<md-button ng-show="registerObj.systems[1].steps[0].agentName && registerObj.systems[1].steps[0].managerIP &&
registerObj.systems[1].steps[0].authdIP"
class="wz-button btn height-40 next-btn" ng-click="nextStep()"><i class="fa fa-success"></i>
Next
</md-button>
</div>
</div>
</div>
<!-- OSX -->
<div ng-if="registerObj.selectedSystem === 2">
<div class="tutorial-step" ng-class="{'active' : registerObj.currentStep === 0, 'completed' : registerObj.currentStep > 0, 'disabled' : registerObj.currentStep < 0 }">
<div layout="row" class="tutorial-header">
<div class="step-number">
<p>1</p>
</div>
<h3 class="step-title">{{registerObj.systems[2].steps[0].title}}</h3>
</div>
<div class="tutorial-body">
<div class="wz-margin-bottom-10">
<label class="euiFormLabel">Manager IP</label>
<div class="euiFormControlLayout wz-margin-top-4">
<div class="euiFormControlLayout__childrenWrapper">
<input type="text" class="euiFieldText" aria-label="default columns" ng-model="registerObj.systems[2].steps[0].managerIp">
</div>
</div>
</div>
<p class="tutorial-exit" ng-show="registerObj.systems[2].steps[0].managerIp">Please execute the
following
command:</p>
<div ng-show="registerObj.systems[2].steps[0].managerIp" class="wz-code-viewer">
/Library/Ossec/bin/agent-auth -m {{registerObj.systems[2].steps[0].managerIp}}
</div>
<md-button ng-show="registerObj.systems[2].steps[0].managerIp" class="wz-button btn height-40 next-btn"
ng-click="nextStep()"><i class="fa fa-success"></i> Next
</md-button>
</div>
</div>
<div class="tutorial-step" ng-class="{'active' : registerObj.currentStep === 1, 'completed' : registerObj.currentStep > 1, 'disabled' : registerObj.currentStep < 1 }">
<div layout="row" class="tutorial-header">
<div class="step-number">
<p>2</p>
</div>
<h3 class="step-title">{{registerObj.systems[2].steps[1].title}}</h3>
</div>
<div class="tutorial-body">
<p class="tutorial-exit">Please execute the following command:</p>
<div class="wz-code-viewer">sed -i '' -e 's/MANAGER_IP/{{registerObj.systems[2].steps[0].managerIp}}/g'
/Library/Ossec/etc/ossec.conf</div>
<md-button class="wz-button btn height-40 next-btn" ng-click="nextStep()"><i class="fa fa-success"></i>
Next
</md-button>
</div>
</div>
<div class="tutorial-step" ng-class="{'active' : registerObj.currentStep === 2, 'completed' : registerObj.currentStep > 2, 'disabled' : registerObj.currentStep < 2 }">
<div layout="row" class="tutorial-header">
<div class="step-number">
<p>3</p>
</div>
<h3 class="step-title">{{registerObj.systems[2].steps[2].title}}</h3>
</div>
<div class="tutorial-body">
<p class="tutorial-exit">Please execute the following command:</p>
<div class="wz-code-viewer">
{{registerObj.systems[2].steps[2].code}}
</div>
<md-button class="wz-button btn height-40 next-btn" ng-click="nextStep()"><i class="fa fa-success"></i>
Next
</md-button>
</div>
</div>
</div>
</div>

View File

@ -1,112 +0,0 @@
/*
* Wazuh app - Wazuh register agents stylesheet
* Copyright (C) 2015-2019 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.
*/
/* -------------------------------------------------------------------------- */
/* ------------------ Wazuh register agents stylesheet ---------------------- */
/* -------------------------------------------------------------------------- */
.tutorial{
margin: 0 12%;
}
.registerNewAgent .tutorial{
margin: 0 !important;
}
.tutorial-nav-bar .md-nav-bar{
margin-bottom: 25px;
}
.tutorial-nav-bar .md-nav-bar ._md-nav-button{
padding: 15px 20px!important;
}
.tutorial-step{
margin-bottom: 15px;
}
.tutorial .tutorial-step:not(:last-child){
border-bottom: 2px dashed #e9e9e9;
}
.tutorial-header {
padding-bottom: 15px;
}
.tutorial-body {
padding: 0px 0px 15px 40px;
}
.tutorial-body button{
width: 150px;
}
.tutorial-body .layout-row button, .next-btn{
margin: 18px 0 0 0!important;
}
.tutorial-step.disabled .tutorial-body, .tutorial-step.completed .tutorial-body{
display: none;
}
.step-number{
width: 30px;
height: 30px;
border-radius: 50%;
color: white;
line-height: 30px;
font-size: 20px;
text-align: center;
}
.tutorial-step.active .step-number{
background: #007ba4;
}
.tutorial-step.disabled .step-number{
background: #c6c6c6;
}
.tutorial-step.completed .step-number{
background: #70ba56;
}
.step-title{
font-size: 17px;
line-height: 30px;
padding-left: 8px;
}
.tutorial-step.active .step-title{
color: #005571;
}
.tutorial-step.disabled .step-title{
color: #c6c6c6;
}
.tutorial-step.completed .step-title{
color: #70ba56;
}
.tutorial .wz-code-viewer{
text-align: left;
font-size: 12px!important;
padding: 5px;
height: auto;
}
.tutorial-exit{
font-family: monospace;
text-align: left;
padding: 8px 0 5px 0;
font-size: 12px;
}

View File

@ -0,0 +1,24 @@
/*
* Wazuh app - kbn-src sustitution directive
* Copyright (C) 2015-2019 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 { uiModules } from 'ui/modules';
import chrome from 'ui/chrome';
const app = uiModules.get('app/wazuh', []);
app.directive('wzSrc', function() {
return {
restrict: 'A',
link: function($scope, $el, $attr) {
const url = chrome.addBasePath($attr.wzSrc);
$attr.$set('src', url);
}
};
});

View File

@ -0,0 +1,124 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { EuiBasicTable } from '@elastic/eui';
export class BasicTable extends Component {
constructor(props) {
super(props);
this.state = {
items: this.props.items,
pageIndex: this.props.pageIndex,
pageSize: 10,
sortField: this.props.initialSortField || this.props.columns[0].field,
sortDirection: 'desc'
};
this.lastSorting = {
sortField: this.props.initialSortField || this.props.columns[0].field,
sortDirection: 'desc'
};
}
UNSAFE_componentWillReceiveProps(nextProps) {
this.setState({
items: nextProps.items,
pageIndex: nextProps.pageIndex
});
}
async onTableChange({ page = {}, sort = {} }) {
const { index: pageIndex, size: pageSize } = page;
const { field: sortField, direction: sortDirection } = sort;
const sortingChanged =
this.lastSorting.sortField !== sortField ||
this.lastSorting.sortDirection !== sortDirection;
if (sortingChanged) {
this.lastSorting = { sortField, sortDirection };
await this.props.sortByField(sortField);
}
const { pageOfItems } = this.filterItems(pageIndex, pageSize);
let needMoreData = pageOfItems.includes(null);
while (needMoreData) {
const { items } = this.state;
const nonNull = items.filter(item => !!item).length;
const newItems = await this.props.getData({ offset: nonNull });
await this.promisedSetState({
items: newItems
});
const result = this.filterItems(pageIndex, pageSize);
needMoreData = result.pageOfItems.includes(null);
}
this.setState({
pageIndex,
pageSize,
sortField,
sortDirection
});
}
promisedSetState(newState) {
return new Promise(resolve => {
this.setState(newState, () => {
resolve();
});
});
}
filterItems(pageIndex, pageSize) {
const { items } = this.state;
const pages = [];
const len = items.length;
for (let i = 0, j = len; i < j; i += pageSize) {
pages.push(items.slice(i, i + pageSize));
}
return { pageOfItems: pages[pageIndex] || [], totalItemCount: len };
}
render() {
const { pageIndex, pageSize, sortField, sortDirection } = this.state;
const { pageOfItems, totalItemCount } = this.filterItems(
pageIndex,
pageSize
);
const columns = [...this.props.columns];
const pagination = {
pageIndex,
pageSize,
totalItemCount,
pageSizeOptions: [10, 20, 50],
hidePerPageOptions: this.state.items.length <= 10
};
const sorting = {
sort: {
field: sortField,
direction: sortDirection
}
};
return (
<div>
<EuiBasicTable
sorting={sorting}
items={pageOfItems}
columns={columns}
pagination={pagination}
onChange={obj => this.onTableChange(obj)}
/>
</div>
);
}
}
BasicTable.propTypes = {
items: PropTypes.array
};

View File

@ -9,5 +9,4 @@
*
* Find more information about this on the LICENSE file.
*/
import '../wz-table/wz-table-filter-service';
import './wz-data-table-directive';
import './wz-table-directive';

View File

@ -0,0 +1,98 @@
/*
* Wazuh app - Wazuh table directive data methods
* Copyright (C) 2015-2019 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 async function searchData(
term,
removeFilters,
$scope,
instance,
fetch,
wzTableFilter,
errorHandler
) {
try {
$scope.error = false;
$scope.wazuh_table_loading = true;
if (removeFilters) instance.removeFilters();
instance.addFilter('search', term);
wzTableFilter.set(instance.filters);
await fetch();
$scope.wazuh_table_loading = false;
} catch (error) {
$scope.wazuh_table_loading = false;
$scope.error = errorHandler.handle(error.message || error, 0, 0, 1);
errorHandler.handle(error.message || error);
}
$scope.$applyAsync();
return;
}
export async function filterData(
filter,
$scope,
instance,
wzTableFilter,
fetch,
errorHandler
) {
try {
$scope.error = false;
$scope.wazuh_table_loading = true;
if (filter.name === 'platform' && instance.path === '/agents') {
const platform = filter.value.split(' - ')[0];
const version = filter.value.split(' - ')[1];
instance.addFilter('os.platform', platform);
instance.addFilter('os.version', version);
} else {
instance.addFilter(filter.name, filter.value);
}
wzTableFilter.set(instance.filters);
await fetch();
$scope.wazuh_table_loading = false;
} catch (error) {
$scope.wazuh_table_loading = false;
$scope.error = errorHandler.handle(error.message || error, 0, 0, 1);
errorHandler.handle(error.message || error);
}
$scope.$applyAsync();
return;
}
export async function queryData(
query,
term,
instance,
wzTableFilter,
$scope,
fetch,
errorHandler
) {
try {
$scope.error = false;
$scope.wazuh_table_loading = true;
instance.removeFilters();
if (term) {
instance.addFilter('search', term);
}
if (query) {
instance.addFilter('q', query);
}
wzTableFilter.set(instance.filters);
await fetch();
$scope.wazuh_table_loading = false;
} catch (error) {
$scope.wazuh_table_loading = false;
$scope.error = errorHandler.handle(error.message || error, 0, 0, 1);
errorHandler.handle(error.message || error);
}
$scope.$applyAsync();
return;
}

View File

@ -0,0 +1,40 @@
/*
* Wazuh app - Wazuh table directive init function
* Copyright (C) 2015-2019 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 async function initTable(
$scope,
fetch,
wzTableFilter,
instance,
errorHandler,
skipFetching = false
) {
try {
$scope.error = false;
$scope.wazuh_table_loading = true;
instance.addSorting($scope.initialSortField);
await fetch({ skipFetching });
wzTableFilter.set(instance.filters);
$scope.wazuh_table_loading = false;
} catch (error) {
$scope.wazuh_table_loading = false;
$scope.error = errorHandler.handle(
error.message || error,
false,
false,
true
);
errorHandler.handle(error.message || error);
}
$scope.$applyAsync();
return;
}

View File

@ -0,0 +1,57 @@
/*
* Wazuh app - Wazuh table directive event listeners
* Copyright (C) 2015-2019 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 function wazuhUpdateInstancePath(parameters, instance, init) {
instance.filters = [];
instance.path = parameters.path;
return init();
}
export function wazuhFilter(parameters, filter) {
return filter(parameters.filter);
}
export function wazuhQuery(parameters, query) {
return query(parameters.query, parameters.search);
}
export function wazuhSearch(parameters, instance, search) {
try {
const matchesSpecificPath =
parameters &&
parameters.specificPath &&
!instance.path.includes(parameters.specificPath);
const matchesSpecificFilter =
parameters &&
parameters.specificFilter &&
!instance.filters.filter(
filter =>
filter.name === parameters.specificFilter.name &&
filter.value === parameters.specificFilter.value
).length;
if (matchesSpecificPath || matchesSpecificFilter) {
return;
}
return search(parameters.term, parameters.removeFilters);
} catch (error) {
return;
}
}
export function wazuhRemoveFilter(parameters, instance, wzTableFilter, init) {
instance.filters = instance.filters.filter(
item => item.name !== parameters.filterName
);
wzTableFilter.set(instance.filters);
return init();
}

View File

@ -0,0 +1,29 @@
/*
* Wazuh app - Wazuh table directive helper
* Copyright (C) 2015-2019 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 async function sort(field, $scope, instance, fetch, errorHandler) {
try {
$scope.error = false;
$scope.wazuh_table_loading = true;
instance.addSorting(field.value || field);
$scope.sortValue = instance.sortValue;
$scope.sortDir = instance.sortDir;
await fetch();
$scope.wazuh_table_loading = false;
} catch (error) {
$scope.wazuh_table_loading = false;
$scope.error = errorHandler.handle(error.message || error, 0, 0, 1);
errorHandler.handle(error.message || error);
}
$scope.$applyAsync();
return;
}

View File

@ -0,0 +1,209 @@
/*
* Wazuh app - Wazuh table directive
* Copyright (C) 2015-2019 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 template from './wz-table.html';
import { uiModules } from 'ui/modules';
import { DataFactory } from '../../services/data-factory';
import { KeyEquivalence } from '../../../util/csv-key-equivalence';
import * as listeners from './lib/listeners';
import { searchData, filterData, queryData } from './lib/data';
import { initTable } from './lib/init';
import { sort } from './lib/sort';
import React, { Component } from 'react';
import { EuiHealth } from '@elastic/eui';
import * as ProcessEquivalence from '../../../util/process-state-equivalence';
const app = uiModules.get('app/wazuh', []);
app.directive('wzTableEui', function() {
return {
restrict: 'E',
scope: {
path: '=path',
keys: '=keys',
initialSortField: '=initialSortField'
},
controller($scope, apiReq, errorHandler, wzTableFilter, timeService) {
const health = (state, config) => (
<EuiHealth color={state === config.success ? 'success' : 'danger'}>
{state}
</EuiHealth>
);
const processStatus = value => ProcessEquivalence[value] || value;
const defaultRender = value => value || '-';
const parseColumns = columnsArray => {
return columnsArray.map(item => ({
name: KeyEquivalence[item.value || item] || item.value || item,
field: item.value || item,
width: item.width || undefined,
sortable: typeof item.sortable !== 'undefined' ? item.sortable : true,
render: value =>
item.isHealth
? health(value, item.isHealth)
: item.isProcessStatus
? processStatus(value)
: defaultRender(value)
}));
};
let items = [];
const fetch = async (options = {}) => {
try {
const result = await instance.fetch(options);
items = result.items;
$scope.items = items;
$scope.$applyAsync();
return items;
} catch (error) {
if (
error &&
!error.data &&
error.status === -1 &&
error.xhrStatus === 'abort'
) {
return Promise.reject('Request took too long, aborted');
}
return Promise.reject(error);
}
};
const offsetTimestamp = (text, time) => {
try {
return text + timeService.offset(time);
} catch (error) {
return time !== '-' ? `${text}${time} (UTC)` : time;
}
};
$scope.basicTableProps = {
path: $scope.path,
initialSortField: $scope.initialSortField || false,
pageIndex: 0,
columns: parseColumns($scope.keys),
items: [],
getData: options => fetch(options),
sortByField: field =>
sort(field, $scope, instance, fetch, errorHandler),
offsetTimestamp: (text, time) => offsetTimestamp(text, time)
//noItemsMessage: 'Change this'
};
const instance = new DataFactory(apiReq, $scope.path);
$scope.wazuh_table_loading = true;
$scope.items = [];
$scope.$watch('items', () => {
$scope.basicTableProps.items = [...$scope.items];
$scope.basicTableProps.pageIndex = 0;
});
$scope.$watch('keys', () => {
$scope.basicTableProps.columns = parseColumns($scope.keys);
});
/**
* This search in table data with a given term
*/
const search = async (term, removeFilters) => {
searchData(
term,
removeFilters,
$scope,
instance,
fetch,
wzTableFilter,
errorHandler
);
};
/**
* This filter table with a given filter
* @param {Object} filter
*/
const filter = async filter =>
filterData(
filter,
$scope,
instance,
wzTableFilter,
fetch,
errorHandler
);
/**
* This filter table with using a q search
* @param {Object} filter
*/
const query = async (query, search) =>
queryData(
query,
search,
instance,
wzTableFilter,
$scope,
fetch,
errorHandler
);
/**
* On controller loads
*/
const init = async (skipFetching = false) => {
await initTable(
$scope,
fetch,
wzTableFilter,
instance,
errorHandler,
skipFetching
);
};
/**
* Event listeners
*/
$scope.$on('wazuhUpdateInstancePath', (event, parameters) =>
listeners.wazuhUpdateInstancePath(parameters, instance, init)
);
$scope.$on('wazuhFilter', (event, parameters) =>
listeners.wazuhFilter(parameters, filter)
);
$scope.$on('wazuhSearch', (event, parameters) =>
listeners.wazuhSearch(parameters, instance, search)
);
$scope.$on('wazuhQuery', (event, parameters) =>
listeners.wazuhQuery(parameters, query)
);
$scope.$on('wazuhSort', (event, parameters) =>
$scope.sort(parameters.field)
);
$scope.$on('wazuhRemoveFilter', (event, parameters) =>
listeners.wazuhRemoveFilter(parameters, instance, wzTableFilter, init)
);
$scope.$on('$destroy', () => {
wzTableFilter.set([]);
});
init();
},
template
};
});

View File

@ -0,0 +1,11 @@
<div class="md-padding md-padding-top-16" ng-show="wazuh_table_loading">
<react-component name="EuiProgress" props="{size: 'xs', color: 'primary'}" />
</div>
<div layout="row" ng-if="!error">
<react-component name="BasicTable" props="basicTableProps" />
</div>
<div layout="row" ng-if="error">
<react-component flex name="EuiCallOut" props="{color:'warning',iconType:'help', title: error}" />
</div>

View File

@ -13,7 +13,7 @@
import template from './wz-table.html';
import { uiModules } from 'ui/modules';
import { DataFactory } from '../../services/data-factory';
import { KeyEquivalenece } from '../../../util/csv-key-equivalence';
import { KeyEquivalence } from '../../../util/csv-key-equivalence';
import { calcTableRows } from './lib/rows';
import { parseValue } from './lib/parse-value';
import * as pagination from './lib/pagination';
@ -35,7 +35,6 @@ app.directive('wzTable', function() {
allowClick: '=allowClick',
implicitFilter: '=implicitFilter',
rowSizes: '=rowSizes',
extraLimit: '=extraLimit',
emptyResults: '=emptyResults',
customColumns: '=customColumns',
implicitSort: '=implicitSort'
@ -100,7 +99,7 @@ app.directive('wzTable', function() {
$scope.implicitFilter,
$scope.implicitSort
);
$scope.keyEquivalence = KeyEquivalenece;
$scope.keyEquivalence = KeyEquivalence;
$scope.totalItems = 0;
$scope.wazuh_table_loading = true;
$scope.items = [];
@ -277,6 +276,10 @@ app.directive('wzTable', function() {
$scope.parseValue = (key, item) =>
parseValue(key, item, instance.path, $sce, timeService);
$scope.parseKey = key => {
return key ? key.value || key : key;
};
/**
* On controller loads
*/
@ -299,17 +302,24 @@ app.directive('wzTable', function() {
$scope.currentOffset = 0;
let items = [];
$scope.gap = 0;
$scope.searchTable = () => pagination.searchTable($scope, items);
$scope.groupToPages = () => pagination.groupToPages($scope);
$scope.range = (size, start, end) =>
pagination.range(size, start, end, $scope.gap);
$scope.prevPage = () => pagination.prevPage($scope);
$scope.nextPage = async (currentPage, last = false) =>
pagination.nextPage(currentPage, $scope, errorHandler, fetch, last);
$scope.firstPage = function() {
$scope.setPage(1);
$scope.prevPage();
};
$scope.setPage = function(page = false, logs = false, last = false) {
this.n = page || this.n;
$scope.currentPage = this.n;
@ -493,26 +503,16 @@ app.directive('wzTable', function() {
}
};
$scope.showTooltip = (id1, id2, item, path) => {
const $element = $(`#${path}-td-` + id1 + '-' + id2 + ' div');
const $c = $element
.clone()
.css({ display: 'inline', width: 'auto', visibility: 'hidden' })
.appendTo('body');
if (
$c.width() > $element.width() &&
(($element || [])[0].children || [])[0].innerText !== '-'
) {
$scope.showTooltip = (id1, id2, item) => {
const $element = $(
'#td-' + id1 + '-' + id2 + ' div span.wz-text-truncatable'
);
if ($element[0].offsetWidth < $element[0].scrollWidth) {
if (!item.showTooltip) {
item.showTooltip = [];
}
item.showTooltip[id2] = true;
}
$c.remove();
};
$scope.parseKey = key => {
return key ? key.value || key : key;
};
const filterableColumns = () => {
@ -552,7 +552,6 @@ app.directive('wzTable', function() {
const lastKeyValue = $scope.keys[0].value || $scope.keys[0];
return exists && keysLength && keyValue && lastKeyValue;
};
$scope.setColResizable = () => {
$(`#table${$scope.scapepath} th`).resizable({
handles: 'e',
@ -569,14 +568,14 @@ app.directive('wzTable', function() {
$scope.getSyscheckRowProps = item => {
const excluded = ['$$hashKey', 'expanded', 'showTooltip'];
isWindows() && excluded.push(...['inode', 'gid', 'gname']);
isWindows() ? excluded.push(...['inode', 'gid', 'gname']) : excluded.push('attributes');
const isRegistry = (item || {}).type === 'registry';
isRegistry &&
excluded.push(...['size', 'uname', 'sha256', 'uid', 'inode']);
const items = [];
for (const key in item) {
!excluded.includes(key) &&
items.push({ key: KeyEquivalenece[key] || key, value: item[key] });
items.push({ key: KeyEquivalence[key] || key, value: item[key] });
}
const props = {
items,

View File

@ -1,6 +1,12 @@
<div class="md-padding md-padding-top-16" ng-show="wazuh_table_loading">
<react-component name="EuiProgress" props="{size: 'xs', color: 'primary'}" />
</div>
<div layout="row" layout-padding ng-if="isSyscollector">
<react-component name="BasicTable" props="basicTableProps" />
</div>
<div ng-if="customColumns" layout="row" ng-show="!error && !wazuh_table_loading && items.length" class="columns-bar"
ng-class="{'columns-bar-active': showColumns}" ng-style="!showColumns && {'margin-bottom': '-35px'}">
<div ng-if="showColumns" class="euiCheckbox wz-margin-right-8" ng-repeat="key in originalkeys" ng-click="updateColumns(key)">
@ -15,7 +21,7 @@
</span>
</div>
<div ng-if="!wazuh_table_loading && !isPolicyMonitoring() && !isSyscheck()" ng-show="!error && items.length">
<div ng-if="!isSyscollector && !wazuh_table_loading && !isPolicyMonitoring() && !isSyscheck()" ng-show="!error && items.length">
<table class="table table-striped table-condensed table-hover no-margin-bottom" ng-class="customColumns ? 'table-resizable' : ''"
id="table{{scapepath}}">
<thead class="wz-text-bold">
@ -36,12 +42,12 @@
</thead>
<tbody>
<tr ng-class="allowClick ? 'cursor-pointer' : ''" class="wz-word-wrap" ng-repeat="item in pagedItems[currentPage] | filter:{item:'!'}"
ng-click="clickAction(item)" ng-mouseover="selectedRow = $index" ng-mouseleave="selectedRow = false">
<td ng-repeat="key in keys" id="td-{{$parent.$index}}-{{$index}}" ng-mouseover="selectedCell = $index; showTooltip($parent.$index, $index, item);"
ng-mouseleave="selectedCell = false" ng-click="handleClick(key,item,$event)">
<div class="wz-text-truncatable-container">
<span class="wzTableCellFilter" ng-show="filterableColumns[parseKey(key)] && selectedRow === $parent.$index && selectedCell == $index">
<span class="fa fa-search-plus"></span> </span>
ng-click="clickAction(item)" ng-mouseover="selectedRow = $index" ng-mouseleave="selectedRow = false">
<td ng-repeat="key in keys" id="td-{{$parent.$index}}-{{$index}}" ng-mouseover="selectedCell = $index; showTooltip($parent.$index, $index, item);"
ng-mouseleave="selectedCell = false" ng-click="handleClick(key,item,$event)">
<div class="wz-text-truncatable-container">
<span class="wzTableCellFilter" ng-show="filterableColumns[parseKey(key)] && selectedRow === $parent.$index && selectedCell == $index">
<span class="fa fa-search-plus"></span> </span>
<span class="wz-text-truncatable" ng-class="parseKey(key) === 'file' && (path === '/rules' || path === '/decoders' )? 'euiLink euiLink--primary' : ''">
{{
parseValue(key,item)
@ -177,9 +183,9 @@
</table>
</div>
<div ng-if="!wazuh_table_loading && isPolicyMonitoring()" ng-show="!error && items.length">
<table class="table table-striped table-striped-duo table-condensed table-hover no-margin-bottom euiTable euiTable--responsive"
ng-class="customColumns ? 'table-resizable' : ''" id="table{{scapepath}}">
<div ng-if="!isSyscollector && !wazuh_table_loading && isPolicyMonitoring()" ng-show="!error && items.length">
<table class="table table-striped table-striped-duo table-condensed table-hover no-margin-bottom" ng-class="customColumns ? 'table-resizable' : ''"
id="table{{scapepath}}">
<thead class="wz-text-bold">
<th ng-repeat="key in keys" class="euiTableHeaderCell wz-text-left" ng-style="key.width && {'width':key.width}">
<p ng-class="{ 'cursor-pointer' : !key.nosortable }" ng-click="!key.nosortable && sort(key)">
@ -192,8 +198,8 @@
<tbody>
<tr class="wz-word-wrap cursor-pointer" ng-repeat-start="item in pagedItems[currentPage] | filter:{item:'!'}"
ng-click="expandTableRow(item)" ng-class="{'selected': item.expanded}">
<td ng-repeat="key in keys" id="{{scapepath}}-td-{{$parent.$index}}-{{$index}}" ng-mouseover="showTooltip($parent.$index, $index, item, scapepath)">
<div ng-class="customColumns ? 'wz-text-truncatable-container' : ''">
<td ng-repeat="key in keys" id="td-{{$parent.$index}}-{{$index}}" ng-mouseover="showTooltip($parent.$index, $index, item)">
<div class="wz-text-truncatable-container">
<span class="wz-text-truncatable">
<span ng-show="(key.value || key ) === 'result'" class="no-wrap">
<p class="round status little" ng-class="item.result ? (item.result === 'passed' ? 'teal' : 'red') : 'gray'">
@ -329,7 +335,7 @@
</table>
</div>
<div ng-if="!wazuh_table_loading && isSyscheck()" ng-show="!error && items.length">
<div ng-if="!isSyscollector && !wazuh_table_loading && isSyscheck()" ng-show="!error && items.length">
<table class="table table-striped table-striped-duo table-condensed table-hover no-margin-bottom" ng-class="customColumns ? 'table-resizable' : 'euiTable euiTable--responsive'"
id="table{{scapepath}}">
<thead class="wz-text-bold">
@ -404,7 +410,7 @@
</table>
</div>
<div layout="row" ng-if="!error && !wazuh_table_loading && !totalItems">
<div layout="row" ng-if="!isSyscollector && !error && !wazuh_table_loading && !totalItems">
<div flex class="euiCallOut euiCallOut--warning">
<div class="euiCallOutHeader">
<span class="euiCallOutHeader__title">
@ -415,7 +421,7 @@
</div>
</div>
<div layout="row" ng-if="error" class="wz-margin-bottom-45">
<div layout="row" ng-if="!isSyscollector && error" class="wz-margin-bottom-45">
<div flex class="euiCallOut euiCallOut--warning">
<div class="euiCallOutHeader">
<span class="euiCallOutHeader__title">

View File

@ -0,0 +1,54 @@
import React, { Component, Fragment } from 'react';
import PropTypes from 'prop-types';
import { EuiTabs, EuiTab } from '@elastic/eui';
export class Tabs extends Component {
constructor(props) {
super(props);
this.tabs = [];
this.props.tabs.forEach(tab => {
this.tabs.push({
id: tab.id,
name: tab.name
});
});
this.state = {
selectedTabId: this.props.selectedTab
};
}
onSelectedTabChanged = id => {
this.setState({
selectedTabId: id
});
this.props.clickAction(id);
};
renderTabs() {
return this.tabs.map((tab, index) => (
<EuiTab
onClick={() => this.onSelectedTabChanged(tab.id)}
isSelected={tab.id === this.state.selectedTabId}
key={index}
>
{tab.name}
</EuiTab>
));
}
render() {
return (
<Fragment>
<EuiTabs>{this.renderTabs()}</EuiTabs>
</Fragment>
);
}
}
Tabs.propTypes = {
tabs: PropTypes.array,
selectedTab: PropTypes.string,
clickAction: PropTypes.func
};

View File

@ -434,6 +434,11 @@ app.directive('wzTagFilter', function() {
}
});
$('#wz-search-filter-bar-input').attr('autocomplete', 'off');
$scope.$on('reloadSearchFilterBar', () => {
buildQuery($scope.groupedTagList);
$scope.$applyAsync();
});
load();
},
template

View File

@ -15,113 +15,116 @@
/* -------------------------------------------------------------------------- */
#wz-search-filter-bar {
padding: 8px;
overflow: auto;
padding: 8px;
background: #fff;
overflow: auto;
font-size: 14px;
}
#wz-search-filter-bar > .fa-search{
padding: 8px 3px;
float: left;
opacity: .5;
width: calc(~'0% + 25px');
#wz-search-filter-bar > .fa-search {
padding: 8px 3px;
float: left;
opacity: 0.5;
width: calc(~'0% + 25px');
}
.wz-tags {
height: 30px;
width: calc(~'100% - 25px');
display: flex;
height: 30px;
width: calc(~'100% - 25px');
display: flex;
}
.wz-tags > ul{
flex: 0 1 auto;
display: flex;
.wz-tags > ul {
flex: 0 1 auto;
display: flex;
}
.wz-tags > ul li{
white-space: nowrap;
display: flex;
.wz-tags > ul li {
white-space: nowrap;
display: flex;
}
.wz-tags > ul li .grouped{
background: #e8e8e8;
border-radius: 3px;
padding: 3px;
height: 36px;
margin-top: -3px;
display: flex;
.wz-tags > ul li .grouped {
background: #e8e8e8;
border-radius: 3px;
padding: 3px;
height: 36px;
margin-top: -3px;
display: flex;
}
.wz-tag-item {
color: white;
border-radius: 3px;
display: inline-block;
padding: 3px 5px;
background: #0079a5;
margin: 1px 3px;
color: white;
border-radius: 3px;
display: inline-block;
padding: 3px 5px;
background: #0079a5;
margin: 1px 3px;
}
.wz-tag-item-connector {
font-size: 10px;
line-height: 30px;
color: #006bb4;
font-weight: bold;
padding: 0px 5px;
font-size: 10px;
line-height: 30px;
color: #006bb4;
font-weight: bold;
padding: 0px 5px;
}
.wz-tag-item-connector:hover {
text-decoration: underline;
text-decoration: underline;
}
.wz-tag-remove-button{
color: #b2ccd4;
.wz-tag-remove-button {
color: #b2ccd4;
}
.wz-tag-remove-button-group{
padding: 4px 2px;
color: gray;
.wz-tag-remove-button-group {
padding: 4px 2px;
color: gray;
}
.wz-text-gray{
color: gray;
.wz-text-gray {
color: gray;
}
.wz-search-filter-bar-input{
border: none;
padding: 5px;
width: auto;
margin-left: 5px;
min-width: 225px;
flex: 1;
.wz-search-filter-bar-input {
border: none;
padding: 5px;
width: auto;
margin-left: 5px;
min-width: 225px;
flex: 1;
}
.wz-search-filter-bar-autocomplete{
min-width: 200px;
background: #000000d9;
position: absolute;
border: 1px solid black;
border-radius: 5px;
padding: 10px 0;
margin-top: 30px;
z-index: 10000;
.wz-search-filter-bar-autocomplete {
min-width: 200px;
background: #000000d9;
position: absolute;
border: 1px solid black;
border-radius: 5px;
padding: 10px 0;
margin-top: 30px;
z-index: 10000;
}
.wz-search-filter-bar-autocomplete p b{
color: white;
font-weight: 700;
padding: 10px;
.wz-search-filter-bar-autocomplete p b {
color: white;
font-weight: 700;
padding: 10px;
}
.wz-search-filter-bar-autocomplete ul li{
color: white;
padding: 0 10px;
.wz-search-filter-bar-autocomplete ul li {
color: white;
padding: 0 10px;
}
.wz-search-filter-bar-autocomplete ul li span{
padding-left: 5px;
.wz-search-filter-bar-autocomplete ul li span {
padding-left: 5px;
}
.wz-search-filter-bar-autocomplete ul li:hover, .wz-search-filter-bar-autocomplete ul li.selected{
background: black;
color:#00a4e1;
cursor: pointer;
}
.wz-search-filter-bar-autocomplete ul li:hover,
.wz-search-filter-bar-autocomplete ul li.selected {
background: black;
color: #00a4e1;
cursor: pointer;
}

View File

@ -2,7 +2,7 @@
<div class="synopsis">
<div class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--responsive" ng-click="callSwitchTab()">
<div class="euiFlexItem euiFlexItem--flexGrowZero">
<img class="synopsisIcon" kbn-src="/plugins/wazuh/img/{{logo || wzLogo}}">
<img class="synopsisIcon" wz-src="/plugins/wazuh/img/{{logo || wzLogo}}">
</div>
<div class="euiFlexItem synopsisContent">
<h4 class="euiTitle euiTitle--small synopsisTitle wz-text-link">{{ cardTitle }}</h4>

View File

@ -18,7 +18,7 @@ export class TabVisualizations {
this.agents = {
welcome: 0,
general: 7,
fim: 6,
fim: 7,
pm: 4,
vuls: 10,
oscap: 13,
@ -26,6 +26,8 @@ export class TabVisualizations {
audit: 9,
gdpr: 6,
pci: 6,
hipaa: 6,
nist: 6,
virustotal: 6,
configuration: 0,
osquery: 5,
@ -43,6 +45,8 @@ export class TabVisualizations {
audit: 6,
pci: 5,
gdpr: 5,
hipaa: 5,
nist: 7,
aws: 8,
virustotal: 7,
osquery: 5,

View File

@ -1 +1 @@
import './less/icon-style.less';
import './less/icon-style.less';

Binary file not shown.

Before

Width:  |  Height:  |  Size: 181 KiB

After

Width:  |  Height:  |  Size: 149 KiB

View File

@ -1,10 +1 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 160.4 216" style="enable-background:new 0 0 160.4 216;" xml:space="preserve">
<style type="text/css">
.st0{fill:#1A80B6;}
</style>
<path class="st0"
d="M 121.07369,22.226687 100.0111,66.524184 H 61.380628 L 38.310977,22.226687 22.254301,78.495875 0.1999994,103.66707 49.786767,149.21605 69.125615,193.77331 H 91.250766 L 109.52704,149.99523 160.19999,104.06839 137.62621,79.298722 Z M 96.374704,141.0932 a 4.7225498,4.7225498 0 0 1 -1.912585,1.60573 6.2337654,6.2337654 0 0 1 -2.502962,0.51939 6.0920891,6.0920891 0 0 1 -2.361296,-0.51939 4.4864223,4.4864223 0 0 1 -1.8418,-1.60573 L 80.294448,129.80632 72.59669,141.0932 a 4.7225498,4.7225498 0 0 1 -1.8418,1.60573 6.1865398,6.1865398 0 0 1 -2.361264,0.51939 4.9822898,4.9822898 0 0 1 -4.415589,-2.1252 l -38.229041,-35.6316 h 10.578511 l 32.302242,28.3353 6.469881,-10.03544 h 10.15348 l 7.083857,10.08265 32.727263,-29.51592 h 9.68122 z"
/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 80.43 80.43"><defs><style>.a{fill:#00a9e5;}</style></defs><title>wazuh_blue_iso</title><path class="a" d="M59.79-.28,49.91,20.5H31.8L21-.28l-7.52,26.4L3.12,37.91,26.4,59.27l9.05,20.88H45.8l8.57-20.52L78.12,38.11,67.54,26.49ZM48.2,55.46a2.16,2.16,0,0,1-.89.75,3,3,0,0,1-1.17.24A2.92,2.92,0,0,1,45,56.21a2.17,2.17,0,0,1-.87-.75l-3.49-5.29-3.52,5.29a2.24,2.24,0,0,1-.87.75,3,3,0,0,1-1.15.24,2.34,2.34,0,0,1-2.07-1L15.1,38.76h5L35.2,52l3-4.73H43L46.27,52,61.61,38.19h4.54Z"/></svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 530 B

1
public/img/logo.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 368.15 72.67"><defs><style>.a{fill:#00a9e5;}</style></defs><title>wazuh_blue_full copia</title><path class="a" d="M143.93,16,124.3,53l-6.62-13.29h-9.63L100.76,53,81.26,16H71.18L96.12,62.19a4.92,4.92,0,0,0,6.83,2,5,5,0,0,0,1.85-2l7.5-14.12,7.42,14.12a5.13,5.13,0,0,0,1.86,2,4.68,4.68,0,0,0,2.46.66,4.83,4.83,0,0,0,2.48-.66,5,5,0,0,0,1.9-2L153.34,16Z"/><path class="a" d="M174.47,16a3.83,3.83,0,0,0-2.36-.73,4,4,0,0,0-2.38.73A6.25,6.25,0,0,0,168,18L140.28,64.18h9.62l7.38-12.48h28.91l7.62,12.48H204L176.16,18A6.57,6.57,0,0,0,174.47,16Zm-13.36,29.2,10.68-18.06,10.74,18Z"/><path class="a" d="M253.48,20.23A3.76,3.76,0,0,0,252,17,5.37,5.37,0,0,0,248.74,16H207v8H239.8l-31.74,32a5,5,0,0,0-1.71,3.54,3.77,3.77,0,0,0,1.42,3.19,5.28,5.28,0,0,0,3.31,1.08h42.2V55.79H220l31.74-32A5,5,0,0,0,253.48,20.23Z"/><path class="a" d="M305.42,42.89a19.11,19.11,0,0,1-.91,6.24,9.25,9.25,0,0,1-2.91,4.24,13,13,0,0,1-5.4,2.42,45.77,45.77,0,0,1-16.72,0,13,13,0,0,1-5.4-2.42,9.27,9.27,0,0,1-2.92-4.24,19.15,19.15,0,0,1-.89-6.24V16h-8.7V42.89A29,29,0,0,0,263,52.6a15.92,15.92,0,0,0,4.57,6.87,19.74,19.74,0,0,0,8.06,4,46.12,46.12,0,0,0,11.9,1.33,46.56,46.56,0,0,0,12-1.33,19.82,19.82,0,0,0,8.05-4,16,16,0,0,0,4.58-6.87,29.26,29.26,0,0,0,1.46-9.71V16h-8.22Z"/><polygon class="a" points="360.06 15.97 360.06 35.92 329.69 35.92 329.69 15.97 321.26 15.97 321.26 64.38 329.69 64.38 329.69 43.42 360.06 43.42 360.06 64.38 368.48 64.38 368.48 15.97 360.06 15.97"/><path class="a" d="M51.53,0,42.61,18.76H26.25L16.48,0,9.68,23.83.34,34.49l21,19.29,8.19,18.87H38.9l7.74-18.54L68.1,34.66,58.54,24.17ZM41.07,50.34a2,2,0,0,1-.81.68,2.64,2.64,0,0,1-1.06.22,2.58,2.58,0,0,1-1-.22,1.9,1.9,0,0,1-.78-.68l-3.16-4.78L31,50.34a2,2,0,0,1-.78.68,2.62,2.62,0,0,1-1,.22,2.11,2.11,0,0,1-1.87-.9L11.16,35.25h4.48l13.68,12L32.06,43h4.3l3,4.27,13.86-12.5h4.1Z"/></svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

View File

@ -1,285 +1,285 @@
import _ from 'lodash';
import templateBar from '../templates/kibana-template/kibana-filterbar-template.html';
import templatePill from '../templates/kibana-template/kibana-filterpill-template.html';
import 'ui/directives/json_input';
import 'ui/filter_editor';
import 'ui/filter_bar/filter_pill/filter_pill';
import { filterAppliedAndUnwrap } from 'ui/filter_bar/lib/filter_applied_and_unwrap';
import { FilterBarLibMapAndFlattenFiltersProvider } from 'ui/filter_bar/lib/map_and_flatten_filters';
import { FilterBarLibMapFlattenAndWrapFiltersProvider } from 'ui/filter_bar/lib/map_flatten_and_wrap_filters';
import { FilterBarLibExtractTimeFilterProvider } from 'ui/filter_bar/lib/extract_time_filter';
import { FilterBarLibFilterOutTimeBasedFilterProvider } from 'ui/filter_bar/lib/filter_out_time_based_filter';
import { changeTimeFilter } from 'ui/filter_bar/lib/change_time_filter';
import { FilterBarQueryFilterProvider } from 'ui/filter_bar/query_filter';
import { compareFilters } from 'ui/filter_bar/lib/compare_filters';
import { uiModules } from 'ui/modules';
export {
disableFilter,
enableFilter,
toggleFilterDisabled
} from 'ui/filter_bar/lib/disable_filter';
const module = uiModules.get('kibana');
module
.directive('filterBarW', function(Private, Promise, getAppState, i18n) {
const mapAndFlattenFilters = Private(
FilterBarLibMapAndFlattenFiltersProvider
);
const mapFlattenAndWrapFilters = Private(
FilterBarLibMapFlattenAndWrapFiltersProvider
);
const extractTimeFilter = Private(FilterBarLibExtractTimeFilterProvider);
const filterOutTimeBasedFilter = Private(
FilterBarLibFilterOutTimeBasedFilterProvider
);
const queryFilter = Private(FilterBarQueryFilterProvider);
return {
template: templateBar,
restrict: 'E',
scope: {
indexPatterns: '=',
tooltipContent: '='
},
link: function($scope, $elem) {
// bind query filter actions to the scope
[
'addFilters',
'toggleFilter',
'toggleAll',
'pinFilter',
'pinAll',
'invertFilter',
'invertAll',
'removeFilter',
'removeAll'
].forEach(function(method) {
$scope[method] = queryFilter[method];
});
$scope.state = getAppState();
$scope.showCollapseLink = () => {
const pill = $elem.find('filter-pill-w');
return pill[pill.length - 1].offsetTop > 10;
};
const collapseFilterTooltip = i18n(
'common.ui.filterBar.collapseFilterTooltip',
{
defaultMessage: 'Collapse filter bar \n to show less'
}
);
const expandFilterTooltip = i18n(
'common.ui.filterBar.expandFilterTooltip',
{ defaultMessage: 'Expand filter bar \n to show more' }
);
$scope.filterNavToggle = {
isOpen: true,
tooltipContent: collapseFilterTooltip
};
$scope.toggleFilterShown = () => {
const collapser = $elem.find('.filter-nav-link__collapser');
const filterPanelPill = $elem.find('.filter-panel__pill');
if ($scope.filterNavToggle.isOpen) {
$scope.filterNavToggle.tooltipContent = expandFilterTooltip;
collapser.attr('aria-expanded', 'false');
filterPanelPill.attr('style', 'width: calc(100% - 80px)');
} else {
$scope.filterNavToggle.tooltipContent = collapseFilterTooltip;
collapser.attr('aria-expanded', 'true');
filterPanelPill.attr('style', 'width: auto');
}
$scope.filterNavToggle.isOpen = !$scope.filterNavToggle.isOpen;
};
$scope.applyFilters = function(filters) {
addAndInvertFilters(filterAppliedAndUnwrap(filters));
$scope.newFilters = [];
// change time filter
if (
$scope.changeTimeFilter &&
$scope.changeTimeFilter.meta &&
$scope.changeTimeFilter.meta.apply
) {
changeTimeFilter($scope.changeTimeFilter);
}
};
$scope.addFilter = () => {
$scope.editingFilter = {
meta: { isNew: true }
};
};
$scope.deleteFilter = filter => {
$scope.removeFilter(filter);
if (filter === $scope.editingFilter) $scope.cancelEdit();
};
$scope.editFilter = filter => {
$scope.editingFilter = filter;
};
$scope.cancelEdit = () => {
delete $scope.editingFilter;
};
$scope.saveEdit = (filter, newFilter, isPinned) => {
if (!filter.meta.isNew) $scope.removeFilter(filter);
delete $scope.editingFilter;
$scope.addFilters([newFilter], isPinned);
};
$scope.clearFilterBar = function() {
$scope.newFilters = [];
$scope.changeTimeFilter = null;
};
// update the scope filter list on filter changes
$scope.$listen(queryFilter, 'update', function() {
updateFilters();
});
// when appState changes, update scope's state
$scope.$watch(getAppState, function(appState) {
$scope.state = appState;
});
$scope.$watch('state.$newFilters', function(filters) {
if (!filters) return;
// If filters is not undefined and the length is greater than
// one we need to set the newFilters attribute and allow the
// users to decide what they want to apply.
if (filters.length > 1) {
return mapFlattenAndWrapFilters(filters)
.then(function(results) {
extractTimeFilter(results).then(function(filter) {
$scope.changeTimeFilter = filter;
});
return results;
})
.then(filterOutTimeBasedFilter)
.then(function(results) {
$scope.newFilters = results;
});
}
// Just add single filters to the state.
if (filters.length === 1) {
Promise.resolve(filters)
.then(function(filters) {
extractTimeFilter(filters).then(function(timeFilter) {
if (timeFilter) changeTimeFilter(timeFilter);
});
return filters;
})
.then(filterOutTimeBasedFilter)
.then(addAndInvertFilters);
}
});
function addAndInvertFilters(filters) {
const existingFilters = queryFilter.getFilters();
const inversionFilters = _.filter(existingFilters, existingFilter => {
const newMatchingFilter = _.find(
filters,
_.partial(compareFilters, existingFilter)
);
return (
newMatchingFilter &&
newMatchingFilter.meta &&
existingFilter.meta &&
existingFilter.meta.negate !== newMatchingFilter.meta.negate
);
});
const newFilters = _.reject(filters, filter => {
return _.find(inversionFilters, _.partial(compareFilters, filter));
});
_.forEach(inversionFilters, $scope.invertFilter);
$scope.addFilters(newFilters);
}
function updateFilters() {
let filters = queryFilter.getFilters();
// Kibana filter pills should not alter their order when switching between Dashboard and Discover sub-tabs
// https://github.com/wazuh/wazuh-kibana-app/issues/1051
if (filters && Array.isArray(filters)) {
const nonRemovable = filters.filter(
item =>
item &&
item.meta &&
typeof item.meta.removable !== 'undefined' &&
!item.meta.removable
);
const globalState = filters.filter(
item => item && item.$state && item.$state.store === 'globalState'
);
const other = filters.filter(
item =>
item &&
!(
item.meta &&
typeof item.meta.removable !== 'undefined' &&
!item.meta.removable
) &&
!(item.$state && item.$state.store === 'globalState')
);
const newFilters = [];
if (nonRemovable.length) newFilters.push(...nonRemovable);
if (globalState.length) newFilters.push(...globalState);
if (other.length) newFilters.push(...other);
filters = newFilters;
}
mapAndFlattenFilters(filters).then(function(results) {
// used to display the current filters in the state
$scope.filters = _.sortBy(results, function(filter) {
return !filter.meta.pinned;
});
$scope.$emit('filterbar:updated');
});
}
updateFilters();
}
};
})
.directive('filterPillW', function() {
return {
template: templatePill,
restrict: 'E',
scope: {
filter: '=',
onToggleFilter: '=',
onPinFilter: '=',
onInvertFilter: '=',
onDeleteFilter: '=',
onEditFilter: '='
},
bindToController: true,
controllerAs: 'pill',
controller: function filterPillController() {
this.activateActions = () => {
this.areActionsActivated = true;
};
this.deactivateActions = () => {
this.areActionsActivated = false;
};
this.isControlledByPanel = () => {
return _.has(this.filter, 'meta.controlledBy');
};
}
};
});
import _ from 'lodash';
import templateBar from '../templates/kibana-template/kibana-filterbar-template.html';
import templatePill from '../templates/kibana-template/kibana-filterpill-template.html';
import 'ui/directives/json_input';
import 'ui/filter_editor';
import 'ui/filter_bar/filter_pill/filter_pill';
import { filterAppliedAndUnwrap } from 'ui/filter_bar/lib/filter_applied_and_unwrap';
import { FilterBarLibMapAndFlattenFiltersProvider } from 'ui/filter_bar/lib/map_and_flatten_filters';
import { FilterBarLibMapFlattenAndWrapFiltersProvider } from 'ui/filter_bar/lib/map_flatten_and_wrap_filters';
import { FilterBarLibExtractTimeFilterProvider } from 'ui/filter_bar/lib/extract_time_filter';
import { FilterBarLibFilterOutTimeBasedFilterProvider } from 'ui/filter_bar/lib/filter_out_time_based_filter';
import { changeTimeFilter } from 'ui/filter_bar/lib/change_time_filter';
import { FilterBarQueryFilterProvider } from 'ui/filter_bar/query_filter';
import { compareFilters } from 'ui/filter_bar/lib/compare_filters';
import { uiModules } from 'ui/modules';
export {
disableFilter,
enableFilter,
toggleFilterDisabled
} from 'ui/filter_bar/lib/disable_filter';
const module = uiModules.get('kibana');
module
.directive('filterBarW', function(Private, Promise, getAppState, i18n) {
const mapAndFlattenFilters = Private(
FilterBarLibMapAndFlattenFiltersProvider
);
const mapFlattenAndWrapFilters = Private(
FilterBarLibMapFlattenAndWrapFiltersProvider
);
const extractTimeFilter = Private(FilterBarLibExtractTimeFilterProvider);
const filterOutTimeBasedFilter = Private(
FilterBarLibFilterOutTimeBasedFilterProvider
);
const queryFilter = Private(FilterBarQueryFilterProvider);
return {
template: templateBar,
restrict: 'E',
scope: {
indexPatterns: '=',
tooltipContent: '='
},
link: function($scope, $elem) {
// bind query filter actions to the scope
[
'addFilters',
'toggleFilter',
'toggleAll',
'pinFilter',
'pinAll',
'invertFilter',
'invertAll',
'removeFilter',
'removeAll'
].forEach(function(method) {
$scope[method] = queryFilter[method];
});
$scope.state = getAppState();
$scope.showCollapseLink = () => {
const pill = $elem.find('filter-pill-w');
return pill[pill.length - 1].offsetTop > 10;
};
const collapseFilterTooltip = i18n(
'common.ui.filterBar.collapseFilterTooltip',
{
defaultMessage: 'Collapse filter bar \n to show less'
}
);
const expandFilterTooltip = i18n(
'common.ui.filterBar.expandFilterTooltip',
{ defaultMessage: 'Expand filter bar \n to show more' }
);
$scope.filterNavToggle = {
isOpen: true,
tooltipContent: collapseFilterTooltip
};
$scope.toggleFilterShown = () => {
const collapser = $elem.find('.filter-nav-link__collapser');
const filterPanelPill = $elem.find('.filter-panel__pill');
if ($scope.filterNavToggle.isOpen) {
$scope.filterNavToggle.tooltipContent = expandFilterTooltip;
collapser.attr('aria-expanded', 'false');
filterPanelPill.attr('style', 'width: calc(100% - 80px)');
} else {
$scope.filterNavToggle.tooltipContent = collapseFilterTooltip;
collapser.attr('aria-expanded', 'true');
filterPanelPill.attr('style', 'width: auto');
}
$scope.filterNavToggle.isOpen = !$scope.filterNavToggle.isOpen;
};
$scope.applyFilters = function(filters) {
addAndInvertFilters(filterAppliedAndUnwrap(filters));
$scope.newFilters = [];
// change time filter
if (
$scope.changeTimeFilter &&
$scope.changeTimeFilter.meta &&
$scope.changeTimeFilter.meta.apply
) {
changeTimeFilter($scope.changeTimeFilter);
}
};
$scope.addFilter = () => {
$scope.editingFilter = {
meta: { isNew: true }
};
};
$scope.deleteFilter = filter => {
$scope.removeFilter(filter);
if (filter === $scope.editingFilter) $scope.cancelEdit();
};
$scope.editFilter = filter => {
$scope.editingFilter = filter;
};
$scope.cancelEdit = () => {
delete $scope.editingFilter;
};
$scope.saveEdit = (filter, newFilter, isPinned) => {
if (!filter.meta.isNew) $scope.removeFilter(filter);
delete $scope.editingFilter;
$scope.addFilters([newFilter], isPinned);
};
$scope.clearFilterBar = function() {
$scope.newFilters = [];
$scope.changeTimeFilter = null;
};
// update the scope filter list on filter changes
$scope.$listen(queryFilter, 'update', function() {
updateFilters();
});
// when appState changes, update scope's state
$scope.$watch(getAppState, function(appState) {
$scope.state = appState;
});
$scope.$watch('state.$newFilters', function(filters) {
if (!filters) return;
// If filters is not undefined and the length is greater than
// one we need to set the newFilters attribute and allow the
// users to decide what they want to apply.
if (filters.length > 1) {
return mapFlattenAndWrapFilters(filters)
.then(function(results) {
extractTimeFilter(results).then(function(filter) {
$scope.changeTimeFilter = filter;
});
return results;
})
.then(filterOutTimeBasedFilter)
.then(function(results) {
$scope.newFilters = results;
});
}
// Just add single filters to the state.
if (filters.length === 1) {
Promise.resolve(filters)
.then(function(filters) {
extractTimeFilter(filters).then(function(timeFilter) {
if (timeFilter) changeTimeFilter(timeFilter);
});
return filters;
})
.then(filterOutTimeBasedFilter)
.then(addAndInvertFilters);
}
});
function addAndInvertFilters(filters) {
const existingFilters = queryFilter.getFilters();
const inversionFilters = _.filter(existingFilters, existingFilter => {
const newMatchingFilter = _.find(
filters,
_.partial(compareFilters, existingFilter)
);
return (
newMatchingFilter &&
newMatchingFilter.meta &&
existingFilter.meta &&
existingFilter.meta.negate !== newMatchingFilter.meta.negate
);
});
const newFilters = _.reject(filters, filter => {
return _.find(inversionFilters, _.partial(compareFilters, filter));
});
_.forEach(inversionFilters, $scope.invertFilter);
$scope.addFilters(newFilters);
}
function updateFilters() {
let filters = queryFilter.getFilters();
// Kibana filter pills should not alter their order when switching between Dashboard and Discover sub-tabs
// https://github.com/wazuh/wazuh-kibana-app/issues/1051
if (filters && Array.isArray(filters)) {
const nonRemovable = filters.filter(
item =>
item &&
item.meta &&
typeof item.meta.removable !== 'undefined' &&
!item.meta.removable
);
const globalState = filters.filter(
item => item && item.$state && item.$state.store === 'globalState'
);
const other = filters.filter(
item =>
item &&
!(
item.meta &&
typeof item.meta.removable !== 'undefined' &&
!item.meta.removable
) &&
!(item.$state && item.$state.store === 'globalState')
);
const newFilters = [];
if (nonRemovable.length) newFilters.push(...nonRemovable);
if (globalState.length) newFilters.push(...globalState);
if (other.length) newFilters.push(...other);
filters = newFilters;
}
mapAndFlattenFilters(filters).then(function(results) {
// used to display the current filters in the state
$scope.filters = _.sortBy(results, function(filter) {
return !filter.meta.pinned;
});
$scope.$emit('filterbar:updated');
});
}
updateFilters();
}
};
})
.directive('filterPillW', function() {
return {
template: templatePill,
restrict: 'E',
scope: {
filter: '=',
onToggleFilter: '=',
onPinFilter: '=',
onInvertFilter: '=',
onDeleteFilter: '=',
onEditFilter: '='
},
bindToController: true,
controllerAs: 'pill',
controller: function filterPillController() {
this.activateActions = () => {
this.areActionsActivated = true;
};
this.deactivateActions = () => {
this.areActionsActivated = false;
};
this.isControlledByPanel = () => {
return _.has(this.filter, 'meta.controlledBy');
};
}
};
});

View File

@ -143,7 +143,7 @@ export class VisualizeLoader {
// lets add Private to the params, we'll need to pass it to visualize later
Private: this.Private,
};
// @ts-ignore
return new EmbeddedVisualizeHandler(element, savedObj, handlerParams, this.injector, this.errorHandler);
}
}

View File

@ -73,7 +73,6 @@
position: relative;
height: 25px;
color: #666666;
background-color: white !important;
}
/* Custom JSON viewer settings */
@ -257,9 +256,9 @@
box-shadow: none !important;
}
.btn-as-i:hover {
.btn-as-i:hover, .btn-as-i:focus {
background: none !important;
color: #006bb4;
color: #006bb4!important;
}
/* Custom reporting button styles */
@ -409,6 +408,10 @@
/* Custom Kibana styles */
md-content.md-default-theme, md-content{
background: transparent!important;
}
.wz-border-none,
.wz-border-none md-select-value {
border: none !important;
@ -424,7 +427,7 @@
}
.table-hover > tbody > tr:hover {
background-color: #f0f0f0;
background-color: #fafbfd!important;
}
/* .table > tbody {
@ -493,7 +496,6 @@ md-sidenav {
margin-top: 25px;
background: #dddddd;
float: left;
height: calc(~'100vh - 110px');
cursor: ew-resize;
text-align: center;
display: block !important;
@ -510,7 +512,6 @@ md-sidenav {
}
.wz-dev-column-separator span {
height: calc(~'100vh - 110px');
display: table-cell;
vertical-align: middle;
}
@ -520,7 +521,6 @@ md-sidenav {
min-width: calc(~'20% - 7px');
max-width: calc(~'80% - 7px');
float: left;
height: calc(~'100vh - 85px');
}
#wz-dev-right-column {
@ -528,7 +528,6 @@ md-sidenav {
min-width: calc(~'20% - 7px');
max-width: calc(~'80% - 7px');
float: left;
height: calc(~'100vh - 85px');
}
.wz-dev-box .CodeMirror {
@ -539,7 +538,7 @@ md-sidenav {
.wz-md-card:not(.wz-metric-color) {
box-shadow: 0 2px 2px -1px rgba(0, 0, 0, 0.1) !important;
border: 1px solid #d3dae6 !important;
border: 1px solid #d3dae6;
overflow: hidden;
}
@ -851,6 +850,14 @@ wz-xml-file-editor {
height: inherit;
}
.table-striped > tbody > tr:nth-of-type(odd) {
background-color: #f5f7fa;
}
.euiTableHeaderCell {
font-weight: 600;
}
.table-striped-duo .selected {
background: #ecf6fb !important;
}
@ -988,6 +995,88 @@ discover-app-w .container-fluid {
background: #fff;
}
.registerAgent{
min-height: calc(~'100vh - 100px');
background: #fafbfd;
padding-top: 25px;
}
.navBarLogo {
width: 110px;
margin-top: 4px;
}
.application.tab-health-check wz-menu{
display: none;
}
.health-check table tr td{
border-top: none!important;
}
.health-check{
padding-top: 5%;
}
@keyframes rotation {
from {
transform: rotate(0deg);
}
to {
transform: rotate(359deg);
}
}
.health-check-loader{
height: 150px;
width: 150px;
margin: 0 auto;
position: relative;
border: 2px solid transparent;
border-top: 2px solid #00A9E5;
border-radius: 100%;
display: block;
animation: rotation .75s .5s infinite linear;
animation-play-state: running;
}
.health-check-logo{
width: 110px;
height: 110px;
float: left;
margin: 0 auto;
margin-top: -130px;
margin-bottom: 50px;
z-index: 1;
}
.CodeMirror-gutters {
z-index: 1!important;
}
md-toolbar.md-default-theme:not(.md-menu-toolbar), md-toolbar:not(.md-menu-toolbar) {
background-color: transparent!important;
}
.refresh-report-button {
padding: 10px;
text-align: right;
}
.monitoring-discover form{
display: none;
}
.euiBadge, .euiBadge__childButton{
font-size: 12px!important;
}
.wz-link{
cursor:pointer;
color: #006BB4;
text-decoration: none;
}
.wzTableCellFilter{
text-align: right;
height: 0;
@ -996,4 +1085,59 @@ discover-app-w .container-fluid {
.wz-md-card:not(.fullscreen) .sca-vis.table-scrollable .kbnAggTable__paginated{
height: 150px;
}
}
.wz-margin-10 {
margin: 10px;
}
.wz-margin-left-10 {
margin-left: 10px;
}
.wz-margin-right-10 {
margin-right: 10px;
}
.hiddenCard {
opacity: 0;
cursor: default !important;
}
.footer-req {
margin-top: -15px !important;
font-size: 12px !important;
cursor: pointer !important;
}
.wz-padding-bt-5 {
padding-bottom: 5px !important;
}
.wz-margin--10 {
margin-left: -10px;
}
.header-global-wrapper + .app-wrapper:not(.hidden-chrome) {
top: 48px!important;
left: 48px!important;
}
.reqCard {
cursor: default !important;
}
.reqCard:hover, .reqCard:focus {
transform: translateY(0px) !important;
box-shadow: 0 2px 2px -1px rgba(152, 162, 179, 0.3), 0 1px 5px -2px rgba(152, 162, 179, 0.3) !important;
}
.reqCard:hover .euiCard__title, .reqCard:focus .euiCard__title {
text-decoration: none !important;
}
@media only screen and (max-width: 767px){
.header-global-wrapper + .app-wrapper:not(.hidden-chrome) {
left: 0!important;
}
}

4094
public/less/dark_theme/bootstrap_light.css vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,280 @@
body.md-default-theme,
body,
html.md-default-theme,
html {
color: #dfe5ef !important;
}
.euiHeaderSectionItem__button,
.euiListGroupItem__icon {
color: #dfe5ef;
}
.euiToolTipAnchor {
color: white;
}
.app-wrapper-panel {
background-color: #1a1b20;
}
.wz-md-card:not(.wz-metric-color) {
box-shadow: 0 2px 2px -1px rgba(0, 0, 0, 0.3), 0 1px 5px -2px rgba(0, 0, 0, 0.3);
background-color: #1D1E24;
border: 1px solid #343741;
}
.wz-card-actions-vis {
color: white;
border-bottom: 1px solid #343741;
}
.wz-card-actions.wz-card-actions-top, .columns-bar-active {
border-bottom: 1px solid #343741!important;
background: #16171c;
color: #dfe5ef;
border-top: none!important;
}
.kuiButton--secondary:enabled:hover {
background: rgba(27, 169, 245, 0.1)!important;
color: #45b9f6!important;
border-color: #1BA9F5!important;
}
.kuiButton--secondary {
color: #45b9f6!important;
border-color: #1BA9F5;
background: transparent;
}
.btn-info:hover {
background: #ebebeb17 !important;
color: #fff !important;
}
#wz-search-filter-bar-input {
background: transparent;
color: white;
}
.registerAgent {
background: #1a1b20!important;
}
.json-beautifier {
background: black;
color: gray;
}
.wz-configuration-value {
background: transparent;
border-color: #343741;
}
.kuiSelect{
filter: invert(1);
}
md-card md-card-content {
color: #fff;
}
.ui-select-choices-row-inner {
color: #dfe5ef;
}
md-content.md-default-theme,
md-content {
background-color: #1a1b20;
color: #c8dad9;
}
.wz-metric-color {
background-color: #343741 !important;
border: 1px solid #131417;
color: white;
}
.visLegend__toggle {
color: white!important;
}
discover-app-w .container-fluid {
background-color: #1D1E24;
}
.euiBreadcrumbs--truncate
.euiBreadcrumb:not(.euiBreadcrumb--collapsed).euiBreadcrumb--last,
.euiNavDrawerGroup__item .euiListGroupItem__label,
.euiNavDrawer .euiNavDrawer__expandButton .euiListGroupItem__button {
color: #dfe5ef;
}
.percentage {
color: #fff;
}
.wz-nav-item button.md-primary {
color: #0079a5 !important;
background-color: #232635!important;
border-bottom: 2px solid #006BB4;
}
md-nav-bar.md-default-theme .md-nav-bar, md-nav-bar .md-nav-bar {
border-color: rgb(52, 55, 65);
}
.wz-nav-item button.md-unselected {
color: #fff !important;
}
.sidebar-container .index-pattern {
background-color: #1ba9f5!important;
color: white!important;
}
.wz-menu-content {
background-color: #1a1b20;
border-bottom: 1px solid #343741;
color: white;
}
.wz-menu-button.wz-menu-active {
background-color: #16171c !important;
}
.wz-menu-button:not([disabled]):hover {
background: #16171c;
}
.wzXmlEditor {
background: #1d1e24;
border: 1px solid #343741;
color: #c8dad9;
}
.CodeMirror {
border-top: 1px solid #5c606f;
}
.wz-select-input {
border: none !important;
}
.euiCard {
color: #dfe5ef;
}
.md-subheader.md-default-theme, .md-subheader {
color: #dfe5ef;
}
.euiCard__top.wz-card-actions-top {
background: #272931;
}
.table {
color: #dfe5ef !important;
}
.table-striped > tbody > tr:nth-of-type(odd) {
background-color: #1a1b21;
color: #dfe5ef !important;
}
.table-hover > tbody > tr:hover {
background-color: rgba(27, 169, 245, 0.05) !important;
}
#wz-search-filter-bar {
background: #16171c;
color: #dfe5ef;
}
#wz-search-filter-bar-input{
box-shadow: none;
}
.kuiLocalSearchInput, .kuiLocalSearchInput:focus {
border: 1px solid #343741 !important;
background: #16171c;
color: #dfe5ef;
}
.wzMultipleSelector .panel-primary {
border: 1px solid #343741!important;
-webkit-box-shadow: 0 2px 2px -1px rgba(0, 0, 0, 0.1) !important;
box-shadow: 0 2px 2px -1px rgba(0, 0, 0, 0.1) !important;
border-radius: 2px;
}
.wzMultipleSelector .panel-primary > .panel-heading {
color: #fff;
border-color: #343741;
}
.wzMultipleSelector select {
border-color: #343741;
}
.btn-info {
border: 1px solid #343741 !important;
}
.table-resizable > thead th:not(:first-child) {
border-left: 1px dashed #343741;
}
md-dialog.md-default-theme.md-content-overflow .md-actions,
md-dialog.md-content-overflow .md-actions,
md-dialog.md-default-theme.md-content-overflow md-dialog-actions,
md-dialog.md-content-overflow md-dialog-actions,
md-divider.md-default-theme, md-divider {
border-top-color: rgb(52, 55, 65);
}
.wz-item-detail {
border: 1px solid #343741;
}
.wz-item-list {
background-color: #16171c;
border: 1px solid #343741;
}
.euiFlexGroup .euiFlexGroup:hover {
background: #1D1E24;
}
.wz-dev-box .CodeMirror {
border: 1px solid #343741 !important;
}
.wz-dev-column-separator {
background: #1d1e24;
}
.CodeMirror-styled-background {
background-color: #343741;
}
.wz-dev-column-separator:hover {
background-color: #0b4462;
}
.CodeMirror-hints{
background-color: #16171c !important;
border-color: #000;
color: #dfe5ef;
}
.wz-input-text {
background-color: #16171c;
border: 1px solid #343741;
color: #dfe5ef;
}
.wz-menu-content {
box-shadow: 0 2px 2px -1px rgba(0, 0, 0, 0.3)!important;
}

View File

@ -1,5 +1,5 @@
.kbnGlobalNavLink__iconImage {
height: 42px!important;
margin-top: 0!important;
width: 20px!important;
height: 42px!important;
margin-top: 0!important;
width: 20px!important;
}

View File

@ -26,7 +26,6 @@
outline: 0;
line-height: 1.3;
text-decoration: none;
font-size: 100%;
list-style: none;
}
.ui-helper-clearfix:before,
@ -90,7 +89,6 @@
margin: 2px 0 0 0;
padding: 0.5em 0.5em 0.5em 0.7em;
min-height: 0; /* support: IE7 */
font-size: 100%;
}
.ui-accordion .ui-accordion-icons {
padding-left: 2.2em;

View File

@ -14,438 +14,483 @@
/* ------------------------ Wazuh layouts stylesheet ------------------------ */
/* -------------------------------------------------------------------------- */
html {
background: white !important;
}
#kibana-body{
min-height: calc(~'100vh - 1px') !important;
}
/* Margins */
.wz-margin-right-8 {
margin-right: 8px;
margin-right: 8px;
}
.wz-margin-left-8 {
margin-left: 8px;
margin-left: 8px;
}
.wz-margin-8-no-left {
margin: 8px 8px 8px 0px;
margin: 8px 8px 8px 0px;
}
.wz-margin-right-15 {
margin-right: 15px;
margin-right: 15px;
}
.wz-margin-right-16 {
margin-right: 16px;
margin-right: 16px;
}
.wz-margin-right-20 {
margin-right: 20px;
margin-right: 20px;
}
.wz-margin-right-24 {
margin-right: 24px;
margin-right: 24px;
}
.no-margin-right {
margin-right: 0px !important;
margin-right: 0px !important;
}
.wz-margin-top-0 {
margin-top: 4px;
margin-top: 4px;
}
.wz-margin-top-4 {
margin-top: 4px;
margin-top: 4px;
}
.wz-margin-top-8 {
margin-top: 8px;
margin-top: 8px;
}
.wz-margin-top-10 {
margin-top: 10px;
margin-top: 10px;
}
.wz-margin-top-16 {
margin-top: 16px;
margin-top: 16px;
}
.wz-margin-top-17 {
margin-top: 17px;
margin-top: 17px;
}
.margin-top-30 {
margin-top: 30px;
margin-top: 30px;
}
.wz-margin-top-40 {
margin-top: 40px;
margin-top: 40px;
}
.wz-margin-left-7 {
margin-left: 7px;
margin-left: 7px;
}
.wz-margin-left-16 {
margin-left: 16px;
margin-left: 16px;
}
.no-margin-left {
margin-left: 0px;
margin-left: 0px;
}
.wz-margin-bottom-10 {
margin-bottom: 10px;
margin-bottom: 10px;
}
.wz-margin-bottom-25 {
margin-bottom: 25px;
margin-bottom: 25px;
}
.wz-margin-bottom-40 {
margin-bottom: 40px;
margin-bottom: 40px;
}
.wz-margin-bottom-45 {
margin-bottom: 45px;
margin-bottom: 45px;
}
.wz-margin-bottom-40-inv {
margin-bottom: -40px !important;
margin-bottom: -40px !important;
}
.no-margin-bottom {
margin-bottom: 0px;
margin-bottom: 0px;
}
.wz-no-margin {
margin: 0 !important;
margin: 0 !important;
}
.wz-no-top-bottom-margin {
margin-top: 0 !important;
margin-bottom: 0 !important;
margin-top: 0 !important;
margin-bottom: 0 !important;
}
/* Paddings */
.wz-no-padding {
padding: 0 !important;
padding: 0 !important;
}
.wz-padding-top-0 {
padding-top: 0px !important;
padding-top: 0px !important;
}
.wz-padding-bottom-0 {
padding-bottom: 0px !important;
padding-bottom: 0px !important;
}
.wz-padding-bottom-5 {
padding-bottom: 5px !important;
padding-bottom: 5px !important;
}
.wz-padding-top-5 {
padding-top: 5px !important;
padding-top: 5px !important;
}
.wz-padding-top-10 {
padding-top: 10px;
padding-top: 10px;
}
.wz-padding-top-14{
padding-top: 14px;
.wz-padding-top-14 {
padding-top: 14px;
}
.padding-left-0 {
padding-left: 0px;
padding-left: 0px;
}
.wz-padding-left-8 {
padding-left: 8px
padding-left: 8px;
}
.wz-padding-left-16 {
padding-left: 16px
padding-left: 16px;
}
.padding-right-0 {
padding-right: 0px !important;
padding-right: 0px !important;
}
.wz-padding-right-8 {
padding-right: 8px;
padding-right: 8px;
}
.wz-padding-right-16 {
padding-right: 16px
padding-right: 16px;
}
.wz-padding-right-17 {
padding-right: 17px;
padding-right: 17px;
}
.wz-padding-metric {
line-height: 16px;
padding-top: 10px;
padding-bottom: 10px;
line-height: 16px;
padding-top: 10px;
padding-bottom: 10px;
}
.wz-lateral-padding-16 {
padding-left: 16px;
padding-right: 16px;
padding-left: 16px;
padding-right: 16px;
}
/* Overflows */
.overflow-hidden {
overflow: hidden;
overflow: hidden;
}
.wz-overflow-y-auto {
overflow-y: auto;
overflow-y: auto;
}
/* Widths */
.wz-width-100 {
width: 100%;
width: 100%;
}
/* Others */
.wz-no-display {
display: none !important;
display: none !important;
}
.wazuh-column {
display: flex;
flex-direction: column;
flex: 1 1 100%;
display: flex;
flex-direction: column;
flex: 1 1 100%;
}
.no-opacity {
opacity: 0 !important;
height: 0px !important;
opacity: 0 !important;
height: 0px !important;
}
.no-opacity-overview-monitoring {
opacity: 0 !important;
height: 0px !important;
width: 0px !important;
margin-right: -8px !important;
opacity: 0 !important;
height: 0px !important;
width: 0px !important;
margin-right: -8px !important;
}
.wz-no-margin-padding {
margin: 0 !important;
padding: 0 !important;
margin: 0 !important;
padding: 0 !important;
}
.table-vertical-align-middle tr td{
vertical-align: middle!important;
.table-vertical-align-middle tr td {
vertical-align: middle !important;
}
.table-layout-fixed {
table-layout: fixed !important;
table-layout: fixed !important;
}
.inventory-metrics {
margin-top:10px;
margin-left:10px;
margin-right:10px
margin-top: 10px;
margin-left: 10px;
margin-right: 10px;
}
.wz-inline-block {
display: inline;
display: inline;
}
md-dialog-actions button {
color: white !important;
transition: none !important;
background-color: rgb(0, 85, 113) !important;
color: white !important;
transition: none !important;
background-color: rgb(0, 85, 113) !important;
}
md-backdrop.md-opaque {
opacity: 1!important;
background-color: rgba(255, 255, 255, 0.8);
opacity: 1 !important;
background-color: rgba(255, 255, 255, 0.8);
}
md-backdrop.md-opaque.ng-leave {
opacity: 0!important;
transition: none!important;
transform: none!important;
opacity: 0 !important;
transition: none !important;
transform: none !important;
}
md-dialog.md-transition-in {
transition: 150ms ease-in!important;
transform: none!important;
animation: 350ms cubic-bezier(0.34, 1.61, 0.7, 1);
transition: 150ms ease-in !important;
transform: none !important;
animation: 350ms cubic-bezier(0.34, 1.61, 0.7, 1);
}
md-dialog.md-transition-out {
transition: none!important;
transform: none!important;
transition: none !important;
transform: none !important;
}
.md-dialog-container{
//padding-bottom: 10vh;
z-index: 100!important;
.md-dialog-container {
//padding-bottom: 10vh;
z-index: 100 !important;
}
md-dialog .md-dialog-content {
padding: 0px!important;
padding: 0px !important;
}
md-dialog{
animation: 350ms cubic-bezier(0.34, 1.61, 0.7, 1);
border: 1px solid #D9D9D9;
border-color: #c4cace;
border-top-color: #e2e5e7;
border-bottom-color: #a7b0b6;
min-width: 400px;
max-width: 768px;
padding: 15px;
box-shadow: 0 40px 64px 0 rgba(59, 79, 93, 0.1),
0 24px 32px 0 rgba(59, 79, 93, 0.1),
0 16px 16px 0 rgba(59, 79, 93, 0.1),
0 8px 8px 0 rgba(59, 79, 93, 0.1),
0 4px 4px 0 rgba(59, 79, 93, 0.1),
0 2px 2px 0 rgba(59, 79, 93, 0.1)!important;
md-dialog {
animation: 350ms cubic-bezier(0.34, 1.61, 0.7, 1);
border: 1px solid #d9d9d9;
border-color: #c4cace;
border-top-color: #e2e5e7;
border-bottom-color: #a7b0b6;
min-width: 400px;
max-width: 768px;
padding: 15px;
box-shadow: 0 40px 64px 0 rgba(59, 79, 93, 0.1),
0 24px 32px 0 rgba(59, 79, 93, 0.1), 0 16px 16px 0 rgba(59, 79, 93, 0.1),
0 8px 8px 0 rgba(59, 79, 93, 0.1), 0 4px 4px 0 rgba(59, 79, 93, 0.1),
0 2px 2px 0 rgba(59, 79, 93, 0.1) !important;
}
.md-cancel-button{
background-color: #ffffff !important;
color: #005571!important;
.md-cancel-button {
background-color: #ffffff !important;
color: #005571 !important;
}
.md-title {
color: #1a1a1a;
font-size: 24px;
font-weight: 600;
line-height: 2.5rem;
color: #1a1a1a;
font-size: 24px;
font-weight: 600;
line-height: 2.5rem;
}
md-dialog md-dialog-content .md-dialog-content-body{
padding-top: 15px;
margin-bottom: 45px;
md-dialog md-dialog-content .md-dialog-content-body {
padding-top: 15px;
margin-bottom: 45px;
}
md-dialog .md-dialog-content {
padding: 25px 5px 35px 5px;
overflow: hidden!important;
height: auto!important;
padding: 25px 5px 35px 5px;
overflow: hidden !important;
height: auto !important;
}
md-dialog .md-actions, md-dialog md-dialog-actions{
border: none!important;
md-dialog .md-actions,
md-dialog md-dialog-actions {
border: none !important;
}
md-dialog .md-button.md-primary:not(.md-cancel-button){
color: #ffffff;
background-color: #006BB4!important;
border-color: #006BB4!important;
border-radius: 4px;
md-dialog .md-button.md-primary:not(.md-cancel-button) {
color: #ffffff;
background-color: #006bb4 !important;
border-color: #006bb4 !important;
border-radius: 4px;
}
md-dialog .md-button.md-primary:not(.md-cancel-button):hover{
background-color: #005472!important;
border-color: #004c68!important;
md-dialog .md-button.md-primary:not(.md-cancel-button):hover {
background-color: #005472 !important;
border-color: #004c68 !important;
}
md-dialog.modalTheme {
animation: none!important;
transition: none!important;
transform: none!important;
bottom: 0;
right: 0;
position: fixed;
width: 350px;
animation: none !important;
transition: none !important;
transform: none !important;
bottom: 0;
right: 0;
position: fixed;
width: 350px;
}
md-dialog.modalTheme .md-button{
margin-bottom: 0;
md-dialog.modalTheme .md-button {
margin-bottom: 0;
}
.md-dialog-body{
top: 0!important;
width: 100vw!important;
.md-dialog-body {
top: 0 !important;
width: 100vw !important;
}
.md-dialog-body{
top: 0!important;
width: 100vw!important;
.md-dialog-body {
top: 0 !important;
width: 100vw !important;
}
.md-dialog-body .md-scroll-mask{
display: none!important;
.md-dialog-body .md-scroll-mask {
display: none !important;
}
.md-dialog-body .md-dialog-container{
height: 0!important;
width: 0!important;
.md-dialog-body .md-dialog-container {
height: 0 !important;
width: 0 !important;
}
.kbnGlobalNav.kbnGlobalNav-isOpen + .app-wrapper .fullscreen {
width: calc(~'100vw - 250px')!important;
left: 180px!important;
width: calc(~'100vw - 250px') !important;
left: 180px !important;
}
.fullscreen {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
z-index: 9999;
height: calc(~'100vh - 60px');
width: calc(~'100vw - 133px');
position: fixed;
top: 0px;
left: 53px;
right: 20px;
background-color: white;
max-width: 100% !important;
margin: 35px;
outline: 35px solid rgba(0, 0, 0, 0.2);
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
z-index: 9999;
height: calc(~'100vh - 119px');
width: calc(~'100vw - 128px');
position: fixed;
top: 49px;
left: 48px;
right: 20px;
background-color: white;
max-width: 100% !important;
margin: 35px;
outline: 35px solid rgba(0, 0, 0, 0.2);
}
.fullscreen kbn-vis {
padding: 5%;
padding: 5%;
}
.filter-bar .filter {
background-color: #0079a5 !important;
}
.euiButton--primary.euiButton--fill {
background-color: #0079a5 !important;
border-color: #0079a5 !important;
background-color: #0079a5 !important;
}
.columns-bar {
margin-top: -17px;
margin-left: -16px;
margin-right: -16px;
padding-right: 16px;
padding-left: 16px;
min-height: 42px;
padding-top: 8px;
margin-top: -17px;
margin-left: -16px;
margin-right: -16px;
padding-right: 16px;
padding-left: 16px;
min-height: 42px;
padding-top: 8px;
}
.columns-bar-active {
padding-top: 7px !important;
border-top: 1px solid #dfeff8 !important;
background-color: #ecf6fb;
border-bottom: 1px solid #dfeff8 !important;
margin-bottom: 10px;
padding-top: 7px !important;
border-top: 1px solid #dfeff8 !important;
background-color: #ecf6fb;
border-bottom: 1px solid #dfeff8 !important;
margin-bottom: 10px;
}
.error-enum-configuration {
max-height: 16px !important;
min-height: 16px !important;
max-height: 16px !important;
min-height: 16px !important;
}
.table-vis {
overflow: hidden !important;
overflow: hidden !important;
}
.kbnAggTable__paginated th {
min-width: 110px;
}
min-width: 110px;
}
.wzKbnTopNav .kbnTopNav {
background-color: transparent !important;
border-bottom: 0 !important;
}
.wz-menu-discover-icon path {
fill: #fff !important;
}
:focus {
outline: none !important;
outline-color: transparent !important;
}
.globalQueryBar:not(:empty) {
padding: 8px!important;
}
.dscWrapper {
.table > thead > tr > th,
.table > tbody > tr > th,
.table > tfoot > tr > th,
.table > thead > tr > td,
.table > tbody > tr > td,
.table > tfoot > tr > td {
font-size: 12px !important;
font-kerning: normal !important;
}
}
.table > thead > tr > th,
.table > tbody > tr > th,
.table > tfoot > tr > th,
.table > thead > tr > td,
.table > tbody > tr > td,
.table > tfoot > tr > td {
font-size: 14px !important;
font-kerning: normal !important;
}
.kbnTableCellFilter {
cursor: pointer;
}

View File

@ -1,5 +1,6 @@
/*
* Wazuh app - Stylesheets loader
* Copyright (C) 2015-2019 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

View File

@ -15,159 +15,166 @@
/* -------------------------------------------------------------------------- */
/* Apply the Open Sans font in the whole app */
html, body, button:not(.fa):not(.fa-times), textarea, input, select, .wz-chip {
font-family: "Open Sans", Helvetica, Arial, sans-serif !important;
html,
body,
button:not(.fa):not(.fa-times),
textarea,
input,
select,
.wz-chip {
font-family: 'Open Sans', Helvetica, Arial, sans-serif !important;
font-size: 14px;
}
.wz-headline-title {
font-size: 15px;
font-size: 15px;
}
/* Override font size of md-headline class */
.wz-headline {
font-size: 16px !important;
font-size: 16px !important;
}
.color-grey {
color: grey !important;
color: grey !important;
}
.color-f9 {
color: #ff9999;
color: #ff9999;
}
.wz-color-orange {
color: #f39c12 !important;
color: #f39c12 !important;
}
.color-pointer {
color: #006BB4 !important;
color: #006bb4 !important;
}
.wz-line-height {
line-height: 20px !important;
line-height: 20px !important;
}
.wz-line-height-40 {
line-height: 40px !important;
line-height: 40px !important;
}
.wz-text-center {
text-align: center;
text-align: center;
}
.wz-text-right {
text-align: right;
text-align: right;
}
.wz-text-left {
text-align: left;
text-align: left;
}
.wz-text-bold {
font-weight: bold !important;
font-weight: bold !important;
}
.wz-text-monospace {
font-family: monospace !important;
font-family: monospace !important;
}
.wz-text-capitalize {
text-transform: capitalize !important;
text-transform: capitalize !important;
}
.wz-text-uppercase {
text-transform: uppercase !important;
text-transform: uppercase !important;
}
.wz-text-normal {
text-transform: none !important;
text-transform: none !important;
}
.wz-text-truncatable {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
&:before {
content: attr(data-name);
}
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
&:before {
content: attr(data-name);
}
}
.wz-text-truncatable-container {
overflow: hidden;
width: auto;
display: grid;
overflow: hidden;
width: auto;
display: grid;
}
.font-size-12 {
font-size: 12px !important;
font-size: 12px !important;
}
.font-size-16 {
font-size: 16px;
font-size: 16px;
}
.font-size-18 {
font-size: 18px;
font-size: 18px;
}
.font-size-25 {
font-size: 25px;
font-size: 25px;
}
/* JSON viewer font colors */
.json-key {
color: rgb(255, 100, 92);
color: rgb(255, 100, 92);
}
.json-value {
color: rgb(0, 121, 165);
color: rgb(0, 121, 165);
}
.json-string {
color: rgb(0, 166, 155);
color: rgb(0, 166, 155);
}
/* Class for linkable text elements */
.wz-text-link {
cursor: pointer !important;
color: #006BB4 !important;
cursor: pointer !important;
color: #006bb4 !important;
&:hover {
text-decoration: underline !important;
}
&:hover {
text-decoration: underline !important;
}
}
.wz-text-link-add {
cursor: pointer !important;
color: #006BB4 !important;
cursor: pointer !important;
color: #006bb4 !important;
&:hover {
text-decoration: none !important;
}
&:hover {
text-decoration: none !important;
}
}
.wz-text-active {
color: rgb(0, 121, 165);
font-weight: bold;
text-decoration: underline;
color: rgb(0, 121, 165);
font-weight: bold;
text-decoration: underline;
}
/* Special fix for text in navbars */
.md-button {
text-transform: none !important;
text-transform: none !important;
}
.wz-text-teal {
color: rgb(0, 166, 155);
color: rgb(0, 166, 155);
}
.wz-text-red {
color: rgb(255, 100, 92);
color: rgb(255, 100, 92);
}
b{
font-weight: bold;
b {
font-weight: bold;
}

View File

@ -54,13 +54,10 @@
* 4. Fix an IE bug which causes the last child to overflow the container.
* 5. Fixing this bug means we now need to align the children to the right.
*/
:focus:not([class^="eui"]) {
z-index: 1;
/* 1 */
outline: none !important;
/* 2 */
box-shadow: 0 0 0 1px #ffffff, 0 0 0 2px #006BB4;
/* 3 */ }
:focus:not([class^="eui"]):not([class^="kbn-resetFocusState"]) {
-webkit-animation: none !important;
animation: none !important;
}
/**
* 1. Required for IE11.
@ -721,7 +718,6 @@
padding: 4px;
border: none;
line-height: 1;
font-size: 16px;
color: #2d2d2d !important;
/* 1 */
cursor: pointer;
@ -2034,7 +2030,6 @@
/* 1 */ }
.kuiInfoButton {
font-size: 16px;
line-height: 0;
background-color: transparent;
color: #006BB4;
@ -2406,7 +2401,6 @@
padding: 4px;
border: none;
line-height: 1;
font-size: 16px;
color: #2d2d2d !important;
/* 1 */
cursor: pointer;

View File

@ -15,7 +15,8 @@ export class AppState {
* @param {*} $cookies
* @param {*} $window
*/
constructor($cookies, $window) {
constructor($rootScope, $cookies, $window) {
this.$rootScope = $rootScope;
this.$cookies = $cookies;
this.$window = $window;
this.navigate = {};
@ -133,4 +134,8 @@ export class AppState {
getNavigation() {
return this.navigate;
}
setWzMenu() {
this.$rootScope.$emit('loadWazuhMenu', {});
}
}

View File

@ -41,6 +41,11 @@ export class CommonData {
this.globalState = globalState;
this.savedTimefilter = null;
this.refreshInterval = { pause: true, value: 0 };
this.hostMonitoringTabs = ['general', 'fim', 'aws'];
this.systemAuditTabs = ['pm', 'audit', 'oscap', 'ciscat', 'sca'];
this.securityTabs = ['vuls', 'virustotal', 'osquery', 'docker'];
this.complianceTabs = ['pci', 'gdpr', 'hipaa', 'nist'];
}
/**
@ -128,6 +133,8 @@ export class CommonData {
audit: { group: 'audit' },
pci: { group: 'pci_dss' },
gdpr: { group: 'gdpr' },
hipaa: { group: 'hipaa' },
nist: { group: 'nist' },
aws: { group: 'amazon' },
virustotal: { group: 'virustotal' },
osquery: { group: 'osquery' },
@ -153,6 +160,12 @@ export class CommonData {
} else if (tab === 'gdpr') {
this.removeDuplicateExists('rule.gdpr');
filters.push(filterHandler.gdprQuery());
} else if (tab === 'hipaa') {
this.removeDuplicateExists('rule.hipaa');
filters.push(filterHandler.hipaaQuery());
} else if (tab === 'nist') {
this.removeDuplicateExists('rule.nist_800_53');
filters.push(filterHandler.nistQuery());
} else {
this.removeDuplicateRuleGroups(tabFilters[tab].group);
filters.push(filterHandler.ruleGroupQuery(tabFilters[tab].group));
@ -208,6 +221,40 @@ export class CommonData {
}
}
/**
* GET HIPAA
*/
async getHIPAA() {
try {
const hipaaTabs = [];
const data = await this.genericReq.request('GET', '/api/hipaa/all');
if (!data.data) return [];
for (const key in data.data) {
hipaaTabs.push({ title: key, content: data.data[key] });
}
return hipaaTabs;
} catch (error) {
return Promise.reject(error);
}
}
/**
* GET NIST 800-53
*/
async getNIST() {
try {
const nistTabs = [];
const data = await this.genericReq.request('GET', '/api/nist/all');
if (!data.data) return [];
for (const key in data.data) {
nistTabs.push({ title: key, content: data.data[key] });
}
return nistTabs;
} catch (error) {
return Promise.reject(error);
}
}
/**
* Assign given filter
* @param {Object} filterHandler
@ -310,4 +357,32 @@ export class CommonData {
getRefreshInterval() {
return this.refreshInterval;
}
getCurrentPanel(tab) {
return this.hostMonitoringTabs.includes(tab)
? this.hostMonitoringTabs
: this.systemAuditTabs.includes(tab)
? this.systemAuditTabs
: this.securityTabs.includes(tab)
? this.securityTabs
: this.complianceTabs.includes(tab)
? this.complianceTabs
: false;
}
getTabsFromCurrentPanel(currentPanel, extensions, tabNames) {
const keyExists = key => Object.keys(extensions).includes(key);
const keyIsTrue = key => (extensions || [])[key];
let tabs = [];
currentPanel.forEach(x => {
if (!keyExists(x) || keyIsTrue(x)) {
tabs.push({
id: x,
name: tabNames[x]
});
}
});
return tabs;
}
}

View File

@ -26,7 +26,6 @@ export class DataFactory {
this.filters = [];
this.sortValue = false;
this.sortDir = false;
this.sortValue = false;
this.busy = false;
if (this.implicitFilter) this.filters.push(...this.implicitFilter);
if (this.implicitSort) this.addSorting(this.implicitSort);
@ -105,6 +104,7 @@ export class DataFactory {
);
this.items = this.items.filter(item => !!item);
Array.isArray(firstPage.data.data)
? this.items.push(...firstPage.data.data)
: this.items.push(...firstPage.data.data.items);
@ -124,6 +124,7 @@ export class DataFactory {
const end = new Date();
const elapsed = (end - start) / 1000;
this.busy = false;
if (this.items.length > totalItems) this.items.length = totalItems;
return { items: this.items, time: elapsed };
} catch (error) {
this.busy = false;

View File

@ -19,8 +19,8 @@ import { GlobalToastList } from '../../../../src/core/public/notifications/toast
import { ToastsService } from '../../../../src/core/public/notifications/toasts/toasts_service';
import { ToastsServicew } from './notifications/toasts_service';
import { GlobalToastListw } from './notifications/global_toast_list';
ToastsService.prototype.start = ToastsServicew.prototype.start;
GlobalToastList.prototype.render = GlobalToastListw.prototype.render;
//ToastsService.prototype.start = ToastsServicew.prototype.start;
//GlobalToastList.prototype.render = GlobalToastListw.prototype.render;
export class ErrorHandler {
/**
@ -45,7 +45,7 @@ export class ErrorHandler {
origin.includes('/api/csv') ||
origin.includes('/api/agents-unique');
return isFromAPI
? "Wazuh API don't reachable. Reason: timeout."
? 'Wazuh API is not reachable. Reason: timeout.'
: 'Server did not respond';
}
if ((((error || {}).data || {}).errorData || {}).message)
@ -74,8 +74,24 @@ export class ErrorHandler {
*/
info(message, location) {
if (typeof message === 'string') {
message = location ? location + '. ' + message : message;
toastNotifications.addSuccess(message);
// Current date in milliseconds
const date = new Date().getTime();
// Remove errors older than 2s from the error history
this.history = this.history.filter(item => date - item.date <= 2000);
// Check if the incoming error was already shown in the last two seconds
const recentlyShown = this.history
.map(item => item.text)
.includes(message);
// If the incoming error was not shown in the last two seconds, add it to the history
!recentlyShown && this.history.push({ text: message, date });
if (!recentlyShown) {
message = location ? location + '. ' + message : message;
toastNotifications.addSuccess(message);
}
}
return;
}

View File

@ -113,4 +113,51 @@ export class ReportingService {
this.errorHandler.handle(error.message || error);
}
}
async startConfigReport(obj, type, components) {
try {
this.$rootScope.reportBusy = true;
this.$rootScope.reportStatus = 'Generating PDF document...';
this.$rootScope.$applyAsync();
const docType =
type === 'agentConfig'
? `wazuh-agent-${obj.id}`
: `wazuh-group-${obj.name}`;
const name = `${docType}-configuration-${(Date.now() / 1000) | 0}.pdf`;
const browserTimezone = moment.tz.guess(true);
const data = {
array: [],
name,
filters: [
type === 'agentConfig' ? { agent: obj.id } : { group: obj.name }
],
time: '',
searchBar: '',
tables: [],
tab: type,
browserTimezone,
components
};
await this.genericReq.request('POST', '/reports', data);
this.$rootScope.reportBusy = false;
this.$rootScope.reportStatus = false;
this.$rootScope.$applyAsync();
this.errorHandler.info(
'Success. Go to Wazuh > Management > Reporting',
'Reporting'
);
return;
} catch (error) {
this.$rootScope.reportBusy = false;
this.$rootScope.reportStatus = false;
this.errorHandler.handle(error.message || error);
this.$rootScope.$applyAsync();
}
}
}

View File

@ -0,0 +1,45 @@
/*
* Wazuh app - Module to fetch API entries
* Copyright (C) 2015-2019 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.
*/
/**
* If there are no API entries it throws an exception, then it redirects to Settings
* for adding a new API entry.
* If there are API entries, then it continues to the health check itself.
* @param {*} $q Promise library for Angular.js resolves.
* @param {*} genericReq Wazuh module for doing generic requests to our backend.
* @param {*} $location Angular.js library for URL and paths manipulation.
*/
export function apiCount($q, genericReq, $location, appState) {
const deferred = $q.defer();
genericReq
.request('GET', '/elastic/apis')
.then(data => {
if (!data.data.length) throw new Error('No API entries found');
else {
const firstEntry = data.data[0];
appState.setCurrentAPI(
JSON.stringify({
name: firstEntry._source.cluster_info.manager,
id: firstEntry._id
})
);
}
deferred.resolve();
})
.catch(err => {
$location.search('_a', null);
$location.search('tab', 'api');
$location.path('/settings');
deferred.resolve();
});
return deferred.promise;
}

View File

@ -11,13 +11,13 @@
*/
export async function checkTimestamp(appState, genericReq, $location, wzMisc) {
try {
const data = await genericReq.request('GET', '/elastic/timestamp');
const data = await genericReq.request('GET', '/api/timestamp');
const current = appState.getCreatedAt();
if (data && data.data) {
if (!current) appState.setCreatedAt(data.data.lastRestart);
wzMisc.setLastRestart(data.data.lastRestart);
} else {
wzMisc.setBlankScr('Your .wazuh-version index is empty or corrupt.');
wzMisc.setBlankScr('Your wazuh-version registry is empty or corrupt.');
$location.search('tab', null);
$location.path('/blank-screen');
}

View File

@ -20,6 +20,8 @@ export async function getWzConfig($q, genericReq, wazuhConfig) {
'checks.setup': true,
'extensions.pci': true,
'extensions.gdpr': true,
'extensions.hipaa': true,
'extensions.nist': true,
'extensions.audit': true,
'extensions.oscap': false,
'extensions.ciscat': false,
@ -30,8 +32,6 @@ export async function getWzConfig($q, genericReq, wazuhConfig) {
timeout: 20000,
'wazuh.shards': 1,
'wazuh.replicas': 0,
'wazuh-version.shards': 1,
'wazuh-version.replicas': 0,
'ip.selector': true,
'ip.ignore': [],
'xpack.rbac.enabled': true,

View File

@ -16,6 +16,7 @@ import { getSavedSearch } from './get-saved-search';
import { goToKibana } from './go-to-kibana';
import { getIp } from './get-ip';
import { getWzConfig } from './get-config';
import { apiCount } from './api-count';
export {
checkTimestamp,
@ -24,5 +25,6 @@ export {
getSavedSearch,
goToKibana,
getIp,
getWzConfig
getWzConfig,
apiCount
};

View File

@ -114,6 +114,8 @@ export function settingsWizard(
audit: config['extensions.audit'],
pci: config['extensions.pci'],
gdpr: config['extensions.gdpr'],
hipaa: config['extensions.hipaa'],
nist: config['extensions.nist'],
oscap: config['extensions.oscap'],
ciscat: config['extensions.ciscat'],
aws: config['extensions.aws'],
@ -255,7 +257,7 @@ export function settingsWizard(
});
}
}
appState.setWzMenu();
return deferred.promise;
} catch (error) {
!disableErrors && errorHandler.handle(error);

View File

@ -19,7 +19,8 @@ import {
getSavedSearch,
goToKibana,
getIp,
getWzConfig
getWzConfig,
apiCount
} from './resolves';
// HTML templates
@ -79,6 +80,9 @@ function nestedResolve(
appState,
wzMisc
) {
const healthCheckStatus = $window.sessionStorage.getItem('healthCheck');
if (!healthCheckStatus) return;
assignPreviousLocation($rootScope, $location);
const location = $location.path();
return getWzConfig($q, genericReq, wazuhConfig).then(() =>
@ -105,6 +109,8 @@ function savedSearch(
savedSearches,
$route
) {
const healthCheckStatus = $window.sessionStorage.getItem('healthCheck');
if (!healthCheckStatus) return;
assignPreviousLocation($rootScope, $location);
return getSavedSearch(
redirectWhenMissing,
@ -135,7 +141,7 @@ routes.enable();
routes
.when('/health-check', {
template: healthCheckTemplate,
resolve: { nestedResolve, ip }
resolve: { apiCount, wzConfig, ip }
})
.when('/agents/:id?/:tab?/:view?', {
template: agentsTemplate,

View File

@ -1,7 +1,4 @@
<div ng-cloak flex="auto" ng-controller="agentsPreviewController as ctrl" layout="column" class="mozilla-table-size-99">
<wz-menu ng-init="menuNavItem = 'agents'"></wz-menu>
<div ng-cloak flex="auto" ng-controller="agentsPreviewController as ctrl" layout="column">
<div class="md-padding md-padding-top-16" ng-show="ctrl.init || ctrl.loading">
<react-component name="EuiProgress" props="{size: 'xs', color: 'primary'}" />
</div>

View File

@ -22,123 +22,133 @@
</div>
<div ng-if="!ctrl.init && !ctrl.loading && !ctrl.errorInit" layout="column" layout-align="start space-around">
<div class="md-padding euiFlexGroup--wrap layout-row">
<div class="layout-column md-padding">
<span class="wz-headline-title">
<react-component name="EuiIcon" props="{type:'node'}" /> Status
</span>
<md-divider class="wz-margin-top-10"></md-divider>
<canvas id="bar" ng-if="ctrl.lastAgent && ctrl.lastAgent.id" class="wz-margin-top-16 chart chart-doughnut"
chart-data="[ctrl.summary.agentsCountActive,ctrl.summary.agentsCountDisconnected,ctrl.summary.agentsCountNeverConnected]"
chart-labels="['Active','Disconnected', 'Never connected']" chart-colors="['#57C17B', '#BC52BC', '#9E3533']"
chart-options="{cutoutPercentage: 75, legend: {display: true,position: 'right',},responsive: false, maintainAspectRatio: false}" />
<div layout="row" class="wz-margin-top-16 layout-align-center-center" ng-if="!ctrl.lastAgent || !ctrl.lastAgent.id">
There are no agents yet.
</div>
<div layout="row" class="wz-margin-top-16 layout-align-center-center" ng-if="!ctrl.lastAgent || !ctrl.lastAgent.id">
<button class="kuiButton kuiButton--success" ng-click="ctrl.openRegistrationDocs()">
<react-component name="EuiIcon" props="{type: 'help'}" /> How to
</button>
</div>
</div>
<div flex class="layout-column md-padding">
<span class="wz-headline-title">
<react-component name="EuiIcon" props="{type:'visHeatmap'}" /> Details
</span>
<md-divider class="wz-margin-top-10"></md-divider>
<div class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--directionRow euiFlexGroup--responsive wz-margin-top-4">
<div class="euiFlexItem euiFlexItem--flexGrowZero">
<div class="euiStat">
<div class="euiText euiText--small euiStat__description">
<p>Active</p>
</div>
<p class="euiTitle euiTitle--small euiStat__title">{{ctrl.summary.agentsCountActive}}</p>
</div>
</div>
<div class="euiFlexItem euiFlexItem--flexGrowZero">
<div class="euiStat">
<div class="euiText euiText--small euiStat__description">
<p>Disconnected</p>
</div>
<p class="euiTitle euiTitle--small euiStat__title">{{ctrl.summary.agentsCountDisconnected}}</p>
</div>
</div>
<div class="euiFlexItem euiFlexItem--flexGrowZero">
<div class="euiStat">
<div class="euiText euiText--small euiStat__description">
<p>Never connected</p>
</div>
<p class="euiTitle euiTitle--small euiStat__title">{{ctrl.summary.agentsCountNeverConnected}}</p>
</div>
</div>
<div class="euiFlexItem euiFlexItem--flexGrowZero">
<div class="euiStat">
<div class="euiText euiText--small euiStat__description">
<p>Agents coverage</p>
</div>
<p class="euiTitle euiTitle--small euiStat__title">{{(ctrl.summary.agentsCoverity | number:2)}}%</p>
</div>
</div>
</div>
<div class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--directionRow euiFlexGroup--responsive">
<div class="euiFlexItem euiFlexItem--flexGrowZero">
<div class="euiStat">
<div class="euiText euiText--small euiStat__description">
<p>Last registered agent</p>
</div>
<p ng-if="ctrl.lastAgent && ctrl.lastAgent.id && ctrl.lastAgent.id !== '000'" ng-click="ctrl.showAgent(ctrl.lastAgent)"
class="euiTitle euiTitle--small euiStat__title wz-text-link cursor-pointer">
{{ctrl.lastAgent.name}}</p>
<p ng-if="!ctrl.lastAgent || !ctrl.lastAgent.id" class="euiTitle euiTitle--small euiStat__title">
-</p>
</div>
</div>
<div class="euiFlexItem euiFlexItem--flexGrowZero">
<div class="euiStat">
<div class="euiText euiText--small euiStat__description">
<p>Most active agent</p>
</div>
<p ng-if="ctrl.lastAgent && ctrl.lastAgent.id && ctrl.mostActiveAgent.id !== '000'" ng-click="ctrl.showAgent(ctrl.mostActiveAgent)"
class="euiTitle euiTitle--small euiStat__title wz-text-link cursor-pointer">
{{ctrl.mostActiveAgent.name}}</p>
<p ng-if="!ctrl.lastAgent || !ctrl.lastAgent.id" class="euiTitle euiTitle--small euiStat__title">
-</p>
</div>
</div>
</div>
</div>
<div class="registerAgent" ng-show="ctrl.addingNewAgent">
<react-component name="RegisterAgent" props="ctrl.registerAgentsProps" />
</div>
<div layout="row" class="md-padding">
<wz-tag-filter flex path="'/agents'" query-fn="ctrl.query(q, search)" fields-model="ctrl.searchBarModel">
</wz-tag-filter>
<md-button class="agents-prev-btn btn btn-info" style="height: 48px;margin: 0px 0 0 8px!important;" ng-click="ctrl.reloadList()">
<react-component name="EuiIcon" props="{type:'refresh'}" /> Refresh</md-button>
</div>
<div layout="row">
<md-card flex class="wz-md-card _md flex md-margin-h">
<md-card-content>
<div layout="row">
<wz-table custom-columns="true" flex path="'/agents'" keys="[{value: 'id', width: '75px'},'name','ip','status','group','os.name','os.version','version', {value: 'dateAdd', offset: true}, {value: 'lastKeepAlive', offset: true}]"
allow-click="true" row-sizes="[17,15,13]"></wz-table>
<div ng-show="!ctrl.addingNewAgent">
<div class="md-padding euiFlexGroup--wrap layout-row">
<div class="layout-column md-padding">
<span class="wz-headline-title">
<react-component name="EuiIcon" props="{type:'node'}" /> Status
</span>
<md-divider class="wz-margin-top-10"></md-divider>
<canvas id="bar" ng-if="ctrl.lastAgent && ctrl.lastAgent.id" class="wz-margin-top-16 chart chart-doughnut"
chart-data="[ctrl.summary.agentsCountActive,ctrl.summary.agentsCountDisconnected,ctrl.summary.agentsCountNeverConnected]"
chart-labels="['Active','Disconnected', 'Never connected']" chart-colors="['#57C17B', '#BC52BC', '#9E3533']"
chart-options="{cutoutPercentage: 75, legend: {display: true,position: 'right',},responsive: false, maintainAspectRatio: false}" />
<div layout="row" class="wz-margin-top-16 layout-align-center-center" ng-if="!ctrl.lastAgent || !ctrl.lastAgent.id">
There are no agents yet.
</div>
<div layout="row" layout-align="end center">
<button type="button" ng-click="ctrl.downloadCsv()" class="euiButtonEmpty euiButtonEmpty--primary euiButtonEmpty--small">
<span class="euiButtonEmpty__content">
<react-component name="EuiIcon" props="{type:'importAction'}" />
<span class="euiButtonEmpty__text">Formatted</span>
</span>
<div layout="row" class="wz-margin-top-16 layout-align-center-center" ng-if="!ctrl.lastAgent || !ctrl.lastAgent.id">
<button class="kuiButton kuiButton--success" ng-click="ctrl.openRegistrationDocs()">
<react-component name="EuiIcon" props="{type: 'help'}" /> How to
</button>
</div>
</md-card-content>
</md-card>
</div>
<div flex class="layout-column md-padding">
<span class="wz-headline-title">
<react-component name="EuiIcon" props="{type:'visHeatmap'}" /> Details
</span>
<md-divider class="wz-margin-top-10"></md-divider>
<div class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--directionRow euiFlexGroup--responsive wz-margin-top-4">
<div class="euiFlexItem euiFlexItem--flexGrowZero">
<div class="euiStat">
<div class="euiText euiText--small euiStat__description">
<p>Active</p>
</div>
<p class="euiTitle euiTitle--small euiStat__title">{{ctrl.summary.agentsCountActive}}</p>
</div>
</div>
<div class="euiFlexItem euiFlexItem--flexGrowZero">
<div class="euiStat">
<div class="euiText euiText--small euiStat__description">
<p>Disconnected</p>
</div>
<p class="euiTitle euiTitle--small euiStat__title">{{ctrl.summary.agentsCountDisconnected}}</p>
</div>
</div>
<div class="euiFlexItem euiFlexItem--flexGrowZero">
<div class="euiStat">
<div class="euiText euiText--small euiStat__description">
<p>Never connected</p>
</div>
<p class="euiTitle euiTitle--small euiStat__title">{{ctrl.summary.agentsCountNeverConnected}}</p>
</div>
</div>
<div class="euiFlexItem euiFlexItem--flexGrowZero">
<div class="euiStat">
<div class="euiText euiText--small euiStat__description">
<p>Agents coverage</p>
</div>
<p class="euiTitle euiTitle--small euiStat__title">{{(ctrl.summary.agentsCoverity |
number:2)}}%</p>
</div>
</div>
</div>
<div class="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--directionRow euiFlexGroup--responsive">
<div class="euiFlexItem euiFlexItem--flexGrowZero">
<div class="euiStat">
<div class="euiText euiText--small euiStat__description">
<p>Last registered agent</p>
</div>
<p ng-if="ctrl.lastAgent && ctrl.lastAgent.id && ctrl.lastAgent.id !== '000'" ng-click="ctrl.showAgent(ctrl.lastAgent)"
class="euiTitle euiTitle--small euiStat__title wz-text-link cursor-pointer">
{{ctrl.lastAgent.name}}</p>
<p ng-if="!ctrl.lastAgent || !ctrl.lastAgent.id" class="euiTitle euiTitle--small euiStat__title">
-</p>
</div>
</div>
<div class="euiFlexItem euiFlexItem--flexGrowZero">
<div class="euiStat">
<div class="euiText euiText--small euiStat__description">
<p>Most active agent</p>
</div>
<p ng-if="ctrl.lastAgent && ctrl.lastAgent.id && ctrl.mostActiveAgent.id !== '000'"
ng-click="ctrl.showAgent(ctrl.mostActiveAgent)" class="euiTitle euiTitle--small euiStat__title wz-text-link cursor-pointer">
{{ctrl.mostActiveAgent.name}}</p>
<p ng-if="!ctrl.lastAgent || !ctrl.lastAgent.id" class="euiTitle euiTitle--small euiStat__title">
-</p>
</div>
</div>
</div>
</div>
</div>
<div layout="row" class="md-padding">
<wz-tag-filter flex path="'/agents'" query-fn="ctrl.query(q, search)" fields-model="ctrl.searchBarModel">
</wz-tag-filter>
<md-button class="agents-prev-btn btn btn-info" style="height: 48px;margin: 0px 0 0 8px!important;"
ng-click="ctrl.reloadList()">
<react-component name="EuiIcon" props="{type:'refresh'}" /> Refresh</md-button>
</div>
<div layout="row">
<md-card flex class="wz-md-card _md flex md-margin-h">
<md-card-actions ng-if="adminMode" layout="row" layout-align="end center" class="wz-card-actions wz-card-actions-top">
<a ng-click="ctrl.addNewAgent(true)">
<react-component name="EuiIcon" props="{type:'plusInCircle'}" /> Add new agent </a>
<span flex></span>
</md-card-actions>
<md-card-content>
<div layout="row">
<wz-table custom-columns="true" flex path="'/agents'" keys="[{value: 'id', width: '75px'},'name','ip','status','group','os.name','os.version','version', {value: 'dateAdd', offset: true}, {value: 'lastKeepAlive', offset: true}]"
allow-click="true" row-sizes="[17,15,13]"></wz-table>
</div>
<div layout="row" layout-align="end center">
<button type="button" ng-click="ctrl.downloadCsv()" class="euiButtonEmpty euiButtonEmpty--primary euiButtonEmpty--small">
<span class="euiButtonEmpty__content">
<react-component name="EuiIcon" props="{type:'importAction'}" />
<span class="euiButtonEmpty__text">Formatted</span>
</span>
</button>
</div>
</md-card-content>
</md-card>
</div>
</div>
</div>

View File

@ -151,7 +151,7 @@
</div>
<div layout="row" ng-if="agent && !load" class="wz-margin-top-10 wz-margin-bottom-40-inv">
<wz-table flex path="'/syscheck/' + agent.id" implicit-filter="[{name:'type',value:'registry'}]"
row-sizes="[6,6,6]" extra-limit="100" keys="['file']">
row-sizes="[6,6,6]" keys="['file']">
</wz-table>
</div>
</md-card-content>
@ -185,7 +185,7 @@
</div>
<div layout="row" ng-if="agent && !load" class="wz-margin-top-10 wz-margin-bottom-40-inv">
<wz-table flex path="'/syscheck/' + agent.id" implicit-filter="[{name:'type',value:'file'}]"
row-sizes="[6,6,6]" extra-limit="100"
row-sizes="[6,6,6]"
keys="['file',{value: 'size', width: '100px'},'uname','perm','sha256',{value: 'uid', width: '100px'},'mtime']">
</wz-table>
</div>
@ -217,7 +217,7 @@
</div>
<div layout="row" ng-if="agent && !load" class="wz-margin-top-16 wz-margin-bottom-40-inv">
<wz-table custom-columns="true" flex path="'/syscheck/' + agent.id" row-sizes="[16,14,12]"
extra-limit="100"
keys="['file',{value: 'size', width: '100px'},{value: 'gname', width: '150px'},{value: 'uname', width: '150px'},{value: 'perm', width: '125px'},'sha256',{value: 'uid', width: '100px'},{value: 'gid', width: '100px'},{value: 'mtime', width: '150px'}]">
</wz-table>
</div>

View File

@ -3,21 +3,8 @@
<!-- View: Panels -->
<div layout="row" layout-align="center stretch">
<md-card flex class="wz-md-card">
<md-tabs md-selected="selectedGdprIndex" class="wz-md-tab" md-border-bottom md-dynamic-height
id="gdprReq_tab">
<md-tab ng-repeat="tab in gdprTabs" ng-disabled="tab.disabled" label="{{tab.title}}">
<div class="md-padding">
<span class="wz-headline-title">GDPR Requirement: {{tab.title}}</span>
<md-divider class="wz-margin-top-10"></md-divider>
<div layout="row" class="wz-padding-top-10 wz-line-height">
<div ng-bind-html="tab.content"></div>
</div>
</div>
</md-tab>
</md-tabs>
</md-card>
<div ng-if="gdprReqs" class="wz-margin-10">
<react-component name='RequirementCard' props="gdprReqs"/>
</div>
<div layout="row" layout-align="center stretch" class="height-300">

View File

@ -1,15 +1,15 @@
<md-content flex layout="column" ng-if="tab === 'general' && tabView === 'panels'" ng-class="{'no-opacity': resultState !== 'ready' || !rendered}"
layout-align="start">
<div layout="row">
<md-card flex class="wz-metric-color wz-md-card">
<md-card-content layout="row" class="wz-padding-metric">
<div class="wz-text-truncatable" flex>Alerts: <span class="wz-text-bold" ng-bind="totalAlerts()"></span></div>
<div class="wz-text-truncatable" flex>Level 12 or above alerts: <span class="wz-text-bold" ng-bind="level12()"></span></div>
<div class="wz-text-truncatable" flex>Authentication failure: <span class="wz-text-bold" ng-bind="authFailure()"></span></div>
<div class="wz-text-truncatable" flex>Authentication success: <span class="wz-text-bold" ng-bind="authSuccess()"></span></div>
</md-card-content>
</md-card>
<div layout="row" layout-padding>
<react-component flex name="AlertsStats" props="{
items: [
{ description: 'Total', value: totalAlerts(), color: 'primary' },
{ description: 'Level 12 or above alerts', value: level12(), color: 'secondary' },
{ description: 'Authentication failure', value: authFailure(), color: 'danger' },
{ description: 'Authentication success', value: authSuccess(), color: 'subdued' }
]
}" />
</div>
<div class="wz-no-display">

Some files were not shown because too many files have changed in this diff Show More