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

LibreSign / libresign / 22048620506

16 Feb 2026 03:00AM UTC coverage: 51.605%. First build
22048620506

Pull #6891

github

web-flow
Merge 92e331da8 into 4cc0c7397
Pull Request #6891: feat: add id doc approver workflow

171 of 424 new or added lines in 24 files covered. (40.33%)

9145 of 17721 relevant lines covered (51.61%)

6.15 hits per line

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

33.33
/lib/Controller/IdDocsController.php
1
<?php
2

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

9
namespace OCA\Libresign\Controller;
10

11
use Exception;
12
use OCA\Libresign\AppInfo\Application;
13
use OCA\Libresign\Helper\ValidateHelper;
14
use OCA\Libresign\Middleware\Attribute\RequireSignRequestUuid;
15
use OCA\Libresign\ResponseDefinitions;
16
use OCA\Libresign\Service\IdDocsService;
17
use OCA\Libresign\Service\SignFileService;
18
use OCP\AppFramework\Http;
19
use OCP\AppFramework\Http\Attribute\AnonRateLimit;
20
use OCP\AppFramework\Http\Attribute\ApiRoute;
21
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
22
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
23
use OCP\AppFramework\Http\Attribute\PublicPage;
24
use OCP\AppFramework\Http\DataResponse;
25
use OCP\IL10N;
26
use OCP\IRequest;
27
use OCP\IUserSession;
28
use Psr\Log\LoggerInterface;
29

30
/**
31
 * @psalm-import-type LibresignIdDocs from ResponseDefinitions
32
 * @psalm-import-type LibresignPagination from ResponseDefinitions
33
 * @psalm-import-type LibresignFile from ResponseDefinitions
34
 */
35
class IdDocsController extends AEnvironmentAwareController implements ISignatureUuid {
36
        use LibresignTrait;
37
        public function __construct(
38
                IRequest $request,
39
                protected SignFileService $signFileService,
40
                protected IL10N $l10n,
41
                protected IdDocsService $idDocsService,
42
                protected IUserSession $userSession,
43
                protected ValidateHelper $validateHelper,
44
                protected LoggerInterface $logger,
45
        ) {
46
                parent::__construct(Application::APP_ID, $request);
3✔
47
        }
48

49
        /**
50
         * Add identification documents to user profile
51
         *
52
         * @param LibresignIdDocs[] $files The list of files to add to profile
53
         * @return DataResponse<Http::STATUS_OK, array<empty>, array{}>|DataResponse<Http::STATUS_UNAUTHORIZED, array{file: ?int, type: 'info'|'warning'|'danger', message: string}, array{}>
54
         *
55
         * 200: Certificate saved with success
56
         * 401: No file provided or other problem with provided file
57
         */
58
        #[PublicPage]
59
        #[AnonRateLimit(limit: 30, period: 60)]
60
        #[NoAdminRequired]
61
        #[NoCSRFRequired]
62
        #[RequireSignRequestUuid(skipIfAuthenticated: true)]
63
        #[ApiRoute(verb: 'POST', url: '/api/{apiVersion}/id-docs', requirements: ['apiVersion' => '(v1)'])]
64
        public function addFiles(array $files): DataResponse {
65
                try {
66
                        if ($user = $this->userSession->getUser()) {
2✔
67
                                $this->idDocsService->addIdDocs($files, $user);
2✔
68
                        } elseif ($signRequest = $this->getSignRequestEntity()) {
×
69
                                $this->idDocsService->addFilesToDocumentFolder($files, $signRequest);
×
70
                        } else {
71
                                throw new Exception('Invalid data');
×
72
                        }
73
                        return new DataResponse([], Http::STATUS_OK);
1✔
74
                } catch (\Exception $exception) {
1✔
75
                        $exceptionData = json_decode($exception->getMessage());
1✔
76
                        if (isset($exceptionData->file)) {
1✔
77
                                $message = [
1✔
78
                                        'file' => $exceptionData->file,
1✔
79
                                        'type' => $exceptionData->type,
1✔
80
                                        'message' => $exceptionData->message
1✔
81
                                ];
1✔
82
                        } else {
83
                                $message = [
×
84
                                        'file' => null,
×
85
                                        'type' => null,
×
86
                                        'message' => $exception->getMessage()
×
87
                                ];
×
88
                        }
89
                        return new DataResponse(
1✔
90
                                $message,
1✔
91
                                Http::STATUS_UNAUTHORIZED
1✔
92
                        );
1✔
93
                }
94
        }
95

96
        /**
97
         * Delete file from account
98
         *
99
         * @param int $nodeId the nodeId of file to be delete
100
         * @param string|null $uuid Sign request UUID for unauthenticated access
101
         *
102
         * @return DataResponse<Http::STATUS_OK, array<empty>, array{}>|DataResponse<Http::STATUS_UNAUTHORIZED, array{messages: string[]}, array{}>
103
         *
104
         * 200: File deleted with success
105
         * 401: Failure to delete file from account
106
         */
107
        #[PublicPage]
108
        #[AnonRateLimit(limit: 30, period: 60)]
109
        #[NoAdminRequired]
110
        #[NoCSRFRequired]
111
        #[RequireSignRequestUuid(skipIfAuthenticated: true)]
112
        #[ApiRoute(verb: 'DELETE', url: '/api/{apiVersion}/id-docs/{nodeId}', requirements: ['apiVersion' => '(v1)'])]
113
        public function delete(int $nodeId, ?string $uuid = null): DataResponse {
114
                try {
NEW
115
                        if ($user = $this->userSession->getUser()) {
×
NEW
116
                                $this->idDocsService->deleteIdDoc($nodeId, $user);
×
NEW
117
                        } elseif ($signRequest = $this->getSignRequestEntity()) {
×
NEW
118
                                $this->idDocsService->deleteIdDocBySignRequest($nodeId, $signRequest);
×
119
                        } else {
NEW
120
                                throw new Exception('Invalid data');
×
121
                        }
122
                        return new DataResponse([], Http::STATUS_OK);
×
123
                } catch (\Exception $exception) {
×
124
                        return new DataResponse(
×
125
                                [
×
126
                                        'messages' => [
×
127
                                                $exception->getMessage(),
×
128
                                        ],
×
129
                                ],
×
130
                                Http::STATUS_UNAUTHORIZED,
×
131
                        );
×
132
                }
133
        }
134

135
        /**
136
         * List files of unauthenticated account
137
         *
138
         * @param string|null $userId User ID to filter by
139
         * @param int|null $signRequestId Sign request ID to filter by
140
         * @param int|null $page the number of page to return
141
         * @param int|null $length Total of elements to return
142
         * @return DataResponse<Http::STATUS_OK, array{pagination: LibresignPagination, data: LibresignFile[]}, array{}>|DataResponse<Http::STATUS_NOT_FOUND, array{message: string}, array{}>
143
         *
144
         * 200: Certificate saved with success
145
         * 404: No file provided or other problem with provided file
146
         */
147
        #[PublicPage]
148
        #[AnonRateLimit(limit: 30, period: 60)]
149
        #[NoCSRFRequired]
150
        #[RequireSignRequestUuid(skipIfAuthenticated: true)]
151
        #[ApiRoute(verb: 'GET', url: '/api/{apiVersion}/id-docs', requirements: ['apiVersion' => '(v1)'])]
152
        public function listOfUnauthenticatedSigner(
153
                ?string $userId = null,
154
                ?int $signRequestId = null,
155
                ?int $page = null,
156
                ?int $length = null,
157
        ): DataResponse {
158
                try {
159
                        if ($user = $this->userSession->getUser()) {
×
160
                                $userId = $user->getUID();
×
161
                        } elseif ($signRequest = $this->getSignRequestEntity()) {
×
162
                                $signRequestId = $signRequest->getId();
×
163
                        } elseif (!$userId && !$signRequestId) {
×
164
                                throw new Exception('Invalid data');
×
165
                        }
166

167
                        $filter = array_filter([
×
168
                                'userId' => $userId,
×
169
                                'signRequestId' => $signRequestId,
×
170
                        ], static fn ($var) => $var !== null);
×
171

172
                        $return = $this->idDocsService->list($filter, $page, $length);
×
173
                        return new DataResponse($return, Http::STATUS_OK);
×
174
                } catch (\Throwable $th) {
×
175
                        return new DataResponse(
×
176
                                [
×
177
                                        'message' => $th->getMessage()
×
178
                                ],
×
179
                                Http::STATUS_NOT_FOUND
×
180
                        );
×
181
                }
182
        }
183

184
        /**
185
         * List files that need to be approved
186
         *
187
         * @param string|null $userId User ID to filter by
188
         * @param int|null $signRequestId Sign request ID to filter by
189
         * @param int|null $page the number of page to return
190
         * @param int|null $length Total of elements to return
191
         * @param string|null $sortBy Sort field (e.g., 'owner', 'file_type', 'status')
192
         * @param string|null $sortOrder Sort order (ASC or DESC)
193
         * @return DataResponse<Http::STATUS_OK, array{pagination: LibresignPagination, data: ?LibresignFile[]}, array{}>|DataResponse<Http::STATUS_NOT_FOUND, array{message: string}, array{}>
194
         *
195
         * 200: OK
196
         * 404: Account not found
197
         */
198
        #[NoAdminRequired]
199
        #[NoCSRFRequired]
200
        #[ApiRoute(verb: 'GET', url: '/api/{apiVersion}/id-docs/approval/list', requirements: ['apiVersion' => '(v1)'])]
201
        public function listToApproval(
202
                ?string $userId = null,
203
                ?int $signRequestId = null,
204
                ?int $page = null,
205
                ?int $length = null,
206
                ?string $sortBy = null,
207
                ?string $sortOrder = null,
208
        ): DataResponse {
209
                try {
210
                        $this->validateHelper->userCanApproveValidationDocuments($this->userSession->getUser());
1✔
211
                        $filter = array_filter([
1✔
212
                                'userId' => $userId,
1✔
213
                                'signRequestId' => $signRequestId,
1✔
214
                        ], static fn ($var) => $var !== null);
1✔
215
                        $sort = [];
1✔
216
                        if ($sortBy !== null) {
1✔
217
                                $sort[$sortBy] = $sortOrder ?? 'DESC';
×
218
                        }
219
                        $return = $this->idDocsService->list($filter, $page, $length, $sort);
1✔
220
                        return new DataResponse($return, Http::STATUS_OK);
1✔
221
                } catch (\Throwable $th) {
×
222
                        return new DataResponse(
×
223
                                [
×
224
                                        'message' => $th->getMessage()
×
225
                                ],
×
226
                                Http::STATUS_NOT_FOUND
×
227
                        );
×
228
                }
229
        }
230
}
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