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

LibreSign / libresign / 21285303505

23 Jan 2026 11:55AM UTC coverage: 45.248%. First build
21285303505

Pull #6541

github

web-flow
Merge cba74dff7 into 4d49be032
Pull Request #6541: fix: click to sign unauthenticated

22 of 87 new or added lines in 3 files covered. (25.29%)

7422 of 16403 relevant lines covered (45.25%)

4.95 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\Db\FileMapper;
13
use OCA\Libresign\Db\SignRequestMapper;
14
use OCA\Libresign\Exception\LibresignException;
15
use OCA\Libresign\Helper\JSActions;
16
use OCA\Libresign\Helper\ValidateHelper;
17
use OCA\Libresign\Middleware\Attribute\PrivateValidation;
18
use OCA\Libresign\Middleware\Attribute\RequireSetupOk;
19
use OCA\Libresign\Middleware\Attribute\RequireSignRequestUuid;
20
use OCA\Libresign\Service\AccountService;
21
use OCA\Libresign\Service\File\FileListService;
22
use OCA\Libresign\Service\FileService;
23
use OCA\Libresign\Service\IdentifyMethod\SignatureMethod\TokenService;
24
use OCA\Libresign\Service\IdentifyMethodService;
25
use OCA\Libresign\Service\RequestSignatureService;
26
use OCA\Libresign\Service\SessionService;
27
use OCA\Libresign\Service\SignerElementsService;
28
use OCA\Libresign\Service\SignFileService;
29
use OCA\Viewer\Event\LoadViewer;
30
use OCP\AppFramework\Db\DoesNotExistException;
31
use OCP\AppFramework\Http;
32
use OCP\AppFramework\Http\Attribute\AnonRateLimit;
33
use OCP\AppFramework\Http\Attribute\FrontpageRoute;
34
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
35
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
36
use OCP\AppFramework\Http\Attribute\PublicPage;
37
use OCP\AppFramework\Http\ContentSecurityPolicy;
38
use OCP\AppFramework\Http\DataResponse;
39
use OCP\AppFramework\Http\FileDisplayResponse;
40
use OCP\AppFramework\Http\TemplateResponse;
41
use OCP\AppFramework\Services\IInitialState;
42
use OCP\EventDispatcher\IEventDispatcher;
43
use OCP\IAppConfig;
44
use OCP\IL10N;
45
use OCP\IRequest;
46
use OCP\IURLGenerator;
47
use OCP\IUserSession;
48
use OCP\Util;
49
use Psr\Log\LoggerInterface;
50

