From 870a9c1ff6c4f98dce6256d0c0aa03cf4a458744 Mon Sep 17 00:00:00 2001 From: Stanislas Kita <7335054+stonebuzz@users.noreply.github.com> Date: Fri, 28 Aug 2026 09:22:13 +0200 Subject: [PATCH 1/4] Fix(Core): use item-scoped access check on escalation routes --- CHANGELOG.md | 1 + front/climb_group.php | 2 +- front/ticket.form.php | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a3ff56e3..05c0faf0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Fix infinite loop / Gateway Timeout when a ticket's assigned technician and assigned group are changed simultaneously, caused by a synchronous actor removal during `pre_item_update()` - Fixed group reassignment to remove previously assigned groups only when using the Escalade reassignment action +- Use item-scoped access check on escalation routes ## [2.10.6] - 2026-07-31 diff --git a/front/climb_group.php b/front/climb_group.php index 7cac026f..39a3f45e 100644 --- a/front/climb_group.php +++ b/front/climb_group.php @@ -48,7 +48,7 @@ $tickets_id = (int) $_REQUEST['tickets_id']; $ticket = new Ticket(); -if (!$ticket->getFromDB($tickets_id) || !$ticket->canAssign()) { +if (!$ticket->can($tickets_id, UPDATE)) { throw new AccessDeniedHttpException(); } diff --git a/front/ticket.form.php b/front/ticket.form.php index 43826575..82b25bb7 100644 --- a/front/ticket.form.php +++ b/front/ticket.form.php @@ -40,7 +40,7 @@ $tickets_id = (int) $_POST['tickets_id']; $ticket = new Ticket(); - if (!$ticket->getFromDB($tickets_id) || !$ticket->canAssign()) { + if (!$ticket->can($tickets_id, UPDATE)) { throw new AccessDeniedHttpException(); } From 1a83674360d507c4de1c8c241f57576057bda1db Mon Sep 17 00:00:00 2001 From: Stanislas Date: Fri, 28 Aug 2026 10:00:36 +0200 Subject: [PATCH 2/4] Update front/climb_group.php Co-authored-by: Romain B. <8530352+Rom1-B@users.noreply.github.com> --- front/climb_group.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/front/climb_group.php b/front/climb_group.php index 39a3f45e..3ba37e9c 100644 --- a/front/climb_group.php +++ b/front/climb_group.php @@ -48,7 +48,7 @@ $tickets_id = (int) $_REQUEST['tickets_id']; $ticket = new Ticket(); -if (!$ticket->can($tickets_id, UPDATE)) { +if (!$ticket->getFromDB($tickets_id) || !$ticket->checkEntity(true) || !$ticket->canAssign()) { throw new AccessDeniedHttpException(); } From 3418c9085254b9ab888daccf44505b69cc928aaf Mon Sep 17 00:00:00 2001 From: Stanislas Date: Fri, 28 Aug 2026 10:00:42 +0200 Subject: [PATCH 3/4] Update front/ticket.form.php Co-authored-by: Romain B. <8530352+Rom1-B@users.noreply.github.com> --- front/ticket.form.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/front/ticket.form.php b/front/ticket.form.php index 82b25bb7..81ff8c3e 100644 --- a/front/ticket.form.php +++ b/front/ticket.form.php @@ -40,7 +40,7 @@ $tickets_id = (int) $_POST['tickets_id']; $ticket = new Ticket(); - if (!$ticket->can($tickets_id, UPDATE)) { + if (!$ticket->getFromDB($tickets_id) || !$ticket->checkEntity(true) || !$ticket->canAssign()) { throw new AccessDeniedHttpException(); } From 8f492161ef41dc617196f4285f495158c2c15668 Mon Sep 17 00:00:00 2001 From: Stanislas Kita <7335054+stonebuzz@users.noreply.github.com> Date: Fri, 28 Aug 2026 14:37:38 +0200 Subject: [PATCH 4/4] review --- front/climb_group.php | 3 +- front/ticket.form.php | 3 +- tests/Units/EscalationAccessTest.php | 83 ++++++++++++++++++++++++++++ 3 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 tests/Units/EscalationAccessTest.php diff --git a/front/climb_group.php b/front/climb_group.php index 3ba37e9c..5d1cf00e 100644 --- a/front/climb_group.php +++ b/front/climb_group.php @@ -48,7 +48,8 @@ $tickets_id = (int) $_REQUEST['tickets_id']; $ticket = new Ticket(); -if (!$ticket->getFromDB($tickets_id) || !$ticket->checkEntity(true) || !$ticket->canAssign()) { + +if (!$ticket->getFromDB($tickets_id) || !$ticket->canAssign() || !$ticket->checkEntity(true)) { throw new AccessDeniedHttpException(); } diff --git a/front/ticket.form.php b/front/ticket.form.php index 81ff8c3e..b4d53dda 100644 --- a/front/ticket.form.php +++ b/front/ticket.form.php @@ -40,7 +40,8 @@ $tickets_id = (int) $_POST['tickets_id']; $ticket = new Ticket(); - if (!$ticket->getFromDB($tickets_id) || !$ticket->checkEntity(true) || !$ticket->canAssign()) { + + if (!$ticket->getFromDB($tickets_id) || !$ticket->canAssign() || !$ticket->checkEntity(true)) { throw new AccessDeniedHttpException(); } diff --git a/tests/Units/EscalationAccessTest.php b/tests/Units/EscalationAccessTest.php new file mode 100644 index 00000000..d01b907e --- /dev/null +++ b/tests/Units/EscalationAccessTest.php @@ -0,0 +1,83 @@ +. + * ------------------------------------------------------------------------- + * @copyright Copyright (C) 2015-2023 by Escalade plugin team. + * @license GPLv2 https://www.gnu.org/licenses/gpl-2.0.html + * @link https://github.com/pluginsGLPI/escalade + * ------------------------------------------------------------------------- + */ + +namespace GlpiPlugin\Escalade\Tests\Units; + +use GlpiPlugin\Escalade\Tests\EscaladeTestCase; +use ProfileRight; +use Ticket; + +final class EscalationAccessTest extends EscaladeTestCase +{ + private function setTechnicianTicketRight(int $right): void + { + ProfileRight::updateProfileRights( + getItemByTypeName('Profile', 'Technician', true), + ['ticket' => $right], + ); + } + + private function loadTicket(int $tickets_id): Ticket + { + $ticket = new Ticket(); + $ticket->getFromDB($tickets_id); + return $ticket; + } + + public function testAssignOnlyUserCanReachEscalationRoutes(): void + { + $this->initConfig(); + $tickets_id = $this->createItem(Ticket::class, ['name' => 'Escalation access test', 'content' => ''])->getID(); + + $this->setTechnicianTicketRight(Ticket::ASSIGN); + $this->login('tech', 'tech'); + + $ticket = $this->loadTicket($tickets_id); + $this->assertTrue( + $ticket->canAssign() && $ticket->checkEntity(), + 'A user with only the ASSIGN right must be allowed to reach escalation routes', + ); + } + + public function testUpdateOnlyUserCannotReachEscalationRoutes(): void + { + $this->initConfig(); + $tickets_id = $this->createItem(Ticket::class, ['name' => 'Escalation access test', 'content' => ''])->getID(); + + $this->setTechnicianTicketRight(UPDATE); + $this->login('tech', 'tech'); + + $ticket = $this->loadTicket($tickets_id); + $this->assertFalse( + (bool) $ticket->canAssign(), + 'A user with only the UPDATE right must not be allowed to reach escalation routes', + ); + } +}