diff --git a/CHANGELOG.md b/CHANGELOG.md index 11401f9e..15a3f502 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## [Unreleased] + +### Fixed + +- Fix the escalation form updating the ticket referenced by the submitted form data instead of the escalated ticket +- Fix the escalation form applying the submitted ticket fields for users who are not allowed to update the ticket + ## [2.9.21] - 2026-09-07 ### Fixed diff --git a/front/ticket.form.php b/front/ticket.form.php index bbe4dcdc..6b97a90e 100644 --- a/front/ticket.form.php +++ b/front/ticket.form.php @@ -48,6 +48,11 @@ Html::displayRightError(); } + // The submitted form details are only applied when the user may update the ticket itself + if (!$ticket->canUpdateItem()) { + unset($_POST['ticket_details']); + } + PluginEscaladeTicket::timelineClimbAction($group_id, $tickets_id, $_POST); $track = new Ticket(); diff --git a/inc/ticket.class.php b/inc/ticket.class.php index 979627b4..9bbf3fca 100644 --- a/inc/ticket.class.php +++ b/inc/ticket.class.php @@ -1408,13 +1408,17 @@ public static function timelineClimbAction(int $group_id, int $tickets_id, array $_form_object['status'] = $_SESSION['glpi_plugins']['escalade']['config']['ticket_last_status']; } - $update_data = $options['ticket_details'] + [ + // The updated ticket must be the authorized one, never the one named by the submitted details + unset($options['ticket_details']['id']); + + $update_data = [ + 'id' => $tickets_id, '_actors' => PluginEscaladeTicket::getTicketFieldsWithActors($tickets_id, $group_id), '_plugin_escalade_no_history' => true, // Prevent a duplicated task to be added 'actortype' => CommonITILActor::ASSIGN, 'groups_id' => $group_id, '_form_object' => $_form_object, - ]; + ] + $options['ticket_details']; // Preserve existing tags self::preserveExistingTags($tickets_id, $update_data); diff --git a/tests/EscaladeTestCase.php b/tests/EscaladeTestCase.php index 92d10218..97ee1e89 100644 --- a/tests/EscaladeTestCase.php +++ b/tests/EscaladeTestCase.php @@ -33,10 +33,16 @@ use Auth; use Session; use DbTestCase; +use Group; use PluginEscaladeConfig; abstract class EscaladeTestCase extends DbTestCase { + public function createGroup(string $group_name = 'TestGroup'): Group + { + return $this->createItem(Group::class, ['name' => $group_name]); + } + protected function login( string $user_name = TU_USER, string $user_pass = TU_PASS, diff --git a/tests/Units/EscalationAccessTest.php b/tests/Units/EscalationAccessTest.php index d01b907e..7e725831 100644 --- a/tests/Units/EscalationAccessTest.php +++ b/tests/Units/EscalationAccessTest.php @@ -30,7 +30,10 @@ namespace GlpiPlugin\Escalade\Tests\Units; +use CommonITILActor; use GlpiPlugin\Escalade\Tests\EscaladeTestCase; +use Group_Ticket; +use PluginEscaladeTicket; use ProfileRight; use Ticket; @@ -66,6 +69,55 @@ public function testAssignOnlyUserCanReachEscalationRoutes(): void ); } + public function testSubmittedTicketDetailsCannotRetargetAnotherTicket(): void + { + $this->initConfig(); + $escalated_ticket = $this->createItem(Ticket::class, ['name' => 'Escalated ticket', 'content' => '']); + $other_ticket = $this->createItem(Ticket::class, ['name' => 'Other ticket', 'content' => '']); + $group = $this->createGroup('RetargetTestGroup'); + + $_POST['comment'] = 'Escalation comment'; + PluginEscaladeTicket::timelineClimbAction( + $group->getID(), + $escalated_ticket->getID(), + [ + 'ticket_details' => [ + 'id' => $other_ticket->getID(), + 'name' => 'Renamed by the submitted details', + ], + ], + ); + + $this->assertEquals('Other ticket', $this->loadTicket($other_ticket->getID())->fields['name']); + $this->assertEquals( + 'Renamed by the submitted details', + $this->loadTicket($escalated_ticket->getID())->fields['name'], + ); + + $group_ticket = new Group_Ticket(); + $this->assertTrue($group_ticket->getFromDBByCrit([ + 'tickets_id' => $escalated_ticket->getID(), + 'groups_id' => $group->getID(), + 'type' => CommonITILActor::ASSIGN, + ])); + + unset($_POST['comment']); + } + + public function testAssignOnlyUserCannotApplySubmittedTicketDetails(): void + { + $this->initConfig(); + $tickets_id = $this->createItem(Ticket::class, ['name' => 'Escalation access test', 'content' => ''])->getID(); + + $this->setTechnicianTicketRight(Ticket::ASSIGN); + $this->login('tech', 'tech'); + + $this->assertFalse( + $this->loadTicket($tickets_id)->canUpdateItem(), + 'A user with only the ASSIGN right must not have the submitted details applied to the ticket', + ); + } + public function testUpdateOnlyUserCannotReachEscalationRoutes(): void { $this->initConfig();