CertificateManagerTest.php 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
  5. * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
  6. * SPDX-License-Identifier: AGPL-3.0-or-later
  7. */
  8. namespace Test\Security;
  9. use OC\Files\View;
  10. use OC\Security\CertificateManager;
  11. use OCP\IConfig;
  12. use OCP\Security\ISecureRandom;
  13. use Psr\Log\LoggerInterface;
  14. /**
  15. * Class CertificateManagerTest
  16. *
  17. * @group DB
  18. */
  19. class CertificateManagerTest extends \Test\TestCase {
  20. use \Test\Traits\UserTrait;
  21. use \Test\Traits\MountProviderTrait;
  22. /** @var CertificateManager */
  23. private $certificateManager;
  24. /** @var String */
  25. private $username;
  26. /** @var ISecureRandom */
  27. private $random;
  28. protected function setUp(): void {
  29. parent::setUp();
  30. $this->username = $this->getUniqueID('', 20);
  31. $this->createUser($this->username, '');
  32. $storage = new \OC\Files\Storage\Temporary();
  33. $this->registerMount($this->username, $storage, '/' . $this->username . '/');
  34. \OC_Util::tearDownFS();
  35. \OC_User::setUserId('');
  36. \OC\Files\Filesystem::tearDown();
  37. \OC_Util::setupFS($this->username);
  38. $config = $this->createMock(IConfig::class);
  39. $config->expects($this->any())->method('getSystemValueBool')
  40. ->with('installed', false)->willReturn(true);
  41. $this->random = $this->createMock(ISecureRandom::class);
  42. $this->random->method('generate')
  43. ->willReturn('random');
  44. $this->certificateManager = new CertificateManager(
  45. new \OC\Files\View(),
  46. $config,
  47. $this->createMock(LoggerInterface::class),
  48. $this->random
  49. );
  50. }
  51. protected function tearDown(): void {
  52. $user = \OC::$server->getUserManager()->get($this->username);
  53. if ($user !== null) {
  54. $user->delete();
  55. }
  56. parent::tearDown();
  57. }
  58. protected function assertEqualsArrays($expected, $actual) {
  59. sort($expected);
  60. sort($actual);
  61. $this->assertEquals($expected, $actual);
  62. }
  63. public function testListCertificates() {
  64. // Test empty certificate bundle
  65. $this->assertSame([], $this->certificateManager->listCertificates());
  66. // Add some certificates
  67. $this->certificateManager->addCertificate(file_get_contents(__DIR__ . '/../../data/certificates/goodCertificate.crt'), 'GoodCertificate');
  68. $certificateStore = [];
  69. $certificateStore[] = new \OC\Security\Certificate(file_get_contents(__DIR__ . '/../../data/certificates/goodCertificate.crt'), 'GoodCertificate');
  70. $this->assertEqualsArrays($certificateStore, $this->certificateManager->listCertificates());
  71. // Add another certificates
  72. $this->certificateManager->addCertificate(file_get_contents(__DIR__ . '/../../data/certificates/expiredCertificate.crt'), 'ExpiredCertificate');
  73. $certificateStore[] = new \OC\Security\Certificate(file_get_contents(__DIR__ . '/../../data/certificates/expiredCertificate.crt'), 'ExpiredCertificate');
  74. $this->assertEqualsArrays($certificateStore, $this->certificateManager->listCertificates());
  75. }
  76. public function testAddInvalidCertificate() {
  77. $this->expectException(\Exception::class);
  78. $this->expectExceptionMessage('Certificate could not get parsed.');
  79. $this->certificateManager->addCertificate('InvalidCertificate', 'invalidCertificate');
  80. }
  81. /**
  82. * @return array
  83. */
  84. public function dangerousFileProvider() {
  85. return [
  86. ['.htaccess'],
  87. ['../../foo.txt'],
  88. ['..\..\foo.txt'],
  89. ];
  90. }
  91. /**
  92. * @dataProvider dangerousFileProvider
  93. * @param string $filename
  94. */
  95. public function testAddDangerousFile($filename) {
  96. $this->expectException(\Exception::class);
  97. $this->expectExceptionMessage('Filename is not valid');
  98. $this->certificateManager->addCertificate(file_get_contents(__DIR__ . '/../../data/certificates/expiredCertificate.crt'), $filename);
  99. }
  100. public function testRemoveDangerousFile() {
  101. $this->assertFalse($this->certificateManager->removeCertificate('../../foo.txt'));
  102. }
  103. public function testRemoveExistingFile() {
  104. $this->certificateManager->addCertificate(file_get_contents(__DIR__ . '/../../data/certificates/goodCertificate.crt'), 'GoodCertificate');
  105. $this->assertTrue($this->certificateManager->removeCertificate('GoodCertificate'));
  106. }
  107. public function testGetCertificateBundle() {
  108. $this->assertSame('/files_external/rootcerts.crt', $this->certificateManager->getCertificateBundle());
  109. }
  110. /**
  111. * @dataProvider dataTestNeedRebundling
  112. *
  113. * @param int $CaBundleMtime
  114. * @param int $targetBundleMtime
  115. * @param int $targetBundleExists
  116. * @param bool $expected
  117. */
  118. public function testNeedRebundling($CaBundleMtime,
  119. $targetBundleMtime,
  120. $targetBundleExists,
  121. $expected
  122. ) {
  123. $view = $this->getMockBuilder(View::class)
  124. ->disableOriginalConstructor()->getMock();
  125. $config = $this->createMock(IConfig::class);
  126. /** @var CertificateManager | \PHPUnit\Framework\MockObject\MockObject $certificateManager */
  127. $certificateManager = $this->getMockBuilder('OC\Security\CertificateManager')
  128. ->setConstructorArgs([$view, $config, $this->createMock(LoggerInterface::class), $this->random])
  129. ->setMethods(['getFilemtimeOfCaBundle', 'getCertificateBundle'])
  130. ->getMock();
  131. $certificateManager->expects($this->any())->method('getFilemtimeOfCaBundle')
  132. ->willReturn($CaBundleMtime);
  133. $certificateManager->expects($this->once())->method('getCertificateBundle')
  134. ->willReturn('targetBundlePath');
  135. $view->expects($this->any())->method('file_exists')
  136. ->with('targetBundlePath')
  137. ->willReturn($targetBundleExists);
  138. $view->expects($this->any())->method('filemtime')
  139. ->willReturnCallback(function ($path) use ($targetBundleMtime) {
  140. if ($path === 'targetBundlePath') {
  141. return $targetBundleMtime;
  142. }
  143. throw new \Exception('unexpected path');
  144. });
  145. $this->assertSame($expected,
  146. $this->invokePrivate($certificateManager, 'needsRebundling')
  147. );
  148. }
  149. public function dataTestNeedRebundling() {
  150. return [
  151. //values: CaBundleMtime, targetBundleMtime, targetBundleExists, expected
  152. [10, 30, true, false],
  153. [10, 15, true, false],
  154. [10, 8, true, true],
  155. [10, 30, true, false],
  156. [10, 8, true, true],
  157. // if no target bundle exists we always build a new one
  158. [10, 30, false, true],
  159. [10, 15, false, true],
  160. [10, 8, false, true],
  161. [10, 30, false, true],
  162. [10, 8, false, true],
  163. ];
  164. }
  165. }