wazuh-kibana-app/public/controllers/settings/settings.js
2018-11-28 11:19:30 +01:00

646 lines
19 KiB
JavaScript

/*
* Wazuh app - Settings controller
* Copyright (C) 2018 Wazuh, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Find more information about this on the LICENSE file.
*/
import { base64 } from '../../utils/base64';
import { TabNames } from '../../utils/tab-names';
import { configEquivalences } from '../../utils/config-equivalences';
export class SettingsController {
constructor(
$scope,
$window,
$location,
testAPI,
appState,
genericReq,
errorHandler,
wzMisc,
wazuhConfig
) {
this.$scope = $scope;
this.$window = $window;
this.$location = $location;
this.testAPI = testAPI;
this.appState = appState;
this.genericReq = genericReq;
this.errorHandler = errorHandler;
this.wzMisc = wzMisc;
this.wazuhConfig = wazuhConfig;
if (this.wzMisc.getWizard()) {
$window.sessionStorage.removeItem('healthCheck');
this.wzMisc.setWizard(false);
}
this.apiIsDown = this.wzMisc.getApiIsDown();
// Initialize
this.currentApiEntryIndex = false;
this.formData = {};
this.tab = 'api';
this.load = true;
this.loadingLogs = true;
this.addManagerContainer = false;
this.showEditForm = {};
this.formUpdate = {};
this.userRegEx = new RegExp(/^.{3,100}$/);
this.passRegEx = new RegExp(/^.{3,100}$/);
this.urlRegEx = new RegExp(/^https?:\/\/[a-zA-Z0-9-.]{1,300}$/);
this.urlRegExIP = new RegExp(
/^https?:\/\/[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/
);
this.portRegEx = new RegExp(/^[0-9]{2,5}$/);
// Tab names
this.tabNames = TabNames;
this.configuration = wazuhConfig.getConfig();
this.indexPatterns = [];
this.apiEntries = [];
const location = this.$location.search();
if (location && location.tab) {
this.tab = location.tab;
}
this.savingApi = false;
}
$onInit() {
// Loading data
this.getSettings().then(() => this.getAppInfo());
}
switchTab(tab) {
this.tab = tab;
this.$location.search('tab', this.tab);
}
// Remove API entry
async removeManager(item) {
try {
let index = this.apiEntries.indexOf(item);
if (this.appState.getCurrentAPI()) {
if (
this.apiEntries[index]._id ===
JSON.parse(this.appState.getCurrentAPI()).id
) {
// We are trying to remove the one selected as default
this.appState.removeCurrentAPI();
}
}
await this.genericReq.request(
'DELETE',
`/elastic/apis/${this.apiEntries[index]._id}`
);
this.showEditForm[this.apiEntries[index]._id] = false;
this.apiEntries.splice(index, 1);
this.wzMisc.setApiIsDown(false);
this.apiIsDown = false;
this.isEditing = false;
for (const key in this.showEditForm) this.showEditForm[key] = false;
this.$scope.$emit('updateAPI', {});
this.errorHandler.info('The API was removed successfully', 'Settings');
if (!this.$scope.$$phase) this.$scope.$digest();
return;
} catch (error) {
this.errorHandler.handle('Could not remove the API', 'Settings');
}
return;
}
// Get current API index
getCurrentAPIIndex() {
// eslint-disable-next-line
this.apiEntries.map((entry, index, array) => {
if (entry._id === this.currentDefault) this.currentApiEntryIndex = index;
});
}
sortByTimestamp(a, b) {
const intA = parseInt(a._id);
const intB = parseInt(b._id);
return intA > intB ? -1 : intA < intB ? 1 : 0;
}
// Set default API
setDefault(item) {
const index = this.apiEntries.indexOf(item);
this.appState.setClusterInfo(this.apiEntries[index]._source.cluster_info);
if (this.apiEntries[index]._source.cluster_info.status == 'disabled') {
this.appState.setCurrentAPI(
JSON.stringify({
name: this.apiEntries[index]._source.cluster_info.manager,
id: this.apiEntries[index]._id
})
);
} else {
this.appState.setCurrentAPI(
JSON.stringify({
name: this.apiEntries[index]._source.cluster_info.cluster,
id: this.apiEntries[index]._id
})
);
}
this.$scope.$emit('updateAPI', {});
const currentApi = this.appState.getCurrentAPI();
this.currentDefault = JSON.parse(currentApi).id;
this.errorHandler.info(
`API ${
this.apiEntries[index]._source.cluster_info.manager
} set as default`,
'Settings'
);
this.getCurrentAPIIndex();
if (currentApi && !this.appState.getExtensions(JSON.parse(currentApi).id)) {
this.appState.setExtensions(
this.apiEntries[this.currentApiEntryIndex]._id,
this.apiEntries[this.currentApiEntryIndex]._source.extensions
);
}
this.extensions = this.appState.getExtensions(JSON.parse(currentApi).id);
if (!this.$scope.$$phase) this.$scope.$digest();
return;
}
// Get settings function
async getSettings() {
try {
const patternList = await this.genericReq.request(
'GET',
'/elastic/index-patterns',
{}
);
this.indexPatterns = patternList.data.data;
if (!patternList.data.data.length) {
this.wzMisc.setBlankScr('Sorry but no valid index patterns were found');
this.$location.search('tab', null);
this.$location.path('/blank-screen');
return;
}
const data = await this.genericReq.request('GET', '/elastic/apis');
for (const entry of data.data) this.showEditForm[entry._id] = false;
this.apiEntries = data.data.length > 0 ? data.data : [];
this.apiEntries = this.apiEntries.sort(this.sortByTimestamp);
const currentApi = this.appState.getCurrentAPI();
if (currentApi) {
this.currentDefault = JSON.parse(currentApi).id;
}
if (!this.$scope.$$phase) this.$scope.$digest();
this.getCurrentAPIIndex();
if (!this.currentApiEntryIndex && this.currentApiEntryIndex !== 0) return;
if (
currentApi &&
!this.appState.getExtensions(JSON.parse(currentApi).id)
) {
this.appState.setExtensions(
this.apiEntries[this.currentApiEntryIndex]._id,
this.apiEntries[this.currentApiEntryIndex]._source.extensions
);
}
this.extensions = this.appState.getExtensions(JSON.parse(currentApi).id);
if (!this.$scope.$$phase) this.$scope.$digest();
return;
} catch (error) {
this.errorHandler.handle('Error getting API entries', 'Settings');
}
return;
}
validator(formName) {
// Validate user
if (!this.userRegEx.test(this[formName].user)) {
return 'Invalid user field';
}
// Validate password
if (!this.passRegEx.test(this[formName].password)) {
return 'Invalid password field';
}
// Validate url
if (
!this.urlRegEx.test(this[formName].url) &&
!this.urlRegExIP.test(this[formName].url)
) {
return 'Invalid url field';
}
// Validate port
const validatePort = parseInt(this[formName].port);
if (
!this.portRegEx.test(this[formName].port) ||
validatePort <= 0 ||
validatePort >= 99999
) {
return 'Invalid port field';
}
return false;
}
toggleEditor(entry) {
if (this.formUpdate && this.formUpdate.password) {
this.formUpdate.password = '';
}
for (const key in this.showEditForm) {
if (entry && entry._id === key) continue;
this.showEditForm[key] = false;
}
this.showEditForm[entry._id] = !this.showEditForm[entry._id];
this.isEditing = this.showEditForm[entry._id];
this.addManagerContainer = false;
if (!this.$scope.$$phase) this.$scope.$digest();
}
// Save settings function
async saveSettings() {
try {
if(this.savingApi) {
this.errorHandler.info('Please, wait for success message', 'Settings');
return;
}
this.savingApi = true;
this.messageError = '';
this.isEditing = false;
const invalid = this.validator('formData');
if (invalid) {
this.messageError = invalid;
this.errorHandler.handle(invalid, 'Settings');
this.savingApi = false;
return;
}
const tmpData = {
user: this.formData.user,
password: base64.encode(this.formData.password),
url: this.formData.url,
port: this.formData.port,
cluster_info: {},
insecure: 'true',
extensions: {}
};
const config = this.wazuhConfig.getConfig();
this.appState.setPatternSelector(config['ip.selector']);
tmpData.extensions.audit = config['extensions.audit'];
tmpData.extensions.pci = config['extensions.pci'];
tmpData.extensions.gdpr = config['extensions.gdpr'];
tmpData.extensions.oscap = config['extensions.oscap'];
tmpData.extensions.ciscat = config['extensions.ciscat'];
tmpData.extensions.aws = config['extensions.aws'];
tmpData.extensions.virustotal = config['extensions.virustotal'];
tmpData.extensions.osquery = config['extensions.osquery'];
const checkData = await this.testAPI.check(tmpData);
// API Check correct. Get Cluster info
tmpData.cluster_info = checkData.data;
// Insert new API entry
const data = await this.genericReq.request(
'PUT',
'/elastic/api',
tmpData
);
this.appState.setExtensions(data.data.response._id, tmpData.extensions);
const newEntry = {
_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,
extensions: tmpData.extensions
}
};
this.apiEntries.push(newEntry);
this.apiEntries = this.apiEntries.sort(this.sortByTimestamp);
this.errorHandler.info('Wazuh API successfully added', 'Settings');
this.addManagerContainer = false;
this.formData = {};
// Setting current API as default if no one is in the cookies
if (!this.appState.getCurrentAPI()) {
// No cookie
if (
this.apiEntries[this.apiEntries.length - 1]._source.cluster_info
.status === 'disabled'
) {
this.appState.setCurrentAPI(
JSON.stringify({
name: this.apiEntries[this.apiEntries.length - 1]._source
.cluster_info.manager,
id: this.apiEntries[this.apiEntries.length - 1]._id
})
);
} else {
this.appState.setCurrentAPI(
JSON.stringify({
name: this.apiEntries[this.apiEntries.length - 1]._source
.cluster_info.cluster,
id: this.apiEntries[this.apiEntries.length - 1]._id
})
);
}
this.$scope.$emit('updateAPI', {});
this.currentDefault = JSON.parse(this.appState.getCurrentAPI()).id;
}
try {
await this.genericReq.request('GET', '/api/monitoring');
} catch (error) {
if (error && error.status && error.status === -1) {
this.errorHandler.handle(
'Wazuh API was inserted correctly, but something happened while fetching agents data.',
'Fetch agents',
true
);
} else {
this.errorHandler.handle(error, 'Fetch agents');
}
}
await this.getSettings();
} catch (error) {
if (error.status === 400) {
error.message =
'Please, fill all the fields in order to connect with Wazuh RESTful API.';
}
this.printError(error);
}
this.savingApi = false;
if (!this.$scope.$$phase) this.$scope.$digest();
return;
}
isUpdating() {
for (let key in this.showEditForm) {
if (this.showEditForm[key]) return true;
}
return false;
}
// Update settings function
async updateSettings(item) {
try {
if(this.savingApi) {
this.errorHandler.info('Please, wait for success message', 'Settings');
return;
}
this.savingApi = true;
this.messageErrorUpdate = '';
const invalid = this.validator('formUpdate');
if (invalid) {
this.messageErrorUpdate = invalid;
this.errorHandler.handle(invalid, 'Settings');
this.savingApi = false;
return;
}
const index = this.apiEntries.indexOf(item);
const tmpData = {
user: this.formUpdate.user,
password: base64.encode(this.formUpdate.password),
url: this.formUpdate.url,
port: this.formUpdate.port,
cluster_info: {},
insecure: 'true',
id: this.apiEntries[index]._id,
extensions: this.apiEntries[index]._source.extensions
};
const data = await this.testAPI.check(tmpData);
tmpData.cluster_info = data.data;
await this.genericReq.request('PUT', '/elastic/api-settings', tmpData);
this.apiEntries[index]._source.cluster_info = tmpData.cluster_info;
this.wzMisc.setApiIsDown(false);
this.apiIsDown = false;
this.apiEntries[index]._source.cluster_info.cluster =
tmpData.cluster_info.cluster;
this.apiEntries[index]._source.cluster_info.manager =
tmpData.cluster_info.manager;
this.apiEntries[index]._source.url = tmpData.url;
this.apiEntries[index]._source.api_port = tmpData.port;
this.apiEntries[index]._source.api_user = tmpData.user;
this.apiEntries = this.apiEntries.sort(this.sortByTimestamp);
this.showEditForm[this.apiEntries[index]._id] = false;
this.isEditing = false;
this.errorHandler.info('The API was updated successfully', 'Settings');
} catch (error) {
this.printError(error, true);
}
this.savingApi = false;
if (!this.$scope.$$phase) this.$scope.$digest();
return;
}
switch() {
this.addManagerContainer = !this.addManagerContainer;
}
// Check manager connectivity
async checkManager(item, isIndex) {
try {
const index = isIndex ? item : this.apiEntries.indexOf(item);
const tmpData = {
user: this.apiEntries[index]._source.api_user,
//password : this.apiEntries[index]._source.api_password,
url: this.apiEntries[index]._source.url,
port: this.apiEntries[index]._source.api_port,
cluster_info: {},
insecure: 'true',
id: this.apiEntries[index]._id
};
const data = await this.testAPI.check(tmpData);
tmpData.cluster_info = data.data;
const tmpUrl = `/elastic/api-hostname/${this.apiEntries[index]._id}`;
await this.genericReq.request('PUT', tmpUrl, {
cluster_info: tmpData.cluster_info
});
// Emit updateAPI event cause the cluster info could had been changed
this.$scope.$emit('updateAPI', { cluster_info: tmpData.cluster_info });
this.apiEntries[index]._source.cluster_info = tmpData.cluster_info;
this.wzMisc.setApiIsDown(false);
this.apiIsDown = false;
this.errorHandler.info('Connection success', 'Settings');
if (!this.$scope.$$phase) this.$scope.$digest();
return;
} catch (error) {
if (!this.wzMisc.getApiIsDown()) this.printError(error);
}
}
// 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;
if (!this.$scope.$$phase) this.$scope.$digest();
} catch (error) {
this.errorHandler.handle(error, 'Settings');
}
}
async changeIndexPattern(newIndexPattern) {
try {
this.appState.setCurrentPattern(newIndexPattern);
await this.genericReq.request(
'GET',
`/elastic/known-fields/${newIndexPattern}`,
{}
);
this.$scope.$emit('updatePattern', {});
this.errorHandler.info(
'Successfully changed the default index-pattern',
'Settings'
);
this.selectedIndexPattern = newIndexPattern;
if (!this.$scope.$$phase) this.$scope.$digest();
return;
} catch (error) {
this.errorHandler.handle(
'Error while changing the default index-pattern',
'Settings'
);
}
return;
}
printError(error, updating) {
const text = this.errorHandler.handle(error, 'Settings');
if (!updating) this.messageError = text;
else this.messageErrorUpdate = text;
}
async getAppLogs() {
try {
this.loadingLogs = true;
const logs = await this.genericReq.request('GET', '/utils/logs', {});
this.logs = logs.data.lastLogs.map(item => JSON.parse(item));
this.loadingLogs = false;
if (!this.$scope.$$phase) this.$scope.$digest();
} catch (error) {
this.logs = [
{
date: new Date(),
level: 'error',
message: 'Error when loading Wazuh app logs'
}
];
}
}
async getAppInfo() {
try {
const data = await this.genericReq.request('GET', '/elastic/setup');
this.appInfo = {};
this.appInfo['app-version'] = data.data.data['app-version'];
this.appInfo['installationDate'] = data.data.data['installationDate'];
this.appInfo['revision'] = data.data.data['revision'];
this.load = false;
const config = this.wazuhConfig.getConfig();
this.appState.setPatternSelector(config['ip.selector']);
if (
this.appState.getCurrentPattern() !== undefined &&
this.appState.getCurrentPattern() !== null
) {
// There's a pattern in the cookies
this.selectedIndexPattern = this.appState.getCurrentPattern();
} else {
// There's no pattern in the cookies, pick the one in the settings
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'];
} else {
this.extensions = this.appState.getExtensions(
JSON.parse(this.appState.getCurrentAPI()).id
);
}
if (this.tab === 'logs') {
this.getAppLogs();
}
this.getCurrentAPIIndex();
if (this.currentApiEntryIndex || this.currentApiEntryIndex === 0) {
await this.checkManager(this.currentApiEntryIndex, true);
}
if (!this.$scope.$$phase) this.$scope.$digest();
return;
} catch (error) {
this.errorHandler.handle(
'Error when loading Wazuh setup info',
'Settings'
);
}
return;
}
refreshLogs() {
return this.getAppLogs();
}
configEquivalence(key) {
return configEquivalences[key] || '-';
}
}