• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

LibreSign / libresign / 20104047174

10 Dec 2025 03:32PM UTC coverage: 43.803%. First build
20104047174

Pull #6069

github

web-flow
Merge 5caf995ab into a49dfb931
Pull Request #6069: feat: sequential signing

46 of 146 new or added lines in 10 files covered. (31.51%)

5733 of 13088 relevant lines covered (43.8%)

5.1 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/lib/Controller/PageController.php
1
<?php
2

3
declare(strict_types=1);
4
/**
5
 * SPDX-FileCopyrightText: 2020-2024 LibreCode coop and contributors
6
 * SPDX-License-Identifier: AGPL-3.0-or-later
7
 */
8

9
namespace OCA\Libresign\Controller;
10

11
use OCA\Libresign\AppInfo\Application;
12
use OCA\Libresign\Exception\LibresignException;
13
use OCA\Libresign\Helper\JSActions;
14
use OCA\Libresign\Helper\ValidateHelper;
15
use OCA\Libresign\Middleware\Attribute\PrivateValidation;
16
use OCA\Libresign\Middleware\Attribute\RequireSetupOk;
17
use OCA\Libresign\Middleware\Attribute\RequireSignRequestUuid;
18
use OCA\Libresign\Service\AccountService;
19
use OCA\Libresign\Service\FileService;
20
use OCA\Libresign\Service\IdentifyMethod\SignatureMethod\TokenService;
21
use OCA\Libresign\Service\IdentifyMethodService;
22
use OCA\Libresign\Service\RequestSignatureService;
23
use OCA\Libresign\Service\SessionService;
24
use OCA\Libresign\Service\SignerElementsService;
25
use OCA\Libresign\Service\SignFileService;
26
use OCA\Viewer\Event\LoadViewer;
27
use OCP\AppFramework\Db\DoesNotExistException;
28
use OCP\AppFramework\Http;
29
use OCP\AppFramework\Http\Attribute\AnonRateLimit;
30
use OCP\AppFramework\Http\Attribute\FrontpageRoute;
31
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
32
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
33
use OCP\AppFramework\Http\Attribute\PublicPage;
34
use OCP\AppFramework\Http\ContentSecurityPolicy;
35
use OCP\AppFramework\Http\DataResponse;
36
use OCP\AppFramework\Http\FileDisplayResponse;
37
use OCP\AppFramework\Http\TemplateResponse;
38
use OCP\AppFramework\Services\IInitialState;
39
use OCP\EventDispatcher\IEventDispatcher;
40
use OCP\IAppConfig;
41
use OCP\IL10N;
42
use OCP\IRequest;
43
use OCP\IURLGenerator;
44
use OCP\IUserSession;
45
use OCP\Util;
46