51
class PageController extends AEnvironmentPageAwareController {
52
        public function __construct(
53
                IRequest $request,
54
                protected IUserSession $userSession,
55
                private SessionService $sessionService,
56
                private IInitialState $initialState,
57
                private AccountService $accountService,
58
                protected SignFileService $signFileService,
59
                protected RequestSignatureService $requestSignatureService,
60
                private SignerElementsService $signerElementsService,
61
                protected IL10N $l10n,
62
                private IdentifyMethodService $identifyMethodService,
63
                private IAppConfig $appConfig,
64
                private FileService $fileService,
65
                private FileListService $fileListService,
66
                private FileMapper $fileMapper,
67
                private SignRequestMapper $signRequestMapper,
68
                private LoggerInterface $logger,
69
                private ValidateHelper $validateHelper,
70
                private IEventDispatcher $eventDispatcher,
71
                private IURLGenerator $urlGenerator,
72
        ) {
73
                parent::__construct(
×
74
                        request: $request,
×
75
                        signFileService: $signFileService,
×
76
                        l10n: $l10n,
×
77
                        userSession: $userSession,
×
78
                );
×
79
        }
80

81
        /**
82
         * Index page
83
         *
84
         * @return TemplateResponse<Http::STATUS_OK, array{}>
85
         *
86
         * 200: OK
87
         */
88
        #[NoAdminRequired]
89
        #[NoCSRFRequired]
90
        #[RequireSetupOk(template: 'main')]
91
        #[FrontpageRoute(verb: 'GET', url: '/')]
92
        public function index(): TemplateResponse {
93
                $this->initialState->provideInitialState('config', $this->accountService->getConfig($this->userSession->getUser()));
×
94
                $this->initialState->provideInitialState('filters', $this->accountService->getConfigFilters($this->userSession->getUser()));
×
95
                $this->initialState->provideInitialState('certificate_engine', $this->accountService->getCertificateEngineName());
×
96

97
                try {
98
                        $this->validateHelper->canRequestSign($this->userSession->getUser());
×
99
                        $this->initialState->provideInitialState('can_request_sign', true);
×
100
                } catch (LibresignException) {
×
101
                        $this->initialState->provideInitialState('can_request_sign', false);
×
102
                }
103

104
                $this->provideSignerSignatues();
×
105
                $this->initialState->provideInitialState('identify_methods', $this->identifyMethodService->getIdentifyMethodsSettings());
×
106
                $this->initialState->provideInitialState('signature_flow', $this->appConfig->getValueString(Application::APP_ID, 'signature_flow', \OCA\Libresign\Enum\SignatureFlow::NONE->value));
×
107
                $this->initialState->provideInitialState('legal_information', $this->appConfig->getValueString(Application::APP_ID, 'legal_information'));
×
108

109
                Util::addScript(Application::APP_ID, 'libresign-main');
×
110

111
                if (class_exists(LoadViewer::class)) {
×
112
                        $this->eventDispatcher->dispatchTyped(new LoadViewer());
×
113
                }
114

115
                $response = new TemplateResponse(Application::APP_ID, 'main');
×
116

117
                $policy = new ContentSecurityPolicy();
×
118
                $policy->allowEvalScript(true);
×
119
                $policy->addAllowedFrameDomain('\'self\'');
×
120
                $response->setContentSecurityPolicy($policy);
×
121

122
                return $response;
×
123
        }
124

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

143
        /**
144
         * Incomplete page
145
         *
146
         * @return TemplateResponse<Http::STATUS_OK, array{}>
147
         *
148
         * 200: OK
149
         */
150
        #[NoAdminRequired]
151
        #[NoCSRFRequired]
152
        #[FrontpageRoute(verb: 'GET', url: '/f/incomplete')]
153
        public function incomplete(): TemplateResponse {
154
                Util::addScript(Application::APP_ID, 'libresign-main');
×
155
                $response = new TemplateResponse(Application::APP_ID, 'main');
×
156
                return $response;
×
157
        }
158

159
        /**
160
         * Incomplete page in full screen
161
         *
162
         * @return TemplateResponse<Http::STATUS_OK, array{}>
163
         *
164
         * 200: OK
165
         */
166
        #[PublicPage]
167
        #[NoCSRFRequired]
168
        #[FrontpageRoute(verb: 'GET', url: '/p/incomplete')]
169
        public function incompleteP(): TemplateResponse {
170
                Util::addScript(Application::APP_ID, 'libresign-main');
×
171
                $response = new TemplateResponse(Application::APP_ID, 'main', [], TemplateResponse::RENDER_AS_BASE);
×
172
                return $response;
×
173
        }
174

175
        /**
176
         * Main page to authenticated signer with a path
177
         *
178
         * The path is used only by frontend
179
         *
180
         * @param string $path The path that was sent from frontend
181
         * @return TemplateResponse<Http::STATUS_OK, array{}>
182
         *
183
         * 200: OK
184
         */
185
        #[NoAdminRequired]
186
        #[NoCSRFRequired]
187
        #[RequireSetupOk(template: 'main')]
188
        #[FrontpageRoute(verb: 'GET', url: '/f/{path}', requirements: ['path' => '.+'])]
189
        public function indexFPath(string $path): TemplateResponse {
190
                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)) {
×
191

192
                        try {
193
                                $this->fileService->setFileByUuid($matches['uuid']);
×
194
                        } catch (LibresignException) {
×
195
                                try {
196
                                        $this->fileService->setFileBySignerUuid($matches['uuid']);
×
197
                                } catch (LibresignException) {
×
198
                                        throw new LibresignException(json_encode([
×
199
                                                'action' => JSActions::ACTION_DO_NOTHING,
×
200
                                                'errors' => [['message' => $this->l10n->t('Invalid UUID')]],
×
201
                                        ]), Http::STATUS_NOT_FOUND);
×
202
                                }
203
                        }
204

205
                        $this->initialState->provideInitialState('file_info',
×
206
                                $this->fileService
×
207
                                        ->setIdentifyMethodId($this->sessionService->getIdentifyMethodId())
×
208
                                        ->setHost($this->request->getServerHost())
×
209
                                        ->setMe($this->userSession->getUser())
×
210
                                        ->showVisibleElements()
×
211
                                        ->showSigners()
×
212
                                        ->showSettings()
×
213
                                        ->showMessages()
×
214
                                        ->showValidateFile()
×
215
                                        ->toArray()
×
216
                        );
×
217
                } 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)) {
×
218
                        try {
219
                                $signRequest = $this->signFileService->getSignRequestByUuid($matches['uuid']);
×
220
                                if ($signRequest->getStatusEnum() === \OCA\Libresign\Enum\SignRequestStatus::SIGNED) {
×
221
                                        $file = $this->signFileService->getFile($signRequest->getFileId());
×
222
                                        $redirectUrl = $this->urlGenerator->linkToRouteAbsolute('libresign.page.indexFPath', [
×
223
                                                'path' => 'validation/' . $file->getUuid(),
×
224
                                        ]);
×
225
                                        throw new LibresignException(json_encode([
×
226
                                                'action' => JSActions::ACTION_REDIRECT,
×
227
                                                'redirect' => $redirectUrl,
×
228
                                        ]), Http::STATUS_SEE_OTHER);
×
229
                                }
230
                                $file = $this->fileService
×
231
                                        ->setFile($this->signFileService->getFile($signRequest->getFileId()))
×
232
                                        ->setMe($this->userSession->getUser())
×
233
                                        ->setSignRequest($signRequest)
×
234
                                        ->showSettings()
×
235
                                        ->toArray();
×
236
                                $this->initialState->provideInitialState('needIdentificationDocuments', $file['settings']['needIdentificationDocuments'] ?? false);
×
237
                                $this->initialState->provideInitialState('identificationDocumentsWaitingApproval', $file['settings']['identificationDocumentsWaitingApproval'] ?? false);
×
238
                        } catch (LibresignException $e) {
×
239
                                throw $e;
×
240
                        } catch (\Throwable $e) {
×
241
                                throw new LibresignException(json_encode([
×
242
                                        'action' => JSActions::ACTION_DO_NOTHING,
×
243
                                        'errors' => [['message' => $this->l10n->t('Invalid UUID')]],
×
244
                                ]), Http::STATUS_NOT_FOUND);
×
245
                        }
246
                }
247
                return $this->index();
×
248
        }
249

250

251
        /**
252
         * Sign page to authenticated signer
253
         *
254
         * @param string $uuid Sign request uuid
255
         * @return TemplateResponse<Http::STATUS_OK, array{}>
256
         *
257
         * 200: OK
258
         */
259
        #[NoAdminRequired]
260
        #[NoCSRFRequired]
261
        #[RequireSetupOk]
262
        #[PublicPage]
263
        #[RequireSignRequestUuid(redirectIfSignedToValidation: true)]
264
        #[FrontpageRoute(verb: 'GET', url: '/f/sign/{uuid}')]
265
        public function signF(string $uuid): TemplateResponse {
266
                $this->initialState->provideInitialState('action', JSActions::ACTION_SIGN_INTERNAL);
×
267
                return $this->index();
×
268
        }
269

270
        /**
271
         * Sign page to authenticated signer with the path of file
272
         *
273
         * The path is used only by frontend
274
         *
275
         * @param string $uuid Sign request uuid
276
         * @return TemplateResponse<Http::STATUS_OK, array{}>
277
         *
278
         * 200: OK
279
         */
280
        #[NoAdminRequired]
281
        #[NoCSRFRequired]
282
        #[RequireSetupOk]
283
        #[PublicPage]
284
        #[RequireSignRequestUuid(redirectIfSignedToValidation: true)]
285
        #[FrontpageRoute(verb: 'GET', url: '/f/sign/{uuid}/{path}', requirements: ['path' => '.+'])]
286
        public function signFPath(string $uuid): TemplateResponse {
287
                $this->initialState->provideInitialState('action', JSActions::ACTION_SIGN_INTERNAL);
×
288
                return $this->index();
×
289
        }
290

291
        /**
292
         * Sign page to unauthenticated signer
293
         *
294
         * The path is used only by frontend
295
         *
296
         * @param string $uuid Sign request uuid
297
         * @return TemplateResponse<Http::STATUS_OK, array{}>
298
         *
299
         * 200: OK
300
         */
301
        #[NoAdminRequired]
302
        #[NoCSRFRequired]
303
        #[RequireSetupOk]
304
        #[PublicPage]
305
        #[RequireSignRequestUuid(redirectIfSignedToValidation: true)]
306
        #[FrontpageRoute(verb: 'GET', url: '/p/sign/{uuid}/{path}', requirements: ['path' => '.+'])]
307
        public function signPPath(string $uuid): TemplateResponse {
308
                return $this->sign($uuid);
×
309
        }
310

311
        /**
312
         * Sign page to unauthenticated signer
313
         *
314
         * The path is used only by frontend
315
         *
316
         * @param string $uuid Sign request uuid
317
         * @return TemplateResponse<Http::STATUS_OK, array{}>
318
         *
319
         * 200: OK
320
         */
321
        #[PrivateValidation]
322
        #[NoAdminRequired]
323
        #[NoCSRFRequired]
324
        #[RequireSetupOk]
325
        #[PublicPage]
326
        #[RequireSignRequestUuid(redirectIfSignedToValidation: true)]
327
        #[FrontpageRoute(verb: 'GET', url: '/p/sign/{uuid}')]
328
        public function sign(string $uuid): TemplateResponse {
329
                $this->initialState->provideInitialState('action', JSActions::ACTION_SIGN);
×
330
                $this->initialState->provideInitialState('config',
×
331
                        $this->accountService->getConfig($this->userSession->getUser())
×
332
                );
×
333
                $this->initialState->provideInitialState('filename', $this->getFileEntity()->getName());
×
334
                $file = $this->fileService
×
335
                        ->setFile($this->getFileEntity())
×
336
                        ->setHost($this->request->getServerHost())
×
337
                        ->setMe($this->userSession->getUser())
×
338
                        ->setSignerIdentified()
×
339
                        ->setIdentifyMethodId($this->sessionService->getIdentifyMethodId())
×
340
                        ->setSignRequest($this->getSignRequestEntity())
×
341
                        ->showVisibleElements()
×
342
                        ->showSigners()
×
343
                        ->showSettings()
×
344
                        ->toArray();
×
345
                $this->initialState->provideInitialState('config', [
×
346
                        'identificationDocumentsFlow' => $file['settings']['needIdentificationDocuments'] ?? false,
×
347
                ]);
×
348
                $this->initialState->provideInitialState('id', $file['id']);
×
349
                $this->initialState->provideInitialState('nodeId', $file['nodeId']);
×
350
                $this->initialState->provideInitialState('needIdentificationDocuments', $file['settings']['needIdentificationDocuments'] ?? false);
×
351
                $this->initialState->provideInitialState('identificationDocumentsWaitingApproval', $file['settings']['identificationDocumentsWaitingApproval'] ?? false);
×
352
                $this->initialState->provideInitialState('status', $file['status']);
×
353
                $this->initialState->provideInitialState('statusText', $file['statusText']);
×
354
                $this->initialState->provideInitialState('signers', $file['signers']);
×
355
                $this->initialState->provideInitialState('sign_request_uuid', $uuid);
×
356
                $this->provideSignerSignatues();
×
357
                $this->initialState->provideInitialState('token_length', TokenService::TOKEN_LENGTH);
×
358
                $this->initialState->provideInitialState('description', $this->getSignRequestEntity()->getDescription() ?? '');
×
359
                if ($this->getFileEntity()->getNodeType() === 'envelope') {
×
360
                        $this->initialState->provideInitialState('pdfs', []);
×
361
                        $this->initialState->provideInitialState('envelopeFiles', $this->getEnvelopeChildFiles());
×
362
                } else {
363
                        $this->initialState->provideInitialState('pdfs', $this->getPdfUrls());
×
364
                        $this->initialState->provideInitialState('envelopeFiles', []);
×
365
                }
366
                $this->initialState->provideInitialState('nodeId', $this->getFileEntity()->getNodeId());
×
367
                $this->initialState->provideInitialState('nodeType', $this->getFileEntity()->getNodeType());
×
368

369
                Util::addScript(Application::APP_ID, 'libresign-external');
×
370
                $response = new TemplateResponse(Application::APP_ID, 'external', [], TemplateResponse::RENDER_AS_BASE);
×
371

372
                $policy = new ContentSecurityPolicy();
×
373
                $policy->allowEvalScript(true);
×
374
                $response->setContentSecurityPolicy($policy);
×
375

376
                return $response;
×
377
        }
