Describe the bug
ID document approval never completes: uploader's draft sign request blocks file from reaching SIGNED status
To reproduce
Affected version
LibreSign 13.2.6 (also present in current main, source ref de81c97).
Environment
- Nextcloud with LibreSign, identification documents feature enabled ("Ablauf der Ausweisdokumente ermöglichen").
- Users provided via LDAP; approval groups configured (e.g.
["admin","libresignadmin"]).
Summary
When the "identification documents" workflow is enabled, an uploaded ID document can never reach the APPROVED/SIGNED state, even after an authorized approver signs it. The file stays at "Partially signed" forever.
Root cause
Two interacting issues in the ID-doc workflow:
-
IdDocsService::addIdDocs() creates a sign request for the uploader without setting a status. SignRequestEntity defaults to status 0 (DRAFT). The uploader therefore can never sign their own document (and by design should not need to).
-
SignFileService::evaluateStatusFromSigners() counts every sign request of the file, including the uploader's DRAFT request. When an approver signs, the count is 1 signed of 2 total, so the file is set to PARTIAL_SIGNED and never to SIGNED, because the uploader DRAFT request is never signed.
Result: the file is stuck at PARTIAL_SIGNED, "Approved by" stays empty, and the uploader can consequently never obtain an approved ID document to sign other documents.
Steps to reproduce
- Enable identification documents, set an approval group.
- As a regular user (member of a signing-request group, not of the approval group), upload an ID document.
- As a member of the approval group, open the document via the approval flow and sign it.
- Observe: status remains "Partially signed", "Approved by" empty.
Expected
After an authorized approver signs the ID document, the document should reach SIGNED/APPROVED. The uploader should not be required to sign their own ID document (they are already authenticated via their login/signature password).
Proposed fix
In SignFileService::evaluateStatusFromSigners(), exclude DRAFT sign requests (status 0) from the completion count, so the file becomes SIGNED once all active signers have signed:
private function evaluateStatusFromSigners(): ?int {
$signers = $this->getSigners();
$signers = array_filter($signers, fn ($s) => $s->getStatus() !== 0); // ignore DRAFT (uploader) requests
$total = count($signers);
if ($total === 0) {
return null;
}
$totalSigned = count(array_filter($signers, fn ($s) => $s->getSigned() !== null));
if ($totalSigned === $total) {
return FileStatus::SIGNED->value;
}
if ($totalSigned > 0) {
return FileStatus::PARTIAL_SIGNED->value;
}
return null;
}
This was verified to make the workflow complete without any manual database intervention.
Additional related findings (separate, lower priority)
While investigating, the following were also observed in 13.2.6 and may deserve their own issues:
IdDocsMapper::list() uses an INNER JOIN on oc_users when filtering by userId. LDAP users do not exist in oc_users, so their uploaded ID documents are invisible on the account page ("Noch nicht gesendet"/"not sent yet") even though the record exists. Changing the join to a LEFT JOIN fixes it; the WHERE clause on id.user_id still filters correctly.
AccountSettingsProvider::canRequestSign appears to read the approval_group setting instead of groups_request_sign, so the frontend "request signature" button visibility can diverge from the backend permission check (which uses groups_request_sign).
- Opening
/f/sign/{uuid} for an ID document without the idDocApproval=true parameter renders an empty (white) content area; the ID-doc approval flow requires that parameter.
I'm happy to open the related findings as separate issues if preferred.
Expected behavior
No response
Screenshots
No response
Operational System
Nethserver 8 rocky linux running nextcloud with app libresign
Browser Type and Version
No response
Libresign Version
13.2.6
Nextcloud Server Version
No response
Logs from Nextcloud Server
No fatal entries in nextcloud.log or php-fpm output at the time of signing.
The failure is logical, not an exception: the file status is computed from all
sign requests including the uploader's DRAFT request, so the file never reaches
SIGNED. Verified directly in the database:
oc_libresign_file.status stays 2 (PARTIAL_SIGNED) after the approver signs.
oc_libresign_sign_request for the file: uploader request status=0 (DRAFT, unsigned),
approver request status=2 (SIGNED). Total counted 2, signed 1 -> PARTIAL_SIGNED.
Logs from Developers tools
No response
Additional context
No response
Describe the bug
ID document approval never completes: uploader's draft sign request blocks file from reaching SIGNED status
To reproduce
Affected version
LibreSign 13.2.6 (also present in current main, source ref de81c97).
Environment
["admin","libresignadmin"]).Summary
When the "identification documents" workflow is enabled, an uploaded ID document can never reach the APPROVED/SIGNED state, even after an authorized approver signs it. The file stays at "Partially signed" forever.
Root cause
Two interacting issues in the ID-doc workflow:
IdDocsService::addIdDocs()creates a sign request for the uploader without setting a status.SignRequestEntitydefaults to status0(DRAFT). The uploader therefore can never sign their own document (and by design should not need to).SignFileService::evaluateStatusFromSigners()counts every sign request of the file, including the uploader's DRAFT request. When an approver signs, the count is 1 signed of 2 total, so the file is set to PARTIAL_SIGNED and never to SIGNED, because the uploader DRAFT request is never signed.Result: the file is stuck at PARTIAL_SIGNED, "Approved by" stays empty, and the uploader can consequently never obtain an approved ID document to sign other documents.
Steps to reproduce
Expected
After an authorized approver signs the ID document, the document should reach SIGNED/APPROVED. The uploader should not be required to sign their own ID document (they are already authenticated via their login/signature password).
Proposed fix
In
SignFileService::evaluateStatusFromSigners(), exclude DRAFT sign requests (status 0) from the completion count, so the file becomes SIGNED once all active signers have signed:This was verified to make the workflow complete without any manual database intervention.
Additional related findings (separate, lower priority)
While investigating, the following were also observed in 13.2.6 and may deserve their own issues:
IdDocsMapper::list()uses an INNER JOIN onoc_userswhen filtering byuserId. LDAP users do not exist inoc_users, so their uploaded ID documents are invisible on the account page ("Noch nicht gesendet"/"not sent yet") even though the record exists. Changing the join to a LEFT JOIN fixes it; the WHERE clause onid.user_idstill filters correctly.AccountSettingsProvider::canRequestSignappears to read theapproval_groupsetting instead ofgroups_request_sign, so the frontend "request signature" button visibility can diverge from the backend permission check (which usesgroups_request_sign)./f/sign/{uuid}for an ID document without theidDocApproval=trueparameter renders an empty (white) content area; the ID-doc approval flow requires that parameter.I'm happy to open the related findings as separate issues if preferred.
Expected behavior
No response
Screenshots
No response
Operational System
Nethserver 8 rocky linux running nextcloud with app libresign
Browser Type and Version
No response
Libresign Version
13.2.6
Nextcloud Server Version
No response
Logs from Nextcloud Server
No fatal entries in nextcloud.log or php-fpm output at the time of signing.
The failure is logical, not an exception: the file status is computed from all
sign requests including the uploader's DRAFT request, so the file never reaches
SIGNED. Verified directly in the database:
oc_libresign_file.status stays 2 (PARTIAL_SIGNED) after the approver signs.
oc_libresign_sign_request for the file: uploader request status=0 (DRAFT, unsigned),
approver request status=2 (SIGNED). Total counted 2, signed 1 -> PARTIAL_SIGNED.
Logs from Developers tools
No response
Additional context
No response