299 lines
7.7 KiB
PHP
299 lines
7.7 KiB
PHP
<?php
|
|
/**
|
|
* Nextcloud - Gallery
|
|
*
|
|
* This file is licensed under the Affero General Public License version 3 or
|
|
* later. See the COPYING file.
|
|
*
|
|
* @author Olivier Paroz <galleryapps@oparoz.com>
|
|
* @author Bernhard Posselt <dev@bernhard-posselt.com>
|
|
* @author Authors of \OCA\Files_Sharing\Helper
|
|
*
|
|
* @copyright Olivier Paroz 2017
|
|
* @copyright Bernhard Posselt 2017
|
|
* @copyright Authors of \OCA\Files_Sharing\Helper 2017
|
|
*/
|
|
|
|
namespace OCA\Gallery\Middleware;
|
|
|
|
use OCP\IRequest;
|
|
use OCP\IURLGenerator;
|
|
use OCP\ISession;
|
|
use OCP\ILogger;
|
|
use OCP\Share;
|
|
use OCP\Share\IShare;
|
|
use OCP\Share\Exceptions\ShareNotFound;
|
|
use OCP\Security\IHasher;
|
|
|
|
use OCP\AppFramework\Http;
|
|
use OCP\AppFramework\Utility\IControllerMethodReflector;
|
|
|
|
use OCA\Gallery\Environment\Environment;
|
|
use OCP\Share\IManager;
|
|
|
|
/**
|
|
* Checks that we have a valid token linked to a valid resource and that the
|
|
* user is authorised to access it
|
|
*
|
|
* Once all checks have been passed, the environment is ready to use
|
|
*
|
|
* @package OCA\Gallery\Middleware
|
|
*/
|
|
class EnvCheckMiddleware extends CheckMiddleware {
|
|
|
|
/** @var IHasher */
|
|
private $hasher;
|
|
/** @var ISession */
|
|
private $session;
|
|
/** @var Environment */
|
|
private $environment;
|
|
/** @var IControllerMethodReflector */
|
|
protected $reflector;
|
|
/** @var IManager */
|
|
protected $shareManager;
|
|
|
|
/***
|
|
* Constructor
|
|
*
|
|
* @param string $appName
|
|
* @param IRequest $request
|
|
* @param IHasher $hasher
|
|
* @param ISession $session
|
|
* @param Environment $environment
|
|
* @param IControllerMethodReflector $reflector
|
|
* @param IURLGenerator $urlGenerator
|
|
* @param ILogger $logger
|
|
* @param IManager $shareManager
|
|
*/
|
|
public function __construct(
|
|
$appName,
|
|
IRequest $request,
|
|
IHasher $hasher,
|
|
ISession $session,
|
|
Environment $environment,
|
|
IControllerMethodReflector $reflector,
|
|
IURLGenerator $urlGenerator,
|
|
IManager $shareManager,
|
|
ILogger $logger
|
|
) {
|
|
parent::__construct(
|
|
$appName,
|
|
$request,
|
|
$urlGenerator,
|
|
$logger
|
|
);
|
|
|
|
$this->hasher = $hasher;
|
|
$this->session = $session;
|
|
$this->environment = $environment;
|
|
$this->reflector = $reflector;
|
|
$this->shareManager = $shareManager;
|
|
}
|
|
|
|
/**
|
|
* Checks that we have a valid token linked to a valid resource and that the
|
|
* user is authorised to access it
|
|
*
|
|
* Inspects the controller method annotations and if PublicPage is found
|
|
* it checks that we have a token and an optional password giving access to a valid resource.
|
|
* Once that's done, the environment is setup so that our services can find the resources they
|
|
* need.
|
|
*
|
|
* The checks are not performed on "guest" pages and the environment is not setup. Typical
|
|
* guest pages are anonymous error ages
|
|
*
|
|
* @inheritDoc
|
|
*/
|
|
public function beforeController($controller, $methodName) {
|
|
if ($this->reflector->hasAnnotation('Guest')) {
|
|
return;
|
|
}
|
|
$isPublicPage = $this->reflector->hasAnnotation('PublicPage');
|
|
if ($isPublicPage) {
|
|
$this->validateAndSetTokenBasedEnv();
|
|
} else {
|
|
$this->environment->setStandardEnv();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks that we have a token and an optional password giving access to a
|
|
* valid resource. Sets the token based environment after that
|
|
*
|
|
* @throws CheckException
|
|
*/
|
|
private function validateAndSetTokenBasedEnv() {
|
|
$token = $this->request->getParam('token');
|
|
if (!$token) {
|
|
throw new CheckException(
|
|
"Can't access a public resource without a token", Http::STATUS_NOT_FOUND
|
|
);
|
|
} else {
|
|
$share = $this->getShare($token);
|
|
$password = $this->request->getParam('password');
|
|
// Let's see if the user needs to provide a password
|
|
$this->checkAuthorisation($share, $password);
|
|
|
|
$this->environment->setTokenBasedEnv($share);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Validates a token to make sure its linked to a valid resource
|
|
*
|
|
* Uses Share 2.0
|
|
*
|
|
* @fixme setIncognitoMode in 8.1 https://github.com/owncloud/core/pull/12912
|
|
*
|
|
* @param string $token
|
|
*
|
|
* @throws CheckException
|
|
* @return IShare
|
|
*/
|
|
private function getShare($token) {
|
|
// Allows a logged in user to access public links
|
|
\OC_User::setIncognitoMode(true);
|
|
|
|
try {
|
|
$share = $this->shareManager->getShareByToken($token);
|
|
} catch (ShareNotFound $e) {
|
|
throw new CheckException($e->getMessage(), Http::STATUS_NOT_FOUND);
|
|
}
|
|
|
|
$this->checkShareIsValid($share, $token);
|
|
$this->checkItemType($share);
|
|
|
|
return $share;
|
|
}
|
|
|
|
/**
|
|
* Makes sure that the token contains all the information that we need
|
|
*
|
|
* @param IShare $share
|
|
* @param string $token
|
|
*
|
|
* @throws CheckException
|
|
*/
|
|
private function checkShareIsValid($share, $token) {
|
|
if ($share->getShareOwner() === null
|
|
|| $share->getTarget() === null
|
|
) {
|
|
$message =
|
|
'Passed token seems to be valid, but it does not contain all necessary information . ("'
|
|
. $token . '")';
|
|
throw new CheckException($message, Http::STATUS_NOT_FOUND);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Makes sure an item type was set for that token
|
|
*
|
|
* @param IShare $share
|
|
*
|
|
* @throws CheckException
|
|
*/
|
|
private function checkItemType($share) {
|
|
if ($share->getNodeType() === null) {
|
|
$message = 'No item type set for share id: ' . $share->getId();
|
|
throw new CheckException($message, Http::STATUS_NOT_FOUND);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Checks if a password is required or if the one supplied is working
|
|
*
|
|
* @param IShare $share
|
|
* @param string|null $password optional password
|
|
*
|
|
* @throws CheckException
|
|
*/
|
|
private function checkAuthorisation($share, $password) {
|
|
$passwordRequired = $share->getPassword();
|
|
|
|
if (isset($passwordRequired)) {
|
|
if ($password !== null) {
|
|
$this->authenticate($share, $password);
|
|
} else {
|
|
$this->checkSession($share);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Authenticate link item with the given password
|
|
* or with the session if no password was given.
|
|
*
|
|
* @param IShare $share
|
|
* @param string $password
|
|
*
|
|
* @return bool true if authorized, an exception is raised otherwise
|
|
*
|
|
* @throws CheckException
|
|
*/
|
|
private function authenticate($share, $password) {
|
|
if ((int)$share->getShareType() === Share::SHARE_TYPE_LINK) {
|
|
$this->checkPassword($share, $password);
|
|
} else {
|
|
throw new CheckException(
|
|
'Unknown share type ' . $share->getShareType() . ' for share id '
|
|
. $share->getId(), Http::STATUS_NOT_FOUND
|
|
);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Validates the given password
|
|
*
|
|
* @fixme @LukasReschke says: Migrate old hashes to new hash format
|
|
* Due to the fact that there is no reasonable functionality to update the password
|
|
* of an existing share no migration is yet performed there.
|
|
* The only possibility is to update the existing share which will result in a new
|
|
* share ID and is a major hack.
|
|
*
|
|
* In the future the migration should be performed once there is a proper method
|
|
* to update the share's password. (for example `$share->updatePassword($password)`
|
|
*
|
|
* @link https://github.com/owncloud/core/issues/10671
|
|
*
|
|
* @param IShare $share
|
|
* @param string $password
|
|
*
|
|
* @throws CheckException
|
|
*/
|
|
private function checkPassword($share, $password) {
|
|
$newHash = '';
|
|
if ($this->shareManager->checkPassword($share, $password)) {
|
|
// Save item id in session for future requests
|
|
$this->session->set('public_link_authenticated', (string)$share->getId());
|
|
// @codeCoverageIgnoreStart
|
|
if (!empty($newHash)) {
|
|
// For future use
|
|
}
|
|
// @codeCoverageIgnoreEnd
|
|
} else {
|
|
throw new CheckException("Wrong password", Http::STATUS_UNAUTHORIZED);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Makes sure the user is already properly authenticated when a password is required and none
|
|
* was provided
|
|
*
|
|
* @param IShare $share
|
|
*
|
|
* @throws CheckException
|
|
*/
|
|
private function checkSession($share) {
|
|
// Not authenticated ?
|
|
if (!$this->session->exists('public_link_authenticated')
|
|
|| $this->session->get('public_link_authenticated') !== (string)$share->getId()
|
|
) {
|
|
throw new CheckException("Missing password", Http::STATUS_UNAUTHORIZED);
|
|
}
|
|
}
|
|
|
|
}
|