47
class PageController extends AEnvironmentPageAwareController {
48
        public function __construct(
49
                IRequest $request,
50
                protected IUserSession $userSession,
51
                private SessionService $sessionService,
52
                private IInitialState $initialState,
53
                private AccountService $accountService,
54
                protected SignFileService $signFileService,
55
                protected RequestSignatureService $requestSignatureService,
56
                private SignerElementsService $signerElementsService,
57
                protected IL10N $l10n,
58
                private IdentifyMethodService $identifyMethodService,
59
                private IAppConfig $appConfig,
60
                private FileService $fileService,
61
                private ValidateHelper $validateHelper,
62
                private IEventDispatcher $eventDispatcher,
63
                private IURLGenerator $urlGenerator,
64
        ) {
65
                parent::__construct(
×
66
                        request: $request,
×
67
                        signFileService: $signFileService,
×
68
                        l10n: $l10n,
×
69
                        userSession: $userSession,
×
70
                );
×
71
        }
72

73
        /**
74
         * Index page
75
         *
76
         * @return TemplateResponse<Http::STATUS_OK, array{}>
77
         *
78
         * 200: OK
79
         */
80
        #[NoAdminRequired]
81
        #[NoCSRFRequired]
82
        #[RequireSetupOk(template: 'main')]
83
        #[FrontpageRoute(verb: 'GET', url: '/')]
84
        public function index(): TemplateResponse {
85
                $this->initialState->provideInitialState('config', $this->accountService->getConfig($this->userSession->getUser()));
×
86
                $this->initialState->provideInitialState('filters', $this->accountService->getConfigFilters($this->userSession->getUser()));
×
87
                $this->initialState->provideInitialState('certificate_engine', $this->accountService->getCertificateEngineName());
×
88

89
                try {
90
                        $this->validateHelper->canRequestSign($this->userSession->getUser());
×
91
                        $this->initialState->provideInitialState('can_request_sign', true);
×
92
                } catch (LibresignException) {
×
93
                        $this->initialState->provideInitialState('can_request_sign', false);
×
94
                }
95

96
                $this->provideSignerSignatues();
×
97
                $this->initialState->provideInitialState('identify_methods', $this->identifyMethodService->getIdentifyMethodsSettings());
×
NEW
98
                $this->initialState->provideInitialState('signature_flow', $this->appConfig->getValueString(Application::APP_ID, 'signature_flow', \OCA\Libresign\Service\SignatureFlow::PARALLEL->value));
×
99
                $this->initialState->provideInitialState('legal_information', $this->appConfig->getValueString(Application::APP_ID, 'legal_information'));
×
100

101
                Util::addScript(Application::APP_ID, 'libresign-main');
×
102

103
                if (class_exists(LoadViewer::class)) {
×
104
                        $this->eventDispatcher->dispatchTyped(new LoadViewer());
×
105
                }
106

107
                $response = new TemplateResponse(Application::APP_ID, 'main');
×
108

109
                $policy = new ContentSecurityPolicy();
×
110
                $policy->allowEvalScript(true);
×
111
                $policy->addAllowedFrameDomain('\'self\'');
×
112
                $response->setContentSecurityPolicy($policy);
×
113

114
                return $response;
×
115
        }
116

117
        /**
118
         * Index page to authenticated users
119
         *
120
         * This router is used to be possible render pages with /f/, is a
121
         * workaround at frontend side to identify pages with authenticated accounts
122
         *
123
         * @return TemplateResponse<Http::STATUS_OK, array{}>
124
         *
125
         * 200: OK
126
         */
127
        #[NoAdminRequired]
128
        #[NoCSRFRequired]
129
        #[RequireSetupOk(template: 'main')]
130
        #[FrontpageRoute(verb: 'GET', url: '/f/')]
131
        public function indexF(): TemplateResponse {
132
                return $this->index();
×
133
        }
134

135
        /**
136
         * Incomplete page
137
         *
138
         * @return TemplateResponse<Http::STATUS_OK, array{}>
139
         *
140
         * 200: OK
141
         */
142
        #[NoAdminRequired]
143
        #[NoCSRFRequired]
144
        #[FrontpageRoute(verb: 'GET', url: '/f/incomplete')]
145
        public function incomplete(): TemplateResponse {
146
                Util::addScript(Application::APP_ID, 'libresign-main');
×
147
                $response = new TemplateResponse(Application::APP_ID, 'main');
×
148
                return $response;
×
149
        }
150

151
        /**
152
         * Incomplete page in full screen
153
         *
154
         * @return TemplateResponse<Http::STATUS_OK, array{}>
155
         *
156
         * 200: OK
157
         */
158
        #[PublicPage]
159
        #[NoCSRFRequired]
160
        #[FrontpageRoute(verb: 'GET', url: '/p/incomplete')]
161
        public function incompleteP(): TemplateResponse {
162
                Util::addScript(Application::APP_ID, 'libresign-main');
×
163
                $response = new TemplateResponse(Application::APP_ID, 'main', [], TemplateResponse::RENDER_AS_BASE);
×
164
                return $response;
×
165
        }
166

167
        /**
168
         * Main page to authenticated signer with a path
169
         *
170
         * The path is used only by frontend
171
         *
172
         * @param string $path The path that was sent from frontend
173
         * @return TemplateResponse<Http::STATUS_OK, array{}>
174
         *
175
         * 200: OK
176
         */
177
        #[NoAdminRequired]
178
        #[NoCSRFRequired]
179
        #[RequireSetupOk(template: 'main')]
180
        #[FrontpageRoute(verb: 'GET', url: '/f/{path}', requirements: ['path' => '.+'])]
181
        public function indexFPath(string $path): TemplateResponse {
182
                if (preg_match('/validation\/(?<uuid>[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})$/', $path, $matches)) {
×
183

184
                        try {
185
                                $this->initialState->provideInitialState('file_info',
×
186
                                        $this->fileService
×
187
                                                ->setFileByType('uuid', $matches['uuid'])
×
188
                                                ->setIdentifyMethodId($this->sessionService->getIdentifyMethodId())
×
189
                                                ->setHost($this->request->getServerHost())
×
190
                                                ->setMe($this->userSession->getUser())
×
191
                                                ->showVisibleElements()
×
192
                                                ->showSigners()
×
193
                                                ->showSettings()
×
194
                                                ->showMessages()
×
195
                                                ->showValidateFile()
×
196
                                                ->toArray()
×
197
                                );
×
198
                        } catch (LibresignException) {
×
199
                                throw new LibresignException(json_encode([
×
200
                                        'action' => JSActions::ACTION_DO_NOTHING,
×
201
                                        'errors' => [['message' => $this->l10n->t('Invalid UUID')]],
×
202
                                ]), Http::STATUS_NOT_FOUND);
×
203
                        }
204
                } elseif (preg_match('/sign\/(?<uuid>[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})/', $path, $matches)) {
×
205
                        try {
206
                                $signRequest = $this->signFileService->getSignRequestByUuid($matches['uuid']);
×
207
                                $file = $this->fileService
×
208
                                        ->setFile($this->signFileService->getFile($signRequest->getFileId()))
×
209
                                        ->setMe($this->userSession->getUser())
×
210
                                        ->setSignRequest($signRequest)
×
211
                                        ->showSettings()
×
212
                                        ->toArray();
×
213
                                $this->initialState->provideInitialState('needIdentificationDocuments', $file['settings']['needIdentificationDocuments'] ?? false);
×
214
                                $this->initialState->provideInitialState('identificationDocumentsWaitingApproval', $file['settings']['identificationDocumentsWaitingApproval'] ?? false);
×
215
                        } catch (\Throwable $e) {
×
216
                                throw new LibresignException(json_encode([
×
217
                                        'action' => JSActions::ACTION_DO_NOTHING,
×
218
                                        'errors' => [['message' => $this->l10n->t('Invalid UUID')]],
×
219
                                ]), Http::STATUS_NOT_FOUND);
×
220
                        }
221
                }
222
                return $this->index();
×
223
        }
224

225

226
        /**
227
         * Sign page to authenticated signer
228
         *
229
         * @param string $uuid Sign request uuid
230
         * @return TemplateResponse<Http::STATUS_OK, array{}>
231
         *
232
         * 200: OK
233
         */
234
        #[NoAdminRequired]
235
        #[NoCSRFRequired]
236
        #[RequireSetupOk]
237
        #[PublicPage]
238
        #[RequireSignRequestUuid]
239
        #[FrontpageRoute(verb: 'GET', url: '/f/sign/{uuid}')]
240
        public function signF(string $uuid): TemplateResponse {
241
                $this->initialState->provideInitialState('action', JSActions::ACTION_SIGN_INTERNAL);
×
242
                return $this->index();
×
243
        }
244

245
        /**
246
         * Sign page to authenticated signer with the path of file
247
         *
248
         * The path is used only by frontend
249
         *
250
         * @param string $uuid Sign request uuid
251
         * @return TemplateResponse<Http::STATUS_OK, array{}>
252
         *
253
         * 200: OK
254
         */
255
        #[NoAdminRequired]
256
        #[NoCSRFRequired]
257
        #[RequireSetupOk]
258
        #[PublicPage]
259
        #[RequireSignRequestUuid]
260
        #[FrontpageRoute(verb: 'GET', url: '/f/sign/{uuid}/{path}', requirements: ['path' => '.+'])]
261
        public function signFPath(string $uuid): TemplateResponse {
262
                $this->initialState->provideInitialState('action', JSActions::ACTION_SIGN_INTERNAL);
×
263
                return $this->index();
×
264
        }
265

266
        /**
267
         * Sign page to unauthenticated signer
268
         *
269
         * The path is used only by frontend
270
         *
271
         * @param string $uuid Sign request uuid
272
         * @return TemplateResponse<Http::STATUS_OK, array{}>
273
         *
274
         * 200: OK
275
         */
276
        #[NoAdminRequired]
277
        #[NoCSRFRequired]
278
        #[RequireSetupOk]
279
        #[PublicPage]
280
        #[RequireSignRequestUuid]
281
        #[FrontpageRoute(verb: 'GET', url: '/p/sign/{uuid}/{path}', requirements: ['path' => '.+'])]
282
        public function signPPath(string $uuid): TemplateResponse {
283
                return $this->sign($uuid);
×
284
        }
285

286
        /**
287
         * Sign page to unauthenticated signer
288
         *
289
         * The path is used only by frontend
290
         *
291
         * @param string $uuid Sign request uuid
292
         * @return TemplateResponse<Http::STATUS_OK, array{}>
293
         *
294
         * 200: OK
295
         */
296
        #[PrivateValidation]
297
        #[NoAdminRequired]
298
        #[NoCSRFRequired]
299
        #[RequireSetupOk]
300
        #[PublicPage]
301
        #[RequireSignRequestUuid]
302
        #[FrontpageRoute(verb: 'GET', url: '/p/sign/{uuid}')]
303
        public function sign(string $uuid): TemplateResponse {
304
                $this->initialState->provideInitialState('action', JSActions::ACTION_SIGN);
×
305
                $this->initialState->provideInitialState('config',
×
306
                        $this->accountService->getConfig($this->userSession->getUser())
×
307
                );
×
308
                $this->initialState->provideInitialState('filename', $this->getFileEntity()->getName());
×
309
                $file = $this->fileService
×
310
                        ->setFile($this->getFileEntity())
×
311
                        ->setHost($this->request->getServerHost())
×
312
                        ->setMe($this->userSession->getUser())
×
313
                        ->setSignerIdentified()
×
314
                        ->setIdentifyMethodId($this->sessionService->getIdentifyMethodId())
×
315
                        ->setSignRequest($this->getSignRequestEntity())
×
316
                        ->showVisibleElements()
×
317
                        ->showSigners()
×
318
                        ->showSettings()
×
319
                        ->toArray();
×
320
                $this->initialState->provideInitialState('config', [
×
321
                        'identificationDocumentsFlow' => $file['settings']['needIdentificationDocuments'] ?? false,
×
322
                ]);
×
323
                $this->initialState->provideInitialState('needIdentificationDocuments', $file['settings']['needIdentificationDocuments'] ?? false);
×
324
                $this->initialState->provideInitialState('identificationDocumentsWaitingApproval', $file['settings']['identificationDocumentsWaitingApproval'] ?? false);
×
325
                $this->initialState->provideInitialState('status', $file['status']);
×
326
                $this->initialState->provideInitialState('statusText', $file['statusText']);
×
327
                $this->initialState->provideInitialState('signers', $file['signers']);
×
328
                $this->initialState->provideInitialState('sign_request_uuid', $uuid);
×
329
                $this->provideSignerSignatues();
×
330
                $this->initialState->provideInitialState('token_length', TokenService::TOKEN_LENGTH);
×
331
                $this->initialState->provideInitialState('description', $this->getSignRequestEntity()->getDescription() ?? '');
×
332
                $this->initialState->provideInitialState('pdf',
×
333
                        $this->signFileService->getFileUrl('url', $this->getFileEntity(), $this->getNextcloudFile(), $uuid)
×
334
                );
×
335
                $this->initialState->provideInitialState('nodeId', $this->getFileEntity()->getNodeId());
×
336

337
                Util::addScript(Application::APP_ID, 'libresign-external');
×
338
                $response = new TemplateResponse(Application::APP_ID, 'external', [], TemplateResponse::RENDER_AS_BASE);
×
339

340
                $policy = new ContentSecurityPolicy();
×
341
                $policy->allowEvalScript(true);
×
342
                $response->setContentSecurityPolicy($policy);
×
343

344
                return $response;
×
345
        }
346

347
        private function provideSignerSignatues(): void {
348
                $signatures = [];
×
349
                if ($this->userSession->getUser()) {
×
350
                        $signatures = $this->signerElementsService->getUserElements($this->userSession->getUser()->getUID());
×
351
                } else {
352
                        $signatures = $this->signerElementsService->getElementsFromSessionAsArray();
×
353
                }
354
                $this->initialState->provideInitialState('user_signatures', $signatures);
×
355
        }
356

357
        /**
358
         * Show signature page
359
         *
360
         * @param string $uuid Sign request uuid
361
         * @return TemplateResponse<Http::STATUS_OK, array{}>
362
         *
363
         * 200: OK
364
         * 404: Invalid UUID
365
         */
366
        #[NoAdminRequired]
367
        #[NoCSRFRequired]
368
        #[RequireSetupOk]
369
        #[FrontpageRoute(verb: 'GET', url: '/p/id-docs/approve/{uuid}')]
370
        #[FrontpageRoute(verb: 'GET', url: '/p/id-docs/approve/{uuid}/{path}', requirements: ['path' => '.+'], postfix: 'private')]
371
        public function signIdDoc($uuid): TemplateResponse {
372
                try {
373
                        $fileEntity = $this->signFileService->getFileByUuid($uuid);
×
374
                        $this->signFileService->getIdDocById($fileEntity->getId());
×
375
                } catch (DoesNotExistException) {
×
376
                        throw new LibresignException(json_encode([
×
377
                                'action' => JSActions::ACTION_DO_NOTHING,
×
378
                                'errors' => [['message' => $this->l10n->t('Invalid UUID')]],
×
379
                        ]), Http::STATUS_NOT_FOUND);
×
380
                }
381
                $this->initialState->provideInitialState('action', JSActions::ACTION_SIGN_ID_DOC);
×
382
                $this->initialState->provideInitialState('config',
×
383
                        $this->accountService->getConfig($this->userSession->getUser())
×
384
                );
×
385
                $this->initialState->provideInitialState('signer',
×
386
                        $this->signFileService->getSignerData(
×
387
                                $this->userSession->getUser(),
×
388
                        )
×
389
                );
×
390
                $this->initialState->provideInitialState('identifyMethods',
×
391
                        $this->signFileService->getAvailableIdentifyMethodsFromSettings()
×
392
                );
×
393
                $this->initialState->provideInitialState('filename', $fileEntity->getName());
×
394
                $file = $this->fileService
×
395
                        ->setFile($fileEntity)
×
396
                        ->setHost($this->request->getServerHost())
×
397
                        ->setMe($this->userSession->getUser())
×
398
                        ->setIdentifyMethodId($this->sessionService->getIdentifyMethodId())
×
399
                        ->showVisibleElements()
×
400
                        ->showSigners()
×
401
                        ->toArray();
×
402
                $this->initialState->provideInitialState('fileId', $file['nodeId']);
×
403
                $this->initialState->provideInitialState('status', $file['status']);
×
404
                $this->initialState->provideInitialState('statusText', $file['statusText']);
×
405
                $this->initialState->provideInitialState('visibleElements', []);
×
406
                $this->initialState->provideInitialState('signers', []);
×
407
                $this->provideSignerSignatues();
×
408
                $signatureMethods = $this->identifyMethodService->getSignMethodsOfIdentifiedFactors($this->getSignRequestEntity()->getId());
×
409
                $this->initialState->provideInitialState('signature_methods', $signatureMethods);
×
410
                $this->initialState->provideInitialState('token_length', TokenService::TOKEN_LENGTH);
×
411
                $this->initialState->provideInitialState('description', '');
×
412
                $nextcloudFile = $this->signFileService->getNextcloudFile($fileEntity);
×
413
                $this->initialState->provideInitialState('pdf',
×
414
                        $this->signFileService->getFileUrl('url', $fileEntity, $nextcloudFile, $uuid)
×
415
                );
×
416

417
                Util::addScript(Application::APP_ID, 'libresign-external');
×
418
                $response = new TemplateResponse(Application::APP_ID, 'external', [], TemplateResponse::RENDER_AS_BASE);
×
419

420
                $policy = new ContentSecurityPolicy();
×
421
                $policy->allowEvalScript(true);
×
422
                $response->setContentSecurityPolicy($policy);
×
423

424
                return $response;
×
425
        }
426

427
        /**
428
         * Use UUID of file to get PDF
429
         *
430
         * @param string $uuid File uuid
431
         * @return FileDisplayResponse<Http::STATUS_OK, array{Content-Type: string}>|DataResponse<Http::STATUS_NOT_FOUND, array<empty>, array{}>
432
         *
433
         * 200: OK
434
         * 401: Validation page not accessible if unauthenticated
435
         * 404: File not found
436
         */
437
        #[PrivateValidation]
438
        #[NoAdminRequired]
439
        #[NoCSRFRequired]
440
        #[RequireSetupOk]
441
        #[PublicPage]
442
        #[AnonRateLimit(limit: 30, period: 60)]
443
        #[FrontpageRoute(verb: 'GET', url: '/p/pdf/{uuid}')]
444
        public function getPdf($uuid) {
445
                try {
446
                        $file = $this->accountService->getPdfByUuid($uuid);
×
447
                } catch (DoesNotExistException) {
×
448
                        return new DataResponse([], Http::STATUS_NOT_FOUND);
×
449
                }
450

451
                return new FileDisplayResponse($file, Http::STATUS_OK, ['Content-Type' => $file->getMimeType()]);
×
452
        }
453

454
        /**
455
         * Use UUID of user to get PDF
456
         *
457
         * @param string $uuid Sign request uuid
458
         * @return FileDisplayResponse<Http::STATUS_OK, array{Content-Type: string}>
459
         *
460
         * 200: OK
461
         * 401: Validation page not accessible if unauthenticated
462
         */
463
        #[PrivateValidation]
464
        #[NoAdminRequired]
465
        #[NoCSRFRequired]
466
        #[RequireSignRequestUuid]
467
        #[PublicPage]
468
        #[RequireSetupOk]
469
        #[AnonRateLimit(limit: 30, period: 60)]
470
        #[FrontpageRoute(verb: 'GET', url: '/pdf/{uuid}')]
471
        public function getPdfFile($uuid): FileDisplayResponse {
472
                $file = $this->getNextcloudFile();
×
473
                return new FileDisplayResponse($file, Http::STATUS_OK, ['Content-Type' => $file->getMimeType()]);
×
474
        }
475

476
        /**
477
         * Show validation page
478
         *
479
         * @return TemplateResponse<Http::STATUS_OK, array{}>
480
         *
481
         * 200: OK
482
         * 401: Validation page not accessible if unauthenticated
483
         */
484
        #[PrivateValidation]
485
        #[NoAdminRequired]
486
        #[NoCSRFRequired]
487
        #[RequireSetupOk(template: 'validation')]
488
        #[PublicPage]
489
        #[AnonRateLimit(limit: 30, period: 60)]
490
        #[FrontpageRoute(verb: 'GET', url: '/p/validation')]
491
        public function validation(): TemplateResponse {
492
                if ($this->getFileEntity()) {
×
493
                        $this->initialState->provideInitialState('config',
×
494
                                $this->accountService->getConfig($this->userSession->getUser())
×
495
                        );
×
496
                        $this->initialState->provideInitialState('file', [
×
497
                                'uuid' => $this->getFileEntity()?->getUuid(),
×
498
                                'description' => $this->getSignRequestEntity()?->getDescription(),
×
499
                        ]);
×
500
                        $this->initialState->provideInitialState('filename', $this->getFileEntity()?->getName());
×
501
                        $this->initialState->provideInitialState('pdf',
×
502
                                $this->signFileService->getFileUrl('url', $this->getFileEntity(), $this->getNextcloudFile(), $this->request->getParam('uuid'))
×
503
                        );
×
504
                        $this->initialState->provideInitialState('signer',
×
505
                                $this->signFileService->getSignerData(
×
506
                                        $this->userSession->getUser(),
×
507
                                        $this->getSignRequestEntity(),
×
508
                                )
×
509
                        );
×
510
                }
511

512
                Util::addScript(Application::APP_ID, 'libresign-validation');
×
513
                $response = new TemplateResponse(Application::APP_ID, 'validation', [], TemplateResponse::RENDER_AS_BASE);
×
514

515
                return $response;
×
516
        }
517

518
        /**
519
         * Show validation page
520
         *
521
         * The path is used only by frontend
522
         *
523
         * @param string $uuid Sign request uuid
524
         * @return TemplateResponse<Http::STATUS_OK, array{}>
525
         *
526
         * 200: OK
527
         * 303: Redirected to validation page
528
         * 401: Validation page not accessible if unauthenticated
529
         */
530
        #[PrivateValidation]
531
        #[NoAdminRequired]
532
        #[NoCSRFRequired]
533
        #[RequireSetupOk]
534
        #[PublicPage]
535
        #[AnonRateLimit(limit: 30, period: 60)]
536
        #[FrontpageRoute(verb: 'GET', url: '/validation/{uuid}')]
537
        public function validationFileWithShortUrl(): TemplateResponse {
538
                return $this->validationFilePublic($this->request->getParam('uuid'));
×
539
        }
540

541
        /**
542
         * Show validation page
543
         *
544
         * @param string $uuid Sign request uuid
545
         * @return TemplateResponse<Http::STATUS_OK, array{}>
546
         *
547
         * 200: OK
548
         */
549
        #[NoAdminRequired]
550
        #[NoCSRFRequired]
551
        #[RequireSetupOk(template: 'main')]
552
        #[PublicPage]
553
        #[RequireSignRequestUuid]
554
        #[FrontpageRoute(verb: 'GET', url: '/reset-password')]
555
        public function resetPassword(): TemplateResponse {
556
                $this->initialState->provideInitialState('config',
×
557
                        $this->accountService->getConfig($this->userSession->getUser())
×
558
                );
×
559

560
                Util::addScript(Application::APP_ID, 'libresign-main');
×
561
                $response = new TemplateResponse(Application::APP_ID, 'reset_password');
×
562

563
                return $response;
×
564
        }
565

566
        /**
567
         * Public page to show validation for a specific file UUID
568
         *
569
         * @param string $uuid File uuid
570
         * @return TemplateResponse<Http::STATUS_OK, array{}>
571
         *
572
         * 200: OK
573
         * 401: Validation page not accessible if unauthenticated
574
         */
575
        #[PrivateValidation]
576
        #[NoAdminRequired]
577
        #[NoCSRFRequired]
578
        #[RequireSetupOk(template: 'validation')]
579
        #[PublicPage]
580
        #[AnonRateLimit(limit: 30, period: 60)]
581
        #[FrontpageRoute(verb: 'GET', url: '/p/validation/{uuid}')]
582
        public function validationFilePublic(string $uuid): TemplateResponse {
583
                try {
584
                        $this->signFileService->getFileByUuid($uuid);
×
585
                        $this->fileService->setFileByType('uuid', $uuid);
×
586
                } catch (DoesNotExistException) {
×
587
                        try {
588
                                $signRequest = $this->signFileService->getSignRequestByUuid($uuid);
×
589
                                $libresignFile = $this->signFileService->getFile($signRequest->getFileId());
×
590
                                $this->fileService->setFile($libresignFile);
×
591
                        } catch (DoesNotExistException) {
×
592
                                $this->initialState->provideInitialState('action', JSActions::ACTION_DO_NOTHING);
×
593
                                $this->initialState->provideInitialState('errors', [['message' => $this->l10n->t('Invalid UUID')]]);
×
594
                        }
595
                }
596
                if ($this->userSession->isLoggedIn()) {
×
597
                        $this->initialState->provideInitialState('config',
×
598
                                $this->accountService->getConfig($this->userSession->getUser())
×
599
                        );
×
600
                        $this->fileService->setMe($this->userSession->getUser());
×
601
                } else {
602
                        $this->initialState->provideInitialState('config',
×
603
                                $this->accountService->getConfig()
×
604
                        );
×
605
                }
606

607
                $this->initialState->provideInitialState('legal_information', $this->appConfig->getValueString(Application::APP_ID, 'legal_information'));
×
608

609
                $this->initialState->provideInitialState('file_info',
×
610
                        $this->fileService
×
611
                                ->setIdentifyMethodId($this->sessionService->getIdentifyMethodId())
×
612
                                ->setHost($this->request->getServerHost())
×
613
                                ->showVisibleElements()
×
614
                                ->showSigners()
×
615
                                ->showSettings()
×
616
                                ->showMessages()
×
617
                                ->showValidateFile()
×
618
                                ->toArray()
×
619
                );
×
620

621
                Util::addScript(Application::APP_ID, 'libresign-validation');
×
622
                if (class_exists(LoadViewer::class)) {
×
623
                        $this->eventDispatcher->dispatchTyped(new LoadViewer());
×
624
                }
625
                $response = new TemplateResponse(Application::APP_ID, 'validation', [], TemplateResponse::RENDER_AS_BASE);
×
626

627
                return $response;
×
628
        }
629
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc