summaryrefslogtreecommitdiffstats
path: root/application/forms
diff options
context:
space:
mode:
Diffstat (limited to 'application/forms')
-rw-r--r--application/forms/Config/BackendConfigForm.php29
-rw-r--r--application/forms/Config/SniConfigForm.php79
-rw-r--r--application/forms/Jobs/JobConfigForm.php154
-rw-r--r--application/forms/Jobs/ScheduleForm.php201
4 files changed, 463 insertions, 0 deletions
diff --git a/application/forms/Config/BackendConfigForm.php b/application/forms/Config/BackendConfigForm.php
new file mode 100644
index 0000000..e806d26
--- /dev/null
+++ b/application/forms/Config/BackendConfigForm.php
@@ -0,0 +1,29 @@
+<?php
+
+// Icinga Web 2 X.509 Module | (c) 2018 Icinga GmbH | GPLv2
+
+namespace Icinga\Module\X509\Forms\Config;
+
+use Icinga\Data\ResourceFactory;
+use Icinga\Forms\ConfigForm;
+
+class BackendConfigForm extends ConfigForm
+{
+ public function init()
+ {
+ $this->setName('x509_backend');
+ $this->setSubmitLabel($this->translate('Save Changes'));
+ }
+
+ public function createElements(array $formData)
+ {
+ $dbResources = ResourceFactory::getResourceConfigs('db')->keys();
+
+ $this->addElement('select', 'backend_resource', [
+ 'label' => $this->translate('Database'),
+ 'description' => $this->translate('Database resource'),
+ 'multiOptions' => array_combine($dbResources, $dbResources),
+ 'required' => true
+ ]);
+ }
+}
diff --git a/application/forms/Config/SniConfigForm.php b/application/forms/Config/SniConfigForm.php
new file mode 100644
index 0000000..27a4823
--- /dev/null
+++ b/application/forms/Config/SniConfigForm.php
@@ -0,0 +1,79 @@
+<?php
+
+// Icinga Web 2 X.509 Module | (c) 2018 Icinga GmbH | GPLv2
+
+namespace Icinga\Module\X509\Forms\Config;
+
+use Icinga\Data\Filter\Filter;
+use Icinga\Forms\RepositoryForm;
+
+/**
+ * Create, update and delete jobs
+ */
+class SniConfigForm extends RepositoryForm
+{
+ protected function createInsertElements(array $formData)
+ {
+ $this->addElements([
+ [
+ 'text',
+ 'ip',
+ [
+ 'description' => $this->translate('IP'),
+ 'label' => $this->translate('IP'),
+ 'required' => true
+ ]
+ ],
+ [
+ 'textarea',
+ 'hostnames',
+ [
+ 'description' => $this->translate('Comma-separated list of hostnames'),
+ 'label' => $this->translate('Hostnames'),
+ 'required' => true
+ ]
+ ]
+ ]);
+
+ $this->setSubmitLabel($this->translate('Create'));
+ }
+
+ protected function createUpdateElements(array $formData)
+ {
+ $this->createInsertElements($formData);
+ $this->setTitle(sprintf($this->translate('Edit map for %s'), $this->getIdentifier()));
+ $this->setSubmitLabel($this->translate('Save'));
+ }
+
+ protected function createDeleteElements(array $formData)
+ {
+ $this->setTitle(sprintf($this->translate('Remove map for %s?'), $this->getIdentifier()));
+ $this->setSubmitLabel($this->translate('Yes'));
+ }
+
+ protected function createFilter()
+ {
+ return Filter::where('ip', $this->getIdentifier());
+ }
+
+ protected function getInsertMessage($success)
+ {
+ return $success
+ ? $this->translate('Map created')
+ : $this->translate('Failed to create map');
+ }
+
+ protected function getUpdateMessage($success)
+ {
+ return $success
+ ? $this->translate('Map updated')
+ : $this->translate('Failed to update map');
+ }
+
+ protected function getDeleteMessage($success)
+ {
+ return $success
+ ? $this->translate('Map removed')
+ : $this->translate('Failed to remove map');
+ }
+}
diff --git a/application/forms/Jobs/JobConfigForm.php b/application/forms/Jobs/JobConfigForm.php
new file mode 100644
index 0000000..539bc58
--- /dev/null
+++ b/application/forms/Jobs/JobConfigForm.php
@@ -0,0 +1,154 @@
+<?php
+
+// Icinga Web 2 X.509 Module | (c) 2018 Icinga GmbH | GPLv2
+
+namespace Icinga\Module\X509\Forms\Jobs;
+
+use DateTime;
+use Exception;
+use Icinga\Authentication\Auth;
+use Icinga\Module\X509\Common\Database;
+use Icinga\Module\X509\Model\X509Job;
+use Icinga\User;
+use Icinga\Web\Notification;
+use ipl\Html\Contract\FormSubmitElement;
+use ipl\Html\HtmlDocument;
+use ipl\Stdlib\Str;
+use ipl\Validator\CallbackValidator;
+use ipl\Validator\CidrValidator;
+use ipl\Web\Compat\CompatForm;
+
+/**
+ * Create, update and delete jobs
+ */
+class JobConfigForm extends CompatForm
+{
+ /** @var ?X509Job */
+ protected $job;
+
+ public function __construct(X509Job $job = null)
+ {
+ $this->job = $job;
+ }
+
+ protected function isUpdating(): bool
+ {
+ return $this->job !== null;
+ }
+
+ public function hasBeenSubmitted(): bool
+ {
+ if (! $this->hasBeenSent()) {
+ return false;
+ }
+
+ $button = $this->getPressedSubmitElement();
+
+ return $button && ($button->getName() === 'btn_submit' || $button->getName() === 'btn_remove');
+ }
+
+ protected function assemble(): void
+ {
+ $this->addElement('text', 'name', [
+ 'required' => true,
+ 'label' => $this->translate('Name'),
+ 'description' => $this->translate('Job name'),
+ ]);
+
+ $this->addElement('textarea', 'cidrs', [
+ 'required' => true,
+ 'label' => $this->translate('CIDRs'),
+ 'description' => $this->translate('Comma-separated list of CIDR addresses to scan'),
+ 'validators' => [
+ new CallbackValidator(function ($value, CallbackValidator $validator): bool {
+ $cidrValidator = new CidrValidator();
+ $cidrs = Str::trimSplit($value);
+
+ foreach ($cidrs as $cidr) {
+ if (! $cidrValidator->isValid($cidr)) {
+ $validator->addMessage(...$cidrValidator->getMessages());
+
+ return false;
+ }
+ }
+
+ return true;
+ })
+ ]
+ ]);
+
+ $this->addElement('textarea', 'ports', [
+ 'required' => true,
+ 'label' => $this->translate('Ports'),
+ 'description' => $this->translate('Comma-separated list of ports to scan'),
+ ]);
+
+ $this->addElement('textarea', 'exclude_targets', [
+ 'required' => false,
+ 'label' => $this->translate('Exclude Targets'),
+ 'description' => $this->translate('Comma-separated list of addresses/hostnames to exclude'),
+ ]);
+
+ $this->addElement('submit', 'btn_submit', [
+ 'label' => $this->isUpdating() ? $this->translate('Update') : $this->translate('Create')
+ ]);
+
+ if ($this->isUpdating()) {
+ $removeButton = $this->createElement('submit', 'btn_remove', [
+ 'class' => 'btn-remove',
+ 'label' => $this->translate('Remove Job'),
+ ]);
+ $this->registerElement($removeButton);
+
+ /** @var HtmlDocument $wrapper */
+ $wrapper = $this->getElement('btn_submit')->getWrapper();
+ $wrapper->prepend($removeButton);
+ }
+ }
+
+ protected function onSuccess(): void
+ {
+ $conn = Database::get();
+ /** @var FormSubmitElement $submitElement */
+ $submitElement = $this->getPressedSubmitElement();
+ if ($submitElement->getName() === 'btn_remove') {
+ try {
+ /** @var X509Job $job */
+ $job = $this->job;
+ $conn->delete('x509_job', ['id = ?' => $job->id]);
+
+ Notification::success($this->translate('Removed job successfully'));
+ } catch (Exception $err) {
+ Notification::error($this->translate('Failed to remove job') . ': ' . $err->getMessage());
+ }
+ } else {
+ $values = $this->getValues();
+
+ try {
+ /** @var User $user */
+ $user = Auth::getInstance()->getUser();
+ if ($this->job === null) {
+ $values['author'] = $user->getUsername();
+ $values['ctime'] = (new DateTime())->getTimestamp() * 1000.0;
+ $values['mtime'] = (new DateTime())->getTimestamp() * 1000.0;
+
+ $conn->insert('x509_job', $values);
+ $message = $this->translate('Created job successfully');
+ } else {
+ $values['mtime'] = (new DateTime())->getTimestamp() * 1000.0;
+
+ $conn->update('x509_job', $values, ['id = ?' => $this->job->id]);
+ $message = $this->translate('Updated job successfully');
+ }
+
+ Notification::success($message);
+ } catch (Exception $err) {
+ $message = $this->isUpdating()
+ ? $this->translate('Failed to update job')
+ : $this->translate('Failed to create job');
+
+ Notification::error($message . ': ' . $err->getMessage());
+ }
+ }
+ }
+}
diff --git a/application/forms/Jobs/ScheduleForm.php b/application/forms/Jobs/ScheduleForm.php
new file mode 100644
index 0000000..ae47e58
--- /dev/null
+++ b/application/forms/Jobs/ScheduleForm.php
@@ -0,0 +1,201 @@
+<?php
+
+/* Icinga Web 2 X.509 Module | (c) 2023 Icinga GmbH | GPLv2 */
+
+namespace Icinga\Module\X509\Forms\Jobs;
+
+use DateTime;
+use Exception;
+use Icinga\Application\Icinga;
+use Icinga\Application\Web;
+use Icinga\Authentication\Auth;
+use Icinga\Module\X509\Common\Database;
+use Icinga\Module\X509\Model\X509Schedule;
+use Icinga\User;
+use Icinga\Util\Json;
+use Icinga\Web\Notification;
+use ipl\Html\BaseHtmlElement;
+use ipl\Html\Contract\FormSubmitElement;
+use ipl\Html\HtmlDocument;
+use ipl\Html\HtmlElement;
+use ipl\Validator\CallbackValidator;
+use ipl\Web\Compat\CompatForm;
+use ipl\Web\FormElement\ScheduleElement;
+use Psr\Http\Message\RequestInterface;
+
+use function ipl\Stdlib\get_php_type;
+
+class ScheduleForm extends CompatForm
+{
+ /** @var int */
+ protected $jobId;
+
+ /** @var ?X509Schedule */
+ protected $schedule;
+
+ /** @var ScheduleElement */
+ protected $scheduleElement;
+
+ public function __construct(X509Schedule $schedule = null)
+ {
+ $this->schedule = $schedule;
+ $this->scheduleElement = new ScheduleElement('schedule_element');
+
+ /** @var Web $app */
+ $app = Icinga::app();
+ $this->scheduleElement->setIdProtector([$app->getRequest(), 'protectId']);
+ }
+
+ protected function isUpdating(): bool
+ {
+ return $this->schedule !== null;
+ }
+
+ public function setJobId(int $jobId): self
+ {
+ $this->jobId = $jobId;
+
+ return $this;
+ }
+
+ /**
+ * Get multipart updates
+ *
+ * @return array<int, BaseHtmlElement>
+ */
+ public function getPartUpdates(): array
+ {
+ /** @var RequestInterface $request */
+ $request = $this->getRequest();
+
+ return $this->scheduleElement->prepareMultipartUpdate($request);
+ }
+
+ public function hasBeenSubmitted(): bool
+ {
+ if (! $this->hasBeenSent()) {
+ return false;
+ }
+
+ $button = $this->getPressedSubmitElement();
+
+ return $button && ($button->getName() === 'submit' || $button->getName() === 'btn_remove');
+ }
+
+ protected function assemble(): void
+ {
+ $this->addElement('text', 'name', [
+ 'required' => true,
+ 'label' => $this->translate('Name'),
+ 'description' => $this->translate('Schedule name'),
+ ]);
+
+ $this->addElement('checkbox', 'full_scan', [
+ 'required' => false,
+ 'class' => 'autosubmit',
+ 'label' => $this->translate('Full Scan'),
+ 'description' => $this->translate(
+ 'Scan all known and unknown targets of this job. (Defaults to only scan unknown targets)'
+ )
+ ]);
+
+ if ($this->getPopulatedValue('full_scan', 'n') === 'n') {
+ $this->addElement('checkbox', 'rescan', [
+ 'required' => false,
+ 'class' => 'autosubmit',
+ 'label' => $this->translate('Rescan'),
+ 'description' => $this->translate('Rescan only targets that have been scanned before')
+ ]);
+
+ $this->addElement('text', 'since_last_scan', [
+ 'required' => false,
+ 'label' => $this->translate('Since Last Scan'),
+ 'placeholder' => '-24 hours',
+ 'description' => $this->translate(
+ 'Scan targets whose last scan is older than the specified date/time, which can also be an'
+ . ' English textual datetime description like "2 days". If you want to scan only unknown targets'
+ . ' you can set this to "null".'
+ ),
+ 'validators' => [
+ new CallbackValidator(function ($value, CallbackValidator $validator) {
+ if ($value !== null && $value !== 'null') {
+ try {
+ new DateTime($value);
+ } catch (Exception $_) {
+ $validator->addMessage($this->translate('Invalid textual date time'));
+
+ return false;
+ }
+ }
+
+ return true;
+ })
+ ]
+ ]);
+ }
+
+ $this->addHtml(HtmlElement::create('div', ['class' => 'schedule-element-separator']));
+ $this->addElement($this->scheduleElement);
+
+ $this->addElement('submit', 'submit', [
+ 'label' => $this->isUpdating() ? $this->translate('Update') : $this->translate('Schedule')
+ ]);
+
+ if ($this->isUpdating()) {
+ $removeButton = $this->createElement('submit', 'btn_remove', [
+ 'class' => 'btn-remove',
+ 'label' => $this->translate('Remove Schedule'),
+ ]);
+ $this->registerElement($removeButton);
+
+ /** @var HtmlDocument $wrapper */
+ $wrapper = $this->getElement('submit')->getWrapper();
+ $wrapper->prepend($removeButton);
+ }
+ }
+
+ protected function onSuccess(): void
+ {
+ /** @var X509Schedule $schedule */
+ $schedule = $this->schedule;
+ $conn = Database::get();
+ /** @var FormSubmitElement $submitElement */
+ $submitElement = $this->getPressedSubmitElement();
+ if ($submitElement->getName() === 'btn_remove') {
+ $conn->delete('x509_schedule', ['id = ?' => $schedule->id]);
+
+ Notification::success($this->translate('Deleted schedule successfully'));
+ } else {
+ $config = $this->getValues();
+ unset($config['name']);
+ unset($config['schedule_element']);
+
+ $frequency = $this->scheduleElement->getValue();
+ $config['type'] = get_php_type($frequency);
+ $config['frequency'] = Json::encode($frequency);
+
+ /** @var User $user */
+ $user = Auth::getInstance()->getUser();
+ if (! $this->isUpdating()) {
+ $conn->insert('x509_schedule', [
+ 'job_id' => $this->schedule ? $this->schedule->job_id : $this->jobId,
+ 'name' => $this->getValue('name'),
+ 'author' => $user->getUsername(),
+ 'config' => Json::encode($config),
+ 'ctime' => (new DateTime())->getTimestamp() * 1000.0,
+ 'mtime' => (new DateTime())->getTimestamp() * 1000.0
+ ]);
+ $message = $this->translate('Created schedule successfully');
+ } else {
+ $conn->update('x509_schedule', [
+ 'name' => $this->getValue('name'),
+ 'config' => Json::encode($config),
+ 'mtime' => (new DateTime())->getTimestamp() * 1000.0
+ ], ['id = ?' => $schedule->id]);
+ $message = $this->translate('Updated schedule successfully');
+ }
+
+ Notification::success($message);
+ }
+ }
+}