378

379
        private function provideSignerSignatues(): void {
380
                $signatures = [];
×
381
                if ($this->userSession->getUser()) {
×
382
                        $signatures = $this->signerElementsService->getUserElements($this->userSession->getUser()->getUID());
×
383
                } else {
384
                        $signatures = $this->signerElementsService->getElementsFromSessionAsArray();
×
385
                }
386
                $this->initialState->provideInitialState('user_signatures', $signatures);
×
387
        }
388

389
        /**
390
         * @return string[] Array of PDF URLs
391
         */
392
        private function getPdfUrls(): array {
393
                return $this->signFileService->getPdfUrlsForSigning(
×
394
                        $this->getFileEntity(),
×
395
                        $this->getSignRequestEntity()
×
396
                );
×
397
        }
398

399
        private function getEnvelopeChildFiles(): array {
NEW
400
                $parentFileId = $this->getFileEntity()->getId();
×
NEW
401
                $currentSignRequest = $this->getSignRequestEntity();
×
NEW
402
                $childFiles = $this->fileMapper->getChildrenFiles($parentFileId);
×
NEW
403
                $childSignRequests = $this->signRequestMapper->getByEnvelopeChildrenAndIdentifyMethod(
×
NEW
404
                        $parentFileId,
×
NEW
405
                        $currentSignRequest->getId()
×
NEW
406
                );
×
407

NEW
408
                $signRequestsByFileId = [];
×
NEW
409
                foreach ($childSignRequests as $childSignRequest) {
×
NEW
410
                        $signRequestsByFileId[$childSignRequest->getFileId()] = true;
×
411
                }
412

NEW
413
                foreach ($childFiles as $childFile) {
×
NEW
414
                        if (!isset($signRequestsByFileId[$childFile->getId()])) {
×
NEW
415
                                $this->logger->warning('Missing sign request for envelope child file', [
×
NEW
416
                                        'parentFileId' => $parentFileId,
×
NEW
417
                                        'childFileId' => $childFile->getId(),
×
NEW
418
                                        'signRequestId' => $currentSignRequest->getId(),
×
NEW
419
                                ]);
×
420
                        }
421
                }
422

NEW
423
                return $this->fileListService->formatEnvelopeChildFilesForSignRequest(
×
NEW
424
                        $childFiles,
×
NEW
425
                        $childSignRequests,
×
NEW
426
                        $currentSignRequest,
×
NEW
427
                );
×
428
        }
429

430
        /**
431
         * Show signature page for identification document approval
432
         *
433
         * @param string $uuid File UUID for the identification document approval
434
         * @return TemplateResponse<Http::STATUS_OK, array{}>
435
         *
436
         * 200: OK
437
         * 404: Invalid UUID
438
         */
439
        #[NoAdminRequired]
440
        #[NoCSRFRequired]
441
        #[RequireSetupOk]
442
        #[FrontpageRoute(verb: 'GET', url: '/p/id-docs/approve/{uuid}')]
