mirror of
https://github.com/valitydev/wazuh-kibana-app.git
synced 2024-11-06 01:45:18 +00:00
Backports 6.8 from 7.3
This commit is contained in:
commit
8eae1c20fc
@ -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
27
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal 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.
|
17
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
17
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal 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
1
.yarnignore
Normal file
@ -0,0 +1 @@
|
||||
plugins/wazuh/server/wazuh-version.json
|
@ -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
|
@ -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.
|
@ -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 ---------------------------
|
||||
#
|
||||
|
13
index.js
13
index.js
@ -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);
|
||||
}
|
||||
});
|
||||
|
16
package.json
16
package.json
@ -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",
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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() {
|
||||
|
@ -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
|
||||
|
159
public/controllers/agent/components/export-configuration.js
Normal file
159
public/controllers/agent/components/export-configuration.js
Normal 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
|
||||
};
|
191
public/controllers/agent/components/register-agent.js
Normal file
191
public/controllers/agent/components/register-agent.js
Normal 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
|
||||
};
|
20
public/controllers/agent/components/register-guide-defs.js
Normal file
20
public/controllers/agent/components/register-guide-defs.js
Normal 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'
|
||||
};
|
58
public/controllers/agent/components/stats.js
Normal file
58
public/controllers/agent/components/stats.js
Normal 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
|
||||
};
|
247
public/controllers/agent/components/welcome.js
Normal file
247
public/controllers/agent/components/welcome.js
Normal 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
|
||||
};
|
@ -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);
|
||||
|
@ -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 };
|
||||
}
|
||||
}
|
||||
|
123
public/controllers/management/components/reporting-table.js
Normal file
123
public/controllers/management/components/reporting-table.js
Normal 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
|
||||
};
|
113
public/controllers/management/components/welcome.js
Normal file
113
public/controllers/management/components/welcome.js
Normal 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
|
||||
};
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -303,7 +303,6 @@ export function ClusterController(
|
||||
|
||||
//listeners
|
||||
$scope.$on('$destroy', () => {
|
||||
$location.search('tabView', null);
|
||||
discoverPendingUpdates.removeAll();
|
||||
tabVisualizations.removeAll();
|
||||
rawVisualizations.removeAll();
|
||||
|
@ -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:') &&
|
||||
|
@ -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 || '')
|
||||
);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
45
public/controllers/overview/components/alerts-stats.js
Normal file
45
public/controllers/overview/components/alerts-stats.js
Normal 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
|
||||
};
|
154
public/controllers/overview/components/requirement-card.js
Normal file
154
public/controllers/overview/components/requirement-card.js
Normal 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
|
||||
};
|
61
public/controllers/overview/components/stats.js
Normal file
61
public/controllers/overview/components/stats.js
Normal 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
|
||||
};
|
223
public/controllers/overview/components/welcome.js
Normal file
223
public/controllers/overview/components/welcome.js
Normal 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
|
||||
};
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
90
public/controllers/settings/components/add-api.js
Normal file
90
public/controllers/settings/components/add-api.js
Normal 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
|
||||
};
|
256
public/controllers/settings/components/api-table.js
Normal file
256
public/controllers/settings/components/api-table.js
Normal 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
|
||||
};
|
@ -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);
|
||||
|
@ -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' }
|
||||
]
|
||||
};
|
||||
}
|
||||
|
@ -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';
|
||||
|
@ -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">
|
||||
|
@ -30,6 +30,7 @@
|
||||
|
||||
.chips-dropdown .dropdown-menu{
|
||||
display: block !important;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.ui-select-choices-row-inner {
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
};
|
||||
});
|
@ -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>
|
@ -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
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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>
|
@ -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;
|
||||
}
|
24
public/directives/wz-src/wz-src.js
Normal file
24
public/directives/wz-src/wz-src.js
Normal 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);
|
||||
}
|
||||
};
|
||||
});
|
124
public/directives/wz-table-eui/components/table.js
Normal file
124
public/directives/wz-table-eui/components/table.js
Normal 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
|
||||
};
|
@ -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';
|
98
public/directives/wz-table-eui/lib/data.js
Normal file
98
public/directives/wz-table-eui/lib/data.js
Normal 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;
|
||||
}
|
40
public/directives/wz-table-eui/lib/init.js
Normal file
40
public/directives/wz-table-eui/lib/init.js
Normal 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;
|
||||
}
|
57
public/directives/wz-table-eui/lib/listeners.js
Normal file
57
public/directives/wz-table-eui/lib/listeners.js
Normal 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();
|
||||
}
|
29
public/directives/wz-table-eui/lib/sort.js
Normal file
29
public/directives/wz-table-eui/lib/sort.js
Normal 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;
|
||||
}
|
209
public/directives/wz-table-eui/wz-table-directive.js
Normal file
209
public/directives/wz-table-eui/wz-table-directive.js
Normal 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
|
||||
};
|
||||
});
|
11
public/directives/wz-table-eui/wz-table.html
Normal file
11
public/directives/wz-table-eui/wz-table.html
Normal 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>
|
@ -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,
|
||||
|
@ -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">
|
||||
|
54
public/directives/wz-tabs-eui/components/tabs.js
Normal file
54
public/directives/wz-tabs-eui/components/tabs.js
Normal 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
|
||||
};
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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,
|
||||
|
@ -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 |
@ -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
1
public/img/logo.svg
Normal 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 |
@ -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');
|
||||
};
|
||||
}
|
||||
};
|
||||
});
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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
4094
public/less/dark_theme/bootstrap_light.css
vendored
Normal file
File diff suppressed because it is too large
Load Diff
3333
public/less/dark_theme/kui_light.css
Normal file
3333
public/less/dark_theme/kui_light.css
Normal file
File diff suppressed because it is too large
Load Diff
280
public/less/dark_theme/wz_theme_dark.css
Normal file
280
public/less/dark_theme/wz_theme_dark.css
Normal 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;
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
2
public/less/jquery-ui.css
vendored
2
public/less/jquery-ui.css
vendored
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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', {});
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
45
public/services/resolves/api-count.js
Normal file
45
public/services/resolves/api-count.js
Normal 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;
|
||||
}
|
@ -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');
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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>
|
@ -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>
|
@ -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>
|
||||
|
@ -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">
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user