PDFlibPas odpovídá na otázku FieldMDP přes TPDFlib.GetSignatureModificationVerdict. Když metoda dostane podepsané PDF, jeho heslo a jméno podpisového pole, projde každý inkrementální update přidaný za tímto podpisem, vyřeší plně kvalifikované jméno každého formulářového pole, kterého se tyto updaty dotkly, a vrátí 3, když se změnilo pole zamčené FieldMDP, a 2, když jen pole odemčená. GetSignatureModificationDetail pak pojmenuje revizi a pole
Pracovní postup, který to potřebuje, je formulář s více schvalovateli. Žadatel vyplní Order.Customer a Order.Total v nákupní žádosti a podepíše Approval.Requester, čímž ta dvě pole zamkne. Manažer doplní nákladové středisko a podepíše Approval.Manager. O dva týdny později někdo otevře soubor v desktopovém editoru a ve třetím inkrementálním updatu Order.Total přehoupne nahoru. Každý podpis pořád projde ověřením, protože každý pokrývá jen svůj vlastní byte rozsah. Otázky, které auditor skutečně ptá, zní: které pole se změnilo po kterém podpisu a zda byla ta změna povolená
Proč samo DocMDP neodhalí upravené zamčené pole?
DocMDP to neodhalí, protože DocMDP klasifikuje druhy změn, ne jednotlivá pole. Úrovně certifikačních oprávnění v ISO 32000-1 §12.8.2.2 říkají, zda pozdější updaty můžou vyplňovat formuláře a podepisovat (P 2), nebo i anotovat (P 3), a to oprávnění platí pro všechna pole v dokumentu stejně. Úprava Order.Total je vyplňování formuláře, takže certifikace P 2 je splněna právě tou úpravou, kterou žadatel chtěl zakázat. Klasifikace úprav na úrovni dokumentu v PDFlibPas pokrývá tuhle vrstvu a tenhle článek jde o stupeň jemněji
FieldMDP je vrstva s granularitou na úrovni pole. Podpisové pole může nést slovník /Lock (ISO 32000-1 §12.7.4.5) a podpis, který drží, může ve své referenci nést FieldMDP transformaci (ISO 32000-1 §12.8.2.4). Obojí používá tutéž slovní zásobu: /Action /All zamkne všechna pole, /Action /Include zamkne jména uvedená v /Fields a /Action /Exclude zamkne vše kromě jmen v /Fields. Před v3.586.0 věděl GetSignatureModificationVerdict, že pozdější revize změnila nějaké formulářové pole, ale ne které, takže každý dokument se zámkem pole, jehož pozdější updaty přesáhly validační materiál, se vrátil jako 4, nerozhodnuto. Mapa revizí teď zaznamenává vyřešené jméno pole pro každý objekt související s polem, který klasifikuje, a právě to proměňuje onu 4 ve skutečnou 2 nebo 3
Zápis zámku pole při podpisu každého schvalovatele
TPDFlib.SetSignProcessFieldLock nastavuje zámek pro další podpis, který podpisový proces vytvoří. Akce 1 je All, 2 je Include, 3 je Exclude a 0 zámek smaže; Include a Exclude berou seznam plně kvalifikovaných jmen polí oddělených středníky. Podepsané pole pak nese slovník /Lock a odpovídající FieldMDP transformaci — přesně ten pár, který očekávají přísné validátory
var
Pdf: TPDFlib;
SignId: Integer;
begin
Pdf := TPDFlib.Create;
try
SignId := Pdf.NewSignProcessFromFile('purchase-request.pdf', '');
if SignId = 0 then
raise Exception.Create('cannot open the form');
try
Pdf.SetSignProcessField(SignId, 'Approval.Requester');
Pdf.SetSignProcessPFXFromFile(SignId, 'requester.pfx', PfxPassword);
// 2 = Include: tímto podpisem se zamknou jen tato pole
if Pdf.SetSignProcessFieldLock(SignId, 2,
'Order.Customer;Order.Total') <> 1 then
raise Exception.Create('field lock rejected');
Pdf.EndSignProcessToFile(SignId, 'purchase-request-signed.pdf');
if Pdf.GetSignProcessResult(SignId) <> 1 then
raise Exception.CreateFmt('signing failed, code %d',
[Pdf.GetSignProcessResult(SignId)]);
finally
Pdf.ReleaseSignProcess(SignId);
end;
finally
Pdf.Free;
end;
end;
SetSignProcessFieldLock vrátí 0 pro volání Include nebo Exclude bez jmen a také pro seznam obsahující prázdnou položku, třeba 'Order.Customer;;Order.Total'; odmítnuté volání nechá dříve nastavený zámek v platnosti, takže kontrolujte návratovou hodnotu, místo abyste předpokládali, že poslední volání vyhrálo. Zbytek nastavení podpisu — volbu digestu, rezervaci /Contents a result kódy — popisuje průvodce PAdES podpisem a validací pro PDFlibPas. Jeden designový bod se týká jen schvalovacích řetězců: zámek /All na podpisu žadatele zamkne i Approval.Manager, protože podpisové pole je taky formulářové pole. Pro datová pole, která vlastní jednotliví schvalovatelé, použijte Include, nebo Exclude se seznamem pozdějších podpisových polí
Verdikt napříč schvalovacím řetězcem
GetSignatureModificationVerdict soudí jeden podpis najednou, takže schvalovací řetězec se kontroluje zavoláním jednou za každé podpisové pole. Návratová hodnota je malá uzavřená množina a každá hodnota pro kontrolora znamená něco jiného:
- 0: soubor nebo jmenované podpisové pole se nepodařilo přečíst
- 1: za podpisem se nic nepřidalo
- 2: přidaly se jen změny, které efektivní oprávnění dovolují, a zamčeného pole se nic nedotklo
- 3: pozdější revize udělala změnu, která není povolená, včetně úpravy pole, které FieldMDP zamyká
- 4: odpověď se nepodařilo určit, což se nesmí nikdy číst jako schválení
- 5: podpis nekončí na hranici revize, takže bajty leží uvnitř rozsahu, který tvrdí, že pokrývá
const
Approvals: array[0..2] of WideString =
('Approval.Requester', 'Approval.Manager', 'Approval.Finance');
var
Pdf: TPDFlib;
I, Verdict: Integer;
begin
Pdf := TPDFlib.Create;
try
for I := Low(Approvals) to High(Approvals) do
begin
Verdict := Pdf.GetSignatureModificationVerdict(
'purchase-request-final.pdf', FormPassword, Approvals[I]);
// oba gettery popisují volání, které právě proběhlo, takže je čtěte před dalším voláním
Log.Add(Format('%s: verdict %d, MDP level %d, %s',
[Approvals[I], Verdict, Pdf.GetSignatureEffectiveMDPLevel,
Pdf.GetSignatureModificationDetail]));
case Verdict of
1, 2: ; // přijato
3: RejectForm(Approvals[I]); // detail pojmenuje pole a revizi
else
QueueForManualReview(Approvals[I]); // 0, 4, 5
end;
end;
finally
Pdf.Free;
end;
end;
Vezměte si manipulovanou nákupní žádost, kde revize 0 je prázdný formulář, revize 1 a 2 jsou oba podpisy a revize 3 je ta úprava. Protože definice zámků se sbírají ze všech podepsaných polí, verdikt pro Approval.Manager už vynucuje zámek žadatele a jeho jediným pozdějším updatem je sama úprava. Když tento update přepsal jen slovník pole, verdikt je 3 a detail zní Revision 3 changed locked form field "Order.Total", which FieldMDP does not allow. Ta jediná řádka odpovídá na obě auditní otázky. Verdikt pro Approval.Requester musí nejdřív projít kolem revize s podpisem manažera, a z důvodů z oddílu o omezeních níže často nahlásí 4, takže nejnovější podpis obvykle dává nejostřejší odpověď. GetSignatureEffectiveMDPLevel hlásí nejnižší hodnotu DocMDP P nalezenou mezi podpisy, nebo 3, když dokument nic necertifiovalo, a oba gettery zůstávají platné jen do dalšího volání verdiktu
Jak PDFlibPas pozná, které pole revize změnila?
PDFlibPas rekonstruuje jméno pole z grafu objektů každé revize, protože změněný objekt nosí své celé jméno jen zřídka. Slovník pole s /FT nebo /V, stejně jako widget anotace, drží své částečné jméno jen v /T. Plně kvalifikované jméno popsané v ISO 32000-1 §12.7.3.2 je řetěz částečných jmen od kořene, spojených tečkami, takže mapa revizí stoupá nahoru po odkazech /Parent a každé nalezené /T předřadí. Každý rodič se čte ze své nejnovější definice platné k klasifikované revizi nebo starší, což zabrání tomu, aby pozdější přejmenování rodičovského uzlu přepsalo historii dřívějších úprav
Porovnávání vyřešeného jména se zámky je záměrně jednoduché. Jméno odpovídá položce v /Fields, když se jí rovná přesně, nebo když začíná položkou následovanou tečkou, takže zámek na Order zamkne i Order.Total a každého dalšího potomka. All zamyká všechna jména, Include zamyká odpovídající jména a Exclude zamyká každé jméno, které neodpovídá. Definice zámků přicházejí ze všech podepsaných podpisových polí v souboru; když má pole obojí, slovník /Lock i FieldMDP transformaci, použijí se hodnoty z /Lock. Abychom přesně viděli, co který podpis zamkl, nižší třída TPDFlibSignDoc z unitu PDFlibDigitalSign vystavuje report oprávnění, který verdikt čte:
uses
PDFlibDigitalSign;
var
SignDoc: TPDFlibSignDoc;
Report: TPDFlibSignaturePermissionReport;
Names: TStringList;
I, J: Integer;
Line: string;
begin
SignDoc := TPDFlibSignDoc.Create;
Names := TStringList.Create;
try
if not SignDoc.Open('purchase-request-final.pdf', FormPassword, False) then
Exit;
try
SignDoc.GetSignatureFieldNames(Names);
for I := 0 to Names.Count - 1 do
begin
SignDoc.GetSignaturePermissionInfoByName(Names[I], Report);
if not (Report.HasFieldLock or Report.HasFieldMDP) then
Continue;
Line := Names[I] + ': ' + string(Report.FieldLockAction);
for J := 0 to High(Report.FieldNames) do
Line := Line + ' ' + string(Report.FieldNames[J]);
if Report.HasFieldLock and Report.HasFieldMDP and
not Report.FieldMDPMatches then
Line := Line + ' (lock and FieldMDP transform disagree)';
Log.Add(Line);
end;
finally
SignDoc.Close;
end;
finally
Names.Free;
SignDoc.Free;
end;
end;
Šifrované formuláře: předejte heslo, jinak jsou jména nečitelná
Šifrovaný formulář se musí nejdřív dešifrovat, než se dá vyřešit jakékoli jméno pole, takže GetSignatureModificationVerdict potřebuje argument Password k něčemu víc, než jen k otevření souboru. Řetězce jako /T jsou šifrované po objektech a pole zapsaná do komprimovaných object streamů se nedají přečíst vůbec, dokud se obsahující stream nedešifruje a nerozbalí. Od v3.586.0 interní mapa revizí bere heslo, připojuje dešifrovací kontext, když parsuje object streamy a revizní slovníky, a dešifruje každý objekt, který dekóduje, včetně dřívějších verzí, vůči kterým dělá diff, a rodičů, po kterých stoupá
Dešifrování po objektech v mapě revizí se zapne jen tehdy, když Password není prázdné. Verdikt spočtený na šifrovaném souboru s prázdným heslem je proto potřeba brát jako neověřený, i když se dokument sám otevřel bez dotazu. Uložte heslo formuláře vedle záznamu pracovního postupu a předávejte ho při každém volání
Co verdikt FieldMDP nepokrývá
Kontrola polí je strukturální odpověď na jednu úzkou otázku a několik případů leží mimo ni. Znalost těchto případů předem je to, co brání tomu, aby se ze 4 stala eskalace na podporu:
- Srovnávání polí běží jen poté, co kontrola na úrovni dokumentu vyšla čistě a alespoň jedna pozdější revize změnila víc než validační materiál nebo časová razítka; porušení DocMDP se nahlásí jako 3, než se na jakékoli pole podívá
- Objekty klasifikované jako podpisové, ale bez jména pole — třeba slovník hodnoty podpisu, přepsaný slovník AcroForm nebo změna katalogu omezená na
/AcroFormnebo/Perms— donutí verdikt vrátit 4, místo aby hádal, takže revize přidávající pozdější schvalovací podpis pod aktivním zámkem často skončí na 4 - Jméno pole dostávají jen slovníky polí a widget anotace; mapa revizí nemá žádnou dedikovanou třídu pro appearance streamy, takže editor, který regeneruje vzhledy spolu s hodnotou, může kontrola na úrovni dokumentu zastavit jako změnu obsahu dřív, než se kontrola polí spustí, a detailní řetězec oba případy odliší
- Sken se zastaví u prvního zamčeného pole nebo nepojmenované položky, kterou potká, takže 3 pojmenuje jedno provinilé pole, ne všechny
- Zámky se sbírají ze všech podepsaných podpisových polí bez řazení v čase, takže zámek napsaný pozdějším podepisujícím se aplikuje i na updaty, které přišly před ním; to může výsledek jen tlačit k 3, nikdy k 2
- Jména se porovnávají přesně a včetně velikosti písmen a chůze po
/Parentkončí po osmi úrovních, takže neobvykle hluboká hierarchie se vyřeší na zkrácené jméno - Položka FieldMDP, jejíž struktura neprojde validací a jejíž akci se nepodaří přečíst, donutí verdikt vrátit 4
Když verdikt přijde jako 4, GetSignatureRevisionIndex dá revizi, na které podpis končí, a GetDocumentRevisionChanges vypíše každý objekt, který každá pozdější revize přidala, změnila nebo uvolnila, včetně jeho změnové třídy. To člověku stačí na rozhodnutí během minuty. Mechaniku, jak se tyto revize přidávají, popisuje průvodce inkrementálním updatem pro PDFlibPas
FAQ
Znamená verdikt 2, že podpisy jsou platné?
Ne. GetSignatureModificationVerdict zkoumá, co se přidalo za podpisem, a nikdy nekontroluje hodnotu CMS podpisu, řetěz certifikátů ani revokaci. Dvojka říká, že pozdější updaty zůstaly v rámci oprávnění; kryptografická validace je samostatný krok a potřeba jsou obojí
Proč podpis druhého schvalovatele dá 4 místo 2?
Podepsání přidá slovník hodnoty podpisu a obvykle přepíše formulářovou agendu jako slovník AcroForm. Ty objekty nepatří žádnému pojmenovanému poli, a když je zámek pole aktivní, verdikt odmítne schválit změnu, kterou nedokáže přisoudit poli. Použijte GetDocumentRevisionChanges, abyste potvrdili, že revize obsahuje jen očekávané podpisové objekty
Může jedno jméno zamknout celou skupinu polí?
Ano. Položka zámku odpovídá pojmenovanému poli a každému potomkovi, jehož plně kvalifikované jméno začíná tímto jménem následovaným tečkou, takže uvedení Order v zámku Include zamkne Order.Customer, Order.Total a jakékoli další pole pod tímto rodičem
Verdikty FieldMDP, podpisování se zámky polí i prohlížení revizí dodává PDFlibPas Delphi PDF Library, spolu s certifikací DocMDP a dlouhodobou validací PAdES, na kterých se výše uvedený schvalovací postup spoléhá