diff --git a/api/BusinessLogic/Categories/Category.php b/api/BusinessLogic/Categories/Category.php index 5b975b61..5711f33d 100644 --- a/api/BusinessLogic/Categories/Category.php +++ b/api/BusinessLogic/Categories/Category.php @@ -1,6 +1,6 @@ categoryRetriever = $categoryRetriever; $this->banRetriever = $banRetriever; } @@ -20,8 +30,8 @@ class TicketCreator { * @param $modsForHeskSettings array Mods for HESK settings * @throws ValidationException When a required field in $ticket_request is missing */ - function createTicketByCustomer($ticketRequest, $heskSettings, $modsForHeskSettings) { - $validationModel = $this->validate($ticketRequest, false, $heskSettings, $modsForHeskSettings); + function createTicketByCustomer($ticketRequest, $heskSettings, $modsForHeskSettings, $userContext) { + $validationModel = $this->validate($ticketRequest, false, $heskSettings, $modsForHeskSettings, $userContext); if (count($validationModel->errorKeys) > 0) { // Validation failed @@ -39,7 +49,7 @@ class TicketCreator { * @param $modsForHeskSettings array Mods for HESK settings * @return ValidationModel If errorKeys is empty, validation successful. Otherwise invalid ticket */ - function validate($ticketRequest, $staff, $heskSettings, $modsForHeskSettings) { + function validate($ticketRequest, $staff, $heskSettings, $modsForHeskSettings, $userContext) { $TICKET_PRIORITY_CRITICAL = 0; $validationModel = new ValidationModel(); @@ -48,17 +58,22 @@ class TicketCreator { $validationModel->errorKeys[] = 'NO_NAME'; } - /*if (hesk_validateEmail($ticketRequest->email, $heskSettings['multi_eml'], false)) { + if (!Validators::validateEmail($ticketRequest->email, $heskSettings['multi_eml'], false)) { $validationModel->errorKeys[] = 'INVALID_OR_MISSING_EMAIL'; } - if (intval($ticketRequest->category) === 0) { - $allCategories = null; + $categoryId = intval($ticketRequest->category); + if ($categoryId < 1) { $validationModel->errorKeys[] = 'NO_CATEGORY'; + } else { + $categoryExists = array_key_exists($categoryId, $this->categoryRetriever->getAllCategories($heskSettings, $userContext)); + if (!$categoryExists) { + $validationModel->errorKeys[] = 'CATEGORY_DOES_NOT_EXIST'; + } } // Don't allow critical priority tickets - if ($heskSettings['cust_urgency'] && intval($ticketRequest->priority) === $TICKET_PRIORITY_CRITICAL) { + /*if ($heskSettings['cust_urgency'] && intval($ticketRequest->priority) === $TICKET_PRIORITY_CRITICAL) { $validationModel->errorKeys[] = 'CRITICAL_PRIORITY_FORBIDDEN'; } diff --git a/api/BusinessLogic/Validators.php b/api/BusinessLogic/Validators.php new file mode 100644 index 00000000..7bfea07f --- /dev/null +++ b/api/BusinessLogic/Validators.php @@ -0,0 +1,129 @@ + $v) { + if (!self::isValidEmail($v)) { + unset($all[$k]); + } + } + + /* If at least one is found return the value */ + if (count($all)) { + if ($return_emails) { + return implode(',', $all); + } + + return true; + } elseif (!$return_emails) { + return false; + } + } else { + /* Make sure people don't try to enter multiple addresses */ + $address = str_replace(strstr($address, ','), '', $address); + $address = str_replace(strstr($address, ';'), '', $address); + $address = trim($address); + + /* Valid address? */ + if (self::isValidEmail($address)) { + if ($return_emails) { + return $address; + } + + return true; + } else { + return false; + } + } + + //-- We shouldn't get here + return false; + } // END hesk_validateEmail() + + /** + * @param $email + * @return bool + */ + static function isValidEmail($email) { + /* Check for header injection attempts */ + if (preg_match("/\r|\n|%0a|%0d/i", $email)) { + return false; + } + + /* Does it contain an @? */ + $atIndex = strrpos($email, "@"); + if ($atIndex === false) { + return false; + } + + /* Get local and domain parts */ + $domain = substr($email, $atIndex + 1); + $local = substr($email, 0, $atIndex); + $localLen = strlen($local); + $domainLen = strlen($domain); + + /* Check local part length */ + if ($localLen < 1 || $localLen > 64) { + return false; + } + + /* Check domain part length */ + if ($domainLen < 1 || $domainLen > 254) { + return false; + } + + /* Local part mustn't start or end with a dot */ + if ($local[0] == '.' || $local[$localLen - 1] == '.') { + return false; + } + + /* Local part mustn't have two consecutive dots*/ + if (strpos($local, '..') !== false) { + return false; + } + + /* Check domain part characters */ + if (!preg_match('/^[A-Za-z0-9\\-\\.]+$/', $domain)) { + return false; + } + + /* Domain part mustn't have two consecutive dots */ + if (strpos($domain, '..') !== false) { + return false; + } + + /* Character not valid in local part unless local part is quoted */ + if (!preg_match('/^(\\\\.|[A-Za-z0-9!#%&`_=\\/$\'*+?^{}|~.-])+$/', str_replace("\\\\", "", $local))) /* " */ { + if (!preg_match('/^"(\\\\"|[^"])+"$/', str_replace("\\\\", "", $local))) /* " */ { + return false; + } + } + + /* All tests passed, email seems to be OK */ + return true; + } // END hesk_isValidEmail() +} \ No newline at end of file diff --git a/api/DataAccess/Categories/CategoryGateway.php b/api/DataAccess/Categories/CategoryGateway.php index d12a7e92..68f605e4 100644 --- a/api/DataAccess/Categories/CategoryGateway.php +++ b/api/DataAccess/Categories/CategoryGateway.php @@ -2,7 +2,7 @@ namespace DataAccess\Categories; -use BusinessObjects\Category; +use BusinessLogic\Categories\Category; use DataAccess\CommonDao; use Exception; diff --git a/api/Tests/BusinessLogic/Tickets/TicketCreatorTest.php b/api/Tests/BusinessLogic/Tickets/TicketCreatorTest.php index 91cbb847..d337a23e 100644 --- a/api/Tests/BusinessLogic/Tickets/TicketCreatorTest.php +++ b/api/Tests/BusinessLogic/Tickets/TicketCreatorTest.php @@ -9,8 +9,11 @@ namespace BusinessLogic\Tickets; +use BusinessLogic\Categories\Category; +use BusinessLogic\Categories\CategoryRetriever; use BusinessLogic\Exceptions\ValidationException; use BusinessLogic\Security\BanRetriever; +use BusinessLogic\Security\UserContext; use PHPUnit\Framework\TestCase; class TicketCreatorTest extends TestCase { @@ -20,24 +23,49 @@ class TicketCreatorTest extends TestCase { private $ticketCreator; /** - * @var $banRetriever BanRetriever + * @var $banRetriever \PHPUnit_Framework_MockObject_MockObject */ private $banRetriever; + /** + * @var $categoryRetriever \PHPUnit_Framework_MockObject_MockObject + */ + private $categoryRetriever; + /** * @var $ticketRequest CreateTicketByCustomerModel */ private $ticketRequest; + /** + * @var $userContext UserContext + */ + private $userContext; + private $heskSettings = array(); private $modsForHeskSettings = array(); function setUp() { $this->banRetriever = $this->createMock(BanRetriever::class); - $this->ticketCreator = new TicketCreator($this->banRetriever); + $this->categoryRetriever = $this->createMock(CategoryRetriever::class); + $this->ticketCreator = new TicketCreator($this->categoryRetriever, $this->banRetriever); + $this->userContext = new UserContext(); $this->ticketRequest = new CreateTicketByCustomerModel(); $this->ticketRequest->name = 'Name'; + $this->ticketRequest->email = 'some@e.mail'; + $this->ticketRequest->category = 1; + $this->heskSettings = array( + 'multi_eml' => false + ); + + $category = new Category(); + $category->accessible = true; + $category->id = 1; + $categories = array(); + $categories[1] = $category; + $this->categoryRetriever->method('getAllCategories') + ->willReturn($categories); } function testItAddsTheProperValidationErrorWhenNameIsNull() { @@ -47,7 +75,10 @@ class TicketCreatorTest extends TestCase { //-- Act $exceptionThrown = false; try { - $this->ticketCreator->createTicketByCustomer($this->ticketRequest, $this->heskSettings, $this->modsForHeskSettings); + $this->ticketCreator->createTicketByCustomer($this->ticketRequest, + $this->heskSettings, + $this->modsForHeskSettings, + $this->userContext); } catch (ValidationException $e) { //-- Assert (1/2) $exceptionThrown = true; @@ -65,7 +96,10 @@ class TicketCreatorTest extends TestCase { //-- Act $exceptionThrown = false; try { - $this->ticketCreator->createTicketByCustomer($this->ticketRequest, $this->heskSettings, $this->modsForHeskSettings); + $this->ticketCreator->createTicketByCustomer($this->ticketRequest, + $this->heskSettings, + $this->modsForHeskSettings, + $this->userContext); } catch (ValidationException $e) { //-- Assert (1/2) $exceptionThrown = true; @@ -75,4 +109,151 @@ class TicketCreatorTest extends TestCase { //-- Assert (2/2) $this->assertThat($exceptionThrown, $this->equalTo(true)); } + + function testItAddsTheProperValidationErrorWhenEmailIsNull() { + //-- Arrange + $this->ticketRequest->email = null; + + //-- Act + $exceptionThrown = false; + try { + $this->ticketCreator->createTicketByCustomer($this->ticketRequest, + $this->heskSettings, + $this->modsForHeskSettings, + $this->userContext); + } catch (ValidationException $e) { + //-- Assert (1/2) + $exceptionThrown = true; + $this->assertArraySubset(['INVALID_OR_MISSING_EMAIL'], $e->validationModel->errorKeys); + } + + //-- Assert (2/2) + $this->assertThat($exceptionThrown, $this->equalTo(true)); + } + + function testItAddsTheProperValidationErrorWhenEmailIsBlank() { + //-- Arrange + $this->ticketRequest->email = ''; + + //-- Act + $exceptionThrown = false; + try { + $this->ticketCreator->createTicketByCustomer($this->ticketRequest, + $this->heskSettings, + $this->modsForHeskSettings, + $this->userContext); + } catch (ValidationException $e) { + //-- Assert (1/2) + $exceptionThrown = true; + $this->assertArraySubset(['INVALID_OR_MISSING_EMAIL'], $e->validationModel->errorKeys); + } + + //-- Assert (2/2) + $this->assertThat($exceptionThrown, $this->equalTo(true)); + } + + function testItAddsTheProperValidationErrorWhenEmailIsInvalid() { + //-- Arrange + $this->ticketRequest->email = 'something@'; + + //-- Act + $exceptionThrown = false; + try { + $this->ticketCreator->createTicketByCustomer($this->ticketRequest, + $this->heskSettings, + $this->modsForHeskSettings, + $this->userContext); + } catch (ValidationException $e) { + //-- Assert (1/2) + $exceptionThrown = true; + $this->assertArraySubset(['INVALID_OR_MISSING_EMAIL'], $e->validationModel->errorKeys); + } + + //-- Assert (2/2) + $this->assertThat($exceptionThrown, $this->equalTo(true)); + } + + function testItSupportsMultipleEmails() { + //-- Arrange + $this->ticketRequest->email = 'something@email.com;another@valid.email'; + $this->heskSettings['multi_eml'] = true; + + //-- Act + $exceptionThrown = false; + try { + $this->ticketCreator->createTicketByCustomer($this->ticketRequest, + $this->heskSettings, + $this->modsForHeskSettings, + $this->userContext); + } catch (ValidationException $e) { + var_dump($e->validationModel->errorKeys); + $this->fail('Should not have thrown a ValidationException! Validation error keys are above.'); + } + + //-- Assert (2/2) + $this->assertThat($exceptionThrown, $this->equalTo(false)); + } + + function testItAddsTheProperValidationErrorWhenCategoryIsNotANumber() { + //-- Arrange + $this->ticketRequest->category = 'something'; + + //-- Act + $exceptionThrown = false; + try { + $this->ticketCreator->createTicketByCustomer($this->ticketRequest, + $this->heskSettings, + $this->modsForHeskSettings, + $this->userContext); + } catch (ValidationException $e) { + //-- Assert (1/2) + $exceptionThrown = true; + $this->assertArraySubset(['NO_CATEGORY'], $e->validationModel->errorKeys); + } + + //-- Assert (2/2) + $this->assertThat($exceptionThrown, $this->equalTo(true)); + } + + function testItAddsTheProperValidationErrorWhenCategoryIsNegative() { + //-- Arrange + $this->ticketRequest->category = -5; + + //-- Act + $exceptionThrown = false; + try { + $this->ticketCreator->createTicketByCustomer($this->ticketRequest, + $this->heskSettings, + $this->modsForHeskSettings, + $this->userContext); + } catch (ValidationException $e) { + //-- Assert (1/2) + $exceptionThrown = true; + $this->assertArraySubset(['NO_CATEGORY'], $e->validationModel->errorKeys); + } + + //-- Assert (2/2) + $this->assertThat($exceptionThrown, $this->equalTo(true)); + } + + function testItAddsTheProperValidationErrorWhenTheCategoryDoesNotExist() { + //-- Arrange + $this->ticketRequest->category = 10; + + //-- Act + $exceptionThrown = false; + try { + $this->ticketCreator->createTicketByCustomer($this->ticketRequest, + $this->heskSettings, + $this->modsForHeskSettings, + $this->userContext); + } catch (ValidationException $e) { + //-- Assert (1/2) + $exceptionThrown = true; + $this->assertArraySubset(['CATEGORY_DOES_NOT_EXIST'], $e->validationModel->errorKeys); + } + + //-- Assert (2/2) + $this->assertThat($exceptionThrown, $this->equalTo(true)); + } } diff --git a/api/businesslogic/email_validators.php b/api/businesslogic/email_validators.php deleted file mode 100644 index 1b69d289..00000000 --- a/api/businesslogic/email_validators.php +++ /dev/null @@ -1,119 +0,0 @@ - $v) { - if (!hesk_isValidEmail($v)) { - unset($all[$k]); - } - } - - /* If at least one is found return the value */ - if (count($all)) { - if ($return_emails) { - return implode(',', $all); - } - - return true; - } elseif (!$return_emails) { - return false; - } - } else { - /* Make sure people don't try to enter multiple addresses */ - $address = str_replace(strstr($address, ','), '', $address); - $address = str_replace(strstr($address, ';'), '', $address); - $address = trim($address); - - /* Valid address? */ - if (hesk_isValidEmail($address)) { - if ($return_emails) { - return $address; - } - - return true; - } - } - - if ($return_emails) { - return null; - } - - return true; -} // END hesk_validateEmail() - -/** - * @param $email - * @return bool - */ -function hesk_isValidEmail($email) { - /* Check for header injection attempts */ - if (preg_match("/\r|\n|%0a|%0d/i", $email)) { - return false; - } - - /* Does it contain an @? */ - $atIndex = strrpos($email, "@"); - if ($atIndex === false) { - return false; - } - - /* Get local and domain parts */ - $domain = substr($email, $atIndex + 1); - $local = substr($email, 0, $atIndex); - $localLen = strlen($local); - $domainLen = strlen($domain); - - /* Check local part length */ - if ($localLen < 1 || $localLen > 64) { - return false; - } - - /* Check domain part length */ - if ($domainLen < 1 || $domainLen > 254) { - return false; - } - - /* Local part mustn't start or end with a dot */ - if ($local[0] == '.' || $local[$localLen - 1] == '.') { - return false; - } - - /* Local part mustn't have two consecutive dots*/ - if (strpos($local, '..') !== false) { - return false; - } - - /* Check domain part characters */ - if (!preg_match('/^[A-Za-z0-9\\-\\.]+$/', $domain)) { - return false; - } - - /* Domain part mustn't have two consecutive dots */ - if (strpos($domain, '..') !== false) { - return false; - } - - /* Character not valid in local part unless local part is quoted */ - if (!preg_match('/^(\\\\.|[A-Za-z0-9!#%&`_=\\/$\'*+?^{}|~.-])+$/', str_replace("\\\\", "", $local))) /* " */ { - if (!preg_match('/^"(\\\\"|[^"])+"$/', str_replace("\\\\", "", $local))) /* " */ { - return false; - } - } - - /* All tests passed, email seems to be OK */ - return true; -} // END hesk_isValidEmail() \ No newline at end of file