diff --git a/CHANGELOG.md b/CHANGELOG.md index a39684d..46ed9ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Use translated itemtype name for reservation item select groups - Filter the '0' empty-selection sentinel in Table and LDAP select questions - Fix Table question column type edge cases +- Fix hidden question not available in anonymous forms ### Others diff --git a/src/Model/QuestionType/HiddenQuestion.php b/src/Model/QuestionType/HiddenQuestion.php index cf205da..233db24 100644 --- a/src/Model/QuestionType/HiddenQuestion.php +++ b/src/Model/QuestionType/HiddenQuestion.php @@ -72,6 +72,12 @@ public function getIcon(): string return 'ti ti-eye-off'; } + #[Override] + public function isAllowedForUnauthenticatedAccess(): bool + { + return true; + } + #[Override] public function getWeight(): int { diff --git a/tests/Model/QuestionType/HiddenQuestionTest.php b/tests/Model/QuestionType/HiddenQuestionTest.php index 8a5614d..8ecb767 100644 --- a/tests/Model/QuestionType/HiddenQuestionTest.php +++ b/tests/Model/QuestionType/HiddenQuestionTest.php @@ -33,13 +33,25 @@ namespace GlpiPlugin\Advancedforms\Tests\Model\QuestionType; +use AltchaOrg\Altcha\Algorithm\Pbkdf2; +use AltchaOrg\Altcha\Altcha; +use AltchaOrg\Altcha\SolveChallengeOptions; +use Glpi\Altcha\AltchaManager; +use Glpi\Controller\Form\SubmitAnswerController; +use Glpi\Form\AccessControl\ControlType\DirectAccess; +use Glpi\Form\AccessControl\ControlType\DirectAccessConfig; +use Glpi\Form\AnswersSet; +use Glpi\Form\Form; use Glpi\Form\QuestionType\QuestionTypeInterface; +use Glpi\Tests\FormBuilder; use Glpi\Tests\FormTesterTrait; use GlpiPlugin\Advancedforms\Model\Config\ConfigurableItemInterface; use GlpiPlugin\Advancedforms\Model\QuestionType\HiddenQuestion; use GlpiPlugin\Advancedforms\Tests\QuestionType\QuestionTypeTestCase; use Override; +use Psr\Log\NullLogger; use Symfony\Component\DomCrawler\Crawler; +use Symfony\Component\HttpFoundation\Request; final class HiddenQuestionTest extends QuestionTypeTestCase { @@ -80,4 +92,53 @@ protected function validateHelpdeskRenderingWhenDisabled( $input = $html->filter('input[value="my hidden value"]'); $this->assertEmpty($input); } + + public function testUnauthenticatedSubmissionSavesHiddenDefaultValue(): void + { + $this->enableConfigurableItem(HiddenQuestion::class); + $builder = new FormBuilder("Anonymous form"); + $builder->addQuestion("My hidden question", HiddenQuestion::class, 'my hidden value'); + $builder->setUseDefaultAccessPolicies(false); + $builder->addAccessControl(DirectAccess::class, new DirectAccessConfig( + token: 'my_token', + allow_unauthenticated: true, + )); + $form = $this->createForm($builder); + $question_id = $this->getQuestionId($form, "My hidden question"); + + $this->logOut(); + $request = Request::create('/Form/SubmitAnswers?token=my_token', 'POST', [ + 'forms_id' => $form->getID(), + "answers_$question_id" => 'tampered value', + 'altcha' => $this->solveAltchaChallenge(), + ]); + $response = (new SubmitAnswerController(new NullLogger()))->__invoke($request); + + $this->assertSame(200, $response->getStatusCode(), $response->getContent()); + $answers_set = $this->getLastAnswersSet($form); + $answer = $answers_set->getAnswerByQuestionId($question_id); + $this->assertNotNull($answer); + $this->assertSame('my hidden value', $answer->getRawAnswer()); + } + + private function getLastAnswersSet(Form $form): AnswersSet + { + $rows = (new AnswersSet())->find(['forms_forms_id' => $form->getID()], ['id DESC'], 1); + $this->assertCount(1, $rows); + return AnswersSet::getById(current($rows)['id']); + } + + private function solveAltchaChallenge(): string + { + $challenge = AltchaManager::getInstance()->generateChallenge(); + $solution = (new Altcha())->solveChallenge(new SolveChallengeOptions( + challenge: $challenge, + algorithm: new Pbkdf2(), + )); + + return base64_encode(json_encode([ + 'challenge' => $challenge->toArray(), + 'solution' => $solution, + ])); + } }