443
        public function signIdDoc($uuid): TemplateResponse {
444
                try {
445
                        $fileEntity = $this->signFileService->getFileByUuid($uuid);
×
446
                        $this->signFileService->getIdDocById($fileEntity->getId());
×
447
                } catch (DoesNotExistException) {
×
448
                        throw new LibresignException(json_encode([
×
449
                                'action' => JSActions::ACTION_DO_NOTHING,
×
450
                                'errors' => [['message' => $this->l10n->t('Invalid UUID')]],
×
451
                        ]), Http::STATUS_NOT_FOUND);
×
452
                }
453
                $this->initialState->provideInitialState('action', JSActions::ACTION_SIGN_ID_DOC);
×
454
                $this->initialState->provideInitialState('config',
×
455
                        $this->accountService->getConfig($this->userSession->getUser())
×
456
                );
×
457
                $this->initialState->provideInitialState('signer',
×
458
                        $this->signFileService->getSignerData(
×
459
                                $this->userSession->getUser(),
×
460
                        )
×
461
                );
×
462
                $this->initialState->provideInitialState('identifyMethods',
×
463
                        $this->signFileService->getAvailableIdentifyMethodsFromSettings()
×
464
                );
×
465
                $this->initialState->provideInitialState('filename', $fileEntity->getName());
×
466
                $file = $this->fileService
×
467
                        ->setFile($fileEntity)
×
468
                        ->setHost($this->request->getServerHost())
×
469
                        ->setMe($this->userSession->getUser())
×
470
                        ->setIdentifyMethodId($this->sessionService->getIdentifyMethodId())
×
471
                        ->showVisibleElements()
×
472
                        ->showSigners()
×
473
                        ->toArray();
×
474
                $this->initialState->provideInitialState('id', $file['id']);
×
475
                $this->initialState->provideInitialState('nodeId', $file['nodeId']);
×
476
                $this->initialState->provideInitialState('status', $file['status']);
×
477
                $this->initialState->provideInitialState('statusText', $file['statusText']);
×
478
                $this->initialState->provideInitialState('visibleElements', []);
×
479
                $this->initialState->provideInitialState('signers', []);
×
480
                $this->provideSignerSignatues();
×
481
                $signatureMethods = $this->identifyMethodService->getSignMethodsOfIdentifiedFactors($this->getSignRequestEntity()->getId());
×
482
                $this->initialState->provideInitialState('signature_methods', $signatureMethods);
×
483
                $this->initialState->provideInitialState('token_length', TokenService::TOKEN_LENGTH);
×
484
                $this->initialState->provideInitialState('description', '');
×
485
                $this->initialState->provideInitialState('pdf', [
×
486
                        'url' => $this->signFileService->getFileUrl($fileEntity->getId(), $uuid)
×
487
                ]);
×
488

489
                Util::addScript(Application::APP_ID, 'libresign-external');
×
490
                $response = new TemplateResponse(Application::APP_ID, 'external', [], TemplateResponse::RENDER_AS_BASE);
×
491

492
                $policy = new ContentSecurityPolicy();
×
493
                $policy->allowEvalScript(true);
×
494
                $response->setContentSecurityPolicy($policy);
×
495

496
                return $response;
×
497
        }
498

499
        /**
500
         * Use UUID of file to get PDF
501
         *
502
         * @param string $uuid File uuid
503
         * @return FileDisplayResponse<Http::STATUS_OK, array{Content-Type: string}>|DataResponse<Http::STATUS_NOT_FOUND, array<empty>, array{}>
504
         *
505
         * 200: OK
506
         * 401: Validation page not accessible if unauthenticated
507
         * 404: File not found
508
         */
509
        #[PrivateValidation]
510
        #[NoAdminRequired]
511
        #[NoCSRFRequired]
512
        #[RequireSetupOk]
513
        #[PublicPage]
514
        #[AnonRateLimit(limit: 300, period: 60)]
515
        #[FrontpageRoute(verb: 'GET', url: '/p/pdf/{uuid}')]
516
        public function getPdf($uuid) {
517
                try {
518
                        $file = $this->accountService->getPdfByUuid($uuid);
×
519
                } catch (DoesNotExistException) {
×
520
                        return new DataResponse([], Http::STATUS_NOT_FOUND);
×
521
                }
522

523
                return new FileDisplayResponse($file, Http::STATUS_OK, ['Content-Type' => $file->getMimeType()]);
×
524
        }
525

526
        /**
527
         * Use UUID of user to get PDF
528
         *
529
         * @param string $uuid Sign request uuid
530
         * @return FileDisplayResponse<Http::STATUS_OK, array{Content-Type: string}>
531
         *
532
         * 200: OK
533
         * 401: Validation page not accessible if unauthenticated
534
         */
535
        #[PrivateValidation]
536
        #[NoAdminRequired]
537
        #[NoCSRFRequired]
538
        #[RequireSignRequestUuid]
539
        #[PublicPage]
540
        #[RequireSetupOk]
541
        #[AnonRateLimit(limit: 300, period: 60)]
542
        #[FrontpageRoute(verb: 'GET', url: '/pdf/{uuid}')]
543
        public function getPdfFile($uuid): FileDisplayResponse {
544
                $files = $this->getNextcloudFiles();
×
545
                if (empty($files)) {
×
546
                        throw new LibresignException(json_encode([
×
547
                                'action' => JSActions::ACTION_DO_NOTHING,
×
548
                                'errors' => [['message' => $this->l10n->t('File not found')]],
×
549
                        ]), Http::STATUS_NOT_FOUND);
×
550
                }
551
                $file = current($files);
×
552
                return new FileDisplayResponse($file, Http::STATUS_OK, ['Content-Type' => $file->getMimeType()]);
×
553
        }
554

555
        /**
556
         * Show validation page
557
         *
558
         * @return TemplateResponse<Http::STATUS_OK, array{}>
559
         *
560
         * 200: OK
561
         * 401: Validation page not accessible if unauthenticated
562
         */
563
        #[PrivateValidation]
564
        #[NoAdminRequired]
565
        #[NoCSRFRequired]
566
        #[RequireSetupOk(template: 'validation')]
567
        #[PublicPage]
568
        #[AnonRateLimit(limit: 30, period: 60)]
569
        #[FrontpageRoute(verb: 'GET', url: '/p/validation')]
570
        public function validation(): TemplateResponse {
571
                if ($this->getFileEntity()) {
×
572
                        $this->initialState->provideInitialState('config',
×
573
                                $this->accountService->getConfig($this->userSession->getUser())
×
574
                        );
×
575
                        $this->initialState->provideInitialState('file', [
×
576
                                'uuid' => $this->getFileEntity()?->getUuid(),
×
577
                                'description' => $this->getSignRequestEntity()?->getDescription(),
×
578
                        ]);
×
579
                        $this->initialState->provideInitialState('filename', $this->getFileEntity()?->getName());
×
580
                        $this->initialState->provideInitialState('pdfs', $this->getPdfUrls());
×
581
                        $this->initialState->provideInitialState('signer',
×
582
                                $this->signFileService->getSignerData(
×
583
                                        $this->userSession->getUser(),
×
584
                                        $this->getSignRequestEntity(),
×
585
                                )
×
586
                        );
×
587
                }
588

589
                Util::addScript(Application::APP_ID, 'libresign-validation');
×
590
                $response = new TemplateResponse(Application::APP_ID, 'validation', [], TemplateResponse::RENDER_AS_BASE);
×
591

592
                return $response;
×
593
        }
594

595
        /**
596
         * Show validation page
597
         *
598
         * The path is used only by frontend
599
         *
600
         * @param string $uuid Sign request uuid
601
         * @return TemplateResponse<Http::STATUS_OK, array{}>
602
         *
603
         * 200: OK
604
         * 303: Redirected to validation page
605
         * 401: Validation page not accessible if unauthenticated
606
         */
607
        #[PrivateValidation]
608
        #[NoAdminRequired]
609
        #[NoCSRFRequired]
610
        #[RequireSetupOk]
611
        #[PublicPage]
612
        #[AnonRateLimit(limit: 30, period: 60)]
