aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRoeland Jago Douma <rullzer@users.noreply.github.com>2018-06-21 10:09:20 +0200
committerGitHub <noreply@github.com>2018-06-21 10:09:20 +0200
commit8ebc3d90a0876d243c889108f3a95219c0863458 (patch)
treea7fce3b4416ab1c86f1704b67fedd72555c9b348
parentf9c98d86212f14b006fdf99251e35093d5026e80 (diff)
parenta596b062f520469ca52eed10a407fc4cf8891239 (diff)
downloadnextcloud-server-8ebc3d90a0876d243c889108f3a95219c0863458.tar.gz
nextcloud-server-8ebc3d90a0876d243c889108f3a95219c0863458.zip
Merge pull request #9518 from nextcloud/feature/5986/public_share_controller_middleware
Public share middleware & controller
-rw-r--r--apps/files_sharing/appinfo/routes.php8
-rw-r--r--apps/files_sharing/js/public.js11
-rw-r--r--apps/files_sharing/lib/Controller/PublicPreviewController.php44
-rw-r--r--apps/files_sharing/lib/Controller/ShareController.php169
-rw-r--r--apps/files_sharing/lib/Middleware/SharingCheckMiddleware.php23
-rw-r--r--apps/files_sharing/tests/Controller/PublicPreviewControllerTest.php18
-rw-r--r--apps/files_sharing/tests/Controller/ShareControllerTest.php226
-rw-r--r--apps/files_sharing/tests/Middleware/SharingCheckMiddlewareTest.php89
-rw-r--r--core/css/publicshareauth.css (renamed from apps/files_sharing/css/authenticate.css)0
-rw-r--r--core/js/publicshareauth.js (renamed from apps/files_sharing/js/authenticate.js)0
-rw-r--r--core/templates/publicshareauth.php (renamed from apps/files_sharing/templates/authenticate.php)4
-rw-r--r--lib/composer/composer/autoload_classmap.php4
-rw-r--r--lib/composer/composer/autoload_static.php4
-rw-r--r--lib/private/AppFramework/DependencyInjection/DIContainer.php8
-rw-r--r--lib/private/AppFramework/Middleware/PublicShare/Exceptions/NeedAuthenticationException.php7
-rw-r--r--lib/private/AppFramework/Middleware/PublicShare/PublicShareMiddleware.php112
-rw-r--r--lib/private/legacy/template/functions.php2
-rw-r--r--lib/public/AppFramework/AuthPublicShareController.php192
-rw-r--r--lib/public/AppFramework/PublicShareController.php138
-rw-r--r--tests/acceptance/features/bootstrap/FilesSharingAppContext.php4
-rw-r--r--tests/lib/AppFramework/Controller/AuthPublicShareControllerTest.php159
-rw-r--r--tests/lib/AppFramework/Controller/PublicShareControllerTest.php102
-rw-r--r--tests/lib/AppFramework/Middleware/PublicShare/PublicShareMiddlewareTest.php287
23 files changed, 1133 insertions, 478 deletions
diff --git a/apps/files_sharing/appinfo/routes.php b/apps/files_sharing/appinfo/routes.php
index 863b27da277..8e5110c6a16 100644
--- a/apps/files_sharing/appinfo/routes.php
+++ b/apps/files_sharing/appinfo/routes.php
@@ -34,13 +34,7 @@ return [
],
[
'name' => 'PublicPreview#getPreview',
- 'url' => '/publicpreview',
- 'verb' => 'GET',
- ],
-
- [
- 'name' => 'PublicPreview#getPreview',
- 'url' => '/ajax/publicpreview.php',
+ 'url' => '/publicpreview/{token}',
'verb' => 'GET',
],
diff --git a/apps/files_sharing/js/public.js b/apps/files_sharing/js/public.js
index 1de7c6b4fcd..a2884468a6b 100644
--- a/apps/files_sharing/js/public.js
+++ b/apps/files_sharing/js/public.js
@@ -112,7 +112,6 @@ OCA.Sharing.PublicApp = {
y: Math.ceil(previewHeight * window.devicePixelRatio),
a: 'true',
file: encodeURIComponent(this.initialDir + $('#filename').val()),
- t: token,
scalingup: 0
};
@@ -150,7 +149,7 @@ OCA.Sharing.PublicApp = {
} else if ((previewSupported === 'true' && mimetype.substr(0, mimetype.indexOf('/')) !== 'video') ||
mimetype.substr(0, mimetype.indexOf('/')) === 'image' &&
mimetype !== 'image/svg+xml') {
- img.attr('src', OC.filePath('files_sharing', 'ajax', 'publicpreview.php') + '?' + OC.buildQueryString(params));
+ img.attr('src', OC.linkTo('files_sharing', '/publicpreview/'+token) + '?' + OC.buildQueryString(params));
imgcontainer.appendTo('#imgframe');
} else if (mimetype.substr(0, mimetype.indexOf('/')) !== 'video') {
img.attr('src', OC.Util.replaceSVGIcon(mimetypeIcon));
@@ -158,7 +157,7 @@ OCA.Sharing.PublicApp = {
imgcontainer.appendTo('#imgframe');
}
else if (previewSupported === 'true') {
- $('#imgframe > video').attr('poster', OC.filePath('files_sharing', 'ajax', 'publicpreview.php') + '?' + OC.buildQueryString(params));
+ $('#imgframe > video').attr('poster', OC.generateUrl(OC.linkTo('files_sharing', '/publicpreview/'+token)) + '?' + OC.buildQueryString(params));
}
if (this.fileList) {
@@ -223,8 +222,8 @@ OCA.Sharing.PublicApp = {
urlSpec.y *= window.devicePixelRatio;
urlSpec.x = Math.ceil(urlSpec.x);
urlSpec.y = Math.ceil(urlSpec.y);
- urlSpec.t = $('#dirToken').val();
- return OC.generateUrl('/apps/files_sharing/ajax/publicpreview.php?') + $.param(urlSpec);
+ var token = $('#dirToken').val();
+ return OC.generateUrl(OC.linkTo('files_sharing', '/publicpreview/'+token) + '?' + OC.buildQueryString(urlSpec));
};
this.fileList.updateEmptyContent = function() {
@@ -427,4 +426,4 @@ $(document).ready(function () {
};
}
-}); \ No newline at end of file
+});
diff --git a/apps/files_sharing/lib/Controller/PublicPreviewController.php b/apps/files_sharing/lib/Controller/PublicPreviewController.php
index 0870995fc7b..b13c0a64b0e 100644
--- a/apps/files_sharing/lib/Controller/PublicPreviewController.php
+++ b/apps/files_sharing/lib/Controller/PublicPreviewController.php
@@ -27,15 +27,18 @@ use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\Http\FileDisplayResponse;
+use OCP\AppFramework\PublicShareController;
use OCP\Constants;
use OCP\Files\Folder;
use OCP\Files\NotFoundException;
use OCP\IPreview;
use OCP\IRequest;
+use OCP\ISession;
use OCP\Share\Exceptions\ShareNotFound;
use OCP\Share\IManager as ShareManager;
+use OCP\Share\IShare;
-class PublicPreviewController extends Controller {
+class PublicPreviewController extends PublicShareController {
/** @var ShareManager */
private $shareManager;
@@ -43,16 +46,38 @@ class PublicPreviewController extends Controller {
/** @var IPreview */
private $previewManager;
- public function __construct($appName,
+ /** @var IShare */
+ private $share;
+
+ public function __construct(string $appName,
IRequest $request,
ShareManager $shareManger,
+ ISession $session,
IPreview $previewManager) {
- parent::__construct($appName, $request);
+ parent::__construct($appName, $request, $session);
$this->shareManager = $shareManger;
$this->previewManager = $previewManager;
}
+ protected function getPasswordHash(): string {
+ return $this->share->getPassword();
+ }
+
+ public function isValidToken(): bool {
+ try {
+ $this->share = $this->shareManager->getShareByToken($this->getToken());
+ return true;
+ } catch (ShareNotFound $e) {
+ return false;
+ }
+ }
+
+ protected function isPasswordProtected(): bool {
+ return $this->share->getPassword() !== null;
+ }
+
+
/**
* @PublicPage
* @NoCSRFRequired
@@ -60,24 +85,23 @@ class PublicPreviewController extends Controller {
* @param string $file
* @param int $x
* @param int $y
- * @param string $t
* @param bool $a
* @return DataResponse|FileDisplayResponse
*/
public function getPreview(
- $file = '',
- $x = 32,
- $y = 32,
- $t = '',
+ string $token,
+ string $file = '',
+ int $x = 32,
+ int $y = 32,
$a = false
) {
- if ($t === '' || $x === 0 || $y === 0) {
+ if ($token === '' || $x === 0 || $y === 0) {
return new DataResponse([], Http::STATUS_BAD_REQUEST);
}
try {
- $share = $this->shareManager->getShareByToken($t);
+ $share = $this->shareManager->getShareByToken($token);
} catch (ShareNotFound $e) {
return new DataResponse([], Http::STATUS_NOT_FOUND);
}
diff --git a/apps/files_sharing/lib/Controller/ShareController.php b/apps/files_sharing/lib/Controller/ShareController.php
index 739031d4bc2..0b30a599c7f 100644
--- a/apps/files_sharing/lib/Controller/ShareController.php
+++ b/apps/files_sharing/lib/Controller/ShareController.php
@@ -38,6 +38,7 @@ namespace OCA\Files_Sharing\Controller;
use OC_Files;
use OC_Util;
use OCA\FederatedFileSharing\FederatedShareProvider;
+use OCP\AppFramework\AuthPublicShareController;
use OCP\AppFramework\Http\Template\SimpleMenuAction;
use OCP\AppFramework\Http\Template\ExternalShareMenuAction;
use OCP\AppFramework\Http\Template\LinkMenuAction;
@@ -46,10 +47,8 @@ use OCP\Defaults;
use OCP\IL10N;
use OCP\Template;
use OCP\Share;
-use OCP\AppFramework\Controller;
use OCP\IRequest;
use OCP\AppFramework\Http\TemplateResponse;
-use OCP\AppFramework\Http\RedirectResponse;
use OCP\AppFramework\Http\NotFoundResponse;
use OCP\IURLGenerator;
use OCP\IConfig;
@@ -58,32 +57,27 @@ use OCP\IUserManager;
use OCP\ISession;
use OCP\IPreview;
use OCA\Files_Sharing\Activity\Providers\Downloads;
-use \OCP\Files\NotFoundException;
+use OCP\Files\NotFoundException;
use OCP\Files\IRootFolder;
use OCP\Share\Exceptions\ShareNotFound;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
+use OCP\Share\IManager as ShareManager;
/**
* Class ShareController
*
* @package OCA\Files_Sharing\Controllers
*/
-class ShareController extends Controller {
+class ShareController extends AuthPublicShareController {
/** @var IConfig */
protected $config;
- /** @var IURLGenerator */
- protected $urlGenerator;
/** @var IUserManager */
protected $userManager;
/** @var ILogger */
protected $logger;
/** @var \OCP\Activity\IManager */
protected $activityManager;
- /** @var \OCP\Share\IManager */
- protected $shareManager;
- /** @var ISession */
- protected $session;
/** @var IPreview */
protected $previewManager;
/** @var IRootFolder */
@@ -96,6 +90,11 @@ class ShareController extends Controller {
protected $l10n;
/** @var Defaults */
protected $defaults;
+ /** @var ShareManager */
+ protected $shareManager;
+
+ /** @var Share\IShare */
+ protected $share;
/**
* @param string $appName
@@ -114,14 +113,14 @@ class ShareController extends Controller {
* @param IL10N $l10n
* @param Defaults $defaults
*/
- public function __construct($appName,
+ public function __construct(string $appName,
IRequest $request,
IConfig $config,
IURLGenerator $urlGenerator,
IUserManager $userManager,
ILogger $logger,
\OCP\Activity\IManager $activityManager,
- \OCP\Share\IManager $shareManager,
+ ShareManager $shareManager,
ISession $session,
IPreview $previewManager,
IRootFolder $rootFolder,
@@ -129,108 +128,50 @@ class ShareController extends Controller {
EventDispatcherInterface $eventDispatcher,
IL10N $l10n,
Defaults $defaults) {
- parent::__construct($appName, $request);
+ parent::__construct($appName, $request, $session, $urlGenerator);
$this->config = $config;
- $this->urlGenerator = $urlGenerator;
$this->userManager = $userManager;
$this->logger = $logger;
$this->activityManager = $activityManager;
- $this->shareManager = $shareManager;
- $this->session = $session;
$this->previewManager = $previewManager;
$this->rootFolder = $rootFolder;
$this->federatedShareProvider = $federatedShareProvider;
$this->eventDispatcher = $eventDispatcher;
$this->l10n = $l10n;
$this->defaults = $defaults;
+ $this->shareManager = $shareManager;
}
- /**
- * @PublicPage
- * @NoCSRFRequired
- *
- * @param string $token
- * @return TemplateResponse|RedirectResponse
- */
- public function showAuthenticate($token) {
- $share = $this->shareManager->getShareByToken($token);
-
- if($this->linkShareAuth($share)) {
- return new RedirectResponse($this->urlGenerator->linkToRoute('files_sharing.sharecontroller.showShare', array('token' => $token)));
- }
-
- return new TemplateResponse($this->appName, 'authenticate', array(), 'guest');
+ protected function verifyPassword(string $password): bool {
+ return $this->shareManager->checkPassword($this->share, $password);
}
- /**
- * @PublicPage
- * @UseSession
- * @BruteForceProtection(action=publicLinkAuth)
- *
- * Authenticates against password-protected shares
- * @param string $token
- * @param string $redirect
- * @param string $password
- * @return RedirectResponse|TemplateResponse|NotFoundResponse
- */
- public function authenticate($token, $redirect, $password = '') {
+ protected function getPasswordHash(): string {
+ return $this->share->getPassword();
+ }
- // Check whether share exists
+ public function isValidToken(): bool {
try {
- $share = $this->shareManager->getShareByToken($token);
+ $this->share = $this->shareManager->getShareByToken($this->getToken());
} catch (ShareNotFound $e) {
- return new NotFoundResponse();
+ return false;
}
- $authenticate = $this->linkShareAuth($share, $password);
-
- // if download was requested before auth, redirect to download
- if ($authenticate === true && $redirect === 'download') {
- return new RedirectResponse($this->urlGenerator->linkToRoute(
- 'files_sharing.sharecontroller.downloadShare',
- array('token' => $token))
- );
- } else if ($authenticate === true) {
- return new RedirectResponse($this->urlGenerator->linkToRoute(
- 'files_sharing.sharecontroller.showShare',
- array('token' => $token))
- );
- }
+ return true;
+ }
- $response = new TemplateResponse($this->appName, 'authenticate', array('wrongpw' => true), 'guest');
- $response->throttle();
- return $response;
+ protected function isPasswordProtected(): bool {
+ return $this->share->getPassword() !== null;
}
- /**
- * Authenticate a link item with the given password.
- * Or use the session if no password is provided.
- *
- * This is a modified version of Helper::authenticate
- * TODO: Try to merge back eventually with Helper::authenticate
- *
- * @param \OCP\Share\IShare $share
- * @param string|null $password
- * @return bool
- */
- private function linkShareAuth(\OCP\Share\IShare $share, $password = null) {
- if ($password !== null) {
- if ($this->shareManager->checkPassword($share, $password)) {
- $this->session->regenerateId(true, true);
- $this->session->set('public_link_authenticated', (string)$share->getId());
- } else {
- $this->emitAccessShareHook($share, 403, 'Wrong password');
- return false;
- }
- } else {
- // not authenticated ?
- if ( ! $this->session->exists('public_link_authenticated')
- || $this->session->get('public_link_authenticated') !== (string)$share->getId()) {
- return false;
- }
- }
- return true;
+ protected function authSucceeded() {
+ // For share this was always set so it is still used in other apps
+ $this->session->set('public_link_authenticated', (string)$this->share->getId());
+ }
+
+ protected function authFailed() {
+ $this->emitAccessShareHook($this->share, 403, 'Wrong password');
}
/**
@@ -285,27 +226,21 @@ class ShareController extends Controller {
* @PublicPage
* @NoCSRFRequired
*
- * @param string $token
+
* @param string $path
- * @return TemplateResponse|RedirectResponse|NotFoundResponse
+ * @return TemplateResponse
* @throws NotFoundException
* @throws \Exception
*/
- public function showShare($token, $path = '') {
+ public function showShare($path = ''): TemplateResponse {
\OC_User::setIncognitoMode(true);
// Check whether share exists
try {
- $share = $this->shareManager->getShareByToken($token);
+ $share = $this->shareManager->getShareByToken($this->getToken());
} catch (ShareNotFound $e) {
- $this->emitAccessShareHook($token, 404, 'Share not found');
- return new NotFoundResponse();
- }
-
- // Share is password protected - check whether the user is permitted to access the share
- if ($share->getPassword() !== null && !$this->linkShareAuth($share)) {
- return new RedirectResponse($this->urlGenerator->linkToRoute('files_sharing.sharecontroller.authenticate',
- array('token' => $token, 'redirect' => 'preview')));
+ $this->emitAccessShareHook($this->getToken(), 404, 'Share not found');
+ throw new NotFoundException();
}
if (!$this->validateShare($share)) {
@@ -329,8 +264,8 @@ class ShareController extends Controller {
$shareTmpl['directory_path'] = $share->getTarget();
$shareTmpl['mimetype'] = $share->getNode()->getMimetype();
$shareTmpl['previewSupported'] = $this->previewManager->isMimeSupported($share->getNode()->getMimetype());
- $shareTmpl['dirToken'] = $token;
- $shareTmpl['sharingToken'] = $token;
+ $shareTmpl['dirToken'] = $this->getToken();
+ $shareTmpl['sharingToken'] = $this->getToken();
$shareTmpl['server2serversharing'] = $this->federatedShareProvider->isOutgoingServer2serverShareEnabled();
$shareTmpl['protected'] = $share->getPassword() !== null ? 'true' : 'false';
$shareTmpl['dir'] = '';
@@ -367,7 +302,7 @@ class ShareController extends Controller {
$folder = new Template('files', 'list', '');
$folder->assign('dir', $rootFolder->getRelativePath($folderNode->getPath()));
- $folder->assign('dirToken', $token);
+ $folder->assign('dirToken', $this->getToken());
$folder->assign('permissions', \OCP\Constants::PERMISSION_READ);
$folder->assign('isPublic', true);
$folder->assign('hideFileList', $hideFileList);
@@ -382,8 +317,8 @@ class ShareController extends Controller {
$shareTmpl['hideFileList'] = $hideFileList;
$shareTmpl['shareOwner'] = $this->userManager->get($share->getShareOwner())->getDisplayName();
- $shareTmpl['downloadURL'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.downloadShare', ['token' => $token]);
- $shareTmpl['shareUrl'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', ['token' => $token]);
+ $shareTmpl['downloadURL'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.downloadShare', ['token' => $this->getToken()]);
+ $shareTmpl['shareUrl'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', ['token' => $this->getToken()]);
$shareTmpl['maxSizeAnimateGif'] = $this->config->getSystemValue('max_filesize_animated_gifs_public_sharing', 10);
$shareTmpl['previewEnabled'] = $this->config->getSystemValue('enable_previews', true);
$shareTmpl['previewMaxX'] = $this->config->getSystemValue('preview_max_x', 1024);
@@ -393,19 +328,19 @@ class ShareController extends Controller {
$ogPreview = '';
if ($shareTmpl['previewSupported']) {
$shareTmpl['previewImage'] = $this->urlGenerator->linkToRouteAbsolute( 'files_sharing.PublicPreview.getPreview',
- ['x' => 200, 'y' => 200, 'file' => $shareTmpl['directory_path'], 't' => $shareTmpl['dirToken']]);
+ ['x' => 200, 'y' => 200, 'file' => $shareTmpl['directory_path'], 'token' => $shareTmpl['dirToken']]);
$ogPreview = $shareTmpl['previewImage'];
// We just have direct previews for image files
if ($share->getNode()->getMimePart() === 'image') {
- $shareTmpl['previewURL'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.publicpreview.directLink', ['token' => $token]);
+ $shareTmpl['previewURL'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.publicpreview.directLink', ['token' => $this->getToken()]);
$ogPreview = $shareTmpl['previewURL'];
//Whatapp is kind of picky about their size requirements
if ($this->request->isUserAgent(['/^WhatsApp/'])) {
$ogPreview = $this->urlGenerator->linkToRouteAbsolute('files_sharing.PublicPreview.getPreview', [
- 't' => $token,
+ 'token' => $this->getToken(),
'x' => 256,
'y' => 256,
'a' => true,
@@ -488,12 +423,6 @@ class ShareController extends Controller {
return new \OCP\AppFramework\Http\DataResponse('Share is read-only');
}
- // Share is password protected - check whether the user is permitted to access the share
- if ($share->getPassword() !== null && !$this->linkShareAuth($share)) {
- return new RedirectResponse($this->urlGenerator->linkToRoute('files_sharing.sharecontroller.authenticate',
- ['token' => $token, 'redirect' => 'download']));
- }
-
$files_list = null;
if (!is_null($files)) { // download selected files
$files_list = json_decode($files);
@@ -507,13 +436,15 @@ class ShareController extends Controller {
}
}
- $userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
- $originalSharePath = $userFolder->getRelativePath($share->getNode()->getPath());
if (!$this->validateShare($share)) {
throw new NotFoundException();
}
+ $userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
+ $originalSharePath = $userFolder->getRelativePath($share->getNode()->getPath());
+
+
// Single file share
if ($share->getNode() instanceof \OCP\Files\File) {
// Single file download
diff --git a/apps/files_sharing/lib/Middleware/SharingCheckMiddleware.php b/apps/files_sharing/lib/Middleware/SharingCheckMiddleware.php
index 4b630d0a8da..b5f1178b7f0 100644
--- a/apps/files_sharing/lib/Middleware/SharingCheckMiddleware.php
+++ b/apps/files_sharing/lib/Middleware/SharingCheckMiddleware.php
@@ -101,13 +101,6 @@ class SharingCheckMiddleware extends Middleware {
if ($controller instanceof ExternalSharesController &&
!$this->externalSharesChecks()) {
throw new S2SException('Federated sharing not allowed');
- } else if ($controller instanceof ShareController) {
- $token = $this->request->getParam('token');
- $share = $this->shareManager->getShareByToken($token);
- if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK
- && !$this->isLinkSharingEnabled()) {
- throw new NotFoundException('Link sharing is disabled');
- }
}
}
@@ -165,22 +158,6 @@ class SharingCheckMiddleware extends Middleware {
return true;
}
- /**
- * Check if link sharing is allowed
- * @return bool
- */
- private function isLinkSharingEnabled() {
- // 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;
- }
}
diff --git a/apps/files_sharing/tests/Controller/PublicPreviewControllerTest.php b/apps/files_sharing/tests/Controller/PublicPreviewControllerTest.php
index 174abbb6f60..27e13bc8ced 100644
--- a/apps/files_sharing/tests/Controller/PublicPreviewControllerTest.php
+++ b/apps/files_sharing/tests/Controller/PublicPreviewControllerTest.php
@@ -33,6 +33,7 @@ use OCP\Files\NotFoundException;
use OCP\Files\SimpleFS\ISimpleFile;
use OCP\IPreview;
use OCP\IRequest;
+use OCP\ISession;
use OCP\Share\Exceptions\ShareNotFound;
use OCP\Share\IManager;
use OCP\Share\IShare;
@@ -60,26 +61,27 @@ class PublicPreviewControllerTest extends TestCase {
'files_sharing',
$this->createMock(IRequest::class),
$this->shareManager,
+ $this->createMock(ISession::class),
$this->previewManager
);
}
public function testInvalidToken() {
- $res = $this->controller->getPreview('file', 10, 10, '');
+ $res = $this->controller->getPreview('', 'file', 10, 10, '');
$expected = new DataResponse([], Http::STATUS_BAD_REQUEST);
$this->assertEquals($expected, $res);
}
public function testInvalidWidth() {
- $res = $this->controller->getPreview('file', 0);
+ $res = $this->controller->getPreview('token', 'file', 0);
$expected = new DataResponse([], Http::STATUS_BAD_REQUEST);
$this->assertEquals($expected, $res);
}
public function testInvalidHeight() {
- $res = $this->controller->getPreview('file', 10, 0);
+ $res = $this->controller->getPreview('token', 'file', 10, 0);
$expected = new DataResponse([], Http::STATUS_BAD_REQUEST);
$this->assertEquals($expected, $res);
@@ -90,7 +92,7 @@ class PublicPreviewControllerTest extends TestCase {
->with($this->equalTo('token'))
->willThrowException(new ShareNotFound());
- $res = $this->controller->getPreview('file', 10, 10, 'token');
+ $res = $this->controller->getPreview('token', 'file', 10, 10);
$expected = new DataResponse([], Http::STATUS_NOT_FOUND);
$this->assertEquals($expected, $res);
@@ -105,7 +107,7 @@ class PublicPreviewControllerTest extends TestCase {
$share->method('getPermissions')
->willReturn(0);
- $res = $this->controller->getPreview('file', 10, 10, 'token');
+ $res = $this->controller->getPreview('token', 'file', 10, 10);
$expected = new DataResponse([], Http::STATUS_FORBIDDEN);
$this->assertEquals($expected, $res);
@@ -132,7 +134,7 @@ class PublicPreviewControllerTest extends TestCase {
$preview->method('getMimeType')
->willReturn('myMime');
- $res = $this->controller->getPreview('file', 10, 10, 'token', true);
+ $res = $this->controller->getPreview('token', 'file', 10, 10, true);
$expected = new FileDisplayResponse($preview, Http::STATUS_OK, ['Content-Type' => 'myMime']);
$this->assertEquals($expected, $res);
}
@@ -154,7 +156,7 @@ class PublicPreviewControllerTest extends TestCase {
->with($this->equalTo('file'))
->willThrowException(new NotFoundException());
- $res = $this->controller->getPreview('file', 10, 10, 'token', true);
+ $res = $this->controller->getPreview('token', 'file', 10, 10, true);
$expected = new DataResponse([], Http::STATUS_NOT_FOUND);
$this->assertEquals($expected, $res);
}
@@ -186,7 +188,7 @@ class PublicPreviewControllerTest extends TestCase {
$preview->method('getMimeType')
->willReturn('myMime');
- $res = $this->controller->getPreview('file', 10, 10, 'token', true);
+ $res = $this->controller->getPreview('token', 'file', 10, 10, true);
$expected = new FileDisplayResponse($preview, Http::STATUS_OK, ['Content-Type' => 'myMime']);
$this->assertEquals($expected, $res);
}
diff --git a/apps/files_sharing/tests/Controller/ShareControllerTest.php b/apps/files_sharing/tests/Controller/ShareControllerTest.php
index be99c5ee194..fb417878647 100644
--- a/apps/files_sharing/tests/Controller/ShareControllerTest.php
+++ b/apps/files_sharing/tests/Controller/ShareControllerTest.php
@@ -39,6 +39,7 @@ use OCP\AppFramework\Http\Template\ExternalShareMenuAction;
use OCP\AppFramework\Http\Template\LinkMenuAction;
use OCP\AppFramework\Http\Template\PublicTemplateResponse;
use OCP\AppFramework\Http\Template\SimpleMenuAction;
+use OCP\Files\NotFoundException;
use OCP\IConfig;
use OCP\IL10N;
use OCP\ILogger;
@@ -156,193 +157,24 @@ class ShareControllerTest extends \Test\TestCase {
parent::tearDown();
}
- public function testShowAuthenticateNotAuthenticated() {
- $share = \OC::$server->getShareManager()->newShare();
-
- $this->shareManager
- ->expects($this->once())
- ->method('getShareByToken')
- ->with('token')
- ->willReturn($share);
-
- $response = $this->shareController->showAuthenticate('token');
- $expectedResponse = new TemplateResponse($this->appName, 'authenticate', [], 'guest');
- $this->assertEquals($expectedResponse, $response);
- }
-
- public function testShowAuthenticateAuthenticatedForDifferentShare() {
- $share = \OC::$server->getShareManager()->newShare();
- $share->setId(1);
-
- $this->shareManager
- ->expects($this->once())
- ->method('getShareByToken')
- ->with('token')
- ->willReturn($share);
-
- $this->session->method('exists')->with('public_link_authenticated')->willReturn(true);
- $this->session->method('get')->with('public_link_authenticated')->willReturn('2');
-
- $response = $this->shareController->showAuthenticate('token');
- $expectedResponse = new TemplateResponse($this->appName, 'authenticate', [], 'guest');
- $this->assertEquals($expectedResponse, $response);
- }
-
- public function testShowAuthenticateCorrectShare() {
- $share = \OC::$server->getShareManager()->newShare();
- $share->setId(1);
-
- $this->shareManager
- ->expects($this->once())
- ->method('getShareByToken')
- ->with('token')
- ->willReturn($share);
-
- $this->session->method('exists')->with('public_link_authenticated')->willReturn(true);
- $this->session->method('get')->with('public_link_authenticated')->willReturn('1');
-
- $this->urlGenerator->expects($this->once())
- ->method('linkToRoute')
- ->with('files_sharing.sharecontroller.showShare', ['token' => 'token'])
- ->willReturn('redirect');
-
- $response = $this->shareController->showAuthenticate('token');
- $expectedResponse = new RedirectResponse('redirect');
- $this->assertEquals($expectedResponse, $response);
- }
-
- public function testAuthenticateInvalidToken() {
- $this->shareManager
- ->expects($this->once())
- ->method('getShareByToken')
- ->with('token')
- ->will($this->throwException(new \OCP\Share\Exceptions\ShareNotFound()));
-
- $response = $this->shareController->authenticate('token', 'preview');
- $expectedResponse = new NotFoundResponse();
- $this->assertEquals($expectedResponse, $response);
- }
-
- public function testAuthenticateValidPassword() {
- $share = \OC::$server->getShareManager()->newShare();
- $share->setId(42);
-
- $this->shareManager
- ->expects($this->once())
- ->method('getShareByToken')
- ->with('token')
- ->willReturn($share);
-
- $this->shareManager
- ->expects($this->once())
- ->method('checkPassword')
- ->with($share, 'validpassword')
- ->willReturn(true);
-
- $this->session
- ->expects($this->once())
- ->method('set')
- ->with('public_link_authenticated', '42');
-
- $this->urlGenerator->expects($this->once())
- ->method('linkToRoute')
- ->with('files_sharing.sharecontroller.showShare', ['token'=>'token'])
- ->willReturn('redirect');
-
- $response = $this->shareController->authenticate('token', 'preview', 'validpassword');
- $expectedResponse = new RedirectResponse('redirect');
- $this->assertEquals($expectedResponse, $response);
- }
-
- public function testAuthenticateValidPasswordAndDownload() {
- $share = \OC::$server->getShareManager()->newShare();
- $share->setId(42);
-
- $this->shareManager
- ->expects($this->once())
- ->method('getShareByToken')
- ->with('token')
- ->willReturn($share);
-
- $this->shareManager
- ->expects($this->once())
- ->method('checkPassword')
- ->with($share, 'validpassword')
- ->willReturn(true);
-
- $this->session
- ->expects($this->once())
- ->method('set')
- ->with('public_link_authenticated', '42');
-
- $this->urlGenerator->expects($this->once())
- ->method('linkToRoute')
- ->with('files_sharing.sharecontroller.downloadShare', ['token'=>'token'])
- ->willReturn('redirect');
-
- $response = $this->shareController->authenticate('token', 'download', 'validpassword');
- $expectedResponse = new RedirectResponse('redirect');
- $this->assertEquals($expectedResponse, $response);
- }
-
- public function testAuthenticateInvalidPassword() {
- $share = \OC::$server->getShareManager()->newShare();
- $share->setNodeId(100)
- ->setNodeType('file')
- ->setToken('token')
- ->setSharedBy('initiator')
- ->setId(42);
-
- $this->shareManager
- ->expects($this->once())
- ->method('getShareByToken')
- ->with('token')
- ->willReturn($share);
-
- $this->shareManager
- ->expects($this->once())
- ->method('checkPassword')
- ->with($share, 'invalidpassword')
- ->willReturn(false);
-
- $this->session
- ->expects($this->never())
- ->method('set');
-
- $hookListner = $this->getMockBuilder('Dummy')->setMethods(['access'])->getMock();
- \OCP\Util::connectHook('OCP\Share', 'share_link_access', $hookListner, 'access');
-
- $hookListner->expects($this->once())
- ->method('access')
- ->with($this->callback(function(array $data) {
- return $data['itemType'] === 'file' &&
- $data['itemSource'] === 100 &&
- $data['uidOwner'] === 'initiator' &&
- $data['token'] === 'token' &&
- $data['errorCode'] === 403 &&
- $data['errorMessage'] === 'Wrong password';
- }));
-
- $response = $this->shareController->authenticate('token', 'preview', 'invalidpassword');
- $expectedResponse = new TemplateResponse($this->appName, 'authenticate', array('wrongpw' => true), 'guest');
- $expectedResponse->throttle();
- $this->assertEquals($expectedResponse, $response);
- }
-
public function testShowShareInvalidToken() {
+ $this->shareController->setToken('invalidtoken');
+
$this->shareManager
->expects($this->once())
->method('getShareByToken')
->with('invalidtoken')
->will($this->throwException(new ShareNotFound()));
+ $this->expectException(NotFoundException::class);
+
// Test without a not existing token
- $response = $this->shareController->showShare('invalidtoken');
- $expectedResponse = new NotFoundResponse();
- $this->assertEquals($expectedResponse, $response);
+ $this->shareController->showShare();
}
public function testShowShareNotAuthenticated() {
+ $this->shareController->setToken('validtoken');
+
$share = \OC::$server->getShareManager()->newShare();
$share->setPassword('password');
@@ -352,19 +184,16 @@ class ShareControllerTest extends \Test\TestCase {
->with('validtoken')
->willReturn($share);
- $this->urlGenerator->expects($this->once())
- ->method('linkToRoute')
- ->with('files_sharing.sharecontroller.authenticate', ['token' => 'validtoken', 'redirect' => 'preview'])
- ->willReturn('redirect');
+ $this->expectException(NotFoundException::class);
// Test without a not existing token
- $response = $this->shareController->showShare('validtoken');
- $expectedResponse = new RedirectResponse('redirect');
- $this->assertEquals($expectedResponse, $response);
+ $this->shareController->showShare();
}
public function testShowShare() {
+ $this->shareController->setToken('token');
+
$owner = $this->getMockBuilder(IUser::class)->getMock();
$owner->method('getDisplayName')->willReturn('ownerDisplay');
$owner->method('getUID')->willReturn('ownerUID');
@@ -428,7 +257,7 @@ class ShareControllerTest extends \Test\TestCase {
return vsprintf($text, $parameters);
}));
- $response = $this->shareController->showShare('token');
+ $response = $this->shareController->showShare();
$sharedTmplParams = array(
'displayName' => 'ownerDisplay',
'owner' => 'ownerUID',
@@ -476,6 +305,8 @@ class ShareControllerTest extends \Test\TestCase {
* @expectedException \OCP\Files\NotFoundException
*/
public function testShowShareInvalid() {
+ $this->shareController->setToken('token');
+
$owner = $this->getMockBuilder(IUser::class)->getMock();
$owner->method('getDisplayName')->willReturn('ownerDisplay');
$owner->method('getUID')->willReturn('ownerUID');
@@ -517,32 +348,7 @@ class ShareControllerTest extends \Test\TestCase {
$this->userManager->method('get')->with('ownerUID')->willReturn($owner);
- $this->shareController->showShare('token');
- }
-
- public function testDownloadShare() {
- $share = $this->getMockBuilder(IShare::class)->getMock();
- $share->method('getPassword')->willReturn('password');
- $share
- ->expects($this->once())
- ->method('getPermissions')
- ->willReturn(\OCP\Constants::PERMISSION_READ);
-
- $this->shareManager
- ->expects($this->once())
- ->method('getShareByToken')
- ->with('validtoken')
- ->willReturn($share);
-
- $this->urlGenerator->expects($this->once())
- ->method('linkToRoute')
- ->with('files_sharing.sharecontroller.authenticate', ['token' => 'validtoken', 'redirect' => 'download'])
- ->willReturn('redirect');
-
- // Test with a password protected share and no authentication
- $response = $this->shareController->downloadShare('validtoken');
- $expectedResponse = new RedirectResponse('redirect');
- $this->assertEquals($expectedResponse, $response);
+ $this->shareController->showShare();
}
public function testDownloadShareWithCreateOnlyShare() {
diff --git a/apps/files_sharing/tests/Middleware/SharingCheckMiddlewareTest.php b/apps/files_sharing/tests/Middleware/SharingCheckMiddlewareTest.php
index d8676547a76..1fea73e6b47 100644
--- a/apps/files_sharing/tests/Middleware/SharingCheckMiddlewareTest.php
+++ b/apps/files_sharing/tests/Middleware/SharingCheckMiddlewareTest.php
@@ -98,49 +98,6 @@ class SharingCheckMiddlewareTest extends \Test\TestCase {
$this->assertFalse(self::invokePrivate($this->sharingCheckMiddleware, 'isSharingEnabled'));
}
- public function testIsLinkSharingEnabledWithEverythinEnabled() {
- $this->config
- ->expects($this->at(0))
- ->method('getAppValue')
- ->with('core', 'shareapi_enabled', 'yes')
- ->will($this->returnValue('yes'));
-
- $this->config
- ->expects($this->at(1))
- ->method('getAppValue')
- ->with('core', 'shareapi_allow_links', 'yes')
- ->will($this->returnValue('yes'));
-
- $this->assertTrue(self::invokePrivate($this->sharingCheckMiddleware, 'isLinkSharingEnabled'));
- }
-
-
- public function testIsLinkSharingEnabledWithLinkSharingDisabled() {
- $this->config
- ->expects($this->at(0))
- ->method('getAppValue')
- ->with('core', 'shareapi_enabled', 'yes')
- ->will($this->returnValue('yes'));
-
- $this->config
- ->expects($this->at(1))
- ->method('getAppValue')
- ->with('core', 'shareapi_allow_links', 'yes')
- ->will($this->returnValue('no'));
-
- $this->assertFalse(self::invokePrivate($this->sharingCheckMiddleware, 'isLinkSharingEnabled'));
- }
-
- public function testIsLinkSharingEnabledWithSharingAPIDisabled() {
- $this->config
- ->expects($this->once())
- ->method('getAppValue')
- ->with('core', 'shareapi_enabled', 'yes')
- ->will($this->returnValue('no'));
-
- $this->assertFalse(self::invokePrivate($this->sharingCheckMiddleware, 'isLinkSharingEnabled'));
- }
-
public function externalSharesChecksDataProvider() {
$data = [];
@@ -236,25 +193,6 @@ class SharingCheckMiddlewareTest extends \Test\TestCase {
->with('files_sharing')
->will($this->returnValue(true));
- $this->config
- ->expects($this->at(0))
- ->method('getAppValue')
- ->with('core', 'shareapi_enabled', 'yes')
- ->will($this->returnValue('yes'));
-
- $this->config
- ->expects($this->at(1))
- ->method('getAppValue')
- ->with('core', 'shareapi_allow_links', 'yes')
- ->will($this->returnValue('yes'));
-
- $this->request->expects($this->once())->method('getParam')->with('token')
- ->willReturn('token');
- $this->shareManager->expects($this->once())->method('getShareByToken')
- ->with('token')->willReturn($share);
-
- $share->expects($this->once())->method('getShareType')->willReturn(\OCP\Share::SHARE_TYPE_LINK);
-
$controller = $this->createMock(ShareController::class);
$this->sharingCheckMiddleware->beforeController($controller, 'myMethod');
@@ -262,33 +200,6 @@ class SharingCheckMiddlewareTest extends \Test\TestCase {
/**
* @expectedException \OCP\Files\NotFoundException
- * @expectedExceptionMessage Link sharing is disabled
- */
- public function testBeforeControllerWithShareControllerWithSharingEnabledAPIDisabled() {
-
- $share = $this->createMock(IShare::class);
-
- $this->appManager
- ->expects($this->once())
- ->method('isEnabledForUser')
- ->with('files_sharing')
- ->will($this->returnValue(true));
-
- $controller = $this->createMock(ShareController::class);
-
- $this->request->expects($this->once())->method('getParam')->with('token')
- ->willReturn('token');
- $this->shareManager->expects($this->once())->method('getShareByToken')
- ->with('token')->willReturn($share);
-
- $share->expects($this->once())->method('getShareType')->willReturn(\OCP\Share::SHARE_TYPE_LINK);
-
-
- $this->sharingCheckMiddleware->beforeController($controller, 'myMethod');
- }
-
- /**
- * @expectedException \OCP\Files\NotFoundException
* @expectedExceptionMessage Sharing is disabled.
*/
public function testBeforeControllerWithSharingDisabled() {
diff --git a/apps/files_sharing/css/authenticate.css b/core/css/publicshareauth.css
index 7f83e0b41e7..7f83e0b41e7 100644
--- a/apps/files_sharing/css/authenticate.css
+++ b/core/css/publicshareauth.css
diff --git a/apps/files_sharing/js/authenticate.js b/core/js/publicshareauth.js
index 7f3f0d0a7d4..7f3f0d0a7d4 100644
--- a/apps/files_sharing/js/authenticate.js
+++ b/core/js/publicshareauth.js
diff --git a/apps/files_sharing/templates/authenticate.php b/core/templates/publicshareauth.php
index 6f270c2851a..adcc2853f87 100644
--- a/apps/files_sharing/templates/authenticate.php
+++ b/core/templates/publicshareauth.php
@@ -2,8 +2,8 @@
/** @var $_ array */
/** @var $l \OCP\IL10N */
style('core', 'guest');
- style('files_sharing', 'authenticate');
- script('files_sharing', 'authenticate');
+ style('core', 'publicshareauth');
+ script('core', 'publicshareauth');
?>
<form method="post">
<fieldset class="warning">
diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php
index 77729886601..2426272886b 100644
--- a/lib/composer/composer/autoload_classmap.php
+++ b/lib/composer/composer/autoload_classmap.php
@@ -18,6 +18,7 @@ return array(
'OCP\\App' => $baseDir . '/lib/public/App.php',
'OCP\\AppFramework\\ApiController' => $baseDir . '/lib/public/AppFramework/ApiController.php',
'OCP\\AppFramework\\App' => $baseDir . '/lib/public/AppFramework/App.php',
+ 'OCP\\AppFramework\\AuthPublicShareController' => $baseDir . '/lib/public/AppFramework/AuthPublicShareController.php',
'OCP\\AppFramework\\Controller' => $baseDir . '/lib/public/AppFramework/Controller.php',
'OCP\\AppFramework\\Db\\DoesNotExistException' => $baseDir . '/lib/public/AppFramework/Db/DoesNotExistException.php',
'OCP\\AppFramework\\Db\\Entity' => $baseDir . '/lib/public/AppFramework/Db/Entity.php',
@@ -56,6 +57,7 @@ return array(
'OCP\\AppFramework\\OCS\\OCSException' => $baseDir . '/lib/public/AppFramework/OCS/OCSException.php',
'OCP\\AppFramework\\OCS\\OCSForbiddenException' => $baseDir . '/lib/public/AppFramework/OCS/OCSForbiddenException.php',
'OCP\\AppFramework\\OCS\\OCSNotFoundException' => $baseDir . '/lib/public/AppFramework/OCS/OCSNotFoundException.php',
+ 'OCP\\AppFramework\\PublicShareController' => $baseDir . '/lib/public/AppFramework/PublicShareController.php',
'OCP\\AppFramework\\QueryException' => $baseDir . '/lib/public/AppFramework/QueryException.php',
'OCP\\AppFramework\\Utility\\IControllerMethodReflector' => $baseDir . '/lib/public/AppFramework/Utility/IControllerMethodReflector.php',
'OCP\\AppFramework\\Utility\\ITimeFactory' => $baseDir . '/lib/public/AppFramework/Utility/ITimeFactory.php',
@@ -350,6 +352,8 @@ return array(
'OC\\AppFramework\\Http\\Request' => $baseDir . '/lib/private/AppFramework/Http/Request.php',
'OC\\AppFramework\\Middleware\\MiddlewareDispatcher' => $baseDir . '/lib/private/AppFramework/Middleware/MiddlewareDispatcher.php',
'OC\\AppFramework\\Middleware\\OCSMiddleware' => $baseDir . '/lib/private/AppFramework/Middleware/OCSMiddleware.php',
+ 'OC\\AppFramework\\Middleware\\PublicShare\\Exceptions\\NeedAuthenticationException' => $baseDir . '/lib/private/AppFramework/Middleware/PublicShare/Exceptions/NeedAuthenticationException.php',
+ 'OC\\AppFramework\\Middleware\\PublicShare\\PublicShareMiddleware' => $baseDir . '/lib/private/AppFramework/Middleware/PublicShare/PublicShareMiddleware.php',
'OC\\AppFramework\\Middleware\\Security\\BruteForceMiddleware' => $baseDir . '/lib/private/AppFramework/Middleware/Security/BruteForceMiddleware.php',
'OC\\AppFramework\\Middleware\\Security\\CORSMiddleware' => $baseDir . '/lib/private/AppFramework/Middleware/Security/CORSMiddleware.php',
'OC\\AppFramework\\Middleware\\Security\\Exceptions\\AppNotEnabledException' => $baseDir . '/lib/private/AppFramework/Middleware/Security/Exceptions/AppNotEnabledException.php',
diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php
index be9c71d8246..26a38a29984 100644
--- a/lib/composer/composer/autoload_static.php
+++ b/lib/composer/composer/autoload_static.php
@@ -48,6 +48,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
'OCP\\App' => __DIR__ . '/../../..' . '/lib/public/App.php',
'OCP\\AppFramework\\ApiController' => __DIR__ . '/../../..' . '/lib/public/AppFramework/ApiController.php',
'OCP\\AppFramework\\App' => __DIR__ . '/../../..' . '/lib/public/AppFramework/App.php',
+ 'OCP\\AppFramework\\AuthPublicShareController' => __DIR__ . '/../../..' . '/lib/public/AppFramework/AuthPublicShareController.php',
'OCP\\AppFramework\\Controller' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Controller.php',
'OCP\\AppFramework\\Db\\DoesNotExistException' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Db/DoesNotExistException.php',
'OCP\\AppFramework\\Db\\Entity' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Db/Entity.php',
@@ -86,6 +87,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
'OCP\\AppFramework\\OCS\\OCSException' => __DIR__ . '/../../..' . '/lib/public/AppFramework/OCS/OCSException.php',
'OCP\\AppFramework\\OCS\\OCSForbiddenException' => __DIR__ . '/../../..' . '/lib/public/AppFramework/OCS/OCSForbiddenException.php',
'OCP\\AppFramework\\OCS\\OCSNotFoundException' => __DIR__ . '/../../..' . '/lib/public/AppFramework/OCS/OCSNotFoundException.php',
+ 'OCP\\AppFramework\\PublicShareController' => __DIR__ . '/../../..' . '/lib/public/AppFramework/PublicShareController.php',
'OCP\\AppFramework\\QueryException' => __DIR__ . '/../../..' . '/lib/public/AppFramework/QueryException.php',
'OCP\\AppFramework\\Utility\\IControllerMethodReflector' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Utility/IControllerMethodReflector.php',
'OCP\\AppFramework\\Utility\\ITimeFactory' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Utility/ITimeFactory.php',
@@ -380,6 +382,8 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
'OC\\AppFramework\\Http\\Request' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Http/Request.php',
'OC\\AppFramework\\Middleware\\MiddlewareDispatcher' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Middleware/MiddlewareDispatcher.php',
'OC\\AppFramework\\Middleware\\OCSMiddleware' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Middleware/OCSMiddleware.php',
+ 'OC\\AppFramework\\Middleware\\PublicShare\\Exceptions\\NeedAuthenticationException' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Middleware/PublicShare/Exceptions/NeedAuthenticationException.php',
+ 'OC\\AppFramework\\Middleware\\PublicShare\\PublicShareMiddleware' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Middleware/PublicShare/PublicShareMiddleware.php',
'OC\\AppFramework\\Middleware\\Security\\BruteForceMiddleware' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Middleware/Security/BruteForceMiddleware.php',
'OC\\AppFramework\\Middleware\\Security\\CORSMiddleware' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Middleware/Security/CORSMiddleware.php',
'OC\\AppFramework\\Middleware\\Security\\Exceptions\\AppNotEnabledException' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Middleware/Security/Exceptions/AppNotEnabledException.php',
diff --git a/lib/private/AppFramework/DependencyInjection/DIContainer.php b/lib/private/AppFramework/DependencyInjection/DIContainer.php
index c82ac5255dd..8803ef8c47d 100644
--- a/lib/private/AppFramework/DependencyInjection/DIContainer.php
+++ b/lib/private/AppFramework/DependencyInjection/DIContainer.php
@@ -62,6 +62,7 @@ use OCP\IL10N;
use OCP\ILogger;
use OCP\IRequest;
use OCP\IServerContainer;
+use OCP\ISession;
use OCP\IUserSession;
use OCP\RichObjectStrings\IValidator;
use OCP\Encryption\IManager;
@@ -304,7 +305,7 @@ class DIContainer extends SimpleContainer implements IAppContainer {
});
$middleWares = &$this->middleWares;
- $this->registerService('MiddlewareDispatcher', function($c) use (&$middleWares) {
+ $this->registerService('MiddlewareDispatcher', function(SimpleContainer $c) use (&$middleWares) {
$dispatcher = new MiddlewareDispatcher();
$dispatcher->registerMiddleware($c[OC\AppFramework\Middleware\Security\SameSiteCookieMiddleware::class]);
$dispatcher->registerMiddleware($c['CORSMiddleware']);
@@ -314,6 +315,11 @@ class DIContainer extends SimpleContainer implements IAppContainer {
$dispatcher->registerMiddleware($c['TwoFactorMiddleware']);
$dispatcher->registerMiddleware($c['BruteForceMiddleware']);
$dispatcher->registerMiddleware($c['RateLimitingMiddleware']);
+ $dispatcher->registerMiddleware(new OC\AppFramework\Middleware\PublicShare\PublicShareMiddleware(
+ $c['Request'],
+ $c->query(ISession::class),
+ $c->query(\OCP\IConfig::class)
+ ));
foreach($middleWares as $middleWare) {
$dispatcher->registerMiddleware($c[$middleWare]);
diff --git a/lib/private/AppFramework/Middleware/PublicShare/Exceptions/NeedAuthenticationException.php b/lib/private/AppFramework/Middleware/PublicShare/Exceptions/NeedAuthenticationException.php
new file mode 100644
index 00000000000..27e57fe9505
--- /dev/null
+++ b/lib/private/AppFramework/Middleware/PublicShare/Exceptions/NeedAuthenticationException.php
@@ -0,0 +1,7 @@
+<?php
+
+namespace OC\AppFramework\Middleware\PublicShare\Exceptions;
+
+class NeedAuthenticationException extends \Exception {
+
+}
diff --git a/lib/private/AppFramework/Middleware/PublicShare/PublicShareMiddleware.php b/lib/private/AppFramework/Middleware/PublicShare/PublicShareMiddleware.php
new file mode 100644
index 00000000000..05783b21900
--- /dev/null
+++ b/lib/private/AppFramework/Middleware/PublicShare/PublicShareMiddleware.php
@@ -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;
+ }
+}
diff --git a/lib/private/legacy/template/functions.php b/lib/private/legacy/template/functions.php
index 290ffe120a3..55d3a595689 100644
--- a/lib/private/legacy/template/functions.php
+++ b/lib/private/legacy/template/functions.php
@@ -262,7 +262,7 @@ function preview_icon( $path ) {
* @return string
*/
function publicPreview_icon ( $path, $token ) {
- return \OC::$server->getURLGenerator()->linkToRoute('files_sharing.PublicPreview.getPreview', ['x' => 32, 'y' => 32, 'file' => $path, 't' => $token]);
+ return \OC::$server->getURLGenerator()->linkToRoute('files_sharing.PublicPreview.getPreview', ['x' => 32, 'y' => 32, 'file' => $path, 'token' => $token]);
}
/**
diff --git a/lib/public/AppFramework/AuthPublicShareController.php b/lib/public/AppFramework/AuthPublicShareController.php
new file mode 100644
index 00000000000..ffd2bddd24b
--- /dev/null
+++ b/lib/public/AppFramework/AuthPublicShareController.php
@@ -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));
+ }
+}
diff --git a/lib/public/AppFramework/PublicShareController.php b/lib/public/AppFramework/PublicShareController.php
new file mode 100644
index 00000000000..d0e54a0295b
--- /dev/null
+++ b/lib/public/AppFramework/PublicShareController.php
@@ -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() {
+
+ }
+}
diff --git a/tests/acceptance/features/bootstrap/FilesSharingAppContext.php b/tests/acceptance/features/bootstrap/FilesSharingAppContext.php
index 61357142ae4..1fe12d5f42d 100644
--- a/tests/acceptance/features/bootstrap/FilesSharingAppContext.php
+++ b/tests/acceptance/features/bootstrap/FilesSharingAppContext.php
@@ -137,7 +137,7 @@ class FilesSharingAppContext implements Context, ActorAwareInterface {
*/
public function iSeeThatTheCurrentPageIsTheAuthenticatePageForTheSharedLinkIWroteDown() {
PHPUnit_Framework_Assert::assertEquals(
- $this->actor->getSharedNotebook()["shared link"] . "/authenticate/preview",
+ $this->actor->getSharedNotebook()["shared link"] . "/authenticate/showShare",
$this->actor->getSession()->getCurrentUrl());
}
@@ -146,7 +146,7 @@ class FilesSharingAppContext implements Context, ActorAwareInterface {
*/
public function iSeeThatTheCurrentPageIsTheAuthenticatePageForTheDirectDownloadSharedLinkIWroteDown() {
PHPUnit_Framework_Assert::assertEquals(
- $this->actor->getSharedNotebook()["shared link"] . "/authenticate/download",
+ $this->actor->getSharedNotebook()["shared link"] . "/authenticate/downloadShare",
$this->actor->getSession()->getCurrentUrl());
}
diff --git a/tests/lib/AppFramework/Controller/AuthPublicShareControllerTest.php b/tests/lib/AppFramework/Controller/AuthPublicShareControllerTest.php
new file mode 100644
index 00000000000..169ec82ce6d
--- /dev/null
+++ b/tests/lib/AppFramework/Controller/AuthPublicShareControllerTest.php
@@ -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);
+ }
+}
diff --git a/tests/lib/AppFramework/Controller/PublicShareControllerTest.php b/tests/lib/AppFramework/Controller/PublicShareControllerTest.php
new file mode 100644
index 00000000000..eff7563cc4f
--- /dev/null
+++ b/tests/lib/AppFramework/Controller/PublicShareControllerTest.php
@@ -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());
+ }
+
+}
diff --git a/tests/lib/AppFramework/Middleware/PublicShare/PublicShareMiddlewareTest.php b/tests/lib/AppFramework/Middleware/PublicShare/PublicShareMiddlewareTest.php
new file mode 100644
index 00000000000..de610100c2a
--- /dev/null
+++ b/tests/lib/AppFramework/Middleware/PublicShare/PublicShareMiddlewareTest.php
@@ -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);
+ }
+
+
+}