From dafda4883ddd205ed68fc7099336a4156f4b7f7b Mon Sep 17 00:00:00 2001 From: philipp lang Date: Thu, 14 Aug 2025 23:55:50 +0200 Subject: [PATCH] Add later registration backend --- app/Form/Actions/RegisterAction.php | 28 +++++++++++++++---- tests/Feature/Form/FormRegisterActionTest.php | 20 +++++++++++++ tests/Lib/CreatesFormFields.php | 17 +++++++++++ 3 files changed, 59 insertions(+), 6 deletions(-) diff --git a/app/Form/Actions/RegisterAction.php b/app/Form/Actions/RegisterAction.php index b4cdfa0d..4e5b1a82 100644 --- a/app/Form/Actions/RegisterAction.php +++ b/app/Form/Actions/RegisterAction.php @@ -7,6 +7,8 @@ use App\Form\Models\Form; use App\Form\Models\Participant; use App\Member\Member; use Illuminate\Http\JsonResponse; +use Illuminate\Support\Facades\URL; +use Illuminate\Support\Facades\Validator; use Illuminate\Validation\ValidationException; use Lorisleiva\Actions\ActionRequest; use Lorisleiva\Actions\Concerns\AsAction; @@ -20,13 +22,9 @@ class RegisterAction */ public function handle(Form $form, array $input): Participant { - if (!$form->canRegister()) { - throw ValidationException::withMessages(['event' => 'Anmeldung zzt nicht möglich.']); - } - $memberQuery = FieldCollection::fromRequest($form, $input) ->withNamiType() - ->reduce(fn ($query, $field) => $field->namiType->performQuery($query, $field->value), (new Member())->newQuery()); + ->reduce(fn($query, $field) => $field->namiType->performQuery($query, $field->value), (new Member())->newQuery()); $member = $form->getFields()->withNamiType()->count() && $memberQuery->count() === 1 ? $memberQuery->first() : null; $participant = $form->participants()->create([ @@ -34,7 +32,7 @@ class RegisterAction 'member_id' => $member?->id, ]); - $form->getFields()->each(fn ($field) => $field->afterRegistration($form, $participant, $input)); + $form->getFields()->each(fn($field) => $field->afterRegistration($form, $participant, $input)); $participant->sendConfirmationMail(); ExportSyncAction::dispatch($form->id); @@ -77,8 +75,26 @@ class RegisterAction public function asController(ActionRequest $request, Form $form): JsonResponse { + if (!$form->canRegister() && !$this->isRegisteringLater($request)) { + throw ValidationException::withMessages(['event' => 'Anmeldung zzt nicht möglich.']); + } + $participant = $this->handle($form, $request->validated()); return response()->json($participant); } + + public function isRegisteringLater(ActionRequest $request): bool { + if (!is_array($request->query())) { + return false; + } + + $validator = Validator::make($request->query(), [ + 'later' => 'required|numeric|in:1', + 'id' => 'required|string|uuid:4', + 'signature' => 'required|string', + ]); + + return URL::hasValidSignature($request) && $validator->passes(); + } } diff --git a/tests/Feature/Form/FormRegisterActionTest.php b/tests/Feature/Form/FormRegisterActionTest.php index a62f0e30..d4ea32e6 100644 --- a/tests/Feature/Form/FormRegisterActionTest.php +++ b/tests/Feature/Form/FormRegisterActionTest.php @@ -735,3 +735,23 @@ it('testItSetsRegionIfMemberIsDirectRegionMember', function () { $this->register($form, ['bezirk' => $bezirk->id, 'members' => [['id' => '5505']]])->assertOk(); $this->assertEquals($bezirk->id, $form->participants->get(1)->data['bezirk']); }); + +it('registers via later link', function () { + $this->login()->loginNami(); + $form = Form::factory()->fields([]) + ->registrationUntil(now()->subDay()) + ->create(); + + $this->registerLater($form, [], str()->uuid())->assertOk(); + $this->assertDatabaseCount('participants', 1); +}); + +it('checks signature of later link', function () { + $this->login()->loginNami(); + $form = Form::factory()->fields([]) + ->registrationUntil(now()->subDay()) + ->create(); + + $this->registerLaterWithWrongSignature($form, [], str()->uuid())->assertStatus(422); + $this->assertDatabaseCount('participants', 0); +}); diff --git a/tests/Lib/CreatesFormFields.php b/tests/Lib/CreatesFormFields.php index 07ca411a..ecdb4bf1 100644 --- a/tests/Lib/CreatesFormFields.php +++ b/tests/Lib/CreatesFormFields.php @@ -18,6 +18,7 @@ use App\Form\Models\Form; use App\Member\Member; use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Storage; +use Illuminate\Support\Facades\URL; use Illuminate\Testing\TestResponse; use Tests\Feature\Form\FormtemplateFieldRequest; @@ -42,6 +43,22 @@ trait CreatesFormFields return $this->postJson(route('form.register', ['form' => $form]), $payload); } + /** + * @param array $payload + */ + public function registerLater(Form $form, array $payload, string $laterId): TestResponse + { + return $this->postJson(URL::signedRoute('form.register', ['form' => $form, 'later' => '1', 'id' => $laterId]), $payload); + } + + /** + * @param array $payload + */ + public function registerLaterWithWrongSignature(Form $form, array $payload, string $laterId): TestResponse + { + return $this->postJson(route('form.register', ['form' => $form, 'later' => '1', 'id' => $laterId, 'signature' => '-1']), $payload); + } + public function setUpForm() { app(FormSettings::class)->fill(['clearCacheUrl' => 'http://event.com/clear-cache'])->save();