613
        #[FrontpageRoute(verb: 'GET', url: '/validation/{uuid}')]
614
        public function validationFileWithShortUrl(): TemplateResponse {
615
                return $this->validationFilePublic($this->request->getParam('uuid'));
×
616
        }
617

618
        /**
619
         * Show validation page
620
         *
621
         * @param string $uuid Sign request uuid
622
         * @return TemplateResponse<Http::STATUS_OK, array{}>
623
         *
624
         * 200: OK
625
         */
626
        #[NoAdminRequired]
627
        #[NoCSRFRequired]
628
        #[RequireSetupOk(template: 'main')]
629
        #[PublicPage]
630
        #[RequireSignRequestUuid]
631
        #[FrontpageRoute(verb: 'GET', url: '/reset-password')]
632
        public function resetPassword(): TemplateResponse {
633
                $this->initialState->provideInitialState('config',
×
634
                        $this->accountService->getConfig($this->userSession->getUser())
×
635
                );
×
636

637
                Util::addScript(Application::APP_ID, 'libresign-main');
×
638
                $response = new TemplateResponse(Application::APP_ID, 'reset_password');
×
639

640
                return $response;
×
641
        }
642

643
        /**
644
         * Public page to show validation for a specific file UUID
645
         *
646
         * @param string $uuid File uuid
647
         * @return TemplateResponse<Http::STATUS_OK, array{}>
648
         *
649
         * 200: OK
650
         * 401: Validation page not accessible if unauthenticated
651
         */
652
        #[PrivateValidation]
653
        #[NoAdminRequired]
654
        #[NoCSRFRequired]
655
        #[RequireSetupOk(template: 'validation')]
656
        #[PublicPage]
657
        #[AnonRateLimit(limit: 30, period: 60)]
658
        #[FrontpageRoute(verb: 'GET', url: '/p/validation/{uuid}')]
659
        public function validationFilePublic(string $uuid): TemplateResponse {
660
                try {
661
                        $this->signFileService->getFileByUuid($uuid);
×
662
                        $this->fileService->setFileByUuid($uuid);
×
663
                } catch (DoesNotExistException) {
×
664
                        try {
665
                                $signRequest = $this->signFileService->getSignRequestByUuid($uuid);
×
666
                                $libresignFile = $this->signFileService->getFile($signRequest->getFileId());
×
667
                                $this->fileService->setFile($libresignFile);
×
668
                        } catch (DoesNotExistException) {
×
669
                                $this->initialState->provideInitialState('action', JSActions::ACTION_DO_NOTHING);
×
670
                                $this->initialState->provideInitialState('errors', [['message' => $this->l10n->t('Invalid UUID')]]);
×
671
                        }
672
                }
673
                if ($this->userSession->isLoggedIn()) {
×
674
                        $this->initialState->provideInitialState('config',
×
675
                                $this->accountService->getConfig($this->userSession->getUser())
×
676
                        );
×
677
                        $this->fileService->setMe($this->userSession->getUser());
×
678
                } else {
679
                        $this->initialState->provideInitialState('config',
×
680
                                $this->accountService->getConfig()
×
681
                        );
×
682
                }
683

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

686
                $this->initialState->provideInitialState('file_info',
×
687
                        $this->fileService
×
688
                                ->setIdentifyMethodId($this->sessionService->getIdentifyMethodId())
×
689
                                ->setHost($this->request->getServerHost())
×
690
                                ->showVisibleElements()
×
691
                                ->showSigners()
×
692
                                ->showSettings()
×
693
                                ->showMessages()
×
694
                                ->showValidateFile()
×
695
                                ->toArray()
×
696
                );
×
697

698
                Util::addScript(Application::APP_ID, 'libresign-validation');
×
699
                if (class_exists(LoadViewer::class)) {
×
700
                        $this->eventDispatcher->dispatchTyped(new LoadViewer());
×
701
                }
702
                $response = new TemplateResponse(Application::APP_ID, 'validation', [], TemplateResponse::RENDER_AS_BASE);
×
703

704
                return $response;
×
705
        }
706
}
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