Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions lib/Service/File/CertificateSignersMergeService.php
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,16 @@ private function processChainData(\stdClass $signer, array $chain): void {
if (isset($chain[0])) {
$this->enrichSignerWithCertificateValidation($signer, $chain[0]);
}

// Promote the end-entity (leaf) certificate validity dates to the signer root so the
// validation UI and the SignerDetail API contract can read them from a stable location.
// The chain still carries the full per-certificate dates for the certificate-chain view.
if (isset($signer->chain[0]['valid_from']) && !isset($signer->valid_from)) {
$signer->valid_from = $signer->chain[0]['valid_from'];
}
if (isset($signer->chain[0]['valid_to']) && !isset($signer->valid_to)) {
$signer->valid_to = $signer->chain[0]['valid_to'];
}
}

private function enrichSignerWithCertificateValidation(\stdClass $signer, array $endEntityCert): void {
Expand Down
2 changes: 2 additions & 0 deletions tests/integration/features/file/validate.feature
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ Feature: validate
| (jq).ocs.data.signers[0].subject.O | Organization |
| (jq).ocs.data.signers[0].signature_validation | {"id":1,"label":"Signature is valid."} |
| (jq).ocs.data.signers[0].signatureTypeSN | RSA-SHA256 |
| (jq)(.ocs.data.signers[0].valid_from \| test("^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}[+-][0-9]{2}:[0-9]{2}$")) | true |
| (jq)(.ocs.data.signers[0].valid_to \| test("^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}[+-][0-9]{2}:[0-9]{2}$")) | true |

Scenario Outline: Unauthenticated user can fetch the validation ednpoint
Given as user "admin"
Expand Down
31 changes: 31 additions & 0 deletions tests/php/Unit/Service/File/CertificateSignersMergeServiceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,37 @@ public static function providerCertificateInfoMatching(): array {
];
}

public function testMergePromotesLeafCertValidityDatesToSignerRoot(): void {
$fileData = new \stdClass();
$fileData->signers = [];

$certData = [[
'uid' => 'email:signer@example.com',
'chain' => [[
'subject' => ['CN' => 'Signer User'],
'validFrom_time_t' => 1769644731,
'validTo_time_t' => 1769731131,
]],
]];

$this->getService()->merge(
$fileData,
$certData,
'example.com',
'Signed',
fn (array $cert, string $host): ?string => $cert['subject']['UID'] ?? null,
fn (string $method, string $value): string => $method . ':' . $value,
fn (string $accountId): ?string => null,
);

$this->assertCount(1, $fileData->signers);
$signer = $fileData->signers[0];
$this->assertSame('2026-01-28T23:58:51+00:00', $signer->valid_from);
$this->assertSame('2026-01-29T23:58:51+00:00', $signer->valid_to);
$this->assertSame('2026-01-28T23:58:51+00:00', $signer->chain[0]['valid_from']);
$this->assertSame('2026-01-29T23:58:51+00:00', $signer->chain[0]['valid_to']);
}

public function testMergeDoesNotExportTopLevelTsaWithTimestampData(): void {
$fileData = new \stdClass();
$fileData->signers = [];
Expand Down
10 changes: 6 additions & 4 deletions tests/php/Unit/Service/File/SignersLoaderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -528,10 +528,12 @@ public function testLoadSignersFromCertDataPreventsDuplicateFormattedDates(): vo
$this->assertSame('2026-01-28T23:58:51+00:00', $signer->chain[0]['valid_from']);
$this->assertSame('2026-01-29T23:58:51+00:00', $signer->chain[0]['valid_to']);

// Root level should NOT have the formatted dates from backend
// These fields should only exist in the chain, not duplicated at root level
$this->assertObjectNotHasProperty('valid_from', $signer, 'valid_from should not be copied to root level');
$this->assertObjectNotHasProperty('valid_to', $signer, 'valid_to should not be copied to root level');
// Root level should expose the ISO UTC validity dates (from validFrom/validTo_time_t) so the
// validation UI and the SignerDetail API contract can read them from a stable location. The
// locale-formatted backend strings (e.g. 'January 28, 2026, 11:58:51 PM') must NOT leak here,
// but the ISO form IS promoted.
$this->assertSame('2026-01-28T23:58:51+00:00', $signer->valid_from);
$this->assertSame('2026-01-29T23:58:51+00:00', $signer->valid_to);

// Also verify other technical fields are not duplicated
$this->assertObjectNotHasProperty('validFrom_time_t', $signer);
Expand Down
Loading