diff --git a/www/src/app/pages/admin/organizations/components/responders/responder-config-form.controller.js b/www/src/app/pages/admin/organizations/components/responders/responder-config-form.controller.js
new file mode 100644
index 000000000..e1d8b04b2
--- /dev/null
+++ b/www/src/app/pages/admin/organizations/components/responders/responder-config-form.controller.js
@@ -0,0 +1,28 @@
+'use strict';
+
+import _ from 'lodash/core';
+
+export default class ResponderConfigFormController {
+ constructor($log, Tlps, ResponderService) {
+ 'ngInject';
+
+ this.ResponderService = ResponderService;
+ this.Tlps = Tlps;
+ this.rateUnits = ['Day', 'Month'];
+ }
+
+ applyConfig(config) {
+ _.forEach(
+ _.keys(config),
+ k => (this.responder.configuration[k] = config[k])
+ );
+ }
+
+ applyGlobalConfig() {
+ this.applyConfig(this.globalConfig.config);
+ }
+
+ applyBaseConfig() {
+ this.applyConfig(this.baseConfig.config);
+ }
+}
\ No newline at end of file
diff --git a/www/src/app/pages/admin/organizations/components/responders/responder-config-form.html b/www/src/app/pages/admin/organizations/components/responders/responder-config-form.html
new file mode 100644
index 000000000..0b04facc4
--- /dev/null
+++ b/www/src/app/pages/admin/organizations/components/responders/responder-config-form.html
@@ -0,0 +1,89 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/www/src/app/pages/admin/organizations/components/responders/responder.edit.controller.js b/www/src/app/pages/admin/organizations/components/responders/responder.edit.controller.js
new file mode 100644
index 000000000..5d2f17bca
--- /dev/null
+++ b/www/src/app/pages/admin/organizations/components/responders/responder.edit.controller.js
@@ -0,0 +1,84 @@
+'use strict';
+
+import _ from 'lodash/core';
+
+export default class ResponderEditController {
+ constructor(
+ $log,
+ $uibModalInstance,
+ definition,
+ globalConfig,
+ baseConfig,
+ configuration,
+ responder,
+ mode
+ ) {
+ 'ngInject';
+
+ this.$log = $log;
+ this.$uibModalInstance = $uibModalInstance;
+ this.mode = mode;
+ this.definition = definition;
+ this.globalConfig = globalConfig;
+ this.baseConfig = baseConfig;
+ this.configuration = configuration;
+ this.responder = responder;
+ }
+
+ $onInit() {
+ if (_.isEmpty(this.responder)) {
+ let responder = {
+ name: this.definition.id,
+ configuration: {},
+ rate: undefined,
+ rateUnit: undefined
+ };
+
+ _.forEach(this.definition.configurationItems, item => {
+ const property = item.name,
+ configValue = (this.configuration.config || {})[property];
+
+ responder.configuration[property] =
+ configValue ||
+ item.defaultValue ||
+ (item.multi ? [undefined] : undefined);
+ });
+
+ // Handle TLP default config
+ const globalConfig = [
+ 'proxy_http',
+ 'proxy_https'
+ ];
+ _.forEach(globalConfig, cnf => {
+ if (responder.configuration[cnf] === undefined) {
+ responder.configuration[cnf] =
+ this.configuration.config[cnf] !== undefined ?
+ this.configuration.config[cnf] :
+ undefined;
+ }
+ });
+
+ if (responder.configuration.check_tlp === undefined) {
+ responder.configuration.check_tlp = true;
+ }
+ if (responder.configuration.max_tlp === undefined) {
+ responder.configuration.max_tlp = 2;
+ }
+ if (responder.configuration.check_pap === undefined) {
+ responder.configuration.check_pap = true;
+ }
+ if (responder.configuration.max_pap === undefined) {
+ responder.configuration.max_pap = 2;
+ }
+
+ this.responder = responder;
+ }
+ }
+
+ save() {
+ this.$uibModalInstance.close(this.responder);
+ }
+ cancel() {
+ this.$uibModalInstance.dismiss('cancel');
+ }
+}
\ No newline at end of file
diff --git a/www/src/app/pages/admin/organizations/components/responders/responder.edit.modal.html b/www/src/app/pages/admin/organizations/components/responders/responder.edit.modal.html
new file mode 100644
index 000000000..d6be8d337
--- /dev/null
+++ b/www/src/app/pages/admin/organizations/components/responders/responder.edit.modal.html
@@ -0,0 +1,15 @@
+
\ No newline at end of file
diff --git a/www/src/app/pages/admin/organizations/components/responders/responders-list.controller.js b/www/src/app/pages/admin/organizations/components/responders/responders-list.controller.js
new file mode 100644
index 000000000..01b62188b
--- /dev/null
+++ b/www/src/app/pages/admin/organizations/components/responders/responders-list.controller.js
@@ -0,0 +1,181 @@
+'use strict';
+
+import _ from 'lodash';
+
+import ResponderEditController from './responder.edit.controller';
+import editModalTpl from './responder.edit.modal.html';
+
+export default class OrganizationRespondersController {
+ constructor(
+ $log,
+ $q,
+ $uibModal,
+ ResponderService,
+ OrganizationService,
+ ModalService,
+ NotificationService
+ ) {
+ 'ngInject';
+
+ this.$log = $log;
+ this.$q = $q;
+ this.$uibModal = $uibModal;
+ this.ResponderService = ResponderService;
+ this.OrganizationService = OrganizationService;
+ this.ModalService = ModalService;
+ this.NotificationService = NotificationService;
+
+ this.state = {
+ filterAvailable: ''
+ };
+ }
+
+ $onInit() {
+ this.activeResponders = _.keyBy(this.responders, 'workerDefinitionId');
+ this.definitionsIds = _.keys(this.responderDefinitions).sort();
+ this.invalidResponders = _.filter(this.responders, a =>
+ _.isEmpty(a.dataTypeList)
+ );
+ }
+
+ openModal(mode, definition, responder) {
+ let baseConfigName = definition ? definition.baseConfig : undefined;
+ return this.ResponderService.getBaseConfig(baseConfigName)
+ .then(baseConfig => {
+ let configs = {
+ globalConfig: {},
+ baseConfig: baseConfig,
+ responderConfig: {
+ config: {}
+ }
+ };
+
+ return this.ResponderService.getConfiguration('global').then(
+ globalConfig => {
+ configs.globalConfig = globalConfig;
+
+ if (!baseConfig.config) {
+ baseConfig.config = {};
+ }
+
+ _.merge(
+ configs.responderConfig.config,
+ configs.baseConfig.config,
+ configs.globalConfig.config
+ );
+
+ return configs;
+ }
+ );
+ })
+ .then(configs => {
+ let modal = this.$uibModal.open({
+ animation: true,
+ controller: ResponderEditController,
+ controllerAs: '$modal',
+ templateUrl: editModalTpl,
+ size: 'lg',
+ resolve: {
+ definition: () => definition,
+ globalConfig: () => configs.globalConfig,
+ baseConfig: () => configs.baseConfig,
+ configuration: () => configs.responderConfig,
+ responder: () => angular.copy(responder),
+ mode: () => mode
+ }
+ });
+
+ return modal.result;
+ })
+ .then(response => {
+ if (mode === 'create') {
+ return this.OrganizationService.enableResponder(
+ definition.id,
+ response
+ );
+ } else {
+ return this.OrganizationService.updateResponder(
+ responder.id,
+ _.pick(
+ response,
+ 'configuration',
+ 'rate',
+ 'rateUnit',
+ 'name'
+ )
+ );
+ }
+ })
+ .then(() => this.reload());
+ }
+
+ edit(mode, definition, responder) {
+ this.openModal(mode, definition, responder)
+ .then(() => {
+ this.NotificationService.success('Responder updated successfully');
+ })
+ .catch(err => {
+ if (!_.isString(err)) {
+ this.NotificationService.error('Failed to edit the responder.');
+ }
+ });
+ }
+
+ enable(responderId) {
+ let definition = this.responderDefinitions[responderId];
+
+ this.openModal('create', definition, {})
+ .then(() => {
+ this.NotificationService.success('Responder enabled successfully');
+ })
+ .catch(err => {
+ if (!_.isString(err)) {
+ this.NotificationService.error('Failed to enable the responder.');
+ }
+ });
+ }
+
+ disable(responderId) {
+ let modalInstance = this.ModalService.confirm(
+ 'Disable responder',
+ 'Are you sure you want to disable this responder? The corresponding configuration will be lost.', {
+ flavor: 'danger',
+ okText: 'Yes, disable it'
+ }
+ );
+
+ modalInstance.result
+ .then(() => this.OrganizationService.disableResponder(responderId))
+ .then(() => {
+ this.reload();
+ this.NotificationService.success('Responder disabled successfully');
+ })
+ .catch(err => {
+ if (!_.isString(err)) {
+ this.NotificationService.error('Unable to delete the responder.');
+ }
+ });
+ }
+
+ reload() {
+ this.OrganizationService.responders(this.organization.id).then(responders => {
+ this.responders = responders;
+ this.$onInit();
+ });
+ }
+
+ refreshResponders() {
+ this.ResponderService.scan()
+ .then(() => this.ResponderService.definitions(true))
+ .then(defintions => {
+ this.responderDefinitions = defintions;
+ this.reload();
+ this.NotificationService.success('Responder definitions refreshed.');
+ })
+ .catch(() =>
+ this.NotificationService.error(
+ 'Failed to refresh responder definitions.'
+ )
+ );
+ }
+}
\ No newline at end of file
diff --git a/www/src/app/pages/admin/organizations/components/responders/responders-list.html b/www/src/app/pages/admin/organizations/components/responders/responders-list.html
new file mode 100644
index 000000000..9d5207dbb
--- /dev/null
+++ b/www/src/app/pages/admin/organizations/components/responders/responders-list.html
@@ -0,0 +1,97 @@
+
+
+
+
+
You have {{$ctrl.invalidReponders.length}} invalid
+
+
+
Invalid responders have no definition and cannot be run on any observable. You have to remove them.
+
+
+
+
+ Available responders ({{$ctrl.definitionsIds.length || 0}})
+
+ Refresh responders
+
+
+
+
+
No responders available.
+
+
+
+
+
\ No newline at end of file
diff --git a/www/src/app/pages/admin/organizations/details/organization.page.controller.js b/www/src/app/pages/admin/organizations/details/organization.page.controller.js
index bfca1e0ed..f66ad1969 100644
--- a/www/src/app/pages/admin/organizations/details/organization.page.controller.js
+++ b/www/src/app/pages/admin/organizations/details/organization.page.controller.js
@@ -1,12 +1,11 @@
'use strict';
-import _ from 'lodash';
-
export default class OrganizationPageController {
constructor(
$log,
$stateParams,
AnalyzerService,
+ ResponderService,
OrganizationService,
AuthService
) {
@@ -15,7 +14,8 @@ export default class OrganizationPageController {
this.$log = $log;
this.orgId = $stateParams.id;
this.AnalyzerService = AnalyzerService;
+ this.ResponderService = ResponderService;
this.OrganizationService = OrganizationService;
this.AuthService = AuthService;
}
-}
+}
\ No newline at end of file
diff --git a/www/src/app/pages/admin/organizations/details/organization.page.html b/www/src/app/pages/admin/organizations/details/organization.page.html
index 1927403a9..4477b6141 100644
--- a/www/src/app/pages/admin/organizations/details/organization.page.html
+++ b/www/src/app/pages/admin/organizations/details/organization.page.html
@@ -14,7 +14,7 @@ Organization:
- Configurations
+ Analyzers Config
@@ -22,6 +22,17 @@ Organization:
Analyzers
+
+
+
+ Responders Config
+
+
+
+
+ Responders
+
+
diff --git a/www/src/app/pages/admin/organizations/organizations.module.js b/www/src/app/pages/admin/organizations/organizations.module.js
index 6b551b3e9..6a2567cbc 100644
--- a/www/src/app/pages/admin/organizations/organizations.module.js
+++ b/www/src/app/pages/admin/organizations/organizations.module.js
@@ -8,21 +8,31 @@ import organizationsPageTpl from './list/organizations.page.html';
import OrganizationPageController from './details/organization.page.controller';
import organizationPageTpl from './details/organization.page.html';
+// Users
+import OrganizationUsersController from './components/users-list.controller';
+import organizationUsersTpl from './components/users-list.html';
+
+// Analyzers
import AnalyzerConfigFormController from './components/analyzer-config-form.controller';
import analyzerConfigFormTpl from './components/analyzer-config-form.html';
import OrganizationAnalyzersController from './components/analyzers-list.controller';
import organizationAnalyzersTpl from './components/analyzers-list.html';
-import OrganizationUsersController from './components/users-list.controller';
-import organizationUsersTpl from './components/users-list.html';
-
import OrganizationConfigsController from './components/config-list.controller';
import organizationConfigsTpl from './components/config-list.html';
import ConfigurationForm from './components/config-form.controller';
import configurationFormTpl from './components/config-form.html';
+// Responders
+import ResponderConfigFormController from './components/responders/responder-config-form.controller';
+import responderConfigFormTpl from './components/responders/responder-config-form.html';
+
+import OrganizationRespondersController from './components/responders/responders-list.controller';
+import organizationRespondersTpl from './components/responders/responders-list.html';
+
+
import organizationService from './organizations.service.js';
import './organizations.scss';
@@ -67,6 +77,20 @@ const organizationsModule = angular
} else {
return $q.resolve([]);
}
+ },
+ responderDefinitions: (AuthService, ResponderService, $q) => {
+ if (AuthService.hasRole([Roles.ORGADMIN])) {
+ return ResponderService.definitions();
+ } else {
+ return $q.resolve({});
+ }
+ },
+ responders: (AuthService, OrganizationService, $q) => {
+ if (AuthService.hasRole([Roles.ORGADMIN])) {
+ return OrganizationService.responders();
+ } else {
+ return $q.resolve([]);
+ }
}
},
data: {
@@ -86,7 +110,9 @@ const organizationsModule = angular
users: '<',
analyzerDefinitions: '<',
analyzers: '<',
- configurations: '<'
+ configurations: '<',
+ responderDefinitions: '<',
+ responders: '<'
}
})
.component('organizationAnalyzersList', {
@@ -98,6 +124,15 @@ const organizationsModule = angular
analyzers: '<'
}
})
+ .component('organizationRespondersList', {
+ controller: OrganizationRespondersController,
+ templateUrl: organizationRespondersTpl,
+ bindings: {
+ organization: '<',
+ responderDefinitions: '<',
+ responders: '<'
+ }
+ })
.component('organizationUsersList', {
controller: OrganizationUsersController,
templateUrl: organizationUsersTpl,
@@ -132,6 +167,17 @@ const organizationsModule = angular
baseConfig: '<'
}
})
+ .component('responderConfigForm', {
+ controller: ResponderConfigFormController,
+ templateUrl: responderConfigFormTpl,
+ bindings: {
+ responder: '=',
+ definition: '=',
+ configuration: '<',
+ globalConfig: '<',
+ baseConfig: '<'
+ }
+ })
.service('OrganizationService', organizationService);
-export default organizationsModule;
+export default organizationsModule;
\ No newline at end of file
diff --git a/www/src/app/pages/admin/organizations/organizations.service.js b/www/src/app/pages/admin/organizations/organizations.service.js
index 255024450..dc229b377 100644
--- a/www/src/app/pages/admin/organizations/organizations.service.js
+++ b/www/src/app/pages/admin/organizations/organizations.service.js
@@ -60,20 +60,37 @@ export default class OrganizationService {
}
enable(id) {
- return this.$http.patch(`./api/organization/${id}`, { status: 'Active' });
+ return this.$http.patch(`./api/organization/${id}`, {
+ status: 'Active'
+ });
}
analyzers() {
let defer = this.$q.defer();
this.$http
- .get(`./api/organization/analyzer`, { params: { range: 'all' } })
+ .get(`./api/organization/analyzer`, {
+ params: {
+ range: 'all'
+ }
+ })
.then(response => defer.resolve(response.data))
.catch(err => defer.reject(err));
return defer.promise;
}
+ responders() {
+ return this.$http
+ .get(`./api/organization/responder`, {
+ params: {
+ range: 'all'
+ }
+ })
+ .then(response => this.$q.resolve(response.data))
+ .catch(err => this.$q.reject(err));
+ }
+
users(id) {
let defer = this.$q.defer();
@@ -96,4 +113,16 @@ export default class OrganizationService {
disableAnalyzer(analyzerId) {
return this.$http.delete(`./api/analyzer/${analyzerId}`);
}
-}
+
+ enableResponder(responderId, config) {
+ return this.$http.post(`./api/organization/responder/${responderId}`, config);
+ }
+
+ updateResponder(responderId, config) {
+ return this.$http.patch(`./api/responder/${responderId}`, config);
+ }
+
+ disableResponder(responderId) {
+ return this.$http.delete(`./api/responder/${responderId}`);
+ }
+}
\ No newline at end of file