diff --git a/lib/Handler/CertificateEngine/AEngineHandler.php b/lib/Handler/CertificateEngine/AEngineHandler.php index 269148f29a..62ecd571f3 100644 --- a/lib/Handler/CertificateEngine/AEngineHandler.php +++ b/lib/Handler/CertificateEngine/AEngineHandler.php @@ -55,8 +55,8 @@ * @method string getLocality() * @method IEngineHandler setOrganization(string $organization) * @method string getOrganization() - * @method IEngineHandler setOrganizationUnit(string $organizationUnit) - * @method string getOrganizationUnit() + * @method IEngineHandler setOrganizationalUnit(string $organizationalUnit) + * @method string getOrganizationalUnit() * @method string getName() */ class AEngineHandler { @@ -69,7 +69,7 @@ class AEngineHandler { protected string $state = ''; protected string $locality = ''; protected string $organization = ''; - protected string $organizationUnit = ''; + protected string $organizationalUnit = ''; protected string $password = ''; protected string $configPath = ''; protected string $engine = ''; @@ -135,9 +135,6 @@ public function readCertificate(string $certificate, string $privateKey): array $return['name'] = $parsed['name']; $return['subject'] = $parsed['subject']; - if (is_array($return['subject']['OU']) && !empty($return['subject']['OU'])) { - $return['subject']['OU'] = implode(', ', $return['subject']['OU']); - } $return['issuer'] = $parsed['issuer']; $return['extensions'] = $parsed['extensions']; $return['validate'] = [ @@ -160,7 +157,7 @@ public function translateToLong($name): string { case 'O': return 'Organization'; case 'OU': - return 'OrganizationUnit'; + return 'OrganizationalUnit'; } return ''; } @@ -259,7 +256,7 @@ protected function getNames(): array { 'ST' => $this->getState(), 'L' => $this->getLocality(), 'O' => $this->getOrganization(), - 'OU' => $this->getOrganizationUnit(), + 'OU' => $this->getOrganizationalUnit(), ]; $names = array_filter($names, function ($v) { return !empty($v); diff --git a/lib/Handler/CertificateEngine/IEngineHandler.php b/lib/Handler/CertificateEngine/IEngineHandler.php index 0420905cee..154bdc7528 100644 --- a/lib/Handler/CertificateEngine/IEngineHandler.php +++ b/lib/Handler/CertificateEngine/IEngineHandler.php @@ -41,8 +41,8 @@ * @method string getLocality() * @method IEngineHandler setOrganization(string $organization) * @method string getOrganization() - * @method IEngineHandler setOrganizationUnit(string $organizationUnit) - * @method string getOrganizationUnit() + * @method IEngineHandler setOrganizationalUnit(string $organizationalUnit) + * @method string getOrganizationalUnit() * @method string getName() */ interface IEngineHandler { diff --git a/lib/Migration/Version7000Date20221026003343.php b/lib/Migration/Version7000Date20221026003343.php index dbe174f597..e75f50d8be 100644 --- a/lib/Migration/Version7000Date20221026003343.php +++ b/lib/Migration/Version7000Date20221026003343.php @@ -49,9 +49,9 @@ public function preSchemaChange(IOutput $output, \Closure $schemaClosure, array $rootCert['names']['O'] = $organization; $this->appConfig->deleteAppValue('organization'); } - if ($organizationUnit = $this->appConfig->getAppValue('organizationUnit')) { - $rootCert['names']['OU'] = $organizationUnit; - $this->appConfig->deleteAppValue('organizationUnit'); + if ($organizationalUnit = $this->appConfig->getAppValue('organizationalUnit')) { + $rootCert['names']['OU'] = $organizationalUnit; + $this->appConfig->deleteAppValue('organizationalUnit'); } if ($rootCert) { $this->appConfig->setAppValue('rootCert', json_encode($rootCert)); diff --git a/tests/Unit/Handler/OpenSslHandlerTest.php b/tests/Unit/Handler/OpenSslHandlerTest.php new file mode 100644 index 0000000000..8ef52b2ef0 --- /dev/null +++ b/tests/Unit/Handler/OpenSslHandlerTest.php @@ -0,0 +1,94 @@ +config = $this->createMock(IConfig::class); + $this->appConfig = $this->createMock(IAppConfig::class); + $this->appDataFactory = $this->createMock(IAppDataFactory::class); + $this->dateTimeFormatter = $this->createMock(IDateTimeFormatter::class); + $this->dateTimeFormatter + ->method('formatDateTime') + ->willReturn('fake date'); + $this->tempManager = $this->createMock(ITempManager::class); + $this->tempManager + ->method('getTemporaryFile') + ->willReturn(tempnam(sys_get_temp_dir(), 'temp')); + $this->openSslHandler = new OpenSslHandler( + $this->config, + $this->appConfig, + $this->appDataFactory, + $this->dateTimeFormatter, + $this->tempManager, + ); + vfsStream::setup('certificate'); + $this->openSslHandler->setConfigPath('vfs://certificate/'); + } + + /** + * @dataProvider dataReadCertificate + */ + public function testReadCertificate(string $commonName, array $hosts, string $password, array $csrNames): void { + if (isset($csrNames['C'])) { + $this->openSslHandler->setCountry($csrNames['C']); + } + if (isset($csrNames['ST'])) { + $this->openSslHandler->setState($csrNames['ST']); + } + if (isset($csrNames['O'])) { + $this->openSslHandler->setOrganization($csrNames['O']); + } + if (isset($csrNames['OU'])) { + $this->openSslHandler->setOrganizationalUnit($csrNames['OU']); + } + $this->openSslHandler->generateRootCert($commonName, $csrNames); + + $this->openSslHandler->setHosts($hosts); + $this->openSslHandler->setPassword($password); + $certificateContent = $this->openSslHandler->generateCertificate(); + $parsed = $this->openSslHandler->readCertificate($certificateContent, $password); + $this->assertJsonStringEqualsJsonString( + json_encode($csrNames), + json_encode($parsed['subject']) + ); + } + + public static function dataReadCertificate(): array { + return [ + [ + 'common name', + ['user@domain.tld'], + 'password', + [ + 'C' => 'CT', + 'ST' => 'Some-State', + 'O' => 'Organization Name', + ], + ], + [ + 'common name', + ['user@domain.tld'], + 'password', + [ + 'C' => 'CT', + 'ST' => 'Some-State', + 'O' => 'Organization Name', + 'OU' => 'Organization Unit', + ], + ], + ]; + } +} diff --git a/tests/Unit/Service/InstallServiceTest.php b/tests/Unit/Service/InstallServiceTest.php index 0f2d0b9fb2..c54e42290f 100644 --- a/tests/Unit/Service/InstallServiceTest.php +++ b/tests/Unit/Service/InstallServiceTest.php @@ -74,63 +74,78 @@ protected function getInstallService(): InstallService { /** * @dataProvider providerDownloadCli */ - public function testDownloadCli(string $url, string $filename, string $path, string $hash, string $algorithm, string $expectedOutput): void { + public function testDownloadCli(string $url, string $filename, string $content, string $hash, string $algorithm, string $expectedOutput): void { $installService = $this->getInstallService(); $output = new BufferedOutput(); $installService->setOutput($output); + + if ($content) { + vfsStream::setup('download'); + $path = 'vfs://download/dummy.svg'; + file_put_contents($path, $content); + } else { + $path = ''; + } + self::invokePrivate($installService, 'downloadCli', [$url, $filename, $path, $hash, $algorithm]); $actual = $output->fetch(); $this->assertEquals($expectedOutput, $actual); } public function providerDownloadCli(): array { - vfsStream::setup('download'); - - $pathInvalid = 'vfs://download/appInvalid.svg'; - file_put_contents($pathInvalid, 'invalidContent'); - $pathValid = 'vfs://download/validContent.svg'; - file_put_contents($pathValid, 'invalidContent'); return [ [ - "http://localhost/apps/libresign/img/app.svg", - 'app.svg', - 'vfs://download/app.svg', - '', - 'md5', - "Downloading app.svg...\n" . - " 0 [>---------------------------]\n". - "Failure on download app.svg, empty file, try again\n", + 'url' => 'http://localhost/apps/libresign/img/app.svg', + 'filename' => 'app.svg', + 'content' => '', + 'hash' => '', + 'algorithm' => 'md5', + 'expectedOutput' => <<---------------------------] + Failure on download app.svg, empty file, try again + + EXPECTEDOUTPUT ], [ - "http://localhost/apps/libresign/img/appInvalid.svg", - 'appInvalid.svg', - $pathInvalid, - 'hashInvalid', - 'md5', - "Downloading appInvalid.svg...\n" . - " 0 [>---------------------------]\n" . - "Failure on download appInvalid.svg try again\n" . - "Invalid md5\n", + 'url' => 'http://localhost/apps/libresign/img/appInvalid.svg', + 'filename' => 'appInvalid.svg', + 'content' => 'content', + 'hash' => 'invalidContent', + 'algorithm' => 'md5', + 'expectedOutput' => <<---------------------------] + Failure on download appInvalid.svg try again + Invalid md5 + + EXPECTEDOUTPUT ], [ - "http://localhost/apps/libresign/img/appInvalid.svg", - 'appInvalid.svg', - $pathInvalid, - 'hashInvalid', - 'sha256', - "Downloading appInvalid.svg...\n" . - " 0 [>---------------------------]\n" . - "Failure on download appInvalid.svg try again\n" . - "Invalid sha256\n", + 'url' => 'http://localhost/apps/libresign/img/appInvalid.svg', + 'filename' => 'appInvalid.svg', + 'content' => 'content', + 'hash' => 'invalidContent', + 'algorithm' => 'sha256', + 'expectedOutput' => <<---------------------------] + Failure on download appInvalid.svg try again + Invalid sha256 + + EXPECTEDOUTPUT ], [ - "http://localhost/apps/libresign/img/validContent.svg", - 'validContent.svg', - $pathValid, - hash_file('sha256', $pathValid), - 'sha256', - "Downloading validContent.svg...\n" . - " 0 [>---------------------------]\n", + 'url' => 'http://localhost/apps/libresign/img/validContent.svg', + 'filename' => 'validContent.svg', + 'content' => 'content', + 'hash' => hash('sha256', 'content'), + 'algorithm' => 'sha256', + 'expectedOutput' => <<---------------------------] + + EXPECTEDOUTPUT ], ]; } diff --git a/tests/Unit/TestCase.php b/tests/Unit/TestCase.php index 235052cb43..8eb2b6aa0d 100644 --- a/tests/Unit/TestCase.php +++ b/tests/Unit/TestCase.php @@ -226,7 +226,7 @@ public function requestSignFile($data): File { 'commonName' => 'CommonName', 'country' => 'Brazil', 'organization' => 'Organization', - 'organizationUnit' => 'organizationUnit', + 'organizationalUnit' => 'organizationalUnit', 'cfsslUri' => self::$server->getServerRoot() . '/api/v1/cfssl/' ]); diff --git a/tests/integration/composer.lock b/tests/integration/composer.lock index eaecb3315d..b230948b53 100644 --- a/tests/integration/composer.lock +++ b/tests/integration/composer.lock @@ -1215,16 +1215,16 @@ }, { "name": "php-http/promise", - "version": "1.3.0", + "version": "1.3.1", "source": { "type": "git", "url": "https://github.com/php-http/promise.git", - "reference": "2916a606d3b390f4e9e8e2b8dd68581508be0f07" + "reference": "fc85b1fba37c169a69a07ef0d5a8075770cc1f83" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-http/promise/zipball/2916a606d3b390f4e9e8e2b8dd68581508be0f07", - "reference": "2916a606d3b390f4e9e8e2b8dd68581508be0f07", + "url": "https://api.github.com/repos/php-http/promise/zipball/fc85b1fba37c169a69a07ef0d5a8075770cc1f83", + "reference": "fc85b1fba37c169a69a07ef0d5a8075770cc1f83", "shasum": "" }, "require": { @@ -1261,9 +1261,9 @@ ], "support": { "issues": "https://github.com/php-http/promise/issues", - "source": "https://github.com/php-http/promise/tree/1.3.0" + "source": "https://github.com/php-http/promise/tree/1.3.1" }, - "time": "2024-01-04T18:49:48+00:00" + "time": "2024-03-15T13:55:21+00:00" }, { "name": "phpunit/php-code-coverage", @@ -5184,16 +5184,16 @@ }, { "name": "libresign/nextcloud-behat", - "version": "v0.12.0", + "version": "v0.12.1", "source": { "type": "git", "url": "https://github.com/LibreSign/nextcloud-behat.git", - "reference": "a78f0b41432c081ac5854bc70ad4c0be6305e506" + "reference": "f194953e7366ac05e64f44852bbc25a49cf4779a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/LibreSign/nextcloud-behat/zipball/a78f0b41432c081ac5854bc70ad4c0be6305e506", - "reference": "a78f0b41432c081ac5854bc70ad4c0be6305e506", + "url": "https://api.github.com/repos/LibreSign/nextcloud-behat/zipball/f194953e7366ac05e64f44852bbc25a49cf4779a", + "reference": "f194953e7366ac05e64f44852bbc25a49cf4779a", "shasum": "" }, "require": { @@ -5238,9 +5238,9 @@ ], "support": { "issues": "https://github.com/LibreSign/nextcloud-behat/issues", - "source": "https://github.com/LibreSign/nextcloud-behat/tree/v0.12.0" + "source": "https://github.com/LibreSign/nextcloud-behat/tree/v0.12.1" }, - "time": "2024-03-14T22:37:17+00:00" + "time": "2024-03-20T21:39:15+00:00" }, { "name": "symfony/process", diff --git a/tests/integration/features/account/signature.feature b/tests/integration/features/account/signature.feature index c1cc6eff70..ea644d8a64 100644 --- a/tests/integration/features/account/signature.feature +++ b/tests/integration/features/account/signature.feature @@ -33,7 +33,7 @@ Feature: account/signature And as user "signer1" And run the command "config:app:set libresign certificate_engine --value cfssl" with result code 0 And run the command "libresign:install --cfssl" with result code 0 - And run the command "libresign:configure:cfssl --cn=Common\ Name --c=BR --o=Organization --st=State\ of\ Company --l=City\ Name" with result code 0 + And run the command "libresign:configure:cfssl --cn=Common\ Name --c=BR --o=Organization --st=State\ of\ Company --l=City\ Name --ou=Organization\ Unit" with result code 0 And sending "post" to ocs "/apps/libresign/api/v1/account/signature" | signPassword | password | And the response should have a status code 200 @@ -42,9 +42,9 @@ Feature: account/signature | password | password | Then the response should be a JSON array with the following mandatory values | key | value | - | name | /C=BR/ST=State of Company/L=City Name/O=Organization/CN=signer1-displayname | - | issuer | {"CN": "Common Name","C": "BR","ST": "State of Company","L":"City Name","O": "Organization"} | - | subject | {"CN": "signer1-displayname","C": "BR","ST": "State of Company","L":"City Name","O": "Organization"} | + | name | /C=BR/ST=State of Company/L=City Name/O=Organization/OU=Organization Unit/CN=signer1-displayname | + | issuer | {"CN": "Common Name","C": "BR","ST": "State of Company","L":"City Name","O": "Organization","OU":"Organization Unit"} | + | subject | {"CN": "signer1-displayname","C": "BR","ST": "State of Company","L":"City Name","O": "Organization","OU":"Organization Unit"} | | (jq).extensions.basicConstraints | CA:FALSE | | (jq).extensions.subjectAltName | email:signer@domain.test | | (jq).extensions.keyUsage | Digital Signature, Key Encipherment, Certificate Sign | @@ -56,7 +56,7 @@ Feature: account/signature Given user "signer1" exists And set the email of user "signer1" to "signer@domain.test" And as user "signer1" - And run the command "libresign:configure:openssl --cn=Common\ Name --c=BR --o=Organization --st=State\ of\ Company --l=City\ Name" with result code 0 + And run the command "libresign:configure:openssl --cn=Common\ Name --c=BR --o=Organization --st=State\ of\ Company --l=City\ Name --ou=Organization\ Unit" with result code 0 And sending "post" to ocs "/apps/libresign/api/v1/account/signature" | signPassword | password | And the response should have a status code 200 @@ -65,9 +65,10 @@ Feature: account/signature | password | password | Then the response should be a JSON array with the following mandatory values | key | value | - | name | /C=BR/ST=State of Company/L=City Name/O=Organization/CN=signer1-displayname | - | issuer | {"CN": "Common Name","C": "BR","ST": "State of Company","L":"City Name","O": "Organization"} | - | subject | {"CN": "signer1-displayname","C": "BR","ST": "State of Company","L":"City Name","O": "Organization"} | + | name | /C=BR/ST=State of Company/L=City Name/O=Organization/OU=Organization Unit/CN=signer1-displayname | + | issuer | {"CN": "Common Name","C": "BR","ST": "State of Company","L":"City Name","O": "Organization","OU":"Organization Unit"} | + | subject | {"CN": "signer1-displayname","C": "BR","ST": "State of Company","L":"City Name","O": "Organization","OU":"Organization Unit"} | + | (jq).extensions.basicConstraints | CA:FALSE | | (jq).extensions.subjectAltName | email:signer@domain.test | | (jq).extensions.keyUsage | Digital Signature, Key Encipherment, Certificate Sign | | (jq).extensions.extendedKeyUsage | TLS Web Client Authentication, E-mail Protection |