feat: add backend support for signature rejection - #8257
Conversation
| #[\Override] | ||
| public function apply(FileEntity $file, array $data): void { | ||
| $user = ($data['userManager'] ?? null) instanceof IUser ? $data['userManager'] : null; | ||
| $activeContext = $this->extractActiveContext($data); | ||
| $resolvedPolicy = $activeContext === null | ||
| ? $this->policyService->resolveForUser(SignatureRejectionPolicy::KEY, $user) | ||
| : $this->policyService->resolveForUser(SignatureRejectionPolicy::KEY, $user, [], $activeContext); | ||
| $this->storeSignatureRejectionPolicySnapshot($file, $resolvedPolicy); | ||
| } |
There was a problem hiding this comment.
I think the rejection policy should control whether the requester is allowed to enable rejection for a signature request.
When the policy is enabled, the requester should be able to choose if rejection is enabled for that file or envelope. This choice must be stored with the request and become the effective value for the signing flow.
When the policy is disabled, the requester must not be able to enable rejection, including through a direct API request.
The requester should still be able to change this choice while the signing flow has not started. Once the flow starts, the stored choice must stay frozen. A later policy change must also not change the behavior of an existing request.
Could we follow the same request override and snapshot lifecycle used in #8143, and add integration coverage for normal files and envelopes?
There was a problem hiding this comment.
Thanks, this is much closer, but there are still two cases to fix.
First, if the policy has enabled: true and the request does not send a rejection choice, the current code enables rejection for that request automatically.
This is not the behavior we want. The policy should only allow the requester to enable rejection. If the requester does not choose it, rejection should stay disabled for that request.
Second, if a draft already has rejection disabled and later the requester edits something else without sending the rejection setting again, the current code can enable rejection because it resolves the policy again.
Please keep the stored request value when no new rejection value is sent.
Before the signing flow starts, the requester should still be able to change the rejection setting explicitly.
Please add integration tests for these cases, including an envelope:
- policy enabled + no request choice → rejection stays disabled;
- draft with rejection disabled + unrelated update → rejection stays disabled;
- requester can explicitly change the setting before the signing flow starts;
- after the signing flow starts, the stored value cannot change.
There was a problem hiding this comment.
The code now looks correct for the cases we discussed. Thanks.
I think we only need to complete the integration coverage for envelopes before resolving this thread.
We already test that an enabled policy alone does not enable rejection for an envelope, but the request lifecycle tests below only cover a normal file.
Could we add an envelope scenario that confirms the stored rejection choice is preserved after the request is created, including after a later policy change or an unrelated draft update?
This is important because file and envelope requests can follow different backend paths.
435954a to
12fe7d3
Compare
Signed-off-by: Mohammed Sufiyan Ahmed <mohammed.sufiyan.msa@gmail.com>
12fe7d3 to
a277f15
Compare
Resolves : #8159
Summary
Adds the backend foundation for the signature rejection workflow described in #7832: a signer
can decline to sign, the refusal is persisted with its timestamp and optional comment, and the
rules governing it come from a new
signature_rejectionpolicy in the existing Policies & Rulesarchitecture. No temporary global app configuration was introduced.
Disabled by default — with the default policy value, behavior is identical to today.
How to test
POST /ocs/v2.php/apps/libresign/api/v1/policies/system/signature_rejectionwith
value = {"enabled":true,"comment_mode":"required","cancel_workflow":true}POST /ocs/v2.php/apps/libresign/api/v1/sign/file_id/<FILE_ID>/rejectwith no
comment→ 422, "A comment is required to reject this signature request."comment=I do not agree→ 200,status: 3,workflowCanceled: true.cancel_workflow=falseand confirm the second signer can still sign.Automated coverage:
tests/integration/features/sign/reject.feature.API / Back-end changes
signature_rejectionpolicy provider (system + group scope, group-admin delegation).Sub-rules:
comment_mode(disabled/optional/required),allow_private_comment,cancel_workflow,public_status,show_comment_on_validation. A delegated rule cannotdowngrade a comment the parent marked as required.
FilePolicyapplier writing tometadata.policy_snapshot, so a later policy change never alters an in-flight workflow.SignRequestStatus::REJECTEDandFileStatus::CANCELED; migrationVersion18004Date20260906000000addsrejected_at,rejection_commentandrejection_comment_privatetolibresign_sign_request.SignatureRejectionServiceenforces the rules, persists the rejection, closes the workflowwhen configured, and dispatches
SignatureRejectedEvent.POST /api/v1/sign/file_id/{fileId}/rejectandPOST /api/v1/sign/uuid/{uuid}/reject.SignatureRejectionVisibilityService: the requester and the rejecting signer always see thefull record; other readers see only what the policy makes public, and a comment marked
private is never disclosed to them.
fileCanBeSigned()status gate; a secondrejection and rejection after signing are refused explicitly.
hasPendingLowerOrderSigners()now treats a rejected signer as no longer pending, soordered-numeric flows can continue past a rejection when the policy allows it.