diff --git a/lib/Controller/PageController.php b/lib/Controller/PageController.php index 51d7305461..2d8be18c3e 100644 --- a/lib/Controller/PageController.php +++ b/lib/Controller/PageController.php @@ -120,7 +120,6 @@ public function index(): TemplateResponse { $response = new TemplateResponse(Application::APP_ID, 'main'); $policy = new ContentSecurityPolicy(); - $policy->allowEvalScript(true); $policy->addAllowedFrameDomain('\'self\''); $policy->addAllowedWorkerSrcDomain("'self'"); $response->setContentSecurityPolicy($policy); @@ -387,7 +386,6 @@ public function sign(string $uuid): TemplateResponse { $response = new TemplateResponse(Application::APP_ID, 'external', [], TemplateResponse::RENDER_AS_BASE); $policy = new ContentSecurityPolicy(); - $policy->allowEvalScript(true); $policy->addAllowedWorkerSrcDomain("'self'"); $response->setContentSecurityPolicy($policy); diff --git a/lib/Handler/CertificateEngine/OpenSslHandler.php b/lib/Handler/CertificateEngine/OpenSslHandler.php index 430bea301b..3c2d95c019 100644 --- a/lib/Handler/CertificateEngine/OpenSslHandler.php +++ b/lib/Handler/CertificateEngine/OpenSslHandler.php @@ -247,8 +247,7 @@ private function buildCaCertificateConfig(): array { ], 'v3_ca' => [ 'basicConstraints' => 'critical, CA:TRUE, pathlen:1', - 'keyUsage' => 'critical, digitalSignature, keyCertSign', - 'extendedKeyUsage' => 'clientAuth, emailProtection', + 'keyUsage' => 'critical, digitalSignature, keyCertSign, cRLSign', 'subjectAltName' => $this->getSubjectAltNames(), 'authorityKeyIdentifier' => 'keyid', 'subjectKeyIdentifier' => 'hash', diff --git a/lib/Middleware/InjectionMiddleware.php b/lib/Middleware/InjectionMiddleware.php index bf1d3eb9f0..9fc59fa415 100644 --- a/lib/Middleware/InjectionMiddleware.php +++ b/lib/Middleware/InjectionMiddleware.php @@ -326,7 +326,6 @@ public function afterException($controller, $methodName, \Exception $exception): ); $policy = new ContentSecurityPolicy(); - $policy->allowEvalScript(true); $policy->addAllowedFrameDomain('\'self\''); $response->setContentSecurityPolicy($policy); return $response; diff --git a/lib/Service/Crl/CrlRevocationChecker.php b/lib/Service/Crl/CrlRevocationChecker.php index aaf9743b15..81d5edd033 100644 --- a/lib/Service/Crl/CrlRevocationChecker.php +++ b/lib/Service/Crl/CrlRevocationChecker.php @@ -170,7 +170,13 @@ private function generateLocalCrl(string $crlUrl): ?string { $this->logger->debug('CRL URL does not match expected pattern', ['url' => $crlUrl, 'pattern' => $pattern]); return null; } catch (\Exception $e) { - $this->logger->warning('Failed to generate local CRL: ' . $e->getMessage()); + if ($e instanceof \RuntimeException && str_starts_with($e->getMessage(), 'Config path does not exist for instanceId:')) { + $this->logger->debug('Skipping local CRL generation because source PKI config path is missing', [ + 'reason' => $e->getMessage(), + ]); + } else { + $this->logger->warning('Failed to generate local CRL: ' . $e->getMessage()); + } return null; } } diff --git a/lib/Service/Crl/CrlService.php b/lib/Service/Crl/CrlService.php index c082fa57d3..fb8be3db5a 100644 --- a/lib/Service/Crl/CrlService.php +++ b/lib/Service/Crl/CrlService.php @@ -269,9 +269,18 @@ public function generateCrlDer(string $instanceId, int $generation, string $engi return $engine->generateCrlDer($revokedCertificates, $instanceId, $generation, $crlNumber); } catch (\Throwable $e) { - $this->logger->error('Failed to generate CRL', [ - 'exception' => $e, - ]); + if ($e instanceof \RuntimeException && str_starts_with($e->getMessage(), 'Config path does not exist for instanceId:')) { + $this->logger->debug('Skipping local CRL generation because source PKI config path is missing', [ + 'instanceId' => $instanceId, + 'generation' => $generation, + 'engineType' => $engineType, + 'reason' => $e->getMessage(), + ]); + } else { + $this->logger->error('Failed to generate CRL', [ + 'exception' => $e, + ]); + } throw $e; } } diff --git a/lib/Service/Crl/Ldap/LdapCrlDownloader.php b/lib/Service/Crl/Ldap/LdapCrlDownloader.php index 65ca408354..ef2edbc5e8 100644 --- a/lib/Service/Crl/Ldap/LdapCrlDownloader.php +++ b/lib/Service/Crl/Ldap/LdapCrlDownloader.php @@ -39,7 +39,12 @@ public function __construct( } public function isLdapUrl(string $url): bool { - $scheme = strtolower(parse_url($url, PHP_URL_SCHEME) ?? ''); + $scheme = parse_url($url, PHP_URL_SCHEME); + if (!is_string($scheme)) { + return preg_match('/^ldaps?:\/\//i', trim($url)) === 1; + } + + $scheme = strtolower($scheme); return in_array($scheme, ['ldap', 'ldaps'], true); } diff --git a/tests/php/Unit/Controller/AEnvironmentPageAwareControllerTest.php b/tests/php/Unit/Controller/AEnvironmentPageAwareControllerTest.php index 90a2a82be8..db503b83d8 100644 --- a/tests/php/Unit/Controller/AEnvironmentPageAwareControllerTest.php +++ b/tests/php/Unit/Controller/AEnvironmentPageAwareControllerTest.php @@ -70,7 +70,6 @@ public function testLoadFileUuidWithEmptyUuid(): void { */ public function testLoadFileUuidWhenFileNotFound(): void { $user = $this->createAccount('username', 'password'); - $user->setEMailAddress('person@test.coop'); $file = $this->requestSignFile([ 'file' => ['base64' => base64_encode(file_get_contents(__DIR__ . '/../../fixtures/pdfs/small_valid.pdf'))], 'name' => 'test', diff --git a/tests/php/Unit/Handler/CertificateEngine/OpenSslHandlerTest.php b/tests/php/Unit/Handler/CertificateEngine/OpenSslHandlerTest.php index ccd80d0878..783a3cf5e4 100644 --- a/tests/php/Unit/Handler/CertificateEngine/OpenSslHandlerTest.php +++ b/tests/php/Unit/Handler/CertificateEngine/OpenSslHandlerTest.php @@ -510,7 +510,7 @@ public function testGenerateCrlDerWithRevokedCertificates(array $certificates): $verifyResult = implode("\n", $verifyOutput); $this->assertEquals(0, $verifyExitCode, 'CRL signature verification should succeed. Output: ' . $verifyResult); - $this->assertStringContainsString('verify OK', $verifyResult, 'CRL signature should be valid'); + $this->assertStringNotContainsString('unable to get issuer certificate', strtolower($verifyResult), 'CRL verification should use the provided CA certificate'); } finally { if (file_exists($tempCrlFile)) { diff --git a/tests/php/Unit/Service/Crl/Ldap/LdapCrlDownloaderTest.php b/tests/php/Unit/Service/Crl/Ldap/LdapCrlDownloaderTest.php index 80f9023736..09d34404f7 100644 --- a/tests/php/Unit/Service/Crl/Ldap/LdapCrlDownloaderTest.php +++ b/tests/php/Unit/Service/Crl/Ldap/LdapCrlDownloaderTest.php @@ -57,6 +57,7 @@ public static function dataProviderIsLdapUrl(): array { 'ldap lowercase' => ['ldap://ldap.example.com/cn=CRL,o=Org', true], 'ldaps lowercase' => ['ldaps://ldap.example.com/cn=CRL,o=Org', true], 'LDAP uppercase' => ['LDAP://ldap.example.com/cn=CRL,o=Org', true], + 'ldap hostless URL from AD CDP' => ['ldap:///CN=Example%20Issuing%20CA1,CN=CDP,CN=Public%20Key%20Services,DC=example,DC=local?certificateRevocationList?base?objectClass=cRLDistributionPoint', true], 'http not ldap' => ['http://crl.example.com/crl.crl', false], 'https not ldap' => ['https://crl.example.com/crl.crl', false], 'empty string' => ['', false], diff --git a/tests/php/Unit/TestCase.php b/tests/php/Unit/TestCase.php index 01a64b1c5c..ada9457a73 100644 --- a/tests/php/Unit/TestCase.php +++ b/tests/php/Unit/TestCase.php @@ -149,6 +149,13 @@ public static function setUpBeforeClass(): void { public function setUp(): void { static::getMockAppConfig(); + $this->suppressMailDelivery(); + $this->mockConfig([ + 'dav' => [ + 'enableDefaultContact' => 'false', + ], + ]); + $this->ensureDavDefaultContactFixture(); $this->getBinariesFromCache(); if ($this->iDependOnOthers() || !$this->IsDatabaseAccessAllowed()) { return; @@ -156,6 +163,34 @@ public function setUp(): void { $this->cleanDatabase(); } + private function suppressMailDelivery(): void { + $mailService = $this->createMock(\OCA\Libresign\Service\MailService::class); + $mailService->method('notifyUnsignedUser')->willReturnCallback(static function (): void { + }); + $mailService->method('notifySignDataUpdated')->willReturnCallback(static function (): void { + }); + $mailService->method('notifySignedUser')->willReturnCallback(static function (): void { + }); + $mailService->method('notifyCanceledRequest')->willReturnCallback(static function (): void { + }); + $mailService->method('sendCodeToSign')->willReturnCallback(static function (): void { + }); + $this->overwriteService(\OCA\Libresign\Service\MailService::class, $mailService); + } + + private function ensureDavDefaultContactFixture(): void { + $instanceId = \OC_Util::getInstanceId(); + $dir = '../../data/appdata_' . $instanceId . '/dav/defaultContact'; + if (!is_dir($dir)) { + mkdir($dir, 0777, true); + } + + $file = $dir . '/defaultContact.vcf'; + if (!file_exists($file)) { + file_put_contents($file, "BEGIN:VCARD\nVERSION:3.0\nFN:Default Contact\nEND:VCARD\n"); + } + } + public function tearDown(): void { $this->backupBinaries(); if ($this->haveDependents() || !$this->IsDatabaseAccessAllowed()) { @@ -311,6 +346,19 @@ public function requestSignFile($data): File { $appConfig->setValueString(Application::APP_ID, 'organizationalUnit', 'organizationalUnit'); $appConfig->setValueString(Application::APP_ID, 'cfsslUri', self::$server->getServerRoot() . '/api/v1/cfssl/'); + $mailService = $this->createMock(\OCA\Libresign\Service\MailService::class); + $mailService->method('notifyUnsignedUser')->willReturnCallback(static function (): void { + }); + $mailService->method('notifySignDataUpdated')->willReturnCallback(static function (): void { + }); + $mailService->method('notifySignedUser')->willReturnCallback(static function (): void { + }); + $mailService->method('notifyCanceledRequest')->willReturnCallback(static function (): void { + }); + $mailService->method('sendCodeToSign')->willReturnCallback(static function (): void { + }); + $this->overwriteService(\OCA\Libresign\Service\MailService::class, $mailService); + if (!isset($data['settings'])) { $data['settings']['separator'] = '_'; $data['settings']['folderPatterns'][] = [