2014-10-15 05:58:44 -04:00
|
|
|
<?php
|
2025-06-30 09:04:05 -04:00
|
|
|
|
2014-10-15 05:58:44 -04:00
|
|
|
/**
|
2024-06-06 13:48:28 -04:00
|
|
|
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
|
|
|
|
|
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
|
|
|
|
|
* SPDX-License-Identifier: AGPL-3.0-only
|
2014-10-15 05:58:44 -04:00
|
|
|
*/
|
2016-10-24 05:46:25 -04:00
|
|
|
namespace OCA\Files_Sharing\Controller;
|
2014-10-15 05:58:44 -04:00
|
|
|
|
2021-06-29 19:20:33 -04:00
|
|
|
use OC\Security\CSP\ContentSecurityPolicy;
|
2022-05-15 04:38:55 -04:00
|
|
|
use OCA\DAV\Connector\Sabre\PublicAuth;
|
2016-04-18 12:17:08 -04:00
|
|
|
use OCA\FederatedFileSharing\FederatedShareProvider;
|
2020-07-13 10:05:11 -04:00
|
|
|
use OCA\Files_Sharing\Event\BeforeTemplateRenderedEvent;
|
2021-09-08 14:04:37 -04:00
|
|
|
use OCA\Files_Sharing\Event\ShareLinkAccessedEvent;
|
2020-03-12 12:38:18 -04:00
|
|
|
use OCP\Accounts\IAccountManager;
|
2018-05-18 06:29:10 -04:00
|
|
|
use OCP\AppFramework\AuthPublicShareController;
|
2024-07-25 07:14:46 -04:00
|
|
|
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
|
2025-09-17 09:54:17 -04:00
|
|
|
use OCP\AppFramework\Http\Attribute\NoSameSiteCookieRequired;
|
2024-01-18 04:38:37 -05:00
|
|
|
use OCP\AppFramework\Http\Attribute\OpenAPI;
|
2024-07-25 07:14:46 -04:00
|
|
|
use OCP\AppFramework\Http\Attribute\PublicPage;
|
2024-10-10 06:40:31 -04:00
|
|
|
use OCP\AppFramework\Http\DataResponse;
|
2019-11-22 14:52:10 -05:00
|
|
|
use OCP\AppFramework\Http\NotFoundResponse;
|
2024-09-26 16:31:25 -04:00
|
|
|
use OCP\AppFramework\Http\RedirectResponse;
|
2019-11-22 14:52:10 -05:00
|
|
|
use OCP\AppFramework\Http\TemplateResponse;
|
2024-10-10 06:40:31 -04:00
|
|
|
use OCP\Constants;
|
2016-07-22 10:13:26 -04:00
|
|
|
use OCP\Defaults;
|
2020-07-13 10:05:11 -04:00
|
|
|
use OCP\EventDispatcher\IEventDispatcher;
|
2024-10-10 06:40:31 -04:00
|
|
|
use OCP\Files\File;
|
2020-03-19 10:14:00 -04:00
|
|
|
use OCP\Files\Folder;
|
2019-11-22 14:52:10 -05:00
|
|
|
use OCP\Files\IRootFolder;
|
|
|
|
|
use OCP\Files\NotFoundException;
|
2024-10-18 06:04:22 -04:00
|
|
|
use OCP\HintException;
|
2019-11-22 14:52:10 -05:00
|
|
|
use OCP\IConfig;
|
2016-07-22 10:13:26 -04:00
|
|
|
use OCP\IL10N;
|
2019-11-22 14:52:10 -05:00
|
|
|
use OCP\IPreview;
|
2014-10-15 05:58:44 -04:00
|
|
|
use OCP\IRequest;
|
2019-11-22 14:52:10 -05:00
|
|
|
use OCP\ISession;
|
2016-01-15 03:41:51 -05:00
|
|
|
use OCP\IURLGenerator;
|
|
|
|
|
use OCP\IUserManager;
|
2024-08-21 15:58:10 -04:00
|
|
|
use OCP\Security\Events\GenerateSecurePasswordEvent;
|
2022-02-05 14:49:17 -05:00
|
|
|
use OCP\Security\ISecureRandom;
|
2024-08-21 15:58:10 -04:00
|
|
|
use OCP\Security\PasswordContext;
|
2019-11-22 14:52:10 -05:00
|
|
|
use OCP\Share;
|
2016-02-02 08:07:11 -05:00
|
|
|
use OCP\Share\Exceptions\ShareNotFound;
|
2019-11-22 14:52:10 -05:00
|
|
|
use OCP\Share\IManager as ShareManager;
|
2023-01-18 10:53:22 -05:00
|
|
|
use OCP\Share\IPublicShareTemplateFactory;
|
2023-11-23 04:22:34 -05:00
|
|
|
use OCP\Share\IShare;
|
2014-10-15 05:58:44 -04:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @package OCA\Files_Sharing\Controllers
|
|
|
|
|
*/
|
2024-01-18 04:38:37 -05:00
|
|
|
#[OpenAPI(scope: OpenAPI::SCOPE_IGNORE)]
|
2018-05-18 06:29:10 -04:00
|
|
|
class ShareController extends AuthPublicShareController {
|
2024-10-10 06:40:31 -04:00
|
|
|
protected ?IShare $share = null;
|
2023-07-05 10:28:22 -04:00
|
|
|
|
|
|
|
|
public const SHARE_ACCESS = 'access';
|
|
|
|
|
public const SHARE_AUTH = 'auth';
|
|
|
|
|
public const SHARE_DOWNLOAD = 'download';
|
2023-01-18 10:53:22 -05:00
|
|
|
|
|
|
|
|
public function __construct(
|
|
|
|
|
string $appName,
|
|
|
|
|
IRequest $request,
|
2023-07-05 10:28:22 -04:00
|
|
|
protected IConfig $config,
|
2023-01-18 10:53:22 -05:00
|
|
|
IURLGenerator $urlGenerator,
|
2023-07-05 10:28:22 -04:00
|
|
|
protected IUserManager $userManager,
|
|
|
|
|
protected \OCP\Activity\IManager $activityManager,
|
|
|
|
|
protected ShareManager $shareManager,
|
2023-01-18 10:53:22 -05:00
|
|
|
ISession $session,
|
2023-07-05 10:28:22 -04:00
|
|
|
protected IPreview $previewManager,
|
|
|
|
|
protected IRootFolder $rootFolder,
|
|
|
|
|
protected FederatedShareProvider $federatedShareProvider,
|
|
|
|
|
protected IAccountManager $accountManager,
|
|
|
|
|
protected IEventDispatcher $eventDispatcher,
|
|
|
|
|
protected IL10N $l10n,
|
|
|
|
|
protected ISecureRandom $secureRandom,
|
|
|
|
|
protected Defaults $defaults,
|
|
|
|
|
private IPublicShareTemplateFactory $publicShareTemplateFactory,
|
2023-01-18 10:53:22 -05:00
|
|
|
) {
|
2018-05-18 06:29:10 -04:00
|
|
|
parent::__construct($appName, $request, $session, $urlGenerator);
|
2014-10-15 05:58:44 -04:00
|
|
|
}
|
|
|
|
|
|
Add event to load additional scripts in the auth page for public shares
Before the public share authentication page is rendered now an event to
load additional scripts is dispatched. Thanks to this any app can load
its own scripts that, when run on the browser, adjust as needed the page
generated by the server.
Note, however, that during the handling of the event apps are only able
to add scripts or styles to be loaded; they can not render arbitrary
content on the page, or change how the content is rendered by the
original template; all those changes have to be done by the scripts at
run-time.
This implies that the scripts of the apps can use only those parameters,
like the token of the share, added to the page when it is generated by
the "publicshareauth" template. Due to this, and given that the event is
being introduced to be used by Talk to inject the UI needed to request
the password for a share, the token of the share is now provided in the
generated page, just like done in the public share page.
Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
2018-07-12 08:52:36 -04:00
|
|
|
/**
|
|
|
|
|
* Show the authentication page
|
|
|
|
|
* The form has to submit to the authenticate method route
|
|
|
|
|
*/
|
2024-07-25 07:14:46 -04:00
|
|
|
#[PublicPage]
|
|
|
|
|
#[NoCSRFRequired]
|
Add event to load additional scripts in the auth page for public shares
Before the public share authentication page is rendered now an event to
load additional scripts is dispatched. Thanks to this any app can load
its own scripts that, when run on the browser, adjust as needed the page
generated by the server.
Note, however, that during the handling of the event apps are only able
to add scripts or styles to be loaded; they can not render arbitrary
content on the page, or change how the content is rendered by the
original template; all those changes have to be done by the scripts at
run-time.
This implies that the scripts of the apps can use only those parameters,
like the token of the share, added to the page when it is generated by
the "publicshareauth" template. Due to this, and given that the event is
being introduced to be used by Talk to inject the UI needed to request
the password for a share, the token of the share is now provided in the
generated page, just like done in the public share page.
Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
2018-07-12 08:52:36 -04:00
|
|
|
public function showAuthenticate(): TemplateResponse {
|
|
|
|
|
$templateParameters = ['share' => $this->share];
|
|
|
|
|
|
2020-07-13 10:05:11 -04:00
|
|
|
$this->eventDispatcher->dispatchTyped(new BeforeTemplateRenderedEvent($this->share, BeforeTemplateRenderedEvent::SCOPE_PUBLIC_SHARE_AUTH));
|
Add event to load additional scripts in the auth page for public shares
Before the public share authentication page is rendered now an event to
load additional scripts is dispatched. Thanks to this any app can load
its own scripts that, when run on the browser, adjust as needed the page
generated by the server.
Note, however, that during the handling of the event apps are only able
to add scripts or styles to be loaded; they can not render arbitrary
content on the page, or change how the content is rendered by the
original template; all those changes have to be done by the scripts at
run-time.
This implies that the scripts of the apps can use only those parameters,
like the token of the share, added to the page when it is generated by
the "publicshareauth" template. Due to this, and given that the event is
being introduced to be used by Talk to inject the UI needed to request
the password for a share, the token of the share is now provided in the
generated page, just like done in the public share page.
Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
2018-07-12 08:52:36 -04:00
|
|
|
|
2018-10-22 04:57:55 -04:00
|
|
|
$response = new TemplateResponse('core', 'publicshareauth', $templateParameters, 'guest');
|
|
|
|
|
if ($this->share->getSendPasswordByTalk()) {
|
|
|
|
|
$csp = new ContentSecurityPolicy();
|
|
|
|
|
$csp->addAllowedConnectDomain('*');
|
|
|
|
|
$csp->addAllowedMediaDomain('blob:');
|
|
|
|
|
$response->setContentSecurityPolicy($csp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $response;
|
Add event to load additional scripts in the auth page for public shares
Before the public share authentication page is rendered now an event to
load additional scripts is dispatched. Thanks to this any app can load
its own scripts that, when run on the browser, adjust as needed the page
generated by the server.
Note, however, that during the handling of the event apps are only able
to add scripts or styles to be loaded; they can not render arbitrary
content on the page, or change how the content is rendered by the
original template; all those changes have to be done by the scripts at
run-time.
This implies that the scripts of the apps can use only those parameters,
like the token of the share, added to the page when it is generated by
the "publicshareauth" template. Due to this, and given that the event is
being introduced to be used by Talk to inject the UI needed to request
the password for a share, the token of the share is now provided in the
generated page, just like done in the public share page.
Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
2018-07-12 08:52:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The template to show when authentication failed
|
|
|
|
|
*/
|
|
|
|
|
protected function showAuthFailed(): TemplateResponse {
|
|
|
|
|
$templateParameters = ['share' => $this->share, 'wrongpw' => true];
|
|
|
|
|
|
2020-07-13 10:05:11 -04:00
|
|
|
$this->eventDispatcher->dispatchTyped(new BeforeTemplateRenderedEvent($this->share, BeforeTemplateRenderedEvent::SCOPE_PUBLIC_SHARE_AUTH));
|
Add event to load additional scripts in the auth page for public shares
Before the public share authentication page is rendered now an event to
load additional scripts is dispatched. Thanks to this any app can load
its own scripts that, when run on the browser, adjust as needed the page
generated by the server.
Note, however, that during the handling of the event apps are only able
to add scripts or styles to be loaded; they can not render arbitrary
content on the page, or change how the content is rendered by the
original template; all those changes have to be done by the scripts at
run-time.
This implies that the scripts of the apps can use only those parameters,
like the token of the share, added to the page when it is generated by
the "publicshareauth" template. Due to this, and given that the event is
being introduced to be used by Talk to inject the UI needed to request
the password for a share, the token of the share is now provided in the
generated page, just like done in the public share page.
Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
2018-07-12 08:52:36 -04:00
|
|
|
|
2018-10-22 04:57:55 -04:00
|
|
|
$response = new TemplateResponse('core', 'publicshareauth', $templateParameters, 'guest');
|
|
|
|
|
if ($this->share->getSendPasswordByTalk()) {
|
|
|
|
|
$csp = new ContentSecurityPolicy();
|
|
|
|
|
$csp->addAllowedConnectDomain('*');
|
|
|
|
|
$csp->addAllowedMediaDomain('blob:');
|
|
|
|
|
$response->setContentSecurityPolicy($csp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $response;
|
Add event to load additional scripts in the auth page for public shares
Before the public share authentication page is rendered now an event to
load additional scripts is dispatched. Thanks to this any app can load
its own scripts that, when run on the browser, adjust as needed the page
generated by the server.
Note, however, that during the handling of the event apps are only able
to add scripts or styles to be loaded; they can not render arbitrary
content on the page, or change how the content is rendered by the
original template; all those changes have to be done by the scripts at
run-time.
This implies that the scripts of the apps can use only those parameters,
like the token of the share, added to the page when it is generated by
the "publicshareauth" template. Due to this, and given that the event is
being introduced to be used by Talk to inject the UI needed to request
the password for a share, the token of the share is now provided in the
generated page, just like done in the public share page.
Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
2018-07-12 08:52:36 -04:00
|
|
|
}
|
|
|
|
|
|
2022-02-05 14:49:17 -05:00
|
|
|
/**
|
|
|
|
|
* The template to show after user identification
|
|
|
|
|
*/
|
|
|
|
|
protected function showIdentificationResult(bool $success = false): TemplateResponse {
|
|
|
|
|
$templateParameters = ['share' => $this->share, 'identityOk' => $success];
|
|
|
|
|
|
|
|
|
|
$this->eventDispatcher->dispatchTyped(new BeforeTemplateRenderedEvent($this->share, BeforeTemplateRenderedEvent::SCOPE_PUBLIC_SHARE_AUTH));
|
|
|
|
|
|
|
|
|
|
$response = new TemplateResponse('core', 'publicshareauth', $templateParameters, 'guest');
|
|
|
|
|
if ($this->share->getSendPasswordByTalk()) {
|
|
|
|
|
$csp = new ContentSecurityPolicy();
|
|
|
|
|
$csp->addAllowedConnectDomain('*');
|
|
|
|
|
$csp->addAllowedMediaDomain('blob:');
|
|
|
|
|
$response->setContentSecurityPolicy($csp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $response;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Validate the identity token of a public share
|
|
|
|
|
*
|
|
|
|
|
* @param ?string $identityToken
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
protected function validateIdentity(?string $identityToken = null): bool {
|
|
|
|
|
if ($this->share->getShareType() !== IShare::TYPE_EMAIL) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($identityToken === null || $this->share->getSharedWith() === null) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $identityToken === $this->share->getSharedWith();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Generates a password for the share, respecting any password policy defined
|
|
|
|
|
*/
|
|
|
|
|
protected function generatePassword(): void {
|
2024-08-21 15:58:10 -04:00
|
|
|
$event = new GenerateSecurePasswordEvent(PasswordContext::SHARING);
|
2022-02-05 14:49:17 -05:00
|
|
|
$this->eventDispatcher->dispatchTyped($event);
|
|
|
|
|
$password = $event->getPassword() ?? $this->secureRandom->generate(20);
|
|
|
|
|
|
|
|
|
|
$this->share->setPassword($password);
|
|
|
|
|
$this->shareManager->updateShare($this->share);
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-18 06:29:10 -04:00
|
|
|
protected function verifyPassword(string $password): bool {
|
|
|
|
|
return $this->shareManager->checkPassword($this->share, $password);
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-28 09:05:07 -04:00
|
|
|
protected function getPasswordHash(): ?string {
|
2018-05-18 06:29:10 -04:00
|
|
|
return $this->share->getPassword();
|
|
|
|
|
}
|
2014-10-15 05:58:44 -04:00
|
|
|
|
2018-05-18 06:29:10 -04:00
|
|
|
public function isValidToken(): bool {
|
|
|
|
|
try {
|
|
|
|
|
$this->share = $this->shareManager->getShareByToken($this->getToken());
|
|
|
|
|
} catch (ShareNotFound $e) {
|
|
|
|
|
return false;
|
2014-10-15 05:58:44 -04:00
|
|
|
}
|
|
|
|
|
|
2018-05-18 06:29:10 -04:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected function isPasswordProtected(): bool {
|
|
|
|
|
return $this->share->getPassword() !== null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected function authSucceeded() {
|
2022-05-15 04:38:55 -04:00
|
|
|
if ($this->share === null) {
|
|
|
|
|
throw new NotFoundException();
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-18 06:29:10 -04:00
|
|
|
// For share this was always set so it is still used in other apps
|
2025-10-23 05:57:03 -04:00
|
|
|
$allowedShareIds = $this->session->get(PublicAuth::DAV_AUTHENTICATED);
|
|
|
|
|
if (!is_array($allowedShareIds)) {
|
|
|
|
|
$allowedShareIds = [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->session->set(PublicAuth::DAV_AUTHENTICATED, array_merge($allowedShareIds, [$this->share->getId()]));
|
2014-10-15 05:58:44 -04:00
|
|
|
}
|
|
|
|
|
|
2018-06-14 04:20:10 -04:00
|
|
|
protected function authFailed() {
|
|
|
|
|
$this->emitAccessShareHook($this->share, 403, 'Wrong password');
|
2021-09-08 14:04:37 -04:00
|
|
|
$this->emitShareAccessEvent($this->share, self::SHARE_AUTH, 403, 'Wrong password');
|
2016-01-15 02:11:11 -05:00
|
|
|
}
|
|
|
|
|
|
2016-02-08 18:34:10 -05:00
|
|
|
/**
|
|
|
|
|
* throws hooks when a share is attempted to be accessed
|
|
|
|
|
*
|
2024-10-18 06:04:22 -04:00
|
|
|
* @param IShare|string $share the Share instance if available,
|
|
|
|
|
* otherwise token
|
2016-02-08 18:34:10 -05:00
|
|
|
* @param int $errorCode
|
|
|
|
|
* @param string $errorMessage
|
2021-09-08 14:04:37 -04:00
|
|
|
*
|
2024-10-18 06:04:22 -04:00
|
|
|
* @throws HintException
|
2016-07-18 07:34:18 -04:00
|
|
|
* @throws \OC\ServerNotAvailableException
|
2021-09-08 14:04:37 -04:00
|
|
|
*
|
|
|
|
|
* @deprecated use OCP\Files_Sharing\Event\ShareLinkAccessedEvent
|
2016-02-08 18:34:10 -05:00
|
|
|
*/
|
2021-09-08 14:04:37 -04:00
|
|
|
protected function emitAccessShareHook($share, int $errorCode = 200, string $errorMessage = '') {
|
2016-02-08 18:34:10 -05:00
|
|
|
$itemType = $itemSource = $uidOwner = '';
|
|
|
|
|
$token = $share;
|
|
|
|
|
$exception = null;
|
2024-10-10 06:40:31 -04:00
|
|
|
if ($share instanceof IShare) {
|
2016-02-08 18:34:10 -05:00
|
|
|
try {
|
|
|
|
|
$token = $share->getToken();
|
|
|
|
|
$uidOwner = $share->getSharedBy();
|
2016-02-09 04:36:44 -05:00
|
|
|
$itemType = $share->getNodeType();
|
|
|
|
|
$itemSource = $share->getNodeId();
|
2016-02-08 18:34:10 -05:00
|
|
|
} catch (\Exception $e) {
|
|
|
|
|
// we log what we know and pass on the exception afterwards
|
|
|
|
|
$exception = $e;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-09-08 14:04:37 -04:00
|
|
|
|
2018-01-25 17:16:13 -05:00
|
|
|
\OC_Hook::emit(Share::class, 'share_link_access', [
|
2016-02-08 18:34:10 -05:00
|
|
|
'itemType' => $itemType,
|
|
|
|
|
'itemSource' => $itemSource,
|
|
|
|
|
'uidOwner' => $uidOwner,
|
|
|
|
|
'token' => $token,
|
|
|
|
|
'errorCode' => $errorCode,
|
2021-09-08 14:04:37 -04:00
|
|
|
'errorMessage' => $errorMessage
|
2016-02-08 18:34:10 -05:00
|
|
|
]);
|
2021-09-08 14:04:37 -04:00
|
|
|
|
2020-04-10 08:19:56 -04:00
|
|
|
if (!is_null($exception)) {
|
2016-02-08 18:34:10 -05:00
|
|
|
throw $exception;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-08 14:04:37 -04:00
|
|
|
/**
|
|
|
|
|
* Emit a ShareLinkAccessedEvent event when a share is accessed, downloaded, auth...
|
|
|
|
|
*/
|
|
|
|
|
protected function emitShareAccessEvent(IShare $share, string $step = '', int $errorCode = 200, string $errorMessage = ''): void {
|
2025-06-30 09:04:05 -04:00
|
|
|
if ($step !== self::SHARE_ACCESS
|
|
|
|
|
&& $step !== self::SHARE_AUTH
|
|
|
|
|
&& $step !== self::SHARE_DOWNLOAD) {
|
2021-09-08 14:04:37 -04:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
$this->eventDispatcher->dispatchTyped(new ShareLinkAccessedEvent($share, $step, $errorCode, $errorMessage));
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-09 07:00:08 -05:00
|
|
|
/**
|
|
|
|
|
* Validate the permissions of the share
|
|
|
|
|
*
|
|
|
|
|
* @param Share\IShare $share
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
2024-10-10 06:40:31 -04:00
|
|
|
private function validateShare(IShare $share) {
|
2022-07-28 07:11:38 -04:00
|
|
|
// If the owner is disabled no access to the link is granted
|
2019-09-09 16:33:03 -04:00
|
|
|
$owner = $this->userManager->get($share->getShareOwner());
|
|
|
|
|
if ($owner === null || !$owner->isEnabled()) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If the initiator of the share is disabled no access is granted
|
|
|
|
|
$initiator = $this->userManager->get($share->getSharedBy());
|
|
|
|
|
if ($initiator === null || !$initiator->isEnabled()) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-09 07:00:08 -05:00
|
|
|
return $share->getNode()->isReadable() && $share->getNode()->isShareable();
|
|
|
|
|
}
|
|
|
|
|
|
2014-10-15 05:58:44 -04:00
|
|
|
/**
|
|
|
|
|
* @param string $path
|
2018-05-18 06:29:10 -04:00
|
|
|
* @return TemplateResponse
|
2015-09-30 07:32:20 -04:00
|
|
|
* @throws NotFoundException
|
2016-06-08 09:38:11 -04:00
|
|
|
* @throws \Exception
|
2014-10-15 05:58:44 -04:00
|
|
|
*/
|
2024-07-25 07:14:46 -04:00
|
|
|
#[PublicPage]
|
|
|
|
|
#[NoCSRFRequired]
|
2018-05-18 06:29:10 -04:00
|
|
|
public function showShare($path = ''): TemplateResponse {
|
2014-10-15 05:58:44 -04:00
|
|
|
\OC_User::setIncognitoMode(true);
|
|
|
|
|
|
|
|
|
|
// Check whether share exists
|
2016-01-15 02:11:11 -05:00
|
|
|
try {
|
2018-05-18 06:29:10 -04:00
|
|
|
$share = $this->shareManager->getShareByToken($this->getToken());
|
2016-02-02 08:07:11 -05:00
|
|
|
} catch (ShareNotFound $e) {
|
2021-09-08 14:04:37 -04:00
|
|
|
// The share does not exists, we do not emit an ShareLinkAccessedEvent
|
2018-05-18 06:29:10 -04:00
|
|
|
$this->emitAccessShareHook($this->getToken(), 404, 'Share not found');
|
2024-08-01 16:28:08 -04:00
|
|
|
throw new NotFoundException($this->l10n->t('This share does not exist or is no longer available'));
|
2014-10-15 05:58:44 -04:00
|
|
|
}
|
|
|
|
|
|
2016-02-09 07:00:08 -05:00
|
|
|
if (!$this->validateShare($share)) {
|
2024-08-01 16:28:08 -04:00
|
|
|
throw new NotFoundException($this->l10n->t('This share does not exist or is no longer available'));
|
2016-02-09 07:00:08 -05:00
|
|
|
}
|
2018-11-05 14:17:00 -05:00
|
|
|
|
|
|
|
|
$shareNode = $share->getNode();
|
|
|
|
|
|
2023-01-18 10:53:22 -05:00
|
|
|
try {
|
|
|
|
|
$templateProvider = $this->publicShareTemplateFactory->getProvider($share);
|
|
|
|
|
$response = $templateProvider->renderPage($share, $this->getToken(), $path);
|
|
|
|
|
} catch (NotFoundException $e) {
|
|
|
|
|
$this->emitAccessShareHook($share, 404, 'Share not found');
|
|
|
|
|
$this->emitShareAccessEvent($share, ShareController::SHARE_ACCESS, 404, 'Share not found');
|
2024-08-01 16:28:08 -04:00
|
|
|
throw new NotFoundException($this->l10n->t('This share does not exist or is no longer available'));
|
2023-01-18 10:53:22 -05:00
|
|
|
}
|
|
|
|
|
|
2016-01-15 02:11:11 -05:00
|
|
|
// We can't get the path of a file share
|
2016-02-08 18:34:10 -05:00
|
|
|
try {
|
2024-10-10 06:40:31 -04:00
|
|
|
if ($shareNode instanceof File && $path !== '') {
|
2016-02-08 18:34:10 -05:00
|
|
|
$this->emitAccessShareHook($share, 404, 'Share not found');
|
2021-09-08 14:04:37 -04:00
|
|
|
$this->emitShareAccessEvent($share, self::SHARE_ACCESS, 404, 'Share not found');
|
2024-08-01 16:28:08 -04:00
|
|
|
throw new NotFoundException($this->l10n->t('This share does not exist or is no longer available'));
|
2016-02-08 18:34:10 -05:00
|
|
|
}
|
|
|
|
|
} catch (\Exception $e) {
|
|
|
|
|
$this->emitAccessShareHook($share, 404, 'Share not found');
|
2021-09-08 14:04:37 -04:00
|
|
|
$this->emitShareAccessEvent($share, self::SHARE_ACCESS, 404, 'Share not found');
|
2016-02-08 18:34:10 -05:00
|
|
|
throw $e;
|
2014-10-15 05:58:44 -04:00
|
|
|
}
|
|
|
|
|
|
2015-03-10 05:06:15 -04:00
|
|
|
|
2016-02-08 18:34:10 -05:00
|
|
|
$this->emitAccessShareHook($share);
|
2021-09-08 14:04:37 -04:00
|
|
|
$this->emitShareAccessEvent($share, self::SHARE_ACCESS);
|
2016-02-08 18:34:10 -05:00
|
|
|
|
2015-03-10 05:06:15 -04:00
|
|
|
return $response;
|
2014-10-15 05:58:44 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2016-06-08 09:38:11 -04:00
|
|
|
* @throws NotFoundException
|
2024-09-26 16:31:25 -04:00
|
|
|
* @deprecated 31.0.0 Users are encouraged to use the DAV endpoint
|
2014-10-15 05:58:44 -04:00
|
|
|
*/
|
2024-07-25 07:14:46 -04:00
|
|
|
#[PublicPage]
|
|
|
|
|
#[NoCSRFRequired]
|
2025-09-17 09:54:17 -04:00
|
|
|
#[NoSameSiteCookieRequired]
|
|
|
|
|
public function downloadShare(string $token, ?string $files = null, string $path = ''): NotFoundResponse|RedirectResponse|DataResponse {
|
2014-10-15 05:58:44 -04:00
|
|
|
\OC_User::setIncognitoMode(true);
|
|
|
|
|
|
2016-01-15 05:49:50 -05:00
|
|
|
$share = $this->shareManager->getShareByToken($token);
|
2014-10-15 05:58:44 -04:00
|
|
|
|
2024-10-10 06:40:31 -04:00
|
|
|
if (!($share->getPermissions() & Constants::PERMISSION_READ)) {
|
|
|
|
|
return new DataResponse('Share has no read permission');
|
2016-06-08 09:38:11 -04:00
|
|
|
}
|
|
|
|
|
|
2025-02-18 05:58:12 -05:00
|
|
|
$attributes = $share->getAttributes();
|
|
|
|
|
if ($attributes?->getAttribute('permissions', 'download') === false) {
|
|
|
|
|
return new DataResponse('Share has no download permission');
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-09 07:00:08 -05:00
|
|
|
if (!$this->validateShare($share)) {
|
|
|
|
|
throw new NotFoundException();
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-13 10:07:53 -04:00
|
|
|
$node = $share->getNode();
|
|
|
|
|
if ($node instanceof Folder) {
|
|
|
|
|
// Directory share
|
2016-01-15 05:49:50 -05:00
|
|
|
|
|
|
|
|
// Try to get the path
|
|
|
|
|
if ($path !== '') {
|
|
|
|
|
try {
|
|
|
|
|
$node = $node->get($path);
|
|
|
|
|
} catch (NotFoundException $e) {
|
2016-02-08 18:34:10 -05:00
|
|
|
$this->emitAccessShareHook($share, 404, 'Share not found');
|
2021-09-08 14:04:37 -04:00
|
|
|
$this->emitShareAccessEvent($share, self::SHARE_DOWNLOAD, 404, 'Share not found');
|
2016-01-15 05:49:50 -05:00
|
|
|
return new NotFoundResponse();
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-10-15 05:58:44 -04:00
|
|
|
|
2024-10-10 06:40:31 -04:00
|
|
|
if ($node instanceof Folder) {
|
2024-09-26 16:31:25 -04:00
|
|
|
if ($files === null || $files === '') {
|
2025-03-13 10:07:53 -04:00
|
|
|
if ($share->getHideDownload()) {
|
|
|
|
|
throw new NotFoundException('Downloading a folder');
|
2020-03-19 10:14:00 -04:00
|
|
|
}
|
2020-02-28 10:25:09 -05:00
|
|
|
}
|
2015-02-17 09:08:16 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-08 18:34:10 -05:00
|
|
|
$this->emitAccessShareHook($share);
|
2021-09-08 14:04:37 -04:00
|
|
|
$this->emitShareAccessEvent($share, self::SHARE_DOWNLOAD);
|
2016-02-08 18:34:10 -05:00
|
|
|
|
2024-09-26 16:31:25 -04:00
|
|
|
$davUrl = '/public.php/dav/files/' . $token . '/?accept=zip';
|
|
|
|
|
if ($files !== null) {
|
|
|
|
|
$davUrl .= '&files=' . $files;
|
2016-11-03 07:06:22 -04:00
|
|
|
}
|
2024-09-26 16:31:25 -04:00
|
|
|
return new RedirectResponse($this->urlGenerator->getAbsoluteURL($davUrl));
|
2016-11-03 07:06:22 -04:00
|
|
|
}
|
2014-10-15 05:58:44 -04:00
|
|
|
}
|