Skip to content

feat: add observer participant role for signature requests - #8143

Merged
vitormattos merged 83 commits into
LibreSign:mainfrom
lfals:feat/participant-role-observer
Sep 16, 2026
Merged

vitormattos merged 83 commits into
LibreSign:mainfrom
lfals:feat/participant-role-observer

Conversation

@lfals

@lfals lfals commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Resolves: #6957

📝 Summary

Adds an observer participant role (UI label: Observer / Testemunha) to signature requests. Observers can follow document progress and open a read-only view of the request (including PDF and signature positions), but cannot sign or perform write/signing operations. The feature is gated by the policy enable_observer_profile (default: disabled).

Backend is the source of truth: role validation, signing denial, status/progress calculations, DocMDP signer limits, sequential signing, e-mail/notification routing, UUID authorization, and API contracts all treat observers separately from signers. The request UI exposes separate Signers and Observers lists when the policy is enabled.

Authorization is split explicitly:

  • RequireSignRequestUuid — signer-only (signing UI, id-docs, signature elements, and other write paths reject observers)
  • RequireParticipantUuid — participant read-only (PDF/getPdfFile allows both signers and observers)

Also includes: policy snapshots for existing requests/envelopes, preserve observers when reordering signers, require at least one signer before requesting signatures, hide signing-order numbers for observers, OBSERVING status 4 in API summaries, observer mail copy (“ready to view”), Account observer deep-links to the authenticated read-only filelist, and focused regression coverage (PHPUnit, Vitest, Behat, Playwright).

🧪 How to test

  1. Enable the policy Enable observer profile (system, group, or user scope as needed).
  2. Open the Files app, select a PDF, and open Request signature.
  3. Use Add → Signer and Add → Observer to add participants (Add menu appears only when the policy is enabled; otherwise keep the classic Add signer button).
  4. Confirm the UI shows two sections (Signers / Observers).
  5. Try requesting signatures with only observers → must be blocked (toast + API error).
  6. Add at least one signer and request signatures → request should succeed; observers must not appear as pending signers or receive signing-order numbers.
  7. Confirm observers are hidden from the signature-positions placement targets; “View signature positions” stays available for read-only review when there is at least one signer.
  8. As an Account observer, open the notification/link → authenticated read-only request sidebar (filelist/sign?uuid=…), not the public validation/sign flow.
  9. As an Email observer, open the mail link → public validation / view document; subject/body use “ready to view”.
  10. In ordered numeric flow, reorder signers with observers present → observers remain in the list without order counters.
  11. Open validation for a document with both roles → signers and observers appear in separate groups; observer status shows Observing when applicable.
  12. Authorization boundary: with an anonymous request using an observer UUID header:
    • GET /apps/libresign/pdf/<OBSERVER_UUID>200
    • POST /api/v1/id-docs and POST /api/v1/signature/elements422 (“Observers cannot sign this document”)
  13. Confirm visible signature elements cannot be assigned to observers (UI + API).

🎨 UI / Front‑end changes

  • Policy-gated Add → Signer / Observer menu (classic Add signer when policy disabled)
  • Separate Signers and Observers lists in request and validation views
  • Observer-specific labels in participant modal (search, name validation, custom message)
  • Client-side guard: block signature request when only observers are present
  • Drag-and-drop reorder in ordered flow preserves observers; no signing-order enumerator for observers
  • Hide observers from signature-positions placement; allow read-only open of positions when signers exist
  • Authenticated observers open a read-only request sidebar; Files list canSign ignores observers
  • Public validation CSS on UUID pages; anonymous View document opens the public PDF directly
  • “Send notification” action for observers (uses validation/update mail templates)
  • Screenshots before/after
image Captura de Tela 2026-09-04 às 16 12 59 image
🏚️ Before 🏡 After
Single signer list only Signers + Observers sections when policy enabled
  • Tested in multiple browsers (Chrome, Firefox, Safari) – optional but appreciated
  • Components, Unit (with vitest) and/or e2e (with Playwright) tests added - Required
  • Accessibility verified (contrast, keyboard navigation, screen reader friendly) – if applicable
  • Design review approved – optional, link to feedback if available
  • Documentation updated (if applicable) – docs repository

🚧 Tasks

  • Sync translations via Transifex before release (strings live in source until the bot updates l10n/)
  • Add/update user-facing documentation for observer role in LibreSign documentation

⚙️ API / Back‑end changes

  • participant_role column on sign requests (signer | observer) with migration
  • Policy provider enable_observer_profile (default false, system/group/user scopes) with per-request/envelope policy snapshot
  • Validation: reject observers when policy disabled; reject signing and visible-element assignment for observers
  • Exclude observers from signing status, envelope progress, sequential signing, and DocMDP signer counts
  • Observer notifications: Email → validation / “ready to view”; Account → authenticated read-only filelist deep-link
  • Require at least one signing participant when moving out of draft / requesting signatures
  • Map OBSERVING as status 4 in signer summaries; reject invalid ParticipantRole values; freeze role after signed
  • UUID auth split: RequireParticipantUuid (read-only PDF) vs RequireSignRequestUuid (signer-only writes)
  • Unit and/or integration tests added – required for backend changes
  • Capabilities updated (if applicable) – if adding/modifying Nextcloud capabilities
  • Documentation updated (if applicable) - docs repository
  • API documentation updated with the command composer openapi if necessary

🚧 Tasks

  • Confirm no environment still depends on experimental participant_profile_id (column was never shipped in official migrations)

✅ Checklist

  • I have read and followed the contribution guide.
  • OpenAPI specs and TypeScript types regenerated (composer openapi, npm run typescript:generate)
  • Focused PHPUnit, Vitest, Behat, and Playwright coverage for observer flows
  • Conventional Commits with DCO sign-off on all commits

🤖 AI (if applicable)

  • The content of this PR was partially or fully generated using AI

@github-project-automation github-project-automation Bot moved this to 0. Backlog in Roadmap Aug 31, 2026
@lfals
lfals force-pushed the feat/participant-role-observer branch from 130da68 to cfa42bc Compare August 31, 2026 12:35
@lfals
lfals marked this pull request as draft August 31, 2026 13:45
@lfals
lfals marked this pull request as ready for review August 31, 2026 17:33
@lfals
lfals marked this pull request as draft August 31, 2026 17:33
@lfals
lfals marked this pull request as ready for review August 31, 2026 17:45
Comment thread l10n/en_GB.js
@github-project-automation github-project-automation Bot moved this from 0. Backlog to 1. to do in Roadmap Sep 2, 2026
@lfals
lfals marked this pull request as draft September 2, 2026 16:48
@lfals
lfals marked this pull request as ready for review September 2, 2026 19:44
@lfals
lfals requested a review from vitormattos September 2, 2026 20:01
@lfals
lfals marked this pull request as draft September 2, 2026 22:36
@lfals
lfals force-pushed the feat/participant-role-observer branch from 8a74b3e to 6c8ddb1 Compare September 2, 2026 22:41
Comment thread lib/Migration/Version19001Date20260831000000.php Outdated
Comment thread src/components/RightSidebar/RequestSignatureTab.vue
Comment thread lib/Enum/ParticipantRole.php
Comment thread lib/Service/SignRequest/SignRequestService.php
Comment thread src/utils/participantRole.ts Outdated
Comment thread playwright/support/request-signature.ts
Comment thread tests/php/Unit/Service/MailServiceTest.php Outdated
@vitormattos

Copy link
Copy Markdown
Member

If possible, could you also add a few screenshots to the PR description showing the observer flow?

It would be useful to show:

  • the Add menu with Signer and Observer;
  • the Signers and Observers sections;
  • the validation page with an observer.

Also, sorry for reviewing this PR while it is still a draft. I added the comments now only to help keep the work close to the expected flow and avoid bigger changes later.

For the next review, it is better to add me as a reviewer and request the review when the PR is ready and is no longer a draft. After pushing new changes, when the PR is ready for review again, use the GitHub option to request my review again. This makes it clear when a new review is needed.

@lfals

lfals commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

If possible, could you also add a few screenshots to the PR description showing the observer flow?

It would be useful to show:

  • the Add menu with Signer and Observer;
  • the Signers and Observers sections;
  • the validation page with an observer.

Also, sorry for reviewing this PR while it is still a draft. I added the comments now only to help keep the work close to the expected flow and avoid bigger changes later.

For the next review, it is better to add me as a reviewer and request the review when the PR is ready and is no longer a draft. After pushing new changes, when the PR is ready for review again, use the GitHub option to request my review again. This makes it clear when a new review is needed.

No problem, I left it as a draft after the changes you requested. I was fixing the tests that broke. I'm going to add the observer flow and let you know when it's ready for review.

@vitormattos

@lfals
lfals marked this pull request as ready for review September 4, 2026 14:42
@lfals
lfals requested a review from vitormattos September 4, 2026 14:45
@lfals
lfals requested a review from vitormattos September 4, 2026 16:32

@vitormattos vitormattos left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only a small change:

At file appinfo/info.xml, change from:

16.0.0-dev.1

to:

16.0.0-dev.2

This will make all environments of all developers to run the migration because will identify that the version was changed.

@vitormattos

Copy link
Copy Markdown
Member

Could you also check this?

It would be useful to show:

  • the Add menu with Signer and Observer;
  • the Signers and Observers sections;
  • the validation page with an observer.

@vitormattos

Copy link
Copy Markdown
Member

I found another case during manual testing.

I created a signature request with one signer while the Observer policy was disabled. Then I enabled the Observer policy in Administration Settings and opened the same request again.

The UI correctly showed Add → Observer, but saving the observer returned:

Observer participants are not enabled

This happens because the frontend uses the current policy, while the backend keeps the old false value from the file snapshot.

I think the expected behavior should be:

  1. If an existing request has Observer disabled and the current policy becomes enabled, Observer can be added while the request can still be edited.
  2. After an Observer is added to this request, disabling the global policy later must not remove Observer from this existing flow. It should still be possible to keep and add observers to this request.
  3. New requests should continue to use the current policy value.

The saved policy state may need to move from false to true when Observer is first added to an existing request. Once enabled for that request, it should stay enabled for this flow.

There is also a frontend error handling problem in this case. The API returned HTTP 422 with the message Observer participants are not enabled, but the UI did not show this message.

IdentifySigner.vue should show the error message returned by the API when saving a participant fails, instead of losing it in the catch.

Please add regression tests for both parts:

  • backend/integration: create a request with Observer disabled, enable the policy, add an observer, disable the policy again, and confirm that the same request can still use observers;
  • frontend: make the participant save request fail with a 422 OCS response and confirm that the API error message is shown to the user and the participant modal stays open.

vitormattos

This comment was marked as resolved.

@vitormattos

Copy link
Copy Markdown
Member

The same rule also needs backend protection.

lib/Service/FileElementService.php currently accepts the signRequestId for a visible element without checking if this participant can sign.

The API must reject a visible signature element when the signRequestId belongs to an observer.

Please add a unit test for this validation and an integration/Behat scenario that sends an observer signRequestId and confirms that the API rejects it. Keep a signer case to confirm the normal flow still works.

@lfals
lfals requested a review from vitormattos September 9, 2026 23:03
Skip signing validation for observer UUIDs on getPdfFile, reject non-PDF
fetch responses in VisibleElements, and assert the positions modal renders.

Signed-off-by: Luis Amorim <luisfelipeamorim@hotmail.com>
…routing

Details now opens the request sidebar for requesters who can also sign,
and keeps the sign sidebar only when the user cannot manage the request.

Signed-off-by: Luis Amorim <luisfelipeamorim@hotmail.com>
The frozen-geolocation associateToSigners mock still expected afterPersist
as argument 9, which broke PHPUnit after the observer-role parameter was
added.

Signed-off-by: Luis Amorim <luisfelipeamorim@hotmail.com>
Observers must never receive signingOrder numbers when ordered signing is enabled.

Signed-off-by: Luis Amorim <luisfelipeamorim@hotmail.com>
…links

Address PR review feedback for OBSERVING status in the contract, observer email wording, and authenticated deep-links for Account observers.

Signed-off-by: Luis Amorim <luisfelipeamorim@hotmail.com>
Keep RequireSignRequestUuid signer-only so observer UUIDs cannot hit
write endpoints, and authorize PDF viewing via RequireParticipantUuid.

Signed-off-by: Luis Amorim <luisfelipeamorim@hotmail.com>
Signed-off-by: Luis Amorim <luisfelipeamorim@hotmail.com>
Avoid PHPUnit coverage risky failures when the trait stub also executes
entity and page-aware controller code under beStrictAboutCoverageMetadata.

Signed-off-by: Luis Amorim <luisfelipeamorim@hotmail.com>
Show a workbench warning when both policies are enabled, and cover the
email observer flow that cannot open authenticated-only validation.

Signed-off-by: Luis Amorim <luisfelipeamorim@hotmail.com>
Expose sibling effective flags via resolvedStateMeta so the Workbench
warning is backed by policy resolution instead of frontend-only store reads.

Signed-off-by: Luis Amorim <luisfelipeamorim@hotmail.com>
Unify RequireSignRequestUuid and RequireParticipantUuid handling through
authorizeRequiredUuid, and rename loadNextcloudFileFromSignRequestUuid to
the role-neutral loadNextcloudFileFromUuid.

Signed-off-by: Luis Amorim <luisfelipeamorim@hotmail.com>
Centralize policy_snapshot effectiveValue reads in
ObserverProfilePolicyValue so the service and file applier stay in sync.

Signed-off-by: Luis Amorim <luisfelipeamorim@hotmail.com>
Add groupCount, userCount, and everyoneCount so Policy Workbench editor
specs type-check against the OpenAPI EffectivePolicyState shape.

Signed-off-by: Luis Amorim <luisfelipeamorim@hotmail.com>
PolicySourceTest still constructed ValidationAccessPolicy with no args
after the sibling-meta reader dependency was added.

Signed-off-by: Luis Amorim <luisfelipeamorim@hotmail.com>
Use the active resolve context when reading sibling effective booleans so
group/user workbench warnings do not follow the admin personal posture.

Signed-off-by: Luis Amorim <luisfelipeamorim@hotmail.com>
Nextcloud cannot autowire IPolicySource, which broke policy resolution
across PHPUnit, Behat, and Playwright. Also apply php-cs on the new test.

Signed-off-by: Luis Amorim <luisfelipeamorim@hotmail.com>
The only remaining failure was list.feature with cURL error 52 while
195 other scenarios passed on the same run.

Signed-off-by: Luis Amorim <luisfelipeamorim@hotmail.com>
SQLite Behat CI keeps failing randomly with cURL error 52 empty reply
from the PHP built-in server while other DB matrices stay green.

Signed-off-by: Luis Amorim <luisfelipeamorim@hotmail.com>
The retry was added for a SQLite empty-reply flake and changed request
behavior for the whole suite; investigate that CI failure separately.

Signed-off-by: Luis Amorim <luisfelipeamorim@hotmail.com>
When a disabled observer snapshot is upgraded or re-checked, use the
file owner's policy context instead of the acting manager/current user.

Signed-off-by: Luis Amorim <luisfelipeamorim@hotmail.com>
Signed-off-by: Luis Amorim <luisfelipeamorim@hotmail.com>
…updates

Signed-off-by: Luis Amorim <luisfelipeamorim@hotmail.com>
Signed-off-by: Luis Amorim <luisfelipeamorim@hotmail.com>
@lfals
lfals force-pushed the feat/participant-role-observer branch from becc745 to 11f30a2 Compare September 16, 2026 17:13
@LibreSign LibreSign deleted a comment from codecov-commenter Sep 16, 2026
@lfals
lfals requested a review from vitormattos September 16, 2026 17:32
@vitormattos

Copy link
Copy Markdown
Member

Thanks a lot for all the work on this PR, Luiz.

This one was not simple at all. We had many rounds of review, more than 40 comments, and a very large number of files to check. It also touches many important parts of LibreSign: database, frontend, backend, API, security, and some core flows of the project.

Because of this, the review took longer than usual, but I think the result is much better and safer now.

Thank you for your patience with all the feedback and for taking the time to investigate and fix each point. I know this was a lot of work.

Great job getting this one to the finish line.

@vitormattos
vitormattos merged commit 1d5bfa4 into LibreSign:main Sep 16, 2026
69 of 71 checks passed
@github-project-automation github-project-automation Bot moved this from 1. to do to 4. to release in Roadmap Sep 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: 4. to release

Development

Successfully merging this pull request may close these issues.

Add participant role permissions: Sign and Observe

2 participants