@@ -47,6 +47,7 @@ $linkToJs = \OC::$server->getURLGenerator()->linkToRoute( | |||
'script', | |||
[ | |||
'src' => $linkToJs, | |||
'nonce' => \OC::$server->getContentSecurityPolicyNonceManager()->getNonce() | |||
], '' | |||
); | |||
@@ -19,7 +19,7 @@ | |||
<link rel="stylesheet" href="<?php print_unescaped($cssfile); ?>" media="print"> | |||
<?php endforeach; ?> | |||
<?php foreach ($_['jsfiles'] as $jsfile): ?> | |||
<script src="<?php print_unescaped($jsfile); ?>"></script> | |||
<script nonce="<?php p(\OC::$server->getContentSecurityPolicyNonceManager()->getNonce()) ?>" src="<?php print_unescaped($jsfile); ?>"></script> | |||
<?php endforeach; ?> | |||
<?php print_unescaped($_['headers']); ?> | |||
</head> |
@@ -20,7 +20,7 @@ | |||
<link rel="stylesheet" href="<?php print_unescaped($cssfile); ?>" media="print"> | |||
<?php endforeach; ?> | |||
<?php foreach($_['jsfiles'] as $jsfile): ?> | |||
<script src="<?php print_unescaped($jsfile); ?>"></script> | |||
<script nonce="<?php p(\OC::$server->getContentSecurityPolicyNonceManager()->getNonce()) ?>" src="<?php print_unescaped($jsfile); ?>"></script> | |||
<?php endforeach; ?> | |||
<?php print_unescaped($_['headers']); ?> | |||
</head> |
@@ -27,7 +27,7 @@ | |||
<link rel="stylesheet" href="<?php print_unescaped($cssfile); ?>" media="print"> | |||
<?php endforeach; ?> | |||
<?php foreach($_['jsfiles'] as $jsfile): ?> | |||
<script src="<?php print_unescaped($jsfile); ?>"></script> | |||
<script nonce="<?php p(\OC::$server->getContentSecurityPolicyNonceManager()->getNonce()) ?>" src="<?php print_unescaped($jsfile); ?>"></script> | |||
<?php endforeach; ?> | |||
<?php print_unescaped($_['headers']); ?> | |||
</head> |
@@ -674,6 +674,7 @@ return array( | |||
'OC\\Security\\Bruteforce\\Throttler' => $baseDir . '/lib/private/Security/Bruteforce/Throttler.php', | |||
'OC\\Security\\CSP\\ContentSecurityPolicy' => $baseDir . '/lib/private/Security/CSP/ContentSecurityPolicy.php', | |||
'OC\\Security\\CSP\\ContentSecurityPolicyManager' => $baseDir . '/lib/private/Security/CSP/ContentSecurityPolicyManager.php', | |||
'OC\\Security\\CSP\\ContentSecurityPolicyNonceManager' => $baseDir . '/lib/private/Security/CSP/ContentSecurityPolicyNonceManager.php', | |||
'OC\\Security\\CSRF\\CsrfToken' => $baseDir . '/lib/private/Security/CSRF/CsrfToken.php', | |||
'OC\\Security\\CSRF\\CsrfTokenGenerator' => $baseDir . '/lib/private/Security/CSRF/CsrfTokenGenerator.php', | |||
'OC\\Security\\CSRF\\CsrfTokenManager' => $baseDir . '/lib/private/Security/CSRF/CsrfTokenManager.php', |
@@ -704,6 +704,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c | |||
'OC\\Security\\Bruteforce\\Throttler' => __DIR__ . '/../../..' . '/lib/private/Security/Bruteforce/Throttler.php', | |||
'OC\\Security\\CSP\\ContentSecurityPolicy' => __DIR__ . '/../../..' . '/lib/private/Security/CSP/ContentSecurityPolicy.php', | |||
'OC\\Security\\CSP\\ContentSecurityPolicyManager' => __DIR__ . '/../../..' . '/lib/private/Security/CSP/ContentSecurityPolicyManager.php', | |||
'OC\\Security\\CSP\\ContentSecurityPolicyNonceManager' => __DIR__ . '/../../..' . '/lib/private/Security/CSP/ContentSecurityPolicyNonceManager.php', | |||
'OC\\Security\\CSRF\\CsrfToken' => __DIR__ . '/../../..' . '/lib/private/Security/CSRF/CsrfToken.php', | |||
'OC\\Security\\CSRF\\CsrfTokenGenerator' => __DIR__ . '/../../..' . '/lib/private/Security/CSRF/CsrfTokenGenerator.php', | |||
'OC\\Security\\CSRF\\CsrfTokenManager' => __DIR__ . '/../../..' . '/lib/private/Security/CSRF/CsrfTokenManager.php', |
@@ -379,7 +379,8 @@ class DIContainer extends SimpleContainer implements IAppContainer { | |||
$c['AppName'], | |||
$app->isLoggedIn(), | |||
$app->isAdminUser(), | |||
$app->getServer()->getContentSecurityPolicyManager() | |||
$app->getServer()->getContentSecurityPolicyManager(), | |||
$app->getServer()->getCsrfTokenManager() | |||
); | |||
}); | |||
@@ -36,6 +36,7 @@ use OC\AppFramework\Middleware\Security\Exceptions\NotLoggedInException; | |||
use OC\AppFramework\Middleware\Security\Exceptions\StrictCookieMissingException; | |||
use OC\AppFramework\Utility\ControllerMethodReflector; | |||
use OC\Security\CSP\ContentSecurityPolicyManager; | |||
use OC\Security\CSRF\CsrfTokenManager; | |||
use OCP\AppFramework\Http\ContentSecurityPolicy; | |||
use OCP\AppFramework\Http\EmptyContentSecurityPolicy; | |||
use OCP\AppFramework\Http\RedirectResponse; | |||
@@ -77,6 +78,8 @@ class SecurityMiddleware extends Middleware { | |||
private $isAdminUser; | |||
/** @var ContentSecurityPolicyManager */ | |||
private $contentSecurityPolicyManager; | |||
/** @var CsrfTokenManager */ | |||
private $csrfTokenManager; | |||
/** | |||
* @param IRequest $request | |||
@@ -88,6 +91,7 @@ class SecurityMiddleware extends Middleware { | |||
* @param bool $isLoggedIn | |||
* @param bool $isAdminUser | |||
* @param ContentSecurityPolicyManager $contentSecurityPolicyManager | |||
* @param CSRFTokenManager $csrfTokenManager | |||
*/ | |||
public function __construct(IRequest $request, | |||
ControllerMethodReflector $reflector, | |||
@@ -97,7 +101,8 @@ class SecurityMiddleware extends Middleware { | |||
$appName, | |||
$isLoggedIn, | |||
$isAdminUser, | |||
ContentSecurityPolicyManager $contentSecurityPolicyManager) { | |||
ContentSecurityPolicyManager $contentSecurityPolicyManager, | |||
CsrfTokenManager $csrfTokenManager) { | |||
$this->navigationManager = $navigationManager; | |||
$this->request = $request; | |||
$this->reflector = $reflector; | |||
@@ -107,6 +112,7 @@ class SecurityMiddleware extends Middleware { | |||
$this->isLoggedIn = $isLoggedIn; | |||
$this->isAdminUser = $isAdminUser; | |||
$this->contentSecurityPolicyManager = $contentSecurityPolicyManager; | |||
$this->csrfTokenManager = $csrfTokenManager; | |||
} | |||
@@ -171,6 +177,23 @@ class SecurityMiddleware extends Middleware { | |||
} | |||
private function browserSupportsCspV3() { | |||
$browserWhitelist = [ | |||
// Chrome 40+ | |||
'/^Mozilla\/5\.0 \([^)]+\) AppleWebKit\/[0-9.]+ \(KHTML, like Gecko\) Chrome\/[4-9][0-9].[0-9.]+ (Mobile Safari|Safari)\/[0-9.]+$/', | |||
// Firefox 45+ | |||
'/^Mozilla\/5\.0 \([^)]+\) Gecko\/[0-9.]+ Firefox\/(4[5-9]|[5-9][0-9])\.[0-9.]+$/', | |||
// Safari 10+ | |||
'/^Mozilla\/5\.0 \([^)]+\) AppleWebKit\/[0-9.]+ \(KHTML, like Gecko\) Version\/1[0-9.]+ Safari\/[0-9.A-Z]+$/', | |||
]; | |||
if($this->request->isUserAgent($browserWhitelist)) { | |||
return true; | |||
} | |||
return false; | |||
} | |||
/** | |||
* Performs the default CSP modifications that may be injected by other | |||
* applications | |||
@@ -190,6 +213,10 @@ class SecurityMiddleware extends Middleware { | |||
$defaultPolicy = $this->contentSecurityPolicyManager->getDefaultPolicy(); | |||
$defaultPolicy = $this->contentSecurityPolicyManager->mergePolicies($defaultPolicy, $policy); | |||
if($this->browserSupportsCspV3()) { | |||
$defaultPolicy->useJsNonce($this->csrfTokenManager->getToken()->getEncryptedValue()); | |||
} | |||
$response->setContentSecurityPolicy($defaultPolicy); | |||
return $response; |
@@ -0,0 +1,54 @@ | |||
<?php | |||
/** | |||
* @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch> | |||
* | |||
* @license GNU AGPL version 3 or any later version | |||
* | |||
* This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU Affero General Public License as | |||
* published by the Free Software Foundation, either version 3 of the | |||
* License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU Affero General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Affero General Public License | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
* | |||
*/ | |||
namespace OC\Security\CSP; | |||
use OC\Security\CSRF\CsrfTokenManager; | |||
/** | |||
* @package OC\Security\CSP | |||
*/ | |||
class ContentSecurityPolicyNonceManager { | |||
/** @var CsrfTokenManager */ | |||
private $csrfTokenManager; | |||
/** @var string */ | |||
private $nonce = ''; | |||
/** | |||
* @param CsrfTokenManager $csrfTokenManager | |||
*/ | |||
public function __construct(CsrfTokenManager $csrfTokenManager) { | |||
$this->csrfTokenManager = $csrfTokenManager; | |||
} | |||
/** | |||
* Returns the current CSP nounce | |||
* | |||
* @return string | |||
*/ | |||
public function getNonce() { | |||
if($this->nonce === '') { | |||
$this->nonce = base64_encode($this->csrfTokenManager->getToken()->getEncryptedValue()); | |||
} | |||
return $this->nonce; | |||
} | |||
} |
@@ -33,6 +33,8 @@ namespace OC\Security\CSRF; | |||
class CsrfToken { | |||
/** @var string */ | |||
private $value; | |||
/** @var string */ | |||
private $encryptedValue = ''; | |||
/** | |||
* @param string $value Value of the token. Can be encrypted or not encrypted. | |||
@@ -48,8 +50,12 @@ class CsrfToken { | |||
* @return string | |||
*/ | |||
public function getEncryptedValue() { | |||
$sharedSecret = base64_encode(random_bytes(strlen($this->value))); | |||
return base64_encode($this->value ^ $sharedSecret) .':'.$sharedSecret; | |||
if($this->encryptedValue === '') { | |||
$sharedSecret = base64_encode(random_bytes(strlen($this->value))); | |||
$this->encryptedValue = base64_encode($this->value ^ $sharedSecret) . ':' . $sharedSecret; | |||
} | |||
return $this->encryptedValue; | |||
} | |||
/** |
@@ -34,6 +34,8 @@ class CsrfTokenManager { | |||
private $tokenGenerator; | |||
/** @var SessionStorage */ | |||
private $sessionStorage; | |||
/** @var CsrfToken|null */ | |||
private $csrfToken = null; | |||
/** | |||
* @param CsrfTokenGenerator $tokenGenerator | |||
@@ -51,6 +53,10 @@ class CsrfTokenManager { | |||
* @return CsrfToken | |||
*/ | |||
public function getToken() { | |||
if(!is_null($this->csrfToken)) { | |||
return $this->csrfToken; | |||
} | |||
if($this->sessionStorage->hasToken()) { | |||
$value = $this->sessionStorage->getToken(); | |||
} else { | |||
@@ -58,7 +64,8 @@ class CsrfTokenManager { | |||
$this->sessionStorage->setToken($value); | |||
} | |||
return new CsrfToken($value); | |||
$this->csrfToken = new CsrfToken($value); | |||
return $this->csrfToken; | |||
} | |||
/** | |||
@@ -69,13 +76,15 @@ class CsrfTokenManager { | |||
public function refreshToken() { | |||
$value = $this->tokenGenerator->generateToken(); | |||
$this->sessionStorage->setToken($value); | |||
return new CsrfToken($value); | |||
$this->csrfToken = new CsrfToken($value); | |||
return $this->csrfToken; | |||
} | |||
/** | |||
* Remove the current token from the storage. | |||
*/ | |||
public function removeToken() { | |||
$this->csrfToken = null; | |||
$this->sessionStorage->removeToken(); | |||
} | |||
@@ -73,6 +73,7 @@ use OC\Security\Bruteforce\Throttler; | |||
use OC\Security\CertificateManager; | |||
use OC\Security\CSP\ContentSecurityPolicyManager; | |||
use OC\Security\Crypto; | |||
use OC\Security\CSP\ContentSecurityPolicyNonceManager; | |||
use OC\Security\CSRF\CsrfTokenGenerator; | |||
use OC\Security\CSRF\CsrfTokenManager; | |||
use OC\Security\CSRF\TokenStorage\SessionStorage; | |||
@@ -708,6 +709,11 @@ class Server extends ServerContainer implements IServerContainer { | |||
$this->registerService('ContentSecurityPolicyManager', function (Server $c) { | |||
return new ContentSecurityPolicyManager(); | |||
}); | |||
$this->registerService('ContentSecurityPolicyNonceManager', function(Server $c) { | |||
return new ContentSecurityPolicyNonceManager( | |||
$c->getCsrfTokenManager() | |||
); | |||
}); | |||
$this->registerService('ShareManager', function(Server $c) { | |||
$config = $c->getConfig(); | |||
$factoryClass = $config->getSystemValue('sharing.managerFactory', '\OC\Share20\ProviderFactory'); | |||
@@ -1405,6 +1411,13 @@ class Server extends ServerContainer implements IServerContainer { | |||
return $this->query('ContentSecurityPolicyManager'); | |||
} | |||
/** | |||
* @return ContentSecurityPolicyNonceManager | |||
*/ | |||
public function getContentSecurityPolicyNonceManager() { | |||
return $this->query('ContentSecurityPolicyNonceManager'); | |||
} | |||
/** | |||
* Not a public API as of 8.2, wait for 9.0 | |||
* |
@@ -24,8 +24,6 @@ | |||
namespace OCP\AppFramework\Http; | |||
use OCP\AppFramework\Http; | |||
/** | |||
* Class ContentSecurityPolicy is a simple helper which allows applications to | |||
* modify the Content-Security-Policy sent by ownCloud. Per default only JavaScript, |
@@ -38,6 +38,8 @@ use OCP\AppFramework\Http; | |||
class EmptyContentSecurityPolicy { | |||
/** @var bool Whether inline JS snippets are allowed */ | |||
protected $inlineScriptAllowed = null; | |||
/** @var string Whether JS nonces should be used */ | |||
protected $useJsNonce = null; | |||
/** | |||
* @var bool Whether eval in JS scripts is allowed | |||
* TODO: Disallow per default | |||
@@ -74,12 +76,25 @@ class EmptyContentSecurityPolicy { | |||
* @param bool $state | |||
* @return $this | |||
* @since 8.1.0 | |||
* @deprecated 10.0 CSP tokens are now used | |||
*/ | |||
public function allowInlineScript($state = false) { | |||
$this->inlineScriptAllowed = $state; | |||
return $this; | |||
} | |||
/** | |||
* Use the according JS nonce | |||
* | |||
* @param string $nonce | |||
* @return $this | |||
* @since 9.2.0 | |||
*/ | |||
public function useJsNonce($nonce) { | |||
$this->useJsNonce = $nonce; | |||
return $this; | |||
} | |||
/** | |||
* Whether eval in JavaScript is allowed or forbidden | |||
* @param bool $state | |||
@@ -323,6 +338,15 @@ class EmptyContentSecurityPolicy { | |||
if(!empty($this->allowedScriptDomains) || $this->inlineScriptAllowed || $this->evalScriptAllowed) { | |||
$policy .= 'script-src '; | |||
if(is_string($this->useJsNonce)) { | |||
$policy .= '\'nonce-'.base64_encode($this->useJsNonce).'\''; | |||
$allowedScriptDomains = array_flip($this->allowedScriptDomains); | |||
unset($allowedScriptDomains['\'self\'']); | |||
$this->allowedScriptDomains = array_flip($allowedScriptDomains); | |||
if(count($allowedScriptDomains) !== 0) { | |||
$policy .= ' '; | |||
} | |||
} | |||
if(is_array($this->allowedScriptDomains)) { | |||
$policy .= implode(' ', $this->allowedScriptDomains); | |||
} |
@@ -427,4 +427,28 @@ class EmptyContentSecurityPolicyTest extends \Test\TestCase { | |||
$this->contentSecurityPolicy->disallowChildSrcDomain('www.owncloud.org')->disallowChildSrcDomain('www.owncloud.com'); | |||
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); | |||
} | |||
public function testGetPolicyWithJsNonceAndScriptDomains() { | |||
$expectedPolicy = "default-src 'none';script-src 'nonce-TXlKc05vbmNl' www.nextcloud.com www.nextcloud.org"; | |||
$this->contentSecurityPolicy->addAllowedScriptDomain('www.nextcloud.com'); | |||
$this->contentSecurityPolicy->useJsNonce('MyJsNonce'); | |||
$this->contentSecurityPolicy->addAllowedScriptDomain('www.nextcloud.org'); | |||
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); | |||
} | |||
public function testGetPolicyWithJsNonceAndSelfScriptDomain() { | |||
$expectedPolicy = "default-src 'none';script-src 'nonce-TXlKc05vbmNl'"; | |||
$this->contentSecurityPolicy->useJsNonce('MyJsNonce'); | |||
$this->contentSecurityPolicy->addAllowedScriptDomain("'self'"); | |||
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); | |||
} | |||
public function testGetPolicyWithoutJsNonceAndSelfScriptDomain() { | |||
$expectedPolicy = "default-src 'none';script-src 'self'"; | |||
$this->contentSecurityPolicy->addAllowedScriptDomain("'self'"); | |||
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); | |||
} | |||
} |
@@ -36,6 +36,8 @@ use OC\AppFramework\Middleware\Security\SecurityMiddleware; | |||
use OC\AppFramework\Utility\ControllerMethodReflector; | |||
use OC\Security\CSP\ContentSecurityPolicy; | |||
use OC\Security\CSP\ContentSecurityPolicyManager; | |||
use OC\Security\CSRF\CsrfToken; | |||
use OC\Security\CSRF\CsrfTokenManager; | |||
use OCP\AppFramework\Controller; | |||
use OCP\AppFramework\Http\EmptyContentSecurityPolicy; | |||
use OCP\AppFramework\Http\RedirectResponse; | |||
@@ -72,6 +74,8 @@ class SecurityMiddlewareTest extends \Test\TestCase { | |||
private $urlGenerator; | |||
/** @var ContentSecurityPolicyManager|\PHPUnit_Framework_MockObject_MockObject */ | |||
private $contentSecurityPolicyManager; | |||
/** @var CsrfTokenManager|\PHPUnit_Framework_MockObject_MockObject */ | |||
private $csrfTokenManager; | |||
protected function setUp() { | |||
parent::setUp(); | |||
@@ -83,6 +87,7 @@ class SecurityMiddlewareTest extends \Test\TestCase { | |||
$this->urlGenerator = $this->createMock(IURLGenerator::class); | |||
$this->request = $this->createMock(IRequest::class); | |||
$this->contentSecurityPolicyManager = $this->createMock(ContentSecurityPolicyManager::class); | |||
$this->csrfTokenManager = $this->createMock(CsrfTokenManager::class); | |||
$this->middleware = $this->getMiddleware(true, true); | |||
$this->secException = new SecurityException('hey', false); | |||
$this->secAjaxException = new SecurityException('hey', true); | |||
@@ -103,7 +108,8 @@ class SecurityMiddlewareTest extends \Test\TestCase { | |||
'files', | |||
$isLoggedIn, | |||
$isAdminUser, | |||
$this->contentSecurityPolicyManager | |||
$this->contentSecurityPolicyManager, | |||
$this->csrfTokenManager | |||
); | |||
} | |||
@@ -553,6 +559,10 @@ class SecurityMiddlewareTest extends \Test\TestCase { | |||
} | |||
public function testAfterController() { | |||
$this->request | |||
->expects($this->once()) | |||
->method('isUserAgent') | |||
->willReturn(false); | |||
$response = $this->createMock(Response::class); | |||
$defaultPolicy = new ContentSecurityPolicy(); | |||
$defaultPolicy->addAllowedImageDomain('defaultpolicy'); | |||
@@ -591,4 +601,45 @@ class SecurityMiddlewareTest extends \Test\TestCase { | |||
$this->middleware->afterController($this->controller, 'test', $response); | |||
} | |||
public function testAfterControllerWithContentSecurityPolicy3Support() { | |||
$this->request | |||
->expects($this->once()) | |||
->method('isUserAgent') | |||
->willReturn(true); | |||
$token = $this->createMock(CsrfToken::class); | |||
$token | |||
->expects($this->once()) | |||
->method('getEncryptedValue') | |||
->willReturn('MyEncryptedToken'); | |||
$this->csrfTokenManager | |||
->expects($this->once()) | |||
->method('getToken') | |||
->willReturn($token); | |||
$response = $this->createMock(Response::class); | |||
$defaultPolicy = new ContentSecurityPolicy(); | |||
$defaultPolicy->addAllowedImageDomain('defaultpolicy'); | |||
$currentPolicy = new ContentSecurityPolicy(); | |||
$currentPolicy->addAllowedConnectDomain('currentPolicy'); | |||
$mergedPolicy = new ContentSecurityPolicy(); | |||
$mergedPolicy->addAllowedMediaDomain('mergedPolicy'); | |||
$response | |||
->expects($this->exactly(2)) | |||
->method('getContentSecurityPolicy') | |||
->willReturn($currentPolicy); | |||
$this->contentSecurityPolicyManager | |||
->expects($this->once()) | |||
->method('getDefaultPolicy') | |||
->willReturn($defaultPolicy); | |||
$this->contentSecurityPolicyManager | |||
->expects($this->once()) | |||
->method('mergePolicies') | |||
->with($defaultPolicy, $currentPolicy) | |||
->willReturn($mergedPolicy); | |||
$response->expects($this->once()) | |||
->method('setContentSecurityPolicy') | |||
->with($mergedPolicy); | |||
$this->assertEquals($response, $this->middleware->afterController($this->controller, 'test', $response)); | |||
} | |||
} |
@@ -0,0 +1,57 @@ | |||
<?php | |||
/** | |||
* @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch> | |||
* | |||
* @license GNU AGPL version 3 or any later version | |||
* | |||
* This program is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU Affero General Public License as | |||
* published by the Free Software Foundation, either version 3 of the | |||
* License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU Affero General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Affero General Public License | |||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
* | |||
*/ | |||
namespace Test\Security\CSP; | |||
use OC\Security\CSP\ContentSecurityPolicyNonceManager; | |||
use OC\Security\CSRF\CsrfToken; | |||
use OC\Security\CSRF\CsrfTokenManager; | |||
use Test\TestCase; | |||
class ContentSecurityPolicyNonceManagerTest extends TestCase { | |||
/** @var CsrfTokenManager */ | |||
private $csrfTokenManager; | |||
/** @var ContentSecurityPolicyNonceManager */ | |||
private $nonceManager; | |||
public function setUp() { | |||
$this->csrfTokenManager = $this->createMock(CsrfTokenManager::class); | |||
$this->nonceManager = new ContentSecurityPolicyNonceManager( | |||
$this->csrfTokenManager | |||
); | |||
} | |||
public function testGetNonce() { | |||
$token = $this->createMock(CsrfToken::class); | |||
$token | |||
->expects($this->once()) | |||
->method('getEncryptedValue') | |||
->willReturn('MyToken'); | |||
$this->csrfTokenManager | |||
->expects($this->once()) | |||
->method('getToken') | |||
->willReturn($token); | |||
$this->assertSame('TXlUb2tlbg==', $this->nonceManager->getNonce()); | |||
$this->assertSame('TXlUb2tlbg==', $this->nonceManager->getNonce()); | |||
} | |||
} |
@@ -56,6 +56,22 @@ class CsrfTokenManagerTest extends \Test\TestCase { | |||
$this->assertEquals($expected, $this->csrfTokenManager->getToken()); | |||
} | |||
public function testGetTokenWithExistingTokenKeepsOnSecondRequest() { | |||
$this->storageInterface | |||
->expects($this->once()) | |||
->method('hasToken') | |||
->willReturn(true); | |||
$this->storageInterface | |||
->expects($this->once()) | |||
->method('getToken') | |||
->willReturn('MyExistingToken'); | |||
$expected = new \OC\Security\CSRF\CsrfToken('MyExistingToken'); | |||
$token = $this->csrfTokenManager->getToken(); | |||
$this->assertSame($token, $this->csrfTokenManager->getToken()); | |||
$this->assertSame($token, $this->csrfTokenManager->getToken()); | |||
} | |||
public function testGetTokenWithoutExistingToken() { | |||
$this->storageInterface | |||
->expects($this->once()) |
@@ -28,6 +28,13 @@ class CsrfTokenTest extends \Test\TestCase { | |||
$this->assertSame(':', $csrfToken->getEncryptedValue()[16]); | |||
} | |||
public function testGetEncryptedValueStaysSameOnSecondRequest() { | |||
$csrfToken = new \OC\Security\CSRF\CsrfToken('MyCsrfToken'); | |||
$tokenValue = $csrfToken->getEncryptedValue(); | |||
$this->assertSame($tokenValue, $csrfToken->getEncryptedValue()); | |||
$this->assertSame($tokenValue, $csrfToken->getEncryptedValue()); | |||
} | |||
public function testGetDecryptedValue() { | |||
$csrfToken = new \OC\Security\CSRF\CsrfToken('XlQhHjgWCgBXAEI0Khl+IQEiCXN2LUcDHAQTQAc1HQs=:qgkUlg8l3m8WnkOG4XM9Az33pAt1vSVMx4hcJFsxdqc='); | |||
$this->assertSame('/3JKTq2ldmzcDr1f5zDJ7Wt0lEgqqfKF', $csrfToken->getDecryptedValue()); |