Browse Source
Merge pull request #9518 from nextcloud/feature/5986/public_share_controller_middleware
Merge pull request #9518 from nextcloud/feature/5986/public_share_controller_middleware
Public share middleware & controllerpull/9951/head
committed by
GitHub
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 1133 additions and 478 deletions
-
8apps/files_sharing/appinfo/routes.php
-
11apps/files_sharing/js/public.js
-
44apps/files_sharing/lib/Controller/PublicPreviewController.php
-
169apps/files_sharing/lib/Controller/ShareController.php
-
23apps/files_sharing/lib/Middleware/SharingCheckMiddleware.php
-
18apps/files_sharing/tests/Controller/PublicPreviewControllerTest.php
-
226apps/files_sharing/tests/Controller/ShareControllerTest.php
-
89apps/files_sharing/tests/Middleware/SharingCheckMiddlewareTest.php
-
0core/css/publicshareauth.css
-
0core/js/publicshareauth.js
-
4core/templates/publicshareauth.php
-
4lib/composer/composer/autoload_classmap.php
-
4lib/composer/composer/autoload_static.php
-
8lib/private/AppFramework/DependencyInjection/DIContainer.php
-
7lib/private/AppFramework/Middleware/PublicShare/Exceptions/NeedAuthenticationException.php
-
112lib/private/AppFramework/Middleware/PublicShare/PublicShareMiddleware.php
-
2lib/private/legacy/template/functions.php
-
192lib/public/AppFramework/AuthPublicShareController.php
-
138lib/public/AppFramework/PublicShareController.php
-
4tests/acceptance/features/bootstrap/FilesSharingAppContext.php
-
159tests/lib/AppFramework/Controller/AuthPublicShareControllerTest.php
-
102tests/lib/AppFramework/Controller/PublicShareControllerTest.php
-
287tests/lib/AppFramework/Middleware/PublicShare/PublicShareMiddlewareTest.php
@ -0,0 +1,7 @@ |
|||
<?php |
|||
|
|||
namespace OC\AppFramework\Middleware\PublicShare\Exceptions; |
|||
|
|||
class NeedAuthenticationException extends \Exception { |
|||
|
|||
} |
|||
@ -0,0 +1,112 @@ |
|||
<?php |
|||
|
|||
namespace OC\AppFramework\Middleware\PublicShare; |
|||
|
|||
use OC\AppFramework\Middleware\PublicShare\Exceptions\NeedAuthenticationException; |
|||
use OCP\AppFramework\AuthPublicShareController; |
|||
use OCP\AppFramework\Http\NotFoundResponse; |
|||
use OCP\AppFramework\Http\Response; |
|||
use OCP\AppFramework\Middleware; |
|||
use OCP\AppFramework\PublicShareController; |
|||
use OCP\Files\NotFoundException; |
|||
use OCP\IConfig; |
|||
use OCP\IRequest; |
|||
use OCP\ISession; |
|||
|
|||
class PublicShareMiddleware extends Middleware { |
|||
/** @var IRequest */ |
|||
private $request; |
|||
|
|||
/** @var ISession */ |
|||
private $session; |
|||
|
|||
/** @var IConfig */ |
|||
private $config; |
|||
|
|||
public function __construct(IRequest $request, ISession $session, IConfig $config) { |
|||
$this->request = $request; |
|||
$this->session = $session; |
|||
$this->config = $config; |
|||
} |
|||
|
|||
public function beforeController($controller, $methodName) { |
|||
if (!($controller instanceof PublicShareController)) { |
|||
return; |
|||
} |
|||
|
|||
if (!$this->isLinkSharingEnabled()) { |
|||
throw new NotFoundException('Link sharing is disabled'); |
|||
} |
|||
|
|||
// We require the token parameter to be set
|
|||
$token = $this->request->getParam('token'); |
|||
if ($token === null) { |
|||
throw new NotFoundException(); |
|||
} |
|||
|
|||
// Set the token
|
|||
$controller->setToken($token); |
|||
|
|||
if (!$controller->isValidToken()) { |
|||
$controller->shareNotFound(); |
|||
throw new NotFoundException(); |
|||
} |
|||
|
|||
// No need to check for authentication when we try to authenticate
|
|||
if ($methodName === 'authenticate' || $methodName === 'showAuthenticate') { |
|||
return; |
|||
} |
|||
|
|||
// If authentication succeeds just continue
|
|||
if ($controller->isAuthenticated()) { |
|||
return; |
|||
} |
|||
|
|||
// If we can authenticate to this controller do it else we throw a 404 to not leak any info
|
|||
if ($controller instanceof AuthPublicShareController) { |
|||
$this->session->set('public_link_authenticate_redirect', json_encode($this->request->getParams())); |
|||
throw new NeedAuthenticationException(); |
|||
} |
|||
|
|||
throw new NotFoundException(); |
|||
|
|||
} |
|||
|
|||
public function afterException($controller, $methodName, \Exception $exception) { |
|||
if (!($controller instanceof PublicShareController)) { |
|||
throw $exception; |
|||
} |
|||
|
|||
if ($exception instanceof NotFoundException) { |
|||
return new NotFoundResponse(); |
|||
} |
|||
|
|||
if ($controller instanceof AuthPublicShareController && $exception instanceof NeedAuthenticationException) { |
|||
return $controller->getAuthenticationRedirect($this->getFunctionForRoute($this->request->getParam('_route'))); |
|||
} |
|||
|
|||
throw $exception; |
|||
} |
|||
|
|||
private function getFunctionForRoute(string $route): string { |
|||
$tmp = explode('.', $route); |
|||
return array_pop($tmp); |
|||
} |
|||
|
|||
/** |
|||
* Check if link sharing is allowed |
|||
*/ |
|||
private function isLinkSharingEnabled(): bool { |
|||
// Check if the shareAPI is enabled
|
|||
if ($this->config->getAppValue('core', 'shareapi_enabled', 'yes') !== 'yes') { |
|||
return false; |
|||
} |
|||
|
|||
// Check whether public sharing is enabled
|
|||
if($this->config->getAppValue('core', 'shareapi_allow_links', 'yes') !== 'yes') { |
|||
return false; |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
} |
|||
@ -0,0 +1,192 @@ |
|||
<?php |
|||
/** |
|||
* @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl> |
|||
* |
|||
* @author Roeland Jago Douma <roeland@famdouma.nl> |
|||
* |
|||
* @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/>. |
|||
* |
|||
*/ |
|||
declare(strict_types=1); |
|||
|
|||
namespace OCP\AppFramework; |
|||
|
|||
use OCP\AppFramework\Http\RedirectResponse; |
|||
use OCP\AppFramework\Http\TemplateResponse; |
|||
use OCP\IRequest; |
|||
use OCP\ISession; |
|||
use OCP\IURLGenerator; |
|||
|
|||
/** |
|||
* Base controller for interactive public shares |
|||
* |
|||
* It will verify if the user is properly authenticated to the share. If not the |
|||
* user will be redirected to an authentication page. |
|||
* |
|||
* Use this for a controller that is to be called directly by a user. So the |
|||
* normal public share page for files/calendars etc. |
|||
* |
|||
* @since 14.0.0 |
|||
*/ |
|||
abstract class AuthPublicShareController extends PublicShareController { |
|||
|
|||
/** @var IURLGenerator */ |
|||
protected $urlGenerator; |
|||
|
|||
/** |
|||
* @since 14.0.0 |
|||
*/ |
|||
public function __construct(string $appName, |
|||
IRequest $request, |
|||
ISession $session, |
|||
IURLGenerator $urlGenerator) { |
|||
parent::__construct($appName, $request, $session); |
|||
|
|||
$this->urlGenerator = $urlGenerator; |
|||
} |
|||
|
|||
/** |
|||
* @PublicPage |
|||
* @NoCSRFRequired |
|||
* |
|||
* Show the authentication page |
|||
* The form has to submit to the authenticate method route |
|||
* |
|||
* @since 14.0.0 |
|||
*/ |
|||
public function showAuthenticate(): TemplateResponse { |
|||
return new TemplateResponse('core', 'publicshareauth', [], 'guest'); |
|||
} |
|||
|
|||
/** |
|||
* The template to show when authentication failed |
|||
* |
|||
* @since 14.0.0 |
|||
*/ |
|||
protected function showAuthFailed(): TemplateResponse { |
|||
return new TemplateResponse('core', 'publicshareauth', ['wrongpw' => true], 'guest'); |
|||
} |
|||
|
|||
/** |
|||
* Verify the password |
|||
* |
|||
* @since 14.0.0 |
|||
*/ |
|||
abstract protected function verifyPassword(string $password): bool; |
|||
|
|||
/** |
|||
* Function called after failed authentication |
|||
* |
|||
* You can use this to do some logging for example |
|||
* |
|||
* @since 14.0.0 |
|||
*/ |
|||
protected function authFailed() { |
|||
} |
|||
|
|||
/** |
|||
* Function called after successfull authentication |
|||
* |
|||
* You can use this to do some logging for example |
|||
* |
|||
* @since 14.0.0 |
|||
*/ |
|||
protected function authSucceeded() { |
|||
} |
|||
|
|||
/** |
|||
* @UseSession |
|||
* @PublicPage |
|||
* @BruteForceProtection(action=publicLinkAuth) |
|||
* |
|||
* Authenticate the share |
|||
* |
|||
* @since 14.0.0 |
|||
*/ |
|||
final public function authenticate(string $password = '') { |
|||
// Already authenticated
|
|||
if ($this->isAuthenticated()) { |
|||
return $this->getRedirect(); |
|||
} |
|||
|
|||
if (!$this->verifyPassword($password)) { |
|||
$this->authFailed(); |
|||
$response = $this->showAuthFailed(); |
|||
$response->throttle(); |
|||
return $response; |
|||
} |
|||
|
|||
$this->session->regenerateId(true, true); |
|||
$response = $this->getRedirect(); |
|||
|
|||
$this->session->set('public_link_authenticated_token', $this->getToken()); |
|||
$this->session->set('public_link_authenticated_password_hash', $this->getPasswordHash()); |
|||
|
|||
$this->authSucceeded(); |
|||
|
|||
return $response; |
|||
} |
|||
|
|||
/** |
|||
* Default landing page |
|||
* |
|||
* @since 14.0.0 |
|||
*/ |
|||
abstract public function showShare(): TemplateResponse; |
|||
|
|||
/** |
|||
* @since 14.0.0 |
|||
*/ |
|||
final public function getAuthenticationRedirect(string $redirect): RedirectResponse { |
|||
return new RedirectResponse( |
|||
$this->urlGenerator->linkToRoute($this->getRoute('showAuthenticate'), ['token' => $this->getToken(), 'redirect' => $redirect]) |
|||
); |
|||
} |
|||
|
|||
/** |
|||
* @since 14.0.0 |
|||
*/ |
|||
private function getRoute(string $function): string { |
|||
$app = strtolower($this->appName); |
|||
$class = strtolower((new \ReflectionClass($this))->getShortName()); |
|||
|
|||
return $app . '.' . $class . '.' . $function; |
|||
} |
|||
|
|||
/** |
|||
* @since 14.0.0 |
|||
*/ |
|||
private function getRedirect(): RedirectResponse { |
|||
//Get all the stored redirect parameters:
|
|||
$params = $this->session->get('public_link_authenticate_redirect'); |
|||
|
|||
$route = $this->getRoute('showShare'); |
|||
|
|||
if ($params === null) { |
|||
$params = [ |
|||
'token' => $this->getToken(), |
|||
]; |
|||
} else { |
|||
$params = json_decode($params, true); |
|||
if (isset($params['_route'])) { |
|||
$route = $params['_route']; |
|||
unset($params['_route']); |
|||
} |
|||
} |
|||
|
|||
return new RedirectResponse($this->urlGenerator->linkToRoute($route, $params)); |
|||
} |
|||
} |
|||
@ -0,0 +1,138 @@ |
|||
<?php |
|||
/** |
|||
* @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl> |
|||
* |
|||
* @author Roeland Jago Douma <roeland@famdouma.nl> |
|||
* |
|||
* @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/>. |
|||
* |
|||
*/ |
|||
declare(strict_types=1); |
|||
|
|||
namespace OCP\AppFramework; |
|||
|
|||
use OCP\IRequest; |
|||
use OCP\ISession; |
|||
|
|||
/** |
|||
* Base controller for public shares |
|||
* |
|||
* It will verify if the user is properly authenticated to the share. If not a 404 |
|||
* is thrown by the PublicShareMiddleware. |
|||
* |
|||
* Use this for example for a controller that is not to be called via a webbrowser |
|||
* directly. For example a PublicPreviewController. As this is not meant to be |
|||
* called by a user direclty. |
|||
* |
|||
* To show an auth page extend the AuthPublicShareController |
|||
* |
|||
* @since 14.0.0 |
|||
*/ |
|||
abstract class PublicShareController extends Controller { |
|||
|
|||
/** @var ISession */ |
|||
protected $session; |
|||
|
|||
/** @var string */ |
|||
private $token; |
|||
|
|||
/** |
|||
* @since 14.0.0 |
|||
*/ |
|||
public function __construct(string $appName, |
|||
IRequest $request, |
|||
ISession $session) { |
|||
parent::__construct($appName, $request); |
|||
|
|||
$this->session = $session; |
|||
} |
|||
|
|||
/** |
|||
* Middleware set the token for the request |
|||
* |
|||
* @since 14.0.0 |
|||
*/ |
|||
final public function setToken(string $token) { |
|||
$this->token = $token; |
|||
} |
|||
|
|||
/** |
|||
* Get the token for this request |
|||
* |
|||
* @since 14.0.0 |
|||
*/ |
|||
final public function getToken(): string { |
|||
return $this->token; |
|||
} |
|||
|
|||
/** |
|||
* Get a hash of the password for this share |
|||
* |
|||
* To ensure access is blocked when the password to a share is changed we store |
|||
* a hash of the password for this token. |
|||
* |
|||
* @since 14.0.0 |
|||
*/ |
|||
abstract protected function getPasswordHash(): string; |
|||
|
|||
/** |
|||
* Is the provided token a valid token |
|||
* |
|||
* This function is already called from the middleware directly after setting the token. |
|||
* |
|||
* @since 14.0.0 |
|||
*/ |
|||
abstract public function isValidToken(): bool; |
|||
|
|||
/** |
|||
* Is a share with this token password protected |
|||
* |
|||
* @since 14.0.0 |
|||
*/ |
|||
abstract protected function isPasswordProtected(): bool; |
|||
|
|||
/** |
|||
* Check if a share is authenticated or not |
|||
* |
|||
* @since 14.0.0 |
|||
*/ |
|||
final public function isAuthenticated(): bool { |
|||
// Always authenticated against non password protected shares
|
|||
if (!$this->isPasswordProtected()) { |
|||
return true; |
|||
} |
|||
|
|||
// If we are authenticated properly
|
|||
if ($this->session->get('public_link_authenticated_token') === $this->getToken() && |
|||
$this->session->get('public_link_authenticated_password_hash') === $this->getPasswordHash()) { |
|||
return true; |
|||
} |
|||
|
|||
// Fail by default if nothing matches
|
|||
return false; |
|||
} |
|||
|
|||
/** |
|||
* Function called if the share is not found. |
|||
* |
|||
* You can use this to do some logging for example |
|||
* |
|||
* @since 14.0.0 |
|||
*/ |
|||
public function shareNotFound() { |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,159 @@ |
|||
<?php |
|||
/** |
|||
* @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl> |
|||
* |
|||
* @author Roeland Jago Douma <roeland@famdouma.nl> |
|||
* |
|||
* @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\AppFramework\Controller; |
|||
|
|||
use OC\AppFramework\Middleware\PublicShare\Exceptions\NeedAuthenticationException; |
|||
use OC\AppFramework\Middleware\PublicShare\PublicShareMiddleware; |
|||
use OCP\AppFramework\AuthPublicShareController; |
|||
use OCP\AppFramework\Controller; |
|||
use OCP\AppFramework\Http\NotFoundResponse; |
|||
use OCP\AppFramework\Http\RedirectResponse; |
|||
use OCP\AppFramework\Http\TemplateResponse; |
|||
use OCP\AppFramework\PublicShareController; |
|||
use OCP\Files\NotFoundException; |
|||
use OCP\IConfig; |
|||
use OCP\IRequest; |
|||
use OCP\ISession; |
|||
use OCP\IURLGenerator; |
|||
|
|||
class AuthPublicShareControllerTest extends \Test\TestCase { |
|||
|
|||
/** @var IRequest|\PHPUnit_Framework_MockObject_MockObject */ |
|||
private $request; |
|||
/** @var ISession|\PHPUnit_Framework_MockObject_MockObject */ |
|||
private $session; |
|||
/** @var IURLGenerator|\PHPUnit_Framework_MockObject_MockObject */ |
|||
private $urlGenerator; |
|||
|
|||
/** @var AuthPublicShareController|\PHPUnit_Framework_MockObject_MockObject */ |
|||
private $controller; |
|||
|
|||
|
|||
protected function setUp() { |
|||
parent::setUp(); |
|||
|
|||
$this->request = $this->createMock(IRequest::class); |
|||
$this->session = $this->createMock(ISession::class); |
|||
$this->urlGenerator = $this->createMock(IURLGenerator::class); |
|||
|
|||
$this->controller = $this->getMockBuilder(AuthPublicShareController::class) |
|||
->setConstructorArgs([ |
|||
'app', |
|||
$this->request, |
|||
$this->session, |
|||
$this->urlGenerator |
|||
])->setMethods([ |
|||
'authFailed', |
|||
'getPasswordHash', |
|||
'isAuthenticated', |
|||
'isPasswordProtected', |
|||
'isValidToken', |
|||
'showShare', |
|||
'verifyPassword' |
|||
])->getMock(); |
|||
} |
|||
|
|||
public function testShowAuthenticate() { |
|||
$expects = new TemplateResponse('core', 'publicshareauth', [], 'guest'); |
|||
|
|||
$this->assertEquals($expects, $this->controller->showAuthenticate()); |
|||
} |
|||
|
|||
public function testAuthenticateAuthenticated() { |
|||
$this->controller->method('isAuthenticated') |
|||
->willReturn(true); |
|||
|
|||
$this->controller->setToken('myToken'); |
|||
|
|||
$this->session->method('get') |
|||
->willReturnMap(['public_link_authenticate_redirect', ['foo' => 'bar']]); |
|||
|
|||
$this->urlGenerator->method('linkToRoute') |
|||
->willReturn('myLink!'); |
|||
|
|||
$result = $this->controller->authenticate('password'); |
|||
$this->assertInstanceOf(RedirectResponse::class, $result); |
|||
$this->assertSame('myLink!', $result->getRedirectURL()); |
|||
} |
|||
|
|||
public function testAuthenticateInvalidPassword() { |
|||
$this->controller->setToken('token'); |
|||
$this->controller->method('isPasswordProtected') |
|||
->willReturn(true); |
|||
|
|||
$this->controller->method('verifyPassword') |
|||
->with('password') |
|||
->willReturn(false); |
|||
|
|||
$this->controller->expects($this->once()) |
|||
->method('authFailed'); |
|||
|
|||
$expects = new TemplateResponse('core', 'publicshareauth', ['wrongpw' => true], 'guest'); |
|||
$expects->throttle(); |
|||
|
|||
$result = $this->controller->authenticate('password'); |
|||
|
|||
$this->assertEquals($expects, $result); |
|||
} |
|||
|
|||
public function testAuthenticateValidPassword() { |
|||
$this->controller->setToken('token'); |
|||
$this->controller->method('isPasswordProtected') |
|||
->willReturn(true); |
|||
$this->controller->method('verifyPassword') |
|||
->with('password') |
|||
->willReturn(true); |
|||
$this->controller->method('getPasswordHash') |
|||
->willReturn('hash'); |
|||
|
|||
$this->session->expects($this->once()) |
|||
->method('regenerateId'); |
|||
$this->session->method('get') |
|||
->willReturnMap(['public_link_authenticate_redirect', ['foo' => 'bar']]); |
|||
|
|||
$tokenSet = false; |
|||
$hashSet = false; |
|||
$this->session |
|||
->method('set') |
|||
->will($this->returnCallback(function($key, $value) use (&$tokenSet, &$hashSet) { |
|||
if ($key === 'public_link_authenticated_token' && $value === 'token') { |
|||
$tokenSet = true; |
|||
return true; |
|||
} |
|||
if ($key === 'public_link_authenticated_password_hash' && $value === 'hash') { |
|||
$hashSet = true; |
|||
return true; |
|||
} |
|||
return false; |
|||
})); |
|||
|
|||
$this->urlGenerator->method('linkToRoute') |
|||
->willReturn('myLink!'); |
|||
|
|||
$result = $this->controller->authenticate('password'); |
|||
$this->assertInstanceOf(RedirectResponse::class, $result); |
|||
$this->assertSame('myLink!', $result->getRedirectURL()); |
|||
$this->assertTrue($tokenSet); |
|||
$this->assertTrue($hashSet); |
|||
} |
|||
} |
|||
@ -0,0 +1,102 @@ |
|||
<?php |
|||
/** |
|||
* @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl> |
|||
* |
|||
* @author Roeland Jago Douma <roeland@famdouma.nl> |
|||
* |
|||
* @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\AppFramework\Controller; |
|||
|
|||
use OC\AppFramework\Middleware\PublicShare\Exceptions\NeedAuthenticationException; |
|||
use OC\AppFramework\Middleware\PublicShare\PublicShareMiddleware; |
|||
use OCP\AppFramework\AuthPublicShareController; |
|||
use OCP\AppFramework\Controller; |
|||
use OCP\AppFramework\Http\NotFoundResponse; |
|||
use OCP\AppFramework\Http\RedirectResponse; |
|||
use OCP\AppFramework\PublicShareController; |
|||
use OCP\Files\NotFoundException; |
|||
use OCP\IConfig; |
|||
use OCP\IRequest; |
|||
use OCP\ISession; |
|||
use OCP\IURLGenerator; |
|||
|
|||
|
|||
class PublicShareControllerTest extends \Test\TestCase { |
|||
|
|||
/** @var IRequest|\PHPUnit_Framework_MockObject_MockObject */ |
|||
private $request; |
|||
/** @var ISession|\PHPUnit_Framework_MockObject_MockObject */ |
|||
private $session; |
|||
|
|||
/** @var PublicShareController|\PHPUnit_Framework_MockObject_MockObject */ |
|||
private $controller; |
|||
|
|||
|
|||
protected function setUp() { |
|||
parent::setUp(); |
|||
|
|||
$this->request = $this->createMock(IRequest::class); |
|||
$this->session = $this->createMock(ISession::class); |
|||
|
|||
$this->controller = $this->getMockBuilder(PublicShareController::class) |
|||
->setConstructorArgs([ |
|||
'app', |
|||
$this->request, |
|||
$this->session |
|||
])->getMock(); |
|||
} |
|||
|
|||
public function testGetToken() { |
|||
$this->controller->setToken('test'); |
|||
$this->assertEquals('test', $this->controller->getToken()); |
|||
} |
|||
|
|||
public function dataIsAuthenticated() { |
|||
return [ |
|||
[false, 'token1', 'token1', 'hash1', 'hash1', true], |
|||
[false, 'token1', 'token1', 'hash1', 'hash2', true], |
|||
[false, 'token1', 'token2', 'hash1', 'hash1', true], |
|||
[false, 'token1', 'token2', 'hash1', 'hash2', true], |
|||
[ true, 'token1', 'token1', 'hash1', 'hash1', true], |
|||
[ true, 'token1', 'token1', 'hash1', 'hash2', false], |
|||
[ true, 'token1', 'token2', 'hash1', 'hash1', false], |
|||
[ true, 'token1', 'token2', 'hash1', 'hash2', false], |
|||
]; |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider dataIsAuthenticated |
|||
*/ |
|||
public function testIsAuthenticatedNotPasswordProtected(bool $protected, string $token1, string $token2, string $hash1, string $hash2, bool $expected) { |
|||
$this->controller->method('isPasswordProtected') |
|||
->willReturn($protected); |
|||
|
|||
$this->session->method('get') |
|||
->willReturnMap([ |
|||
['public_link_authenticated_token', $token1], |
|||
['public_link_authenticated_password_hash', $hash1], |
|||
]); |
|||
|
|||
$this->controller->setToken($token2); |
|||
$this->controller->method('getPasswordHash') |
|||
->willReturn($hash2); |
|||
|
|||
$this->assertEquals($expected, $this->controller->isAuthenticated()); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,287 @@ |
|||
<?php |
|||
/** |
|||
* @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl> |
|||
* |
|||
* @author Roeland Jago Douma <roeland@famdouma.nl> |
|||
* |
|||
* @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\AppFramework\Middleware\PublicShare; |
|||
|
|||
use OC\AppFramework\Middleware\PublicShare\Exceptions\NeedAuthenticationException; |
|||
use OC\AppFramework\Middleware\PublicShare\PublicShareMiddleware; |
|||
use OCP\AppFramework\AuthPublicShareController; |
|||
use OCP\AppFramework\Controller; |
|||
use OCP\AppFramework\Http\NotFoundResponse; |
|||
use OCP\AppFramework\Http\RedirectResponse; |
|||
use OCP\AppFramework\PublicShareController; |
|||
use OCP\Files\NotFoundException; |
|||
use OCP\IConfig; |
|||
use OCP\IRequest; |
|||
use OCP\ISession; |
|||
use OCP\IURLGenerator; |
|||
|
|||
|
|||
class PublicShareMiddlewareTest extends \Test\TestCase { |
|||
|
|||
/** @var IRequest|\PHPUnit_Framework_MockObject_MockObject */ |
|||
private $request; |
|||
/** @var ISession|\PHPUnit_Framework_MockObject_MockObject */ |
|||
private $session; |
|||
/** @var IConfig|\PHPUnit_Framework_MockObject_MockObject */ |
|||
private $config; |
|||
|
|||
/** @var PublicShareMiddleware */ |
|||
private $middleware; |
|||
|
|||
|
|||
protected function setUp() { |
|||
parent::setUp(); |
|||
|
|||
$this->request = $this->createMock(IRequest::class); |
|||
$this->session = $this->createMock(ISession::class); |
|||
$this->config = $this->createMock(IConfig::class); |
|||
|
|||
$this->middleware = new PublicShareMiddleware( |
|||
$this->request, |
|||
$this->session, |
|||
$this->config |
|||
); |
|||
} |
|||
|
|||
public function testBeforeControllerNoPublicShareController() { |
|||
$controller = $this->createMock(Controller::class); |
|||
|
|||
$this->middleware->beforeController($controller, 'method'); |
|||
$this->assertTrue(true); |
|||
} |
|||
|
|||
public function dataShareApi() { |
|||
return [ |
|||
['no', 'no',], |
|||
['no', 'yes',], |
|||
['yes', 'no',], |
|||
]; |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider dataShareApi |
|||
*/ |
|||
public function testBeforeControllerShareApiDisabled(string $shareApi, string $shareLinks) { |
|||
$controller = $this->createMock(PublicShareController::class); |
|||
|
|||
$this->config->method('getAppValue') |
|||
->willReturnMap([ |
|||
['core', 'shareapi_enabled', 'yes', $shareApi], |
|||
['core', 'shareapi_allow_links', 'yes', $shareLinks], |
|||
]); |
|||
|
|||
$this->expectException(NotFoundException::class); |
|||
$this->middleware->beforeController($controller, 'mehod'); |
|||
} |
|||
|
|||
public function testBeforeControllerNoTokenParam() { |
|||
$controller = $this->createMock(PublicShareController::class); |
|||
|
|||
$this->config->method('getAppValue') |
|||
->willReturnMap([ |
|||
['core', 'shareapi_enabled', 'yes', 'yes'], |
|||
['core', 'shareapi_allow_links', 'yes', 'yes'], |
|||
]); |
|||
|
|||
$this->expectException(NotFoundException::class); |
|||
$this->middleware->beforeController($controller, 'mehod'); |
|||
} |
|||
|
|||
public function testBeforeControllerInvalidToken() { |
|||
$controller = $this->createMock(PublicShareController::class); |
|||
|
|||
$this->config->method('getAppValue') |
|||
->willReturnMap([ |
|||
['core', 'shareapi_enabled', 'yes', 'yes'], |
|||
['core', 'shareapi_allow_links', 'yes', 'yes'], |
|||
]); |
|||
|
|||
$this->request->method('getParam') |
|||
->with('token', null) |
|||
->willReturn('myToken'); |
|||
|
|||
$controller->method('isValidToken') |
|||
->willReturn(false); |
|||
$controller->expects($this->once()) |
|||
->method('shareNotFound'); |
|||
|
|||
$this->expectException(NotFoundException::class); |
|||
$this->middleware->beforeController($controller, 'mehod'); |
|||
} |
|||
|
|||
public function testBeforeControllerValidTokenNotAuthenticated() { |
|||
$controller = $this->getMockBuilder(PublicShareController::class) |
|||
->setConstructorArgs(['app', $this->request, $this->session]) |
|||
->getMock(); |
|||
|
|||
$this->config->method('getAppValue') |
|||
->willReturnMap([ |
|||
['core', 'shareapi_enabled', 'yes', 'yes'], |
|||
['core', 'shareapi_allow_links', 'yes', 'yes'], |
|||
]); |
|||
|
|||
$this->request->method('getParam') |
|||
->with('token', null) |
|||
->willReturn('myToken'); |
|||
|
|||
$controller->method('isValidToken') |
|||
->willReturn(true); |
|||
|
|||
$controller->method('isPasswordProtected') |
|||
->willReturn(true); |
|||
|
|||
$this->expectException(NotFoundException::class); |
|||
$this->middleware->beforeController($controller, 'mehod'); |
|||
} |
|||
|
|||
public function testBeforeControllerValidTokenAuthenticateMethod() { |
|||
$controller = $this->getMockBuilder(PublicShareController::class) |
|||
->setConstructorArgs(['app', $this->request, $this->session]) |
|||
->getMock(); |
|||
|
|||
$this->config->method('getAppValue') |
|||
->willReturnMap([ |
|||
['core', 'shareapi_enabled', 'yes', 'yes'], |
|||
['core', 'shareapi_allow_links', 'yes', 'yes'], |
|||
]); |
|||
|
|||
$this->request->method('getParam') |
|||
->with('token', null) |
|||
->willReturn('myToken'); |
|||
|
|||
$controller->method('isValidToken') |
|||
->willReturn(true); |
|||
|
|||
$controller->method('isPasswordProtected') |
|||
->willReturn(true); |
|||
|
|||
$this->middleware->beforeController($controller, 'authenticate'); |
|||
$this->assertTrue(true); |
|||
} |
|||
|
|||
public function testBeforeControllerValidTokenShowAuthenticateMethod() { |
|||
$controller = $this->getMockBuilder(PublicShareController::class) |
|||
->setConstructorArgs(['app', $this->request, $this->session]) |
|||
->getMock(); |
|||
|
|||
$this->config->method('getAppValue') |
|||
->willReturnMap([ |
|||
['core', 'shareapi_enabled', 'yes', 'yes'], |
|||
['core', 'shareapi_allow_links', 'yes', 'yes'], |
|||
]); |
|||
|
|||
$this->request->method('getParam') |
|||
->with('token', null) |
|||
->willReturn('myToken'); |
|||
|
|||
$controller->method('isValidToken') |
|||
->willReturn(true); |
|||
|
|||
$controller->method('isPasswordProtected') |
|||
->willReturn(true); |
|||
|
|||
$this->middleware->beforeController($controller, 'showAuthenticate'); |
|||
$this->assertTrue(true); |
|||
} |
|||
|
|||
public function testBeforeControllerAuthPublicShareController() { |
|||
$controller = $this->getMockBuilder(AuthPublicShareController::class) |
|||
->setConstructorArgs(['app', $this->request, $this->session, $this->createMock(IURLGenerator::class)]) |
|||
->getMock(); |
|||
|
|||
$this->config->method('getAppValue') |
|||
->willReturnMap([ |
|||
['core', 'shareapi_enabled', 'yes', 'yes'], |
|||
['core', 'shareapi_allow_links', 'yes', 'yes'], |
|||
]); |
|||
|
|||
$this->request->method('getParam') |
|||
->with('token', null) |
|||
->willReturn('myToken'); |
|||
|
|||
$controller->method('isValidToken') |
|||
->willReturn(true); |
|||
|
|||
$controller->method('isPasswordProtected') |
|||
->willReturn(true); |
|||
|
|||
$this->session->expects($this->once()) |
|||
->method('set') |
|||
->with('public_link_authenticate_redirect', '[]'); |
|||
|
|||
$this->expectException(NeedAuthenticationException::class); |
|||
$this->middleware->beforeController($controller, 'method'); |
|||
} |
|||
|
|||
public function testAfterExceptionNoPublicShareController() { |
|||
$controller = $this->createMock(Controller::class); |
|||
$exception = new \Exception(); |
|||
|
|||
try { |
|||
$this->middleware->afterException($controller, 'method', $exception); |
|||
} catch (\Exception $e) { |
|||
$this->assertEquals($exception, $e); |
|||
} |
|||
} |
|||
|
|||
public function testAfterExceptionPublicShareControllerNotFoundException() { |
|||
$controller = $this->createMock(PublicShareController::class); |
|||
$exception = new NotFoundException(); |
|||
|
|||
$result = $this->middleware->afterException($controller, 'method', $exception); |
|||
$this->assertInstanceOf(NotFoundResponse::class, $result); |
|||
} |
|||
|
|||
public function testAfterExceptionPublicShareController() { |
|||
$controller = $this->createMock(PublicShareController::class); |
|||
$exception = new \Exception(); |
|||
|
|||
try { |
|||
$this->middleware->afterException($controller, 'method', $exception); |
|||
} catch (\Exception $e) { |
|||
$this->assertEquals($exception, $e); |
|||
} |
|||
} |
|||
|
|||
public function testAfterExceptionAuthPublicShareController() { |
|||
$controller = $this->getMockBuilder(AuthPublicShareController::class) |
|||
->setConstructorArgs([ |
|||
'app', |
|||
$this->request, |
|||
$this->session, |
|||
$this->createMock(IURLGenerator::class), |
|||
])->getMock(); |
|||
$controller->setToken('token'); |
|||
|
|||
$exception = new NeedAuthenticationException(); |
|||
|
|||
$this->request->method('getParam') |
|||
->with('_route') |
|||
->willReturn('my.route'); |
|||
|
|||
$result = $this->middleware->afterException($controller, 'method', $exception); |
|||
$this->assertInstanceOf(RedirectResponse::class, $result); |
|||
} |
|||
|
|||
|
|||
} |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue