Skip to content

Commit 2434b94

Browse files
authored
Merge pull request #15139 from nextcloud/backport/15129/stable15
Backport/15129/stable15
2 parents e0b9a40 + 54219fb commit 2434b94

File tree

8 files changed

+318
-2
lines changed

8 files changed

+318
-2
lines changed

core/Application.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
namespace OC\Core;
3030

31+
use OC\Core\Notification\RemoveLinkSharesNotifier;
3132
use OC\DB\MissingIndexInformation;
3233
use OC\DB\SchemaWrapper;
3334
use OCP\AppFramework\App;
@@ -54,6 +55,18 @@ public function __construct() {
5455
$server = $container->getServer();
5556
$eventDispatcher = $server->getEventDispatcher();
5657

58+
$notificationManager = $server->getNotificationManager();
59+
$notificationManager->registerNotifier(function() use ($server) {
60+
return new RemoveLinkSharesNotifier(
61+
$server->getL10NFactory()
62+
);
63+
}, function() use ($server) {
64+
return [
65+
'id' => 'core',
66+
'name' => 'core',
67+
];
68+
});
69+
5770
$eventDispatcher->addListener(IDBConnection::CHECK_MISSING_INDEXES_EVENT,
5871
function(GenericEvent $event) use ($container) {
5972
/** @var MissingIndexInformation $subject */
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
declare(strict_types=1);
3+
/**
4+
* @copyright Copyright (c) 2019, Roeland Jago Douma <roeland@famdouma.nl>
5+
*
6+
* @author Roeland Jago Douma <roeland@famdouma.nl>
7+
*
8+
* @license GNU AGPL version 3 or any later version
9+
*
10+
* This program is free software: you can redistribute it and/or modify
11+
* it under the terms of the GNU Affero General Public License as
12+
* published by the Free Software Foundation, either version 3 of the
13+
* License, or (at your option) any later version.
14+
*
15+
* This program is distributed in the hope that it will be useful,
16+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
17+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18+
* GNU Affero General Public License for more details.
19+
*
20+
* You should have received a copy of the GNU Affero General Public License
21+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
22+
*
23+
*/
24+
25+
namespace OC\Core\Notification;
26+
27+
use OCP\L10N\IFactory;
28+
use OCP\Notification\INotification;
29+
use OCP\Notification\INotifier;
30+
31+
class RemoveLinkSharesNotifier implements INotifier {
32+
/** @var IFactory */
33+
private $l10nFactory;
34+
35+
public function __construct(IFactory $factory) {
36+
$this->l10nFactory = $factory;
37+
}
38+
39+
public function prepare(INotification $notification, $languageCode): INotification {
40+
if($notification->getApp() !== 'core') {
41+
throw new \InvalidArgumentException();
42+
}
43+
$l = $this->l10nFactory->get('core', $languageCode);
44+
45+
if ($notification->getSubject() === 'repair_exposing_links') {
46+
$notification->setParsedSubject($l->t('Some of your link shares have been removed'));
47+
$notification->setParsedMessage($l->t('Due to a security bug we had to remove some of your link shares. Please see the link for more information.'));
48+
$notification->setLink('https://nextcloud.com/security/advisory/?id=NC-SA-2019-003');
49+
return $notification;
50+
}
51+
52+
throw new \InvalidArgumentException('Invalid subject');
53+
}
54+
55+
}

lib/composer/composer/autoload_classmap.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -677,6 +677,7 @@
677677
'OC\\Core\\Migrations\\Version15000Date20180926101451' => $baseDir . '/core/Migrations/Version15000Date20180926101451.php',
678678
'OC\\Core\\Migrations\\Version15000Date20181015062942' => $baseDir . '/core/Migrations/Version15000Date20181015062942.php',
679679
'OC\\Core\\Migrations\\Version15000Date20181029084625' => $baseDir . '/core/Migrations/Version15000Date20181029084625.php',
680+
'OC\\Core\\Notification\\RemoveLinkSharesNotifier' => $baseDir . '/core/Notification/RemoveLinkSharesNotifier.php',
680681
'OC\\DB\\Adapter' => $baseDir . '/lib/private/DB/Adapter.php',
681682
'OC\\DB\\AdapterMySQL' => $baseDir . '/lib/private/DB/AdapterMySQL.php',
682683
'OC\\DB\\AdapterOCI8' => $baseDir . '/lib/private/DB/AdapterOCI8.php',
@@ -976,6 +977,7 @@
976977
'OC\\Repair\\OldGroupMembershipShares' => $baseDir . '/lib/private/Repair/OldGroupMembershipShares.php',
977978
'OC\\Repair\\Owncloud\\DropAccountTermsTable' => $baseDir . '/lib/private/Repair/Owncloud/DropAccountTermsTable.php',
978979
'OC\\Repair\\Owncloud\\SaveAccountsTableData' => $baseDir . '/lib/private/Repair/Owncloud/SaveAccountsTableData.php',
980+
'OC\\Repair\\RemoveLinkShares' => $baseDir . '/lib/private/Repair/RemoveLinkShares.php',
979981
'OC\\Repair\\RemoveRootShares' => $baseDir . '/lib/private/Repair/RemoveRootShares.php',
980982
'OC\\Repair\\RepairInvalidShares' => $baseDir . '/lib/private/Repair/RepairInvalidShares.php',
981983
'OC\\Repair\\RepairMimeTypes' => $baseDir . '/lib/private/Repair/RepairMimeTypes.php',

lib/composer/composer/autoload_static.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -707,6 +707,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
707707
'OC\\Core\\Migrations\\Version15000Date20180926101451' => __DIR__ . '/../../..' . '/core/Migrations/Version15000Date20180926101451.php',
708708
'OC\\Core\\Migrations\\Version15000Date20181015062942' => __DIR__ . '/../../..' . '/core/Migrations/Version15000Date20181015062942.php',
709709
'OC\\Core\\Migrations\\Version15000Date20181029084625' => __DIR__ . '/../../..' . '/core/Migrations/Version15000Date20181029084625.php',
710+
'OC\\Core\\Notification\\RemoveLinkSharesNotifier' => __DIR__ . '/../../..' . '/core/Notification/RemoveLinkSharesNotifier.php',
710711
'OC\\DB\\Adapter' => __DIR__ . '/../../..' . '/lib/private/DB/Adapter.php',
711712
'OC\\DB\\AdapterMySQL' => __DIR__ . '/../../..' . '/lib/private/DB/AdapterMySQL.php',
712713
'OC\\DB\\AdapterOCI8' => __DIR__ . '/../../..' . '/lib/private/DB/AdapterOCI8.php',
@@ -1006,6 +1007,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
10061007
'OC\\Repair\\OldGroupMembershipShares' => __DIR__ . '/../../..' . '/lib/private/Repair/OldGroupMembershipShares.php',
10071008
'OC\\Repair\\Owncloud\\DropAccountTermsTable' => __DIR__ . '/../../..' . '/lib/private/Repair/Owncloud/DropAccountTermsTable.php',
10081009
'OC\\Repair\\Owncloud\\SaveAccountsTableData' => __DIR__ . '/../../..' . '/lib/private/Repair/Owncloud/SaveAccountsTableData.php',
1010+
'OC\\Repair\\RemoveLinkShares' => __DIR__ . '/../../..' . '/lib/private/Repair/RemoveLinkShares.php',
10091011
'OC\\Repair\\RemoveRootShares' => __DIR__ . '/../../..' . '/lib/private/Repair/RemoveRootShares.php',
10101012
'OC\\Repair\\RepairInvalidShares' => __DIR__ . '/../../..' . '/lib/private/Repair/RepairInvalidShares.php',
10111013
'OC\\Repair\\RepairMimeTypes' => __DIR__ . '/../../..' . '/lib/private/Repair/RepairMimeTypes.php',

lib/private/DB/QueryBuilder/QueryBuilder.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1170,6 +1170,10 @@ public function getLastInsertId() {
11701170
* @return string
11711171
*/
11721172
public function getTableName($table) {
1173+
if ($table instanceof IQueryFunction) {
1174+
return (string) $table;
1175+
}
1176+
11731177
$table = $this->prefixTableName($table);
11741178
return $this->helper->quoteColumnName($table);
11751179
}

lib/private/Repair.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,16 @@
4949
use OC\Repair\OldGroupMembershipShares;
5050
use OC\Repair\Owncloud\DropAccountTermsTable;
5151
use OC\Repair\Owncloud\SaveAccountsTableData;
52+
use OC\Repair\RemoveLinkShares;
5253
use OC\Repair\RemoveRootShares;
5354
use OC\Repair\RepairInvalidShares;
5455
use OC\Repair\RepairMimeTypes;
5556
use OC\Repair\SqliteAutoincrement;
5657
use OC\Template\JSCombiner;
5758
use OC\Template\SCSSCacher;
5859
use Symfony\Component\EventDispatcher\EventDispatcher;
60+
use OCP\AppFramework\Utility\ITimeFactory;
61+
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
5962
use Symfony\Component\EventDispatcher\GenericEvent;
6063

6164
class Repair implements IOutput {
@@ -147,7 +150,8 @@ public static function getRepairSteps() {
147150
new AddPreviewBackgroundCleanupJob(\OC::$server->getJobList()),
148151
new AddCleanupUpdaterBackupsJob(\OC::$server->getJobList()),
149152
new RepairPendingCronJobs(\OC::$server->getDatabaseConnection(), \OC::$server->getConfig()),
150-
new SetVcardDatabaseUID(\OC::$server->getDatabaseConnection(), \OC::$server->getConfig(), \OC::$server->getLogger())
153+
new SetVcardDatabaseUID(\OC::$server->getDatabaseConnection(), \OC::$server->getConfig(), \OC::$server->getLogger()),
154+
new RemoveLinkShares(\OC::$server->getDatabaseConnection(), \OC::$server->getConfig(), \OC::$server->getGroupManager(), \OC::$server->getNotificationManager(), \OC::$server->query(ITimeFactory::class)),
151155
];
152156
}
153157

Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
<?php
2+
declare(strict_types=1);
3+
/**
4+
* @copyright Copyright (c) 2019, Roeland Jago Douma <roeland@famdouma.nl>
5+
*
6+
* @author Roeland Jago Douma <roeland@famdouma.nl>
7+
*
8+
* @license GNU AGPL version 3 or any later version
9+
*
10+
* This program is free software: you can redistribute it and/or modify
11+
* it under the terms of the GNU Affero General Public License as
12+
* published by the Free Software Foundation, either version 3 of the
13+
* License, or (at your option) any later version.
14+
*
15+
* This program is distributed in the hope that it will be useful,
16+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
17+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18+
* GNU Affero General Public License for more details.
19+
*
20+
* You should have received a copy of the GNU Affero General Public License
21+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
22+
*
23+
*/
24+
25+
namespace OC\Repair;
26+
27+
use Doctrine\DBAL\Driver\Statement;
28+
use OCP\AppFramework\Utility\ITimeFactory;
29+
use OCP\DB\QueryBuilder\IQueryBuilder;
30+
use OCP\IConfig;
31+
use OCP\IDBConnection;
32+
use OCP\IGroupManager;
33+
use OCP\Migration\IOutput;
34+
use OCP\Migration\IRepairStep;
35+
use OCP\Notification\IManager;
36+
37+
class RemoveLinkShares implements IRepairStep {
38+
/** @var IDBConnection */
39+
private $connection;
40+
/** @var IConfig */
41+
private $config;
42+
/** @var string[] */
43+
private $userToNotify = [];
44+
/** @var IGroupManager */
45+
private $groupManager;
46+
/** @var IManager */
47+
private $notificationManager;
48+
/** @var ITimeFactory */
49+
private $timeFactory;
50+
51+
public function __construct(IDBConnection $connection,
52+
IConfig $config,
53+
IGroupManager $groupManager,
54+
IManager $notificationManager,
55+
ITimeFactory $timeFactory) {
56+
$this->connection = $connection;
57+
$this->config = $config;
58+
$this->groupManager = $groupManager;
59+
$this->notificationManager = $notificationManager;
60+
$this->timeFactory = $timeFactory;
61+
}
62+
63+
64+
public function getName(): string {
65+
return 'Remove potentially over exposing share links';
66+
}
67+
68+
private function shouldRun(): bool {
69+
$versionFromBeforeUpdate = $this->config->getSystemValue('version', '0.0.0');
70+
71+
if (version_compare($versionFromBeforeUpdate, '14.0.11', '<')) {
72+
return true;
73+
}
74+
if (version_compare($versionFromBeforeUpdate, '15.0.8', '<')) {
75+
return true;
76+
}
77+
if (version_compare($versionFromBeforeUpdate, '16.0.0', '<=')) {
78+
return true;
79+
}
80+
81+
return false;
82+
}
83+
84+
/**
85+
* Delete the share
86+
*
87+
* @param int $id
88+
*/
89+
private function deleteShare(int $id) {
90+
$qb = $this->connection->getQueryBuilder();
91+
$qb->delete('share')
92+
->where($qb->expr()->eq('id', $qb->createNamedParameter($id)));
93+
$qb->execute();
94+
}
95+
96+
/**
97+
* Get the total of affected shares
98+
*
99+
* @return int
100+
*/
101+
private function getTotal(): int {
102+
$subSubQuery = $this->connection->getQueryBuilder();
103+
$subSubQuery->select('*')
104+
->from('share')
105+
->where($subSubQuery->expr()->isNotNull('parent'))
106+
->andWhere($subSubQuery->expr()->eq('share_type', $subSubQuery->expr()->literal(3, IQueryBuilder::PARAM_INT)));
107+
108+
$subQuery = $this->connection->getQueryBuilder();
109+
$subQuery->select('s1.id')
110+
->from($subQuery->createFunction('(' . $subSubQuery->getSQL() . ')'), 's1')
111+
->join(
112+
's1', 'share', 's2',
113+
$subQuery->expr()->eq('s1.parent', 's2.id')
114+
)
115+
->where($subQuery->expr()->orX(
116+
$subQuery->expr()->eq('s2.share_type', $subQuery->expr()->literal(1, IQueryBuilder::PARAM_INT)),
117+
$subQuery->expr()->eq('s2.share_type', $subQuery->expr()->literal(2, IQueryBuilder::PARAM_INT))
118+
))
119+
->andWhere($subQuery->expr()->eq('s1.item_source', 's2.item_source'));
120+
121+
$query = $this->connection->getQueryBuilder();
122+
$query->select($query->func()->count('*', 'total'))
123+
->from('share')
124+
->where($query->expr()->in('id', $query->createFunction('(' . $subQuery->getSQL() . ')')));
125+
126+
$result = $query->execute();
127+
$data = $result->fetch();
128+
$result->closeCursor();
129+
130+
return (int) $data['total'];
131+
}
132+
133+
/**
134+
* Get the cursor to fetch all the shares
135+
*
136+
* @return \Doctrine\DBAL\Driver\Statement
137+
*/
138+
private function getShares(): Statement {
139+
$subQuery = $this->connection->getQueryBuilder();
140+
$subQuery->select('*')
141+
->from('share')
142+
->where($subQuery->expr()->isNotNull('parent'))
143+
->andWhere($subQuery->expr()->eq('share_type', $subQuery->expr()->literal(3, IQueryBuilder::PARAM_INT)));
144+
145+
$query = $this->connection->getQueryBuilder();
146+
$query->select('s1.id', 's1.uid_owner', 's1.uid_initiator')
147+
->from($query->createFunction('(' . $subQuery->getSQL() . ')'), 's1')
148+
->join(
149+
's1', 'share', 's2',
150+
$query->expr()->eq('s1.parent', 's2.id')
151+
)
152+
->where($query->expr()->orX(
153+
$query->expr()->eq('s2.share_type', $query->expr()->literal(1, IQueryBuilder::PARAM_INT)),
154+
$query->expr()->eq('s2.share_type', $query->expr()->literal(2, IQueryBuilder::PARAM_INT))
155+
))
156+
->andWhere($query->expr()->eq('s1.item_source', 's2.item_source'));
157+
return $query->execute();
158+
}
159+
160+
/**
161+
* Process a single share
162+
*
163+
* @param array $data
164+
*/
165+
private function processShare(array $data) {
166+
$id = $data['id'];
167+
168+
$this->addToNotify($data['uid_owner']);
169+
$this->addToNotify($data['uid_initiator']);
170+
171+
$this->deleteShare((int)$id);
172+
}
173+
174+
/**
175+
* Update list of users to notify
176+
*
177+
* @param string $uid
178+
*/
179+
private function addToNotify(string $uid) {
180+
if (!isset($this->userToNotify[$uid])) {
181+
$this->userToNotify[$uid] = true;
182+
}
183+
}
184+
185+
/**
186+
* Send all notifications
187+
*/
188+
private function sendNotification() {
189+
$time = $this->timeFactory->getDateTime();
190+
191+
$notification = $this->notificationManager->createNotification();
192+
$notification->setApp('core')
193+
->setDateTime($time)
194+
->setObject('repair', 'exposing_links')
195+
->setSubject('repair_exposing_links', []);
196+
197+
$users = array_keys($this->userToNotify);
198+
foreach ($users as $user) {
199+
$notification->setUser($user);
200+
$this->notificationManager->notify($notification);
201+
}
202+
}
203+
204+
private function repair(IOutput $output) {
205+
$total = $this->getTotal();
206+
$output->startProgress($total);
207+
208+
$shareCursor = $this->getShares();
209+
while($data = $shareCursor->fetch()) {
210+
$this->processShare($data);
211+
$output->advance();
212+
}
213+
$output->finishProgress();
214+
$shareCursor->closeCursor();
215+
216+
// Notifiy all admins
217+
$adminGroup = $this->groupManager->get('admin');
218+
$adminUsers = $adminGroup->getUsers();
219+
foreach ($adminUsers as $user) {
220+
$this->addToNotify($user->getUID());
221+
}
222+
223+
$output->info('Sending notifications to admins and affected users');
224+
$this->sendNotification();
225+
}
226+
227+
public function run(IOutput $output) {
228+
if ($this->shouldRun()) {
229+
$output->info('Removing potentially over exposing link shares');
230+
$this->repair($output);
231+
$output->info('Removed potentially over exposing link shares');
232+
} else {
233+
$output->info('No need to remove link shares.');
234+
}
235+
}
236+
}

version.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
// between betas, final and RCs. This is _not_ the public version number. Reset minor/patchlevel
3030
// when updating major/minor version number.
3131

32-
$OC_Version = array(15, 0, 7, 0);
32+
$OC_Version = array(15, 0, 7, 1);
3333

3434
// The human readable string
3535
$OC_VersionString = '15.0.7';

0 commit comments

Comments
 (0)