Skip to content

[stable31] feat: docmdp implementation#6028

Merged
vitormattos merged 45 commits intostable31from
backport/6021/stable31
Dec 8, 2025
Merged

[stable31] feat: docmdp implementation#6028
vitormattos merged 45 commits intostable31from
backport/6021/stable31

Conversation

@backportbot-libresign
Copy link

@backportbot-libresign backportbot-libresign bot commented Dec 8, 2025

Backport of #6021

Warning, This backport's changes differ from the original and might be incomplete ⚠️

Todo

  • Review and resolve any conflicts
  • Amend HEAD commit to remove the line stating to skip CI

Learn more about backports at https://docs.nextcloud.com/server/stable/go.php?to=developer-backports.

Add database columns to support DocMDP (Document Modification Detection and Prevention):

- libresign_sign_request.docmdp_level (SMALLINT, default 0)
  Stores certification level: 0=none, 1=no changes, 2=form fill, 3=form fill + annotations

- libresign_file.modification_status (SMALLINT, default 0)
  Tracks modification detection: 0=unchecked, 1=unmodified, 2=allowed, 3=violation

Both tables checked for existence before adding columns for safe migration.

Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
Add docmdpLevel field (SMALLINT) to store DocMDP certification level
for each signature request. Includes getter/setter and type mapping.

Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
Service to manage DocMDP configuration stored in app settings:
- isEnabled(): check if DocMDP is enabled
- setEnabled(bool): enable/disable DocMDP
- getLevel(): get default certification level (DocMdpLevel enum)
- setLevel(DocMdpLevel): set default certification level
- getConfig(): return full config with enabled status and available levels

Configuration stored in single key 'docmdp_level' for simplicity.
When disabled, key is deleted from database.

Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
Add DocMDP support to JSignPdf signing process:
- Inject DocMdpConfigService to access admin configuration
- Add getCertificationLevel() to retrieve enabled level name
- Append -cl parameter to JSignParam when DocMDP is enabled
- Uses enum->name directly for JSignPdf compatibility

When DocMDP is enabled, JSignPdf will certify PDFs with configured level:
NOT_CERTIFIED, CERTIFIED_NO_CHANGES_ALLOWED, CERTIFIED_FORM_FILLING,
or CERTIFIED_FORM_FILLING_AND_ANNOTATIONS.

Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
Add POST /api/v1/admin/docmdp/config endpoint:
- Accepts 'enabled' (bool) and 'defaultLevel' (int) parameters
- Validates DocMDP level with DocMdpLevel::tryFrom()
- Saves configuration via DocMdpConfigService
- Returns success message or error with appropriate HTTP status

Allows admins to enable/disable DocMDP and set default certification level.

Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
Inject DocMdpConfigService and provide docmdp_config to frontend:
- Includes enabled status
- Includes default level
- Includes available levels with labels and descriptions

Makes DocMDP configuration available to admin settings UI.

Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
Add Vue component for DocMDP configuration in admin settings:
- Toggle switch to enable/disable DocMDP
- Radio buttons for certification level selection:
  * No certification (P=0)
  * No changes allowed (P=1)
  * Form filling allowed (P=2)
  * Form filling and annotations (P=3)
- Loading/saving/error indicators
- Saves via POST /api/v1/admin/docmdp/config

Uses Nextcloud Vue components (NcCheckboxRadioSwitch, NcLoadingIcon,
NcSavingIndicatorIcon, NcNoteCard).

Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
Add DocMDP information to signers array in getFileData():
- docmdp: certification level and compliance status
- modifications: detected modification types
- modification_validation: validation result details

Also improve error handling with structured logging when file content
retrieval fails, including fileId and exception context.

Makes DocMDP validation results available to frontend for display.

Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
Add proper PHPDoc annotations:
- Detailed parameter descriptions for enabled and defaultLevel
- Complete DataResponse return type with status codes and schemas
- HTTP status code documentation (200, 400, 500)

Fixes OpenAPI generation errors.

Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
Regenerate OpenAPI documentation files to include setDocMdpConfig endpoint:
- openapi-administration.json
- openapi-full.json
- TypeScript type definitions

Generated from updated PHPDoc annotations.

Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
Remove unused import and fix PHPDoc formatting:
- Remove unused DocMdpLevel import from JSignPdfHandler
- Fix trailing whitespace in AdminController PHPDoc

Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
- AdminTest: Add DocMdpConfigService mock parameter to Admin constructor
- FileServiceTest: Remove DocMDP fields (docmdp, modifications, modification_validation) from test comparisons

These tests were failing because:
1. Admin class now requires DocMdpConfigService as 9th constructor parameter
2. FileService.getFileData() now includes DocMDP-related fields in signers array

Both fixes ensure tests properly handle the new DocMDP feature additions.

Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
- Remove duplicate getLabel() and getDescription() methods from DocMdpConfigService
- Use enum methods directly by passing IL10N instance
- Improve descriptions to differentiate between approval and certifying signatures
- Align descriptions with ISO 32000 DocMDP specification terminology

The new descriptions clearly distinguish:
- Approval signature (NOT_CERTIFIED): allows all modifications
- Certifying signature (levels 1-3): restricts modifications based on level

Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
Consolidate PDF generation logic into a reusable trait to:
- Eliminate code duplication across test files
- Provide single source of truth for PDF fixtures
- Support both minimal (DocMdpHandler) and FPDI-valid (FileService) PDFs
- Cover all DocMDP levels and ISO 32000-1 validation scenarios

Includes 25 fixture methods covering:
- DocMDP levels 0-3
- Form field/annotation/structural modifications
- ISO signature validation edge cases
- ICP-Brasil compliance testing

Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
Add public method to check if DocMDP level allows additional signatures.
Returns false only for CERTIFIED_NO_CHANGES_ALLOWED (level 1).

Required for FileService to validate signature requests against DocMDP policy.

Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
Extract file validation logic from getNodeFromData() into dedicated methods:
- validateFileContent(): public method for PDF validation with FPDI
- validateDocMdpAllowsSignatures(): private method for DocMDP level 1 check

Benefits:
- Improved testability with public validation interface
- Separation of concerns (validation vs node creation)
- Enables DocMDP policy enforcement before file processing
- Adds comprehensive @throws documentation

Requires DocMdpHandler injection via FileService constructor.

Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
Add DocMdpHandler dependency to FileService constructor to enable
DocMDP validation in TFile trait.

Assigns to TFile::docMdpHandler property for validateDocMdpAllowsSignatures().

Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
Fix test setup and assertion patterns:
- Add missing appConfig mock initialization
- Inject DocMdpHandler from server container
- Use expectNotToPerformAssertions() for validation tests
- Place expectations at method start (PHPUnit best practice)

Validation tests assert 'no exception thrown' behavior:
- testValidateFileContentAllowsDocMdpLevel2/3
- testValidateFileContentAllowsUnsignedPdf
- testValidateFileContentSkipsNonPdfFiles
- testValidateFileContentRejectsDocMdpLevel1

Uses PdfFixtureTrait for all PDF generation.

All 98 tests passing (154 assertions).

Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
Replace all inline PDF generation with PdfFixtureTrait methods.

Achieves 100% fixture consolidation across 39 tests.

Benefits:
- Zero code duplication
- Consistent PDF structures
- Easier maintenance
- Better test clarity

All tests passing.

Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
Add DocMdpHandler dependency to enable future DocMDP validation
during signature request processing.

Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
Add DocMDP admin configuration component and enhance validation view:

Settings.vue:
- Import and register DocMDP component
- Add to admin settings panel

Validation.vue:
- Display document certification level
- Show modification validation status
- Add collapsible DocMDP details section
- Visual indicators for modification status (success/warning/error)
- Icons: mdiShieldCheck, mdiShieldOff, mdiInformationOutline
- Revision count display with pluralization

Modification status mapping (File::MODIFICATION_*):
- 1 (unmodified) → green checkmark
- 2 (allowed modifications) → yellow alert
- 3 (violations) → red cancel
- 0 (unchecked) → help icon

Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
Sort use statements alphabetically.

Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
- Remove unused $scenarioName parameter from test method and data provider
- Use expectNotToPerformAssertions() for scenarios that shouldn't throw exceptions
- Consolidate exception expectations into if-else block for better readability
- Simplify data provider by removing redundant scenario name keys

Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
- Extract PDF-to-resource conversion logic from validateDocMdpAllowsSignatures
- New protected method getLibreSignFileAsResource() handles file retrieval and resource creation
- Improves testability by separating I/O operations from validation logic
- Enables mocking of file access layer independently from DocMDP validation
- Maintains resource cleanup with try-finally block in validateDocMdpAllowsSignatures

Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
- Implement DocMdpHandler to validate PDF Document Modification Detection and Prevention (DocMDP)
- Add allowsAdditionalSignatures() method to check if PDF permits additional signatures
- Support detection of DocMDP level 1 (no changes allowed) certification
- Parse PDF signature dictionaries and transformation parameters
- Prevent signatures on DocMDP level 1 certified documents per PDF specification
- Enable compliance with PDF document certification and signature workflows

Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
- Test allowsAdditionalSignatures() with various DocMDP permission levels
- Verify unsigned PDFs allow additional signatures
- Test DocMDP level 0 (not certified) allows signatures
- Test DocMDP level 1 (no changes) blocks additional signatures
- Test DocMDP levels 2 and 3 allow signatures with form filling/annotations
- Validate DocMDP detection with real-world ICP-Brasil certificate example
- Test complete certificate chain validation (LYSEON TECH 4-level chain)
- Use PdfFixtureTrait for generating test PDFs with various DocMDP configurations
- Ensure proper resource handling and edge case coverage

Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
- Add validation to ensure fopen() returns valid resource
- Throw LibresignException if temporary resource creation fails
- Prevents false return value that violates psalm type contract
- Add @throws LibresignException to method documentation
- Fixes psalm FalsableReturnStatement error

Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>

[skip ci]
Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
…on failure

Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
…memory only)

Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
…memory resource creation

Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
…dliness

Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
… line 344 to improve behavior as required.\n\nSigned-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>

Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
Changed implementation at line 172 as required.

Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
…rror messages for loading and saving configuration to be more generic and user-friendly.

Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
Tests now only check for exception type, not message, for DocMDP-related logic.

Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
Removes translated message assertion for robustness.

Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
Simplifies test setup and improves maintainability.

Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
@vitormattos vitormattos marked this pull request as ready for review December 8, 2025 20:48
@vitormattos vitormattos merged commit fa52e3e into stable31 Dec 8, 2025
63 checks passed
@vitormattos vitormattos deleted the backport/6021/stable31 branch December 8, 2025 20:52
@github-project-automation github-project-automation bot moved this from 0. Needs triage to 4. to release in Roadmap Dec 8, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

1 participant