2019-10-10 07:40:39 -04:00
|
|
|
<?php
|
|
|
|
|
|
2020-03-13 03:38:01 -04:00
|
|
|
/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
|
|
|
|
|
|
2019-11-04 19:07:30 -05:00
|
|
|
namespace Icinga\Module\Icingadb\Common;
|
2019-10-10 07:40:39 -04:00
|
|
|
|
2024-03-26 11:11:01 -04:00
|
|
|
use Icinga\Exception\Http\HttpNotFoundException;
|
2021-02-12 10:37:20 -05:00
|
|
|
use Icinga\Module\Icingadb\Forms\Command\CommandForm;
|
|
|
|
|
use Icinga\Module\Icingadb\Forms\Command\Object\AcknowledgeProblemForm;
|
|
|
|
|
use Icinga\Module\Icingadb\Forms\Command\Object\AddCommentForm;
|
|
|
|
|
use Icinga\Module\Icingadb\Forms\Command\Object\CheckNowForm;
|
|
|
|
|
use Icinga\Module\Icingadb\Forms\Command\Object\ProcessCheckResultForm;
|
|
|
|
|
use Icinga\Module\Icingadb\Forms\Command\Object\RemoveAcknowledgementForm;
|
|
|
|
|
use Icinga\Module\Icingadb\Forms\Command\Object\ScheduleCheckForm;
|
|
|
|
|
use Icinga\Module\Icingadb\Forms\Command\Object\ScheduleHostDowntimeForm;
|
|
|
|
|
use Icinga\Module\Icingadb\Forms\Command\Object\ScheduleServiceDowntimeForm;
|
|
|
|
|
use Icinga\Module\Icingadb\Forms\Command\Object\SendCustomNotificationForm;
|
|
|
|
|
use Icinga\Module\Icingadb\Forms\Command\Object\ToggleObjectFeaturesForm;
|
2021-03-16 11:34:13 -04:00
|
|
|
use Icinga\Security\SecurityException;
|
2025-06-10 06:01:41 -04:00
|
|
|
use Icinga\Util\Environment;
|
2023-09-05 08:49:09 -04:00
|
|
|
use Icinga\Web\Notification;
|
2019-10-10 07:40:39 -04:00
|
|
|
use ipl\Orm\Model;
|
|
|
|
|
use ipl\Orm\Query;
|
2019-10-24 08:57:54 -04:00
|
|
|
use ipl\Web\Url;
|
2019-10-10 07:40:39 -04:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Trait CommandActions
|
|
|
|
|
*/
|
|
|
|
|
trait CommandActions
|
|
|
|
|
{
|
2024-03-26 11:11:01 -04:00
|
|
|
/** @var null|Query|array<Model> $commandTargets */
|
2019-10-10 07:40:39 -04:00
|
|
|
protected $commandTargets;
|
|
|
|
|
|
|
|
|
|
/** @var Model $commandTargetModel */
|
|
|
|
|
protected $commandTargetModel;
|
|
|
|
|
|
2021-10-07 02:23:28 -04:00
|
|
|
/**
|
|
|
|
|
* Get url to view command targets, used as redirection target
|
|
|
|
|
*
|
|
|
|
|
* @return Url
|
|
|
|
|
*/
|
|
|
|
|
abstract protected function getCommandTargetsUrl(): Url;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get status of toggleable features
|
|
|
|
|
*
|
|
|
|
|
* @return object
|
|
|
|
|
*/
|
|
|
|
|
protected function getFeatureStatus()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Fetch command targets
|
|
|
|
|
*
|
2024-03-26 11:11:01 -04:00
|
|
|
* @return Query|array<Model>
|
2021-10-07 02:23:28 -04:00
|
|
|
*/
|
|
|
|
|
abstract protected function fetchCommandTargets();
|
|
|
|
|
|
2019-10-10 07:40:39 -04:00
|
|
|
/**
|
|
|
|
|
* Get command targets
|
|
|
|
|
*
|
2024-03-26 11:11:01 -04:00
|
|
|
* @return Query|array<Model>
|
2019-10-10 07:40:39 -04:00
|
|
|
*/
|
|
|
|
|
protected function getCommandTargets()
|
|
|
|
|
{
|
|
|
|
|
if (! isset($this->commandTargets)) {
|
2021-10-07 02:23:28 -04:00
|
|
|
$this->commandTargets = $this->fetchCommandTargets();
|
2019-10-10 07:40:39 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $this->commandTargets;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the model of the command targets
|
|
|
|
|
*
|
|
|
|
|
* @return Model
|
2024-03-26 11:11:01 -04:00
|
|
|
* @throws HttpNotFoundException If no command targets were found
|
2019-10-10 07:40:39 -04:00
|
|
|
*/
|
2021-09-22 04:21:15 -04:00
|
|
|
protected function getCommandTargetModel(): Model
|
2019-10-10 07:40:39 -04:00
|
|
|
{
|
|
|
|
|
if (! isset($this->commandTargetModel)) {
|
|
|
|
|
$commandTargets = $this->getCommandTargets();
|
2024-03-26 11:11:01 -04:00
|
|
|
if (empty($commandTargets) || count($commandTargets) === 0) {
|
|
|
|
|
throw new HttpNotFoundException('No command targets found');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (is_array($commandTargets)) {
|
2019-10-10 07:40:39 -04:00
|
|
|
$this->commandTargetModel = $commandTargets[0];
|
2024-03-26 11:11:01 -04:00
|
|
|
} elseif ($commandTargets->count() > 0) {
|
2019-10-10 07:40:39 -04:00
|
|
|
$this->commandTargetModel = $commandTargets->getModel();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $this->commandTargetModel;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-16 11:34:13 -04:00
|
|
|
/**
|
|
|
|
|
* Check whether the permission is granted on any of the command targets
|
|
|
|
|
*
|
|
|
|
|
* @param string $permission
|
|
|
|
|
*
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
2021-09-22 04:21:15 -04:00
|
|
|
protected function isGrantedOnCommandTargets(string $permission): bool
|
2021-03-16 11:34:13 -04:00
|
|
|
{
|
|
|
|
|
$commandTargets = $this->getCommandTargets();
|
|
|
|
|
if (is_array($commandTargets)) {
|
|
|
|
|
foreach ($commandTargets as $commandTarget) {
|
|
|
|
|
if ($this->isGrantedOn($permission, $commandTarget)) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $this->isGrantedOnType(
|
|
|
|
|
$permission,
|
|
|
|
|
$this->getCommandTargetModel()->getTableName(),
|
|
|
|
|
$commandTargets->getFilter()
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Assert that the permission is granted on any of the command targets
|
|
|
|
|
*
|
|
|
|
|
* @param string $permission
|
|
|
|
|
*
|
|
|
|
|
* @throws SecurityException
|
|
|
|
|
*/
|
2021-09-22 04:21:15 -04:00
|
|
|
protected function assertIsGrantedOnCommandTargets(string $permission)
|
2021-03-16 11:34:13 -04:00
|
|
|
{
|
|
|
|
|
if (! $this->isGrantedOnCommandTargets($permission)) {
|
|
|
|
|
throw new SecurityException('No permission for %s', $permission);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-10 07:40:39 -04:00
|
|
|
/**
|
|
|
|
|
* Handle and register the given command form
|
|
|
|
|
*
|
2021-02-12 10:37:20 -05:00
|
|
|
* @param string|CommandForm $form
|
2021-09-22 04:21:15 -04:00
|
|
|
*
|
|
|
|
|
* @return void
|
2019-10-10 07:40:39 -04:00
|
|
|
*/
|
|
|
|
|
protected function handleCommandForm($form)
|
|
|
|
|
{
|
2023-09-05 08:49:09 -04:00
|
|
|
$isXhr = $this->getRequest()->isXmlHttpRequest();
|
2023-12-13 10:47:15 -05:00
|
|
|
$isApi = $this->getRequest()->isApiRequest();
|
|
|
|
|
if ($isXhr && $isApi) {
|
2023-09-05 08:49:09 -04:00
|
|
|
// Prevents the framework already, this is just a fail-safe
|
|
|
|
|
$this->httpBadRequest('Responding with JSON during a Web request is not supported');
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-10 06:01:41 -04:00
|
|
|
// Bulk operations may require more memory and time
|
|
|
|
|
Environment::raiseMemoryLimit();
|
|
|
|
|
Environment::raiseExecutionTime();
|
|
|
|
|
|
2019-10-10 07:40:39 -04:00
|
|
|
if (is_string($form)) {
|
2023-09-05 08:49:09 -04:00
|
|
|
/** @var CommandForm $form */
|
2021-02-12 10:37:20 -05:00
|
|
|
$form = new $form();
|
2019-10-10 07:40:39 -04:00
|
|
|
}
|
|
|
|
|
|
2023-09-05 08:49:09 -04:00
|
|
|
$form->setObjects($this->getCommandTargets());
|
|
|
|
|
|
2023-12-13 10:47:15 -05:00
|
|
|
if (! $isApi || $isXhr) {
|
2023-09-05 08:49:09 -04:00
|
|
|
$this->handleWebRequest($form);
|
|
|
|
|
} else {
|
|
|
|
|
$this->handleApiRequest($form);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Handle a Web request for the given form
|
|
|
|
|
*
|
|
|
|
|
* @param CommandForm $form
|
|
|
|
|
*
|
|
|
|
|
* @return void
|
|
|
|
|
*/
|
|
|
|
|
protected function handleWebRequest(CommandForm $form): void
|
|
|
|
|
{
|
2021-02-12 10:37:20 -05:00
|
|
|
$actionUrl = $this->getRequest()->getUrl();
|
|
|
|
|
if ($this->view->compact) {
|
|
|
|
|
$actionUrl = clone $actionUrl;
|
|
|
|
|
// TODO: This solves https://github.com/Icinga/icingadb-web/issues/124 but I'd like to omit this
|
|
|
|
|
// entirely. I think it should be solved like https://github.com/Icinga/icingaweb2/pull/4300 so
|
|
|
|
|
// that a request's url object still has params like showCompact and _dev
|
|
|
|
|
$actionUrl->getParams()->add('showCompact', true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$form->setAction($actionUrl->getAbsoluteUrl());
|
|
|
|
|
$form->on($form::ON_SUCCESS, function () {
|
|
|
|
|
// This forces the column to reload nearly instantly after the redirect
|
|
|
|
|
// and ensures the effect of the command is visible to the user asap
|
|
|
|
|
$this->getResponse()->setAutoRefreshInterval(1);
|
|
|
|
|
|
|
|
|
|
$this->redirectNow($this->getCommandTargetsUrl());
|
|
|
|
|
});
|
2019-10-24 08:57:54 -04:00
|
|
|
|
2023-09-05 08:49:09 -04:00
|
|
|
$form->handleRequest($this->getServerRequest());
|
2021-02-12 10:37:20 -05:00
|
|
|
|
|
|
|
|
$this->addContent($form);
|
2019-10-10 07:40:39 -04:00
|
|
|
}
|
|
|
|
|
|
2023-09-05 08:49:09 -04:00
|
|
|
/**
|
|
|
|
|
* Handle an API request for the given form
|
|
|
|
|
*
|
|
|
|
|
* @param CommandForm $form
|
|
|
|
|
*
|
|
|
|
|
* @return never
|
|
|
|
|
*/
|
|
|
|
|
protected function handleApiRequest(CommandForm $form)
|
|
|
|
|
{
|
|
|
|
|
$form->setIsApiTarget();
|
|
|
|
|
$form->on($form::ON_SUCCESS, function () {
|
|
|
|
|
$this->getResponse()
|
|
|
|
|
->json()
|
|
|
|
|
->setSuccessData(Notification::getInstance()->popMessages())
|
|
|
|
|
->sendResponse();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
$form->handleRequest($this->getServerRequest());
|
2024-09-18 03:34:20 -04:00
|
|
|
$this->assertHttpMethod($form->getMethod());
|
2023-09-05 08:49:09 -04:00
|
|
|
|
|
|
|
|
$errors = [];
|
|
|
|
|
foreach ($form->getElements() as $element) {
|
|
|
|
|
$errors[$element->getName()] = $element->getMessages();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$response = $this->getResponse()->json();
|
|
|
|
|
$response->setHttpResponseCode(422);
|
|
|
|
|
$response->setFailData($errors)
|
|
|
|
|
->sendResponse();
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-10 07:40:39 -04:00
|
|
|
public function acknowledgeAction()
|
|
|
|
|
{
|
2021-03-16 12:32:59 -04:00
|
|
|
$this->assertIsGrantedOnCommandTargets('icingadb/command/acknowledge-problem');
|
2020-04-24 08:36:37 -04:00
|
|
|
$this->setTitle(t('Acknowledge Problem'));
|
2021-02-12 10:37:20 -05:00
|
|
|
$this->handleCommandForm(AcknowledgeProblemForm::class);
|
2019-10-10 07:40:39 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function addCommentAction()
|
|
|
|
|
{
|
2021-03-16 12:32:59 -04:00
|
|
|
$this->assertIsGrantedOnCommandTargets('icingadb/command/comment/add');
|
2020-04-24 08:36:37 -04:00
|
|
|
$this->setTitle(t('Add Comment'));
|
2021-02-12 10:37:20 -05:00
|
|
|
$this->handleCommandForm(AddCommentForm::class);
|
2019-10-10 07:40:39 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function checkNowAction()
|
|
|
|
|
{
|
2021-03-16 12:32:59 -04:00
|
|
|
if (! $this->isGrantedOnCommandTargets('icingadb/command/schedule-check/active-only')) {
|
|
|
|
|
$this->assertIsGrantedOnCommandTargets('icingadb/command/schedule-check');
|
2021-03-16 11:34:13 -04:00
|
|
|
}
|
|
|
|
|
|
2021-02-12 10:37:20 -05:00
|
|
|
$this->handleCommandForm(CheckNowForm::class);
|
2019-10-10 07:40:39 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function processCheckresultAction()
|
|
|
|
|
{
|
2021-03-16 12:32:59 -04:00
|
|
|
$this->assertIsGrantedOnCommandTargets('icingadb/command/process-check-result');
|
2020-04-24 08:36:37 -04:00
|
|
|
$this->setTitle(t('Submit Passive Check Result'));
|
2021-02-12 10:37:20 -05:00
|
|
|
$this->handleCommandForm(ProcessCheckResultForm::class);
|
2019-10-10 07:40:39 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function removeAcknowledgementAction()
|
|
|
|
|
{
|
2021-03-16 12:32:59 -04:00
|
|
|
$this->assertIsGrantedOnCommandTargets('icingadb/command/remove-acknowledgement');
|
2021-02-12 10:37:20 -05:00
|
|
|
$this->handleCommandForm(RemoveAcknowledgementForm::class);
|
2019-10-10 07:40:39 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function scheduleCheckAction()
|
|
|
|
|
{
|
2021-03-16 12:32:59 -04:00
|
|
|
if (! $this->isGrantedOnCommandTargets('icingadb/command/schedule-check/active-only')) {
|
|
|
|
|
$this->assertIsGrantedOnCommandTargets('icingadb/command/schedule-check');
|
2021-03-16 11:34:13 -04:00
|
|
|
}
|
|
|
|
|
|
2021-02-12 10:37:20 -05:00
|
|
|
$this->setTitle(t('Reschedule Check'));
|
|
|
|
|
$this->handleCommandForm(ScheduleCheckForm::class);
|
2019-10-10 07:40:39 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function scheduleDowntimeAction()
|
|
|
|
|
{
|
2021-03-16 12:32:59 -04:00
|
|
|
$this->assertIsGrantedOnCommandTargets('icingadb/command/downtime/schedule');
|
2019-12-10 04:49:21 -05:00
|
|
|
|
2020-01-17 10:08:04 -05:00
|
|
|
switch ($this->getCommandTargetModel()->getTableName()) {
|
2019-10-10 07:40:39 -04:00
|
|
|
case 'host':
|
2020-04-24 08:36:37 -04:00
|
|
|
$this->setTitle(t('Schedule Host Downtime'));
|
2021-02-12 10:37:20 -05:00
|
|
|
$this->handleCommandForm(ScheduleHostDowntimeForm::class);
|
2019-10-10 07:40:39 -04:00
|
|
|
break;
|
|
|
|
|
case 'service':
|
2020-04-24 08:36:37 -04:00
|
|
|
$this->setTitle(t('Schedule Service Downtime'));
|
2021-02-12 10:37:20 -05:00
|
|
|
$this->handleCommandForm(ScheduleServiceDowntimeForm::class);
|
2019-10-10 07:40:39 -04:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function sendCustomNotificationAction()
|
|
|
|
|
{
|
2021-03-16 12:32:59 -04:00
|
|
|
$this->assertIsGrantedOnCommandTargets('icingadb/command/send-custom-notification');
|
2020-04-24 08:36:37 -04:00
|
|
|
$this->setTitle(t('Send Custom Notification'));
|
2021-02-12 10:37:20 -05:00
|
|
|
$this->handleCommandForm(SendCustomNotificationForm::class);
|
2019-10-10 07:40:39 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function toggleFeaturesAction()
|
|
|
|
|
{
|
2021-02-12 10:37:20 -05:00
|
|
|
$commandObjects = $this->getCommandTargets();
|
2023-08-16 10:17:04 -04:00
|
|
|
$form = null;
|
2019-10-10 07:40:39 -04:00
|
|
|
if (count($commandObjects) > 1) {
|
2021-03-16 11:34:13 -04:00
|
|
|
$this->isGrantedOnCommandTargets('i/am-only-used/to-establish/the-object-auth-cache');
|
2021-02-12 10:37:20 -05:00
|
|
|
$form = new ToggleObjectFeaturesForm($this->getFeatureStatus());
|
2019-10-10 07:40:39 -04:00
|
|
|
} else {
|
|
|
|
|
foreach ($commandObjects as $object) {
|
2021-02-12 10:37:20 -05:00
|
|
|
// There's only a single result, a foreach is the most compatible way to retrieve the object
|
|
|
|
|
$form = new ToggleObjectFeaturesForm($object);
|
2019-10-10 07:40:39 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->handleCommandForm($form);
|
|
|
|
|
}
|
|
|
|
|
}
|