Repository navigation
fix: freeze the PDF certification level with the request snapshot - #9375
Open
paulo-raoni wants to merge 2 commits into
Open
paulo-raoni wants to merge 2 commits into
paulo-raoni wants to merge 2 commits into
Conversation
Later changes to the PDF certification policy no longer apply to it. Signed-off-by: Paulo Raoni <raoni_jkd@hotmail.com>
Signed-off-by: Paulo Raoni <raoni_jkd@hotmail.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Refs: #8715
📝 Summary
Next slice of #8715:
docmdpfollows the request-snapshot freeze. As decided in the issue (2026-10-08, item 1), the signing operation uses the frozen request value: once a request is frozen, both signing engines certify with the level the request stores, not with the current system level.One commit per step:
fix: certify a frozen request with the level it stores– until now JSignPdf and the PHP-native engine read only the current system level (DocMdp\ConfigService::getLevel()), whatever the request stored.SignFileService::configureEngine()now hands the request's level to the engine when the request (or its envelope) is frozen and has a storeddocmdp, throughSignEngineHandler::setDocMdpLevel(), next to the existing setters. A given level is used as is, in both directions: a request stored as not certified is not certified while the system level certifies, and the reverse. Without a stored level, or on a draft that was never sent, the current level applies as before. The level is set on everyconfigureEngine(), null included, so it never carries over to the next file.fix: freeze the PDF certification once the request is sent–DocMdpFilePolicyApplier::sync()returns before any resolution when the request is frozen. Nothing ornullsubmitted keeps the stored level; the same level (also as a numeric string) is a no-op; any other value gets 422, "The PDF certification cannot be changed after the signing flow has started." This also covers a PATCH by uuid:docmdpwas already in the core-flow sync.docmdpABLE_TO_SIGNor later without marker, or document of a frozen envelope), back in DRAFT includednulldocmdpwas stored in the snapshotdocmdp_levelcolumnBehaviour change: besides a later admin change no longer reaching a sent request, frozen requests now certify with their own resolved level instead of the system level. When a group or user rule differs from the system value (for example system 2, group 3), a request from that group was certified at 2 and is now certified at 3, which is the level stored for it. No payload changes; nothing new is sent to anonymous or
#[PublicPage]endpoints. The request sidebar has nodocmdpcontrol, so there is no frontend change.Not in this PR: the other snapshot keys and envelope new-file inheritance.
🧪 How to test
composer test:unit -- --filter 'DocMdpFilePolicyApplierTest|SignFileServiceTest|PhpNativeHandlerTest|Pkcs12HandlerTest' vendor/bin/phpunit -c tests/php/phpunit.xml tests/php/Integration/Handler/SignEngine/JSignPdf/JSignPdfHandlerTest.php vendor/bin/phpunit -c tests/php/phpunit.xml tests/php/Api/Controller/RequestSignatureControllerTest.phpPhpNativeHandlerTest/JSignPdfHandlerTest: a given level wins over the current one (another certifying level, not certified while the system certifies, certified while it does not); without one, the current level applies.Pkcs12HandlerTest: the level reaches the concrete engine.SignFileServiceTest: a frozen request (sent, back in DRAFT, sent before the marker existed, document of a sent envelope) hands its stored level to the engine; a request without a stored level and a draft never sent use the current one.DocMdpFilePolicyApplierTest: the strict sync rules above; a draft still resolves the submitted level.RequestSignatureControllerTest: a sent request keeps level 2 after the admin sets 3 and an unrelated PATCH by uuid; another level by uuid gets 422 and the snapshot is unchanged; a draft sent by uuid picks up the admin change.Manually: enable DocMDP, send a request, change the certification level as admin, then sign: the signed PDF carries the level the request was sent with.
Validation evidence
composer psalm, php-cs-fixer dry-run on the touched filesRan locally, on top of current
main:tests/php/Unit/Service/Policy(1030 tests);--filter 'JSignPdfHandlerTest|PhpNativeHandlerTest|Pkcs12HandlerTest'(181 tests);SignFileServiceTest(157 tests);--filter DocMdp(120 tests);RequestSignatureControllerTest(11 tests);composer psalm; php-cs-fixer on the changed files.Not run locally: the whole PHPUnit, Behat and Playwright suites, and no end-to-end signature checking the certification level of a signed PDF; the engines are covered by unit and integration tests.
composer openapi/npm run typescript:generatewere not needed: no contract change.⚙️ API / Back-end changes
docmdplevel by both engines.docmdpis frozen with the request policy snapshot; a different value after the freeze returns 422.composer openapiwhen necessary – not needed, no contract change❓ Open questions
docmdpwas stored in the snapshot still signs with the current level, like the footer. Itsdocmdp_levelcolumn does hold the level from creation time, but rows older than the column hold 0, so using it would sign those requests without certification. Should signing use the column for them?DocMdp\Validator::getDocMdpLevel()treats a stored level 0 as unset and reads the current config. It runs when signers are added, not at signing time, so it is not changed here. As a result, a second signer can be refused on a request sent as not certified if the admin later enables certification.✅ Checklist
🤖 AI (if applicable)