diff options
author | Cyrille Bollu <cyrpub@bollu.be> | 2022-02-05 20:49:17 +0100 |
---|---|---|
committer | Cyrille Bollu <cyrpub@bollu.be> | 2022-04-11 21:58:24 +0200 |
commit | c6a5c07041d2e5d20771409aede8b755d28372ac (patch) | |
tree | 71051efd25c16bed5a419eb1670477f1f5471933 /apps | |
parent | 60f946aba5862102a81100b09e26b37b6d59a3fa (diff) | |
download | nextcloud-server-c6a5c07041d2e5d20771409aede8b755d28372ac.tar.gz nextcloud-server-c6a5c07041d2e5d20771409aede8b755d28372ac.zip |
Adds a "Request password" button to the public share authentication page for shares
of type TYPE_EMAIL, when the "video verification" checkbox isn't checked. Users accessing
non-anonymous public shares (TYPE_EMAIL shares) can now request a temporary password themselves.
- Creates a migration step for the files_sharing app to add the 'password_expiration_time'
attribute to the oc_shares table.
- Makes share temporary passwords' expiration time configurable via a system value.
- Adds a system config value to allow permanent share passwords
-Fixes a typo in a comment in apps/files_sharing/src/components/SharingEntryLink.vue
See https://github.com/nextcloud/server/issues/31005
Signed-off-by: Cyrille Bollu <cyrpub@bollu.be>
Diffstat (limited to 'apps')
12 files changed, 256 insertions, 75 deletions
diff --git a/apps/files_sharing/appinfo/info.xml b/apps/files_sharing/appinfo/info.xml index a2446f99163..7ab8a1fcfa2 100644 --- a/apps/files_sharing/appinfo/info.xml +++ b/apps/files_sharing/appinfo/info.xml @@ -9,7 +9,7 @@ Turning the feature off removes shared files and folders on the server for all share recipients, and also on the sync clients and mobile apps. More information is available in the Nextcloud Documentation. </description> - <version>1.16.1</version> + <version>1.16.2</version> <licence>agpl</licence> <author>Michael Gapczynski</author> <author>Bjoern Schiessle</author> diff --git a/apps/files_sharing/composer/composer/autoload_classmap.php b/apps/files_sharing/composer/composer/autoload_classmap.php index 95bee7323b7..2810910c8c9 100644 --- a/apps/files_sharing/composer/composer/autoload_classmap.php +++ b/apps/files_sharing/composer/composer/autoload_classmap.php @@ -68,6 +68,7 @@ return array( 'OCA\\Files_Sharing\\Migration\\Version11300Date20201120141438' => $baseDir . '/../lib/Migration/Version11300Date20201120141438.php', 'OCA\\Files_Sharing\\Migration\\Version21000Date20201223143245' => $baseDir . '/../lib/Migration/Version21000Date20201223143245.php', 'OCA\\Files_Sharing\\Migration\\Version22000Date20210216084241' => $baseDir . '/../lib/Migration/Version22000Date20210216084241.php', + 'OCA\\Files_Sharing\\Migration\\Version24000Date20220208195521' => $baseDir . '/../lib/Migration/Version24000Date20220208195521.php', 'OCA\\Files_Sharing\\Migration\\Version24000Date20220404142216' => $baseDir . '/../lib/Migration/Version24000Date20220404142216.php', 'OCA\\Files_Sharing\\MountProvider' => $baseDir . '/../lib/MountProvider.php', 'OCA\\Files_Sharing\\Notification\\Listener' => $baseDir . '/../lib/Notification/Listener.php', diff --git a/apps/files_sharing/composer/composer/autoload_static.php b/apps/files_sharing/composer/composer/autoload_static.php index dd8e9e2b7f9..70149b1cdc0 100644 --- a/apps/files_sharing/composer/composer/autoload_static.php +++ b/apps/files_sharing/composer/composer/autoload_static.php @@ -83,6 +83,7 @@ class ComposerStaticInitFiles_Sharing 'OCA\\Files_Sharing\\Migration\\Version11300Date20201120141438' => __DIR__ . '/..' . '/../lib/Migration/Version11300Date20201120141438.php', 'OCA\\Files_Sharing\\Migration\\Version21000Date20201223143245' => __DIR__ . '/..' . '/../lib/Migration/Version21000Date20201223143245.php', 'OCA\\Files_Sharing\\Migration\\Version22000Date20210216084241' => __DIR__ . '/..' . '/../lib/Migration/Version22000Date20210216084241.php', + 'OCA\\Files_Sharing\\Migration\\Version24000Date20220208195521' => __DIR__ . '/..' . '/../lib/Migration/Version24000Date20220208195521.php', 'OCA\\Files_Sharing\\Migration\\Version24000Date20220404142216' => __DIR__ . '/..' . '/../lib/Migration/Version24000Date20220404142216.php', 'OCA\\Files_Sharing\\MountProvider' => __DIR__ . '/..' . '/../lib/MountProvider.php', 'OCA\\Files_Sharing\\Notification\\Listener' => __DIR__ . '/..' . '/../lib/Notification/Listener.php', diff --git a/apps/files_sharing/composer/composer/installed.php b/apps/files_sharing/composer/composer/installed.php index 5440719fa40..1d332e29100 100644 --- a/apps/files_sharing/composer/composer/installed.php +++ b/apps/files_sharing/composer/composer/installed.php @@ -5,7 +5,7 @@ 'type' => 'library', 'install_path' => __DIR__ . '/../', 'aliases' => array(), - 'reference' => 'c6429e6cd19c57582364338362e543580821cf99', + 'reference' => 'ea4531aaaa6eb9fb3859e05b69ab773bfbfe7437', 'name' => '__root__', 'dev' => false, ), @@ -16,7 +16,7 @@ 'type' => 'library', 'install_path' => __DIR__ . '/../', 'aliases' => array(), - 'reference' => 'c6429e6cd19c57582364338362e543580821cf99', + 'reference' => 'ea4531aaaa6eb9fb3859e05b69ab773bfbfe7437', 'dev_requirement' => false, ), ), diff --git a/apps/files_sharing/lib/Controller/ShareAPIController.php b/apps/files_sharing/lib/Controller/ShareAPIController.php index 069cba42bb6..d324af3e9f2 100644 --- a/apps/files_sharing/lib/Controller/ShareAPIController.php +++ b/apps/files_sharing/lib/Controller/ShareAPIController.php @@ -279,6 +279,7 @@ class ShareAPIController extends OCSController { } elseif ($share->getShareType() === IShare::TYPE_EMAIL) { $result['share_with'] = $share->getSharedWith(); $result['password'] = $share->getPassword(); + $result['password_expiration_time'] = $share->getPasswordExpirationTime(); $result['send_password_by_talk'] = $share->getSendPasswordByTalk(); $result['share_with_displayname'] = $this->getDisplayNameFromAddressBook($share->getSharedWith(), 'EMAIL'); $result['token'] = $share->getToken(); @@ -570,6 +571,10 @@ class ShareAPIController extends OCSController { // Set password if ($password !== '') { $share->setPassword($password); + // Shares shared by email have temporary passwords by default + if ($shareType === IShare::TYPE_EMAIL) { + $this->setSharePasswordExpirationTime($share); + } } // Only share by mail have a recipient @@ -1177,6 +1182,9 @@ class ShareAPIController extends OCSController { $share->setPassword(null); } elseif ($password !== null) { $share->setPassword($password); + if ($share->getShareType() === IShare::TYPE_EMAIL) { + $this->setSharePasswordExpirationTime($share); + } } if ($label !== null) { @@ -1514,6 +1522,35 @@ class ShareAPIController extends OCSController { } /** + * Set the share's password expiration time + */ + private function setSharePasswordExpirationTime(IShare $share): void { + if ($this->config->getSystemValue('allow_mail_share_permanent_password')) { + // Sets password expiration date to NULL + $share->setPasswordExpirationTime(); + return; + } + // Sets password expiration date + $expirationTime = null; + try { + $now = new \DateTime(); + $expirationInterval = $this->config->getSystemValue('share_temporary_password_expiration_interval'); + if ($expirationInterval === '' || is_null($expirationInterval)) { + $expirationInterval = 'P0DT15M'; + } + $expirationTime = $now->add(new \DateInterval($expirationInterval)); + } catch (\Exception $e) { + // Catches invalid format for system value 'share_temporary_password_expiration_interval' + \OC::$server->getLogger()->logException($e, [ + 'message' => 'The \'share_temporary_password_expiration_interval\' system setting does not respect the DateInterval::__construct() format. Setting it to \'P0DT15M\'' + ]); + $expirationTime = $now->add(new \DateInterval('P0DT15M')); + } finally { + $share->setPasswordExpirationTime($expirationTime); + } + } + + /** * Since we have multiple providers but the OCS Share API v1 does * not support this we need to check all backends. * diff --git a/apps/files_sharing/lib/Controller/ShareController.php b/apps/files_sharing/lib/Controller/ShareController.php index 411873c9c86..a12878e6de2 100644 --- a/apps/files_sharing/lib/Controller/ShareController.php +++ b/apps/files_sharing/lib/Controller/ShareController.php @@ -72,6 +72,7 @@ use OCP\ISession; use OCP\IURLGenerator; use OCP\IUser; use OCP\IUserManager; +use OCP\Security\ISecureRandom; use OCP\Share; use OCP\Share\Exceptions\ShareNotFound; use OCP\Share\IManager as ShareManager; @@ -84,53 +85,21 @@ use OCP\Template; * @package OCA\Files_Sharing\Controllers */ class ShareController extends AuthPublicShareController { + protected IConfig $config; + protected IUserManager $userManager; + protected ILogger $logger; + protected \OCP\Activity\IManager $activityManager; + protected IPreview $previewManager; + protected IRootFolder $rootFolder; + protected FederatedShareProvider $federatedShareProvider; + protected IAccountManager $accountManager; + protected IEventDispatcher $eventDispatcher; + protected IL10N $l10n; + protected Defaults $defaults; + protected ShareManager $shareManager; + protected ISecureRandom $secureRandom; + protected ?Share\IShare $share = null; - /** @var IConfig */ - protected $config; - /** @var IUserManager */ - protected $userManager; - /** @var ILogger */ - protected $logger; - /** @var \OCP\Activity\IManager */ - protected $activityManager; - /** @var IPreview */ - protected $previewManager; - /** @var IRootFolder */ - protected $rootFolder; - /** @var FederatedShareProvider */ - protected $federatedShareProvider; - /** @var IAccountManager */ - protected $accountManager; - /** @var IEventDispatcher */ - protected $eventDispatcher; - /** @var IL10N */ - protected $l10n; - /** @var Defaults */ - protected $defaults; - /** @var ShareManager */ - protected $shareManager; - - /** @var Share\IShare */ - protected $share; - - /** - * @param string $appName - * @param IRequest $request - * @param IConfig $config - * @param IURLGenerator $urlGenerator - * @param IUserManager $userManager - * @param ILogger $logger - * @param \OCP\Activity\IManager $activityManager - * @param \OCP\Share\IManager $shareManager - * @param ISession $session - * @param IPreview $previewManager - * @param IRootFolder $rootFolder - * @param FederatedShareProvider $federatedShareProvider - * @param IAccountManager $accountManager - * @param IEventDispatcher $eventDispatcher - * @param IL10N $l10n - * @param Defaults $defaults - */ public function __construct(string $appName, IRequest $request, IConfig $config, @@ -146,6 +115,7 @@ class ShareController extends AuthPublicShareController { IAccountManager $accountManager, IEventDispatcher $eventDispatcher, IL10N $l10n, + ISecureRandom $secureRandom, Defaults $defaults) { parent::__construct($appName, $request, $session, $urlGenerator); @@ -159,6 +129,7 @@ class ShareController extends AuthPublicShareController { $this->accountManager = $accountManager; $this->eventDispatcher = $eventDispatcher; $this->l10n = $l10n; + $this->secureRandom = $secureRandom; $this->defaults = $defaults; $this->shareManager = $shareManager; } @@ -209,6 +180,56 @@ class ShareController extends AuthPublicShareController { return $response; } + /** + * 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 { + $event = new \OCP\Security\Events\GenerateSecurePasswordEvent(); + $this->eventDispatcher->dispatchTyped($event); + $password = $event->getPassword() ?? $this->secureRandom->generate(20); + + $this->share->setPassword($password); + $this->shareManager->updateShare($this->share); + } + protected function verifyPassword(string $password): bool { return $this->shareManager->checkPassword($this->share, $password); } diff --git a/apps/files_sharing/lib/Migration/Version24000Date20220208195521.php b/apps/files_sharing/lib/Migration/Version24000Date20220208195521.php new file mode 100644 index 00000000000..d5f938dde6d --- /dev/null +++ b/apps/files_sharing/lib/Migration/Version24000Date20220208195521.php @@ -0,0 +1,51 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2022 Vincent Petry <vincent@nextloud.com> + * + * @author Vincent Petry <vincent@nextcloud.com> + * + * @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 OCA\Files_Sharing\Migration; + +use Closure; +use OCP\DB\Types; +use OCP\DB\ISchemaWrapper; +use OCP\Migration\IOutput; +use OCP\Migration\SimpleMigrationStep; + +class Version24000Date20220208195521 extends SimpleMigrationStep { + + /** + * @param IOutput $output + * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` + * @param array $options + * @return null|ISchemaWrapper + */ + public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper { + $schema = $schemaClosure(); + $table = $schema->getTable('share'); + $table->addColumn('password_expiration_time', Types::DATETIME, [ + 'notnull' => false, + ]); + return $schema; + } + +} diff --git a/apps/files_sharing/src/components/SharingEntryLink.vue b/apps/files_sharing/src/components/SharingEntryLink.vue index 52215d37ec8..ee7e8d4b930 100644 --- a/apps/files_sharing/src/components/SharingEntryLink.vue +++ b/apps/files_sharing/src/components/SharingEntryLink.vue @@ -780,8 +780,8 @@ export default { /** * Uncheck password protection * We need this method because @update:checked - * is ran simultaneously as @uncheck, so - * so we cannot ensure data is up-to-date + * is ran simultaneously as @uncheck, so we + * cannot ensure data is up-to-date */ onPasswordDisable() { this.share.password = '' diff --git a/apps/files_sharing/tests/Controller/ShareAPIControllerTest.php b/apps/files_sharing/tests/Controller/ShareAPIControllerTest.php index 4b52f7a2a36..5555aef1425 100644 --- a/apps/files_sharing/tests/Controller/ShareAPIControllerTest.php +++ b/apps/files_sharing/tests/Controller/ShareAPIControllerTest.php @@ -4410,6 +4410,7 @@ class ShareAPIControllerTest extends TestCase { 'hide_download' => 0, 'can_edit' => false, 'can_delete' => false, + 'password_expiration_time' => null, ], $share, [], false ]; @@ -4459,6 +4460,7 @@ class ShareAPIControllerTest extends TestCase { 'hide_download' => 0, 'can_edit' => false, 'can_delete' => false, + 'password_expiration_time' => null, ], $share, [], false ]; diff --git a/apps/files_sharing/tests/Controller/ShareControllerTest.php b/apps/files_sharing/tests/Controller/ShareControllerTest.php index be2616f70fc..04d2a08b4e6 100644 --- a/apps/files_sharing/tests/Controller/ShareControllerTest.php +++ b/apps/files_sharing/tests/Controller/ShareControllerTest.php @@ -106,6 +106,8 @@ class ShareControllerTest extends \Test\TestCase { private $eventDispatcher; /** @var IL10N */ private $l10n; + /** @var ISecureRandom */ + private $secureRandom; /** @var Defaults|MockObject */ private $defaults; @@ -127,6 +129,7 @@ class ShareControllerTest extends \Test\TestCase { $this->accountManager = $this->createMock(IAccountManager::class); $this->eventDispatcher = $this->createMock(IEventDispatcher::class); $this->l10n = $this->createMock(IL10N::class); + $this->secureRandom = $this->createMock(ISecureRandom::class); $this->defaults = $this->createMock(Defaults::class); $this->shareController = new \OCA\Files_Sharing\Controller\ShareController( @@ -145,6 +148,7 @@ class ShareControllerTest extends \Test\TestCase { $this->accountManager, $this->eventDispatcher, $this->l10n, + $this->secureRandom, $this->defaults ); diff --git a/apps/sharebymail/lib/ShareByMailProvider.php b/apps/sharebymail/lib/ShareByMailProvider.php index c1438c01125..1e767b7f859 100644 --- a/apps/sharebymail/lib/ShareByMailProvider.php +++ b/apps/sharebymail/lib/ShareByMailProvider.php @@ -52,6 +52,7 @@ use OCP\Files\Folder; use OCP\Files\IRootFolder; use OCP\Files\Node; use OCP\HintException; +use OCP\IConfig; use OCP\IDBConnection; use OCP\IL10N; use OCP\ILogger; @@ -75,6 +76,8 @@ use OCP\Share\IShareProvider; */ class ShareByMailProvider implements IShareProvider { + private IConfig $config; + /** @var IDBConnection */ private $dbConnection; @@ -126,7 +129,8 @@ class ShareByMailProvider implements IShareProvider { return 'ocMailShare'; } - public function __construct(IDBConnection $connection, + public function __construct(IConfig $config, + IDBConnection $connection, ISecureRandom $secureRandom, IUserManager $userManager, IRootFolder $rootFolder, @@ -140,6 +144,7 @@ class ShareByMailProvider implements IShareProvider { IHasher $hasher, IEventDispatcher $eventDispatcher, IShareManager $shareManager) { + $this->config = $config; $this->dbConnection = $connection; $this->secureRandom = $secureRandom; $this->userManager = $userManager; @@ -190,9 +195,14 @@ class ShareByMailProvider implements IShareProvider { } $shareId = $this->createMailShare($share); - $send = $this->sendPassword($share, $password); - if ($passwordEnforced && $send === false) { - $this->sendPasswordToOwner($share, $password); + + // Sends share password to receiver when it's a permanent one (otherwise she will have to request it via the showShare UI) + // or to owner when the password shall be given during a Talk session + if ($this->config->getSystemValue('allow_mail_share_permanent_password') || $share->getSendPasswordByTalk()) { + $send = $this->sendPassword($share, $password); + if ($passwordEnforced && $send === false) { + $this->sendPasswordToOwner($share, $password); + } } $this->createShareActivity($share); @@ -327,6 +337,7 @@ class ShareByMailProvider implements IShareProvider { $share->getPermissions(), $share->getToken(), $share->getPassword(), + $share->getPasswordExpirationTime(), $share->getSendPasswordByTalk(), $share->getHideDownload(), $share->getLabel(), @@ -673,23 +684,24 @@ class ShareByMailProvider implements IShareProvider { } /** - * add share to the database and return the ID - * - * @param int $itemSource - * @param string $itemType - * @param string $shareWith - * @param string $sharedBy - * @param string $uidOwner - * @param int $permissions - * @param string $token - * @param string $password - * @param bool $sendPasswordByTalk - * @param bool $hideDownload - * @param string $label - * @param \DateTime|null $expirationTime - * @return int + * Add share to the database and return the ID */ - protected function addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $uidOwner, $permissions, $token, $password, $sendPasswordByTalk, $hideDownload, $label, $expirationTime, $note = ''): int { + protected function addShareToDB( + ?int $itemSource, + ?string $itemType, + ?string $shareWith, + ?string $sharedBy, + ?string $uidOwner, + ?int $permissions, + ?string $token, + ?string $password, + ?\DateTimeInterface $passwordExpirationTime, + ?bool $sendPasswordByTalk, + ?bool $hideDownload, + ?string $label, + ?\DateTimeInterface $expirationTime, + ?string $note = '' + ): int { $qb = $this->dbConnection->getQueryBuilder(); $qb->insert('share') ->setValue('share_type', $qb->createNamedParameter(IShare::TYPE_EMAIL)) @@ -702,6 +714,7 @@ class ShareByMailProvider implements IShareProvider { ->setValue('permissions', $qb->createNamedParameter($permissions)) ->setValue('token', $qb->createNamedParameter($token)) ->setValue('password', $qb->createNamedParameter($password)) + ->setValue('password_expiration_time', $qb->createNamedParameter($passwordExpirationTime, IQueryBuilder::PARAM_DATE)) ->setValue('password_by_talk', $qb->createNamedParameter($sendPasswordByTalk, IQueryBuilder::PARAM_BOOL)) ->setValue('stime', $qb->createNamedParameter(time())) ->setValue('hide_download', $qb->createNamedParameter((int)$hideDownload, IQueryBuilder::PARAM_INT)) @@ -739,6 +752,7 @@ class ShareByMailProvider implements IShareProvider { ($originalShare->getSendPasswordByTalk() && !$share->getSendPasswordByTalk()))) { $this->sendPassword($share, $plainTextPassword); } + /* * We allow updating the permissions and password of mail shares */ @@ -749,6 +763,7 @@ class ShareByMailProvider implements IShareProvider { ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner())) ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy())) ->set('password', $qb->createNamedParameter($share->getPassword())) + ->set('password_expiration_time', $qb->createNamedParameter($share->getPasswordExpirationTime(), IQueryBuilder::PARAM_DATE)) ->set('label', $qb->createNamedParameter($share->getLabel())) ->set('password_by_talk', $qb->createNamedParameter($share->getSendPasswordByTalk(), IQueryBuilder::PARAM_BOOL)) ->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE)) @@ -1012,6 +1027,8 @@ class ShareByMailProvider implements IShareProvider { $share->setShareTime($shareTime); $share->setSharedWith($data['share_with']); $share->setPassword($data['password']); + $passwordExpirationTime = \DateTime::createFromFormat('Y-m-d H:i:s', $data['password_expiration_time']); + $share->setPasswordExpirationTime($passwordExpirationTime !== false? $passwordExpirationTime : null); $share->setLabel($data['label']); $share->setSendPasswordByTalk((bool)$data['password_by_talk']); $share->setHideDownload((bool)$data['hide_download']); diff --git a/apps/sharebymail/tests/ShareByMailProviderTest.php b/apps/sharebymail/tests/ShareByMailProviderTest.php index af2d5bad57c..b00ef13630f 100644 --- a/apps/sharebymail/tests/ShareByMailProviderTest.php +++ b/apps/sharebymail/tests/ShareByMailProviderTest.php @@ -39,6 +39,7 @@ use OCP\Defaults; use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\File; use OCP\Files\IRootFolder; +use OCP\IConfig; use OCP\IDBConnection; use OCP\IL10N; use OCP\ILogger; @@ -63,6 +64,9 @@ use Test\TestCase; */ class ShareByMailProviderTest extends TestCase { + /** @var IConfig */ + private $config; + /** @var IDBConnection */ private $connection; @@ -111,6 +115,7 @@ class ShareByMailProviderTest extends TestCase { protected function setUp(): void { parent::setUp(); + $this->config = $this->getMockBuilder(IConfig::class)->getMock(); $this->connection = \OC::$server->getDatabaseConnection(); $this->l = $this->getMockBuilder(IL10N::class)->getMock(); @@ -145,6 +150,7 @@ class ShareByMailProviderTest extends TestCase { $instance = $this->getMockBuilder('OCA\ShareByMail\ShareByMailProvider') ->setConstructorArgs( [ + $this->config, $this->connection, $this->secureRandom, $this->userManager, @@ -168,6 +174,7 @@ class ShareByMailProviderTest extends TestCase { } return new ShareByMailProvider( + $this->config, $this->connection, $this->secureRandom, $this->userManager, @@ -267,9 +274,43 @@ class ShareByMailProviderTest extends TestCase { $this->hasher->expects($this->once())->method('hash')->with('password')->willReturn('passwordHashed'); $share->expects($this->once())->method('setPassword')->with('passwordHashed'); + // The given password (but not the autogenerated password) should not be + // mailed to the receiver of the share because permanent passwords are not enforced. + $this->shareManager->expects($this->any())->method('shareApiLinkEnforcePassword')->willReturn(false); + $this->config->expects($this->once())->method('getSystemValue')->with('allow_mail_share_permanent_password')->willReturn(false); + $instance->expects($this->never())->method('autoGeneratePassword'); + + $this->assertSame('shareObject', + $instance->create($share) + ); + } + + public function testCreateSendPasswordByMailWithPasswordAndWithoutEnforcedPasswordProtectionWithPermanentPassword() { + $share = $this->getMockBuilder(IShare::class)->getMock(); + $share->expects($this->any())->method('getSharedWith')->willReturn('receiver@example.com'); + $share->expects($this->any())->method('getSendPasswordByTalk')->willReturn(false); + $share->expects($this->any())->method('getSharedBy')->willReturn('owner'); + + $node = $this->getMockBuilder(File::class)->getMock(); + $node->expects($this->any())->method('getName')->willReturn('filename'); + + $instance = $this->getInstance(['getSharedWith', 'createMailShare', 'getRawShare', 'createShareObject', 'createShareActivity', 'autoGeneratePassword', 'createPasswordSendActivity']); + + $instance->expects($this->once())->method('getSharedWith')->willReturn([]); + $instance->expects($this->once())->method('createMailShare')->with($share)->willReturn(42); + $instance->expects($this->once())->method('createShareActivity')->with($share); + $instance->expects($this->once())->method('getRawShare')->with(42)->willReturn('rawShare'); + $instance->expects($this->once())->method('createShareObject')->with('rawShare')->willReturn('shareObject'); + $share->expects($this->any())->method('getNode')->willReturn($node); + + $share->expects($this->once())->method('getPassword')->willReturn('password'); + $this->hasher->expects($this->once())->method('hash')->with('password')->willReturn('passwordHashed'); + $share->expects($this->once())->method('setPassword')->with('passwordHashed'); + // The given password (but not the autogenerated password) should be - // mailed to the receiver of the share. + // mailed to the receiver of the share because permanent passwords are enforced. $this->shareManager->expects($this->any())->method('shareApiLinkEnforcePassword')->willReturn(false); + $this->config->expects($this->once())->method('getSystemValue')->with('allow_mail_share_permanent_password')->willReturn(true); $this->settingsManager->expects($this->any())->method('sendPasswordByMail')->willReturn(true); $instance->expects($this->never())->method('autoGeneratePassword'); @@ -290,7 +331,7 @@ class ShareByMailProviderTest extends TestCase { ); } - public function testCreateSendPasswordByMailWithEnforcedPasswordProtection() { + public function testCreateSendPasswordByMailWithEnforcedPasswordProtectionWithPermanentPassword() { $share = $this->getMockBuilder(IShare::class)->getMock(); $share->expects($this->any())->method('getSharedWith')->willReturn('receiver@example.com'); $share->expects($this->any())->method('getSendPasswordByTalk')->willReturn(false); @@ -320,8 +361,9 @@ class ShareByMailProviderTest extends TestCase { $this->hasher->expects($this->once())->method('hash')->with('autogeneratedPassword')->willReturn('autogeneratedPasswordHashed'); $share->expects($this->once())->method('setPassword')->with('autogeneratedPasswordHashed'); - // The autogenerated password should be mailed to the receiver of the share. + // The autogenerated password should be mailed to the receiver of the share because permanent passwords are enforced. $this->shareManager->expects($this->any())->method('shareApiLinkEnforcePassword')->willReturn(true); + $this->config->expects($this->once())->method('getSystemValue')->with('allow_mail_share_permanent_password')->willReturn(true); $this->settingsManager->expects($this->any())->method('sendPasswordByMail')->willReturn(true); $message = $this->createMock(IMessage::class); @@ -341,7 +383,7 @@ class ShareByMailProviderTest extends TestCase { ); } - public function testCreateSendPasswordByMailWithPasswordAndWithEnforcedPasswordProtection() { + public function testCreateSendPasswordByMailWithPasswordAndWithEnforcedPasswordProtectionWithPermanentPassword() { $share = $this->getMockBuilder(IShare::class)->getMock(); $share->expects($this->any())->method('getSharedWith')->willReturn('receiver@example.com'); $share->expects($this->any())->method('getSendPasswordByTalk')->willReturn(false); @@ -366,6 +408,7 @@ class ShareByMailProviderTest extends TestCase { // The given password (but not the autogenerated password) should be // mailed to the receiver of the share. $this->shareManager->expects($this->any())->method('shareApiLinkEnforcePassword')->willReturn(true); + $this->config->expects($this->once())->method('getSystemValue')->with('allow_mail_share_permanent_password')->willReturn(true); $this->settingsManager->expects($this->any())->method('sendPasswordByMail')->willReturn(true); $instance->expects($this->never())->method('autoGeneratePassword'); @@ -410,6 +453,7 @@ class ShareByMailProviderTest extends TestCase { // The autogenerated password should be mailed to the owner of the share. $this->shareManager->expects($this->any())->method('shareApiLinkEnforcePassword')->willReturn(true); + $this->config->expects($this->once())->method('getSystemValue')->with('allow_mail_share_permanent_password')->willReturn(false); $this->settingsManager->expects($this->any())->method('sendPasswordByMail')->willReturn(true); $instance->expects($this->once())->method('autoGeneratePassword')->with($share)->willReturn('autogeneratedPassword'); @@ -531,6 +575,7 @@ class ShareByMailProviderTest extends TestCase { $hideDownload = true; $label = 'label'; $expiration = new \DateTime(); + $passwordExpirationTime = new \DateTime(); $instance = $this->getInstance(); @@ -546,6 +591,7 @@ class ShareByMailProviderTest extends TestCase { $permissions, $token, $password, + $passwordExpirationTime, $sendPasswordByTalk, $hideDownload, $label, @@ -572,6 +618,7 @@ class ShareByMailProviderTest extends TestCase { $this->assertSame($permissions, (int)$result[0]['permissions']); $this->assertSame($token, $result[0]['token']); $this->assertSame($password, $result[0]['password']); + $this->assertSame($passwordExpirationTime->getTimestamp(), \DateTime::createFromFormat('Y-m-d H:i:s', $result[0]['password_expiration_time'])->getTimestamp()); $this->assertSame($sendPasswordByTalk, (bool)$result[0]['password_by_talk']); $this->assertSame($hideDownload, (bool)$result[0]['hide_download']); $this->assertSame($label, $result[0]['label']); |