diff --git a/api/ApplicationContext.php b/api/ApplicationContext.php index 5ec0939f..a9107a74 100644 --- a/api/ApplicationContext.php +++ b/api/ApplicationContext.php @@ -10,6 +10,7 @@ use BusinessLogic\Emails\EmailTemplateRetriever; use BusinessLogic\Emails\MailgunEmailSender; use BusinessLogic\Security\BanRetriever; use BusinessLogic\Security\UserContextBuilder; +use BusinessLogic\Security\UserToTicketChecker; use BusinessLogic\Settings\ApiChecker; use BusinessLogic\Tickets\Autoassigner; use BusinessLogic\Tickets\TicketRetriever; @@ -96,6 +97,7 @@ class ApplicationContext { $this->get[ModsForHeskSettingsGateway::class]); // Attachments + $this->get[UserToTicketChecker::class] = new UserToTicketChecker($this->get[UserGateway::class]); $this->get[FileWriter::class] = new FileWriter(); $this->get[AttachmentGateway::class] = new AttachmentGateway(); $this->get[AttachmentHandler::class] = new AttachmentHandler($this->get[TicketGateway::class], diff --git a/api/BusinessLogic/Attachments/AttachmentHandler.php b/api/BusinessLogic/Attachments/AttachmentHandler.php index a05291d4..f11d98f6 100644 --- a/api/BusinessLogic/Attachments/AttachmentHandler.php +++ b/api/BusinessLogic/Attachments/AttachmentHandler.php @@ -4,6 +4,8 @@ namespace BusinessLogic\Attachments; use BusinessLogic\Exceptions\ValidationException; +use BusinessLogic\Security\UserContext; +use BusinessLogic\Security\UserToTicketChecker; use BusinessLogic\Tickets\Attachment; use BusinessLogic\Tickets\Ticket; use BusinessLogic\ValidationModel; @@ -21,24 +23,35 @@ class AttachmentHandler { /* @var $fileWriter FileWriter */ private $fileWriter; - function __construct($ticketGateway, $attachmentGateway, $fileWriter) { + /* @var $userToTicketChecker UserToTicketChecker */ + private $userToTicketChecker; + + function __construct($ticketGateway, $attachmentGateway, $fileWriter, $userToTicketChecker) { $this->ticketGateway = $ticketGateway; $this->attachmentGateway = $attachmentGateway; $this->fileWriter = $fileWriter; + $this->userToTicketChecker = $userToTicketChecker; } /** * @param $createAttachmentModel CreateAttachmentForTicketModel + * @param $userContext UserContext * @param $heskSettings array * @return TicketAttachment the newly created attachment + * @throws \Exception */ - function createAttachmentForTicket($createAttachmentModel, $heskSettings) { + function createAttachmentForTicket($createAttachmentModel, $userContext, $heskSettings) { $this->validate($createAttachmentModel, $heskSettings); $decodedAttachment = base64_decode($createAttachmentModel->attachmentContents); $ticket = $this->ticketGateway->getTicketById($createAttachmentModel->ticketId, $heskSettings); + + if (!$this->userToTicketChecker->isTicketWritableToUser($userContext, $ticket, $createAttachmentModel->isEditing, $heskSettings)) { + throw new \Exception("User does not have access to ticket {$ticket->id} being created / edited!"); + } + $cleanedFileName = $this->cleanFileName($createAttachmentModel->displayName); $fileParts = pathinfo($cleanedFileName); diff --git a/api/BusinessLogic/Attachments/CreateAttachmentModel.php b/api/BusinessLogic/Attachments/CreateAttachmentModel.php index 9a5f5428..14179a44 100644 --- a/api/BusinessLogic/Attachments/CreateAttachmentModel.php +++ b/api/BusinessLogic/Attachments/CreateAttachmentModel.php @@ -15,4 +15,7 @@ class CreateAttachmentModel { /* @var $attachmentContents string */ public $attachmentContents; + + /* @var $isEditing bool */ + public $isEditing; } \ No newline at end of file diff --git a/api/Controllers/Attachments/StaffTicketAttachmentsController.php b/api/Controllers/Attachments/StaffTicketAttachmentsController.php index 47f265ed..caa8b8ed 100644 --- a/api/Controllers/Attachments/StaffTicketAttachmentsController.php +++ b/api/Controllers/Attachments/StaffTicketAttachmentsController.php @@ -7,6 +7,7 @@ use BusinessLogic\Attachments\AttachmentHandler; use BusinessLogic\Attachments\CreateAttachmentForTicketModel; use BusinessLogic\Exceptions\ApiFriendlyException; use BusinessLogic\Helpers; +use BusinessLogic\Security\UserToTicketChecker; use Controllers\JsonRetriever; class StaffTicketAttachmentsController { @@ -23,7 +24,7 @@ class StaffTicketAttachmentsController { } function post($ticketId) { - global $hesk_settings, $applicationContext; + global $hesk_settings, $applicationContext, $userContext; $this->verifyAttachmentsAreEnabled($hesk_settings); diff --git a/api/Tests/BusinessLogic/Attachments/AttachmentHandlerTest.php b/api/Tests/BusinessLogic/Attachments/AttachmentHandlerTest.php index a67bd7e6..f29940b6 100644 --- a/api/Tests/BusinessLogic/Attachments/AttachmentHandlerTest.php +++ b/api/Tests/BusinessLogic/Attachments/AttachmentHandlerTest.php @@ -5,6 +5,8 @@ namespace BusinessLogic\Attachments; use BusinessLogic\Exceptions\ValidationException; +use BusinessLogic\Security\UserContext; +use BusinessLogic\Security\UserToTicketChecker; use BusinessLogic\Tickets\Ticket; use DataAccess\Attachments\AttachmentGateway; use DataAccess\Files\FileWriter; @@ -25,9 +27,15 @@ class AttachmentHandlerTest extends TestCase { /* @var $attachmentGateway \PHPUnit_Framework_MockObject_MockObject */ private $attachmentGateway; + /* @var $userToTicketChecker \PHPUnit_Framework_MockObject_MockObject */ + private $userToTicketChecker; + /* @var $fileWriter \PHPUnit_Framework_MockObject_MockObject */ private $fileWriter; + /* @var $userContext UserContext */ + private $userContext; + /* @var $heskSettings array */ private $heskSettings; @@ -35,6 +43,8 @@ class AttachmentHandlerTest extends TestCase { $this->ticketGateway = $this->createMock(TicketGateway::class); $this->attachmentGateway = $this->createMock(AttachmentGateway::class); $this->fileWriter = $this->createMock(FileWriter::class); + $this->userToTicketChecker = $this->createMock(UserToTicketChecker::class); + $this->userToTicketChecker->method('isTicketWritableToUser')->willReturn(true); $this->heskSettings = array( 'attach_dir' => 'attachments', 'attachments' => array( @@ -43,12 +53,16 @@ class AttachmentHandlerTest extends TestCase { ) ); - $this->attachmentHandler = new AttachmentHandler($this->ticketGateway, $this->attachmentGateway, $this->fileWriter); + $this->attachmentHandler = new AttachmentHandler($this->ticketGateway, + $this->attachmentGateway, + $this->fileWriter, + $this->userToTicketChecker); $this->createAttachmentForTicketModel = new CreateAttachmentForTicketModel(); $this->createAttachmentForTicketModel->attachmentContents = base64_encode('string'); $this->createAttachmentForTicketModel->displayName = 'DisplayName.txt'; $this->createAttachmentForTicketModel->ticketId = 1; $this->createAttachmentForTicketModel->type = AttachmentType::MESSAGE; + $this->userContext = new UserContext(); } function testThatValidateThrowsAnExceptionWhenTheAttachmentBodyIsNull() { @@ -60,7 +74,7 @@ class AttachmentHandlerTest extends TestCase { $this->expectExceptionMessageRegExp('/CONTENTS_EMPTY/'); //-- Act - $this->attachmentHandler->createAttachmentForTicket($this->createAttachmentForTicketModel, $this->heskSettings); + $this->attachmentHandler->createAttachmentForTicket($this->createAttachmentForTicketModel, $this->userContext, $this->heskSettings); } function testThatValidateThrowsAnExceptionWhenTheAttachmentBodyIsEmpty() { @@ -72,7 +86,7 @@ class AttachmentHandlerTest extends TestCase { $this->expectExceptionMessageRegExp('/CONTENTS_EMPTY/'); //-- Act - $this->attachmentHandler->createAttachmentForTicket($this->createAttachmentForTicketModel, $this->heskSettings); + $this->attachmentHandler->createAttachmentForTicket($this->createAttachmentForTicketModel, $this->userContext, $this->heskSettings); } function testThatValidateThrowsAnExceptionWhenTheAttachmentBodyIsInvalidBase64() { @@ -84,7 +98,7 @@ class AttachmentHandlerTest extends TestCase { $this->expectExceptionMessageRegExp('/CONTENTS_NOT_BASE_64/'); //-- Act - $this->attachmentHandler->createAttachmentForTicket($this->createAttachmentForTicketModel, $this->heskSettings); + $this->attachmentHandler->createAttachmentForTicket($this->createAttachmentForTicketModel, $this->userContext, $this->heskSettings); } function testThatValidateThrowsAnExceptionWhenTheDisplayNameIsNull() { @@ -96,7 +110,7 @@ class AttachmentHandlerTest extends TestCase { $this->expectExceptionMessageRegExp('/DISPLAY_NAME_EMPTY/'); //-- Act - $this->attachmentHandler->createAttachmentForTicket($this->createAttachmentForTicketModel, $this->heskSettings); + $this->attachmentHandler->createAttachmentForTicket($this->createAttachmentForTicketModel, $this->userContext, $this->heskSettings); } function testThatValidateThrowsAnExceptionWhenTheDisplayNameIsEmpty() { @@ -108,7 +122,7 @@ class AttachmentHandlerTest extends TestCase { $this->expectExceptionMessageRegExp('/DISPLAY_NAME_EMPTY/'); //-- Act - $this->attachmentHandler->createAttachmentForTicket($this->createAttachmentForTicketModel, $this->heskSettings); + $this->attachmentHandler->createAttachmentForTicket($this->createAttachmentForTicketModel, $this->userContext, $this->heskSettings); } function testThatValidateThrowsAnExceptionWhenTheTicketIdIsNull() { @@ -120,7 +134,7 @@ class AttachmentHandlerTest extends TestCase { $this->expectExceptionMessageRegExp('/TICKET_ID_MISSING/'); //-- Act - $this->attachmentHandler->createAttachmentForTicket($this->createAttachmentForTicketModel, $this->heskSettings); + $this->attachmentHandler->createAttachmentForTicket($this->createAttachmentForTicketModel, $this->userContext, $this->heskSettings); } function testThatValidateThrowsAnExceptionWhenTheTicketIdIsANonPositiveInteger() { @@ -132,7 +146,7 @@ class AttachmentHandlerTest extends TestCase { $this->expectExceptionMessageRegExp('/TICKET_ID_MISSING/'); //-- Act - $this->attachmentHandler->createAttachmentForTicket($this->createAttachmentForTicketModel, $this->heskSettings); + $this->attachmentHandler->createAttachmentForTicket($this->createAttachmentForTicketModel, $this->userContext, $this->heskSettings); } function testThatValidateThrowsAnExceptionWhenTheFileExtensionIsNotPermitted() { @@ -145,7 +159,7 @@ class AttachmentHandlerTest extends TestCase { $this->expectExceptionMessageRegExp('/EXTENSION_NOT_PERMITTED/'); //-- Act - $this->attachmentHandler->createAttachmentForTicket($this->createAttachmentForTicketModel, $this->heskSettings); + $this->attachmentHandler->createAttachmentForTicket($this->createAttachmentForTicketModel, $this->userContext, $this->heskSettings); } function testThatValidateThrowsAnExceptionWhenTheFileSizeIsLargerThanMaxPermitted() { @@ -158,7 +172,7 @@ class AttachmentHandlerTest extends TestCase { $this->expectExceptionMessageRegExp('/FILE_SIZE_TOO_LARGE/'); //-- Act - $this->attachmentHandler->createAttachmentForTicket($this->createAttachmentForTicketModel, $this->heskSettings); + $this->attachmentHandler->createAttachmentForTicket($this->createAttachmentForTicketModel, $this->userContext, $this->heskSettings); } function testItSavesATicketWithTheProperProperties() { @@ -179,7 +193,7 @@ class AttachmentHandlerTest extends TestCase { //-- Act - $actual = $this->attachmentHandler->createAttachmentForTicket($this->createAttachmentForTicketModel, $this->heskSettings); + $actual = $this->attachmentHandler->createAttachmentForTicket($this->createAttachmentForTicketModel, $this->userContext, $this->heskSettings); //-- Assert self::assertThat($actual->id, self::equalTo(50)); @@ -208,9 +222,11 @@ class AttachmentHandlerTest extends TestCase { //-- Act - $actual = $this->attachmentHandler->createAttachmentForTicket($this->createAttachmentForTicketModel, $this->heskSettings); + $actual = $this->attachmentHandler->createAttachmentForTicket($this->createAttachmentForTicketModel, $this->userContext, $this->heskSettings); //-- Assert self::assertThat($actual->fileSize, self::equalTo(1024)); } + + //-- TODO Test UserToTicketChecker }