Skip to content

Commit ae88711

Browse files
committed
feat: add translatable dictionary for validation reasons
Signed-off-by: Vitor Mattos <[email protected]>
1 parent e3687cd commit ae88711

2 files changed

Lines changed: 76 additions & 10 deletions

File tree

lib/Service/Signature/PdfSignatureValidationService.php

Lines changed: 54 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -166,25 +166,25 @@ private function mapSignatureValidation(ValidationResult $result): array {
166166
ValidationState::SIGNATURE_INVALID => [
167167
'id' => 2,
168168
'label' => $this->l10n->t('Signature is invalid.'),
169-
'reason' => $result->reason,
169+
'reason' => $this->translateKnownReason($result->reason),
170170
'isValid' => false,
171171
],
172172
ValidationState::DIGEST_MISMATCH => [
173173
'id' => 3,
174174
'label' => $this->l10n->t('Digest mismatch.'),
175-
'reason' => $result->reason,
175+
'reason' => $this->translateKnownReason($result->reason),
176176
'isValid' => false,
177177
],
178178
ValidationState::NOT_VERIFIED => [
179179
'id' => 5,
180180
'label' => $this->l10n->t('Signature has not yet been verified.'),
181-
'reason' => $result->reason,
181+
'reason' => $this->translateKnownReason($result->reason),
182182
'isValid' => false,
183183
],
184184
default => [
185185
'id' => 6,
186186
'label' => $this->l10n->t('Unknown validation failure.'),
187-
'reason' => $result->reason,
187+
'reason' => $this->translateKnownReason($result->reason),
188188
'isValid' => false,
189189
],
190190
};
@@ -203,42 +203,86 @@ private function mapCertificateValidation(ValidationResult $result): array {
203203
ValidationState::CERT_ISSUER_NOT_TRUSTED => [
204204
'id' => 2,
205205
'label' => $this->l10n->t("Certificate issuer isn't trusted."),
206-
'reason' => $result->reason,
206+
'reason' => $this->translateKnownReason($result->reason),
207207
'isValid' => false,
208208
],
209209
ValidationState::CERT_ISSUER_UNKNOWN => [
210210
'id' => 3,
211211
'label' => $this->l10n->t('Certificate issuer is unknown.'),
212-
'reason' => $result->reason,
212+
'reason' => $this->translateKnownReason($result->reason),
213213
'isValid' => false,
214214
],
215215
ValidationState::CERT_REVOKED => [
216216
'id' => 4,
217217
'label' => $this->l10n->t('Certificate has been revoked.'),
218-
'reason' => $result->reason,
218+
'reason' => $this->translateKnownReason($result->reason),
219219
'isValid' => false,
220220
],
221221
ValidationState::CERT_EXPIRED => [
222222
'id' => 5,
223223
'label' => $this->l10n->t('Certificate has expired.'),
224-
'reason' => $result->reason,
224+
'reason' => $this->translateKnownReason($result->reason),
225225
'isValid' => false,
226226
],
227227
ValidationState::CERT_NOT_VERIFIED => [
228228
'id' => 6,
229229
'label' => $this->l10n->t('Certificate has not yet been verified.'),
230-
'reason' => $result->reason,
230+
'reason' => $this->translateKnownReason($result->reason),
231231
'isValid' => false,
232232
],
233233
default => [
234234
'id' => 7,
235235
'label' => $this->l10n->t('Unknown issue with certificate or corrupted data.'),
236-
'reason' => $result->reason,
236+
'reason' => $this->translateKnownReason($result->reason),
237237
'isValid' => false,
238238
],
239239
};
240240
}
241241

242+
private function translateKnownReason(?string $reason): ?string {
243+
if ($reason === null || $reason === '') {
244+
return $reason;
245+
}
246+
247+
if (preg_match('/^Intermediate certificate at position (\d+) is not signed by issuer$/', $reason, $matches) === 1) {
248+
return $this->l10n->t(
249+
'Intermediate certificate at position %s is not signed by issuer',
250+
[$matches[1]]
251+
);
252+
}
253+
254+
$prefix = 'Certificate validation failed: ';
255+
if (str_starts_with($reason, $prefix)) {
256+
$detail = substr($reason, strlen($prefix));
257+
$translatedDetail = $this->translateKnownReason($detail) ?? $detail;
258+
return $this->l10n->t('Certificate validation failed: %s', [$translatedDetail]);
259+
}
260+
261+
return match ($reason) {
262+
'No ByteRange in signature' => $this->l10n->t('No ByteRange in signature'),
263+
'PDF content hash does not match signed digest' => $this->l10n->t('PDF content hash does not match signed digest'),
264+
'Signature does not match certificate' => $this->l10n->t('Signature does not match certificate'),
265+
'Failed to parse certificate' => $this->l10n->t('Failed to parse certificate'),
266+
'Certificate was not valid at time of signature' => $this->l10n->t('Certificate was not valid at time of signature'),
267+
'Certificate has expired' => $this->l10n->t('Certificate has expired'),
268+
'Empty certificate chain' => $this->l10n->t('Empty certificate chain'),
269+
'Certificate has no serial number' => $this->l10n->t('Certificate has no serial number'),
270+
'Certificate found in CRL' => $this->l10n->t('Certificate found in CRL'),
271+
'Invalid certificate' => $this->l10n->t('Invalid certificate'),
272+
'Leaf certificate is marked as CA' => $this->l10n->t('Leaf certificate is marked as CA'),
273+
'Certificate signature validation failed' => $this->l10n->t('Certificate signature validation failed'),
274+
'Self-signed certificate not in trusted roots' => $this->l10n->t('Self-signed certificate not in trusted roots'),
275+
'Root certificate is not self-signed' => $this->l10n->t('Root certificate is not self-signed'),
276+
'Root certificate is not in trusted list' => $this->l10n->t('Root certificate is not in trusted list'),
277+
'No binary signature' => $this->l10n->t('No binary signature'),
278+
'No certificates in signature' => $this->l10n->t('No certificates in signature'),
279+
'Signing certificate has expired' => $this->l10n->t('Signing certificate has expired'),
280+
'Signing certificate has been revoked' => $this->l10n->t('Signing certificate has been revoked'),
281+
'Signature verification incomplete' => $this->l10n->t('Signature verification incomplete'),
282+
default => $reason,
283+
};
284+
}
285+
242286
/**
243287
* Check if LibreSign CA is loaded.
244288
*/

tests/php/Unit/Service/Signature/PdfSignatureValidationServiceTest.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,28 @@ public function testMapCertificateValidationWithEnumState(): void {
5353
$this->assertTrue($result['isValid']);
5454
}
5555

56+
public function testMapReasonUsesDictionaryForKnownReason(): void {
57+
$service = $this->newServiceWithoutConstructor();
58+
$result = $this->invokePrivateMethod(
59+
$service,
60+
'mapSignatureValidation',
61+
new ValidationResult(ValidationState::DIGEST_MISMATCH, 'PDF content hash does not match signed digest')
62+
);
63+
64+
$this->assertSame('PDF content hash does not match signed digest', $result['reason']);
65+
}
66+
67+
public function testMapReasonKeepsUnknownReasonUntouched(): void {
68+
$service = $this->newServiceWithoutConstructor();
69+
$result = $this->invokePrivateMethod(
70+
$service,
71+
'mapSignatureValidation',
72+
new ValidationResult(ValidationState::DIGEST_MISMATCH, 'custom runtime detail')
73+
);
74+
75+
$this->assertSame('custom runtime detail', $result['reason']);
76+
}
77+
5678
private function newServiceWithoutConstructor(): PdfSignatureValidationService {
5779
$reflection = new \ReflectionClass(PdfSignatureValidationService::class);
5880
/** @var PdfSignatureValidationService $service */

0 commit comments

Comments
 (0)