Merge pull request #9518 from nextcloud/feature/5986/public_share_controller_middleware
Public share middleware & controller
This commit is contained in:
commit
8ebc3d90a0
23 changed files with 1134 additions and 479 deletions
|
@ -34,13 +34,7 @@ return [
|
|||
],
|
||||
[
|
||||
'name' => 'PublicPreview#getPreview',
|
||||
'url' => '/publicpreview',
|
||||
'verb' => 'GET',
|
||||
],
|
||||
|
||||
[
|
||||
'name' => 'PublicPreview#getPreview',
|
||||
'url' => '/ajax/publicpreview.php',
|
||||
'url' => '/publicpreview/{token}',
|
||||
'verb' => 'GET',
|
||||
],
|
||||
|
||||
|
|
|
@ -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 () {
|
|||
};
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
);
|
||||
}
|
||||
|
||||
$response = new TemplateResponse($this->appName, 'authenticate', array('wrongpw' => true), 'guest');
|
||||
$response->throttle();
|
||||
return $response;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 isPasswordProtected(): bool {
|
||||
return $this->share->getPassword() !== null;
|
||||
}
|
||||
|
||||
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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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,57 +193,11 @@ 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');
|
||||
}
|
||||
|
||||
/**
|
||||
* @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.
|
||||
|
|
|
@ -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">
|
|
@ -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',
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
192
lib/public/AppFramework/AuthPublicShareController.php
Normal file
192
lib/public/AppFramework/AuthPublicShareController.php
Normal file
|
@ -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));
|
||||
}
|
||||
}
|
138
lib/public/AppFramework/PublicShareController.php
Normal file
138
lib/public/AppFramework/PublicShareController.php
Normal file
|
@ -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() {
|
||||
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
102
tests/lib/AppFramework/Controller/PublicShareControllerTest.php
Normal file
102
tests/lib/AppFramework/Controller/PublicShareControllerTest.php
Normal file
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
Loading…
Reference in a new issue