Files
Leantime/app/Domain/Blueprints/Controllers/DelCanvasItem.php

116 lines
4.6 KiB
PHP

<?php
declare(strict_types=1);
namespace Leantime\Domain\Blueprints\Controllers;
use Leantime\Core\Auth\Permissions\RequiresPermission;
use Leantime\Core\Controller\Frontcontroller;
use Leantime\Core\Http\IncomingRequest;
use Leantime\Core\Language;
use Leantime\Core\UI\Template;
use Leantime\Domain\Blueprints\Models\CanvasTemplate;
use Leantime\Domain\Blueprints\Permissions\BlueprintsPermissions;
use Leantime\Domain\Blueprints\Services\Blueprints as BlueprintsService;
use Leantime\Domain\Blueprints\Services\TemplateRegistry;
use Symfony\Component\HttpFoundation\Response;
/**
* DelCanvasItem controller - handles canvas item deletion.
*
* Native Laravel controller: route-bound actions, the {canvasSlug}/{id} path segments
* arrive via the route (canvasSlug resolved in the constructor, id as a typed action arg),
* and request input is read from the injected IncomingRequest instead of the legacy
* merged-$params argument and superglobals.
*/
class DelCanvasItem
{
private string $canvasSlug;
private ?CanvasTemplate $template;
public function __construct(
private IncomingRequest $request,
private Template $tpl,
private Language $language,
private BlueprintsService $blueprintsService,
TemplateRegistry $templateRegistry,
) {
$this->canvasSlug = strip_tags((string) ($request->route('canvasSlug') ?? ''));
$this->template = $templateRegistry->get($this->canvasSlug);
}
/**
* get - display the delete confirmation dialog for a canvas item.
*
* @param string|null $canvasSlug Canvas type slug from the route (resolved in the constructor)
* @param string|null $id Canvas item id from the route
*/
#[RequiresPermission(BlueprintsPermissions::DELETE)]
public function get(?string $canvasSlug = null, ?string $id = null): Response
{
if ($this->template === null) {
return $this->tpl->displayPartial('errors.error404');
}
// The route id is optional in the pattern but mandatory in practice: every caller
// links with a concrete id. Validate strictly rather than sanitising — FILTER_SANITIZE_NUMBER_INT
// lets "1-2" through, which a later (int) cast would silently read as 1 and act on the
// wrong record. Fail closed instead.
$canvasId = filter_var($id, FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);
if ($canvasId === false) {
return $this->tpl->displayPartial('errors.error404');
}
$this->tpl->assign('canvasSlug', $this->canvasSlug);
$this->tpl->assign('id', $canvasId);
return $this->tpl->displayPartial('blueprints.delCanvasItem');
}
/**
* post - delete the canvas item identified by the route id.
*
* @param string|null $canvasSlug Canvas type slug from the route (resolved in the constructor)
* @param string|null $id Canvas item id from the route
*/
#[RequiresPermission(BlueprintsPermissions::DELETE, entityScoped: true)]
public function post(?string $canvasSlug = null, ?string $id = null): Response
{
if ($this->template === null) {
return $this->tpl->displayPartial('errors.error404');
}
// The route id is optional in the pattern but mandatory in practice: every caller
// links with a concrete id. Validate strictly rather than sanitising — FILTER_SANITIZE_NUMBER_INT
// lets "1-2" through, which a later (int) cast would silently read as 1 and act on the
// wrong record. Fail closed instead.
$canvasId = filter_var($id, FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);
if ($canvasId === false) {
return $this->tpl->displayPartial('errors.error404');
}
if ($this->request->has('del')) {
// The service resolves the item's REAL project and authorizes DELETE against it
// (throwing 403 for a missing/foreign item) — closing the by-id delete IDOR that
// the previous role-only Auth::authOrRedirect left open.
$this->blueprintsService->deleteCanvasItem($canvasId, $this->template->getDatabaseType());
$this->tpl->setNotification(
$this->language->__('notification.element_deleted'),
'success',
strtoupper($this->canvasSlug).'canvasitem_deleted'
);
return Frontcontroller::redirect(BASE_URL.'/blueprints/'.$this->canvasSlug.'/showCanvas');
}
$this->tpl->assign('canvasSlug', $this->canvasSlug);
$this->tpl->assign('id', $canvasId);
return $this->tpl->displayPartial('blueprints.delCanvasItem');
}
}