Skip to content

Commit 59b9ef6

Browse files
authored
Merge pull request #59552 from nextcloud/backport/59511/stable33
[stable33] Only mask the permissions for the users home directory for public shares
2 parents 522e03a + bbc1030 commit 59b9ef6

File tree

7 files changed

+244
-5
lines changed

7 files changed

+244
-5
lines changed

apps/dav/appinfo/v1/publicwebdav.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* SPDX-License-Identifier: AGPL-3.0-only
77
*/
88
use OC\Files\Filesystem;
9-
use OC\Files\Storage\Wrapper\PermissionsMask;
9+
use OC\Files\Storage\Wrapper\DirPermissionsMask;
1010
use OC\Files\View;
1111
use OCA\DAV\Connector\LegacyPublicAuth;
1212
use OCA\DAV\Connector\Sabre\ServerFactory;
@@ -98,7 +98,11 @@ function (\Sabre\DAV\Server $server) use (
9898
// FIXME: should not add storage wrappers outside of preSetup, need to find a better way
9999
$previousLog = Filesystem::logWarningWhenAddingStorageWrapper(false);
100100
Filesystem::addStorageWrapper('sharePermissions', function ($mountPoint, $storage) use ($share) {
101-
return new PermissionsMask(['storage' => $storage, 'mask' => $share->getPermissions() | Constants::PERMISSION_SHARE]);
101+
return new DirPermissionsMask([
102+
'storage' => $storage,
103+
'mask' => $share->getPermissions() | Constants::PERMISSION_SHARE,
104+
'path' => 'files'
105+
]);
102106
});
103107
Filesystem::addStorageWrapper('shareOwner', function ($mountPoint, $storage) use ($share) {
104108
return new PublicOwnerWrapper(['storage' => $storage, 'owner' => $share->getShareOwner()]);

apps/files_external/lib/Lib/SessionStorageWrapper.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,14 @@
99

1010
use OC\Files\Storage\Wrapper\PermissionsMask;
1111
use OCP\Constants;
12+
use OCP\Files\Storage\IStorage;
1213

1314
/**
1415
* Wrap Storage in PermissionsMask for session ephemeral use
1516
*/
1617
class SessionStorageWrapper extends PermissionsMask {
1718
/**
18-
* @param array $parameters ['storage' => $storage]
19+
* @param array{storage: IStorage, ...} $parameters
1920
*/
2021
public function __construct(array $parameters) {
2122
// disable sharing permission

lib/composer/composer/autoload_classmap.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1725,6 +1725,7 @@
17251725
'OC\\Files\\Cache\\StorageGlobal' => $baseDir . '/lib/private/Files/Cache/StorageGlobal.php',
17261726
'OC\\Files\\Cache\\Updater' => $baseDir . '/lib/private/Files/Cache/Updater.php',
17271727
'OC\\Files\\Cache\\Watcher' => $baseDir . '/lib/private/Files/Cache/Watcher.php',
1728+
'OC\\Files\\Cache\\Wrapper\\CacheDirPermissionsMask' => $baseDir . '/lib/private/Files/Cache/Wrapper/CacheDirPermissionsMask.php',
17281729
'OC\\Files\\Cache\\Wrapper\\CacheJail' => $baseDir . '/lib/private/Files/Cache/Wrapper/CacheJail.php',
17291730
'OC\\Files\\Cache\\Wrapper\\CachePermissionsMask' => $baseDir . '/lib/private/Files/Cache/Wrapper/CachePermissionsMask.php',
17301731
'OC\\Files\\Cache\\Wrapper\\CacheWrapper' => $baseDir . '/lib/private/Files/Cache/Wrapper/CacheWrapper.php',
@@ -1810,6 +1811,7 @@
18101811
'OC\\Files\\Storage\\StorageFactory' => $baseDir . '/lib/private/Files/Storage/StorageFactory.php',
18111812
'OC\\Files\\Storage\\Temporary' => $baseDir . '/lib/private/Files/Storage/Temporary.php',
18121813
'OC\\Files\\Storage\\Wrapper\\Availability' => $baseDir . '/lib/private/Files/Storage/Wrapper/Availability.php',
1814+
'OC\\Files\\Storage\\Wrapper\\DirPermissionsMask' => $baseDir . '/lib/private/Files/Storage/Wrapper/DirPermissionsMask.php',
18131815
'OC\\Files\\Storage\\Wrapper\\Encoding' => $baseDir . '/lib/private/Files/Storage/Wrapper/Encoding.php',
18141816
'OC\\Files\\Storage\\Wrapper\\EncodingDirectoryWrapper' => $baseDir . '/lib/private/Files/Storage/Wrapper/EncodingDirectoryWrapper.php',
18151817
'OC\\Files\\Storage\\Wrapper\\Encryption' => $baseDir . '/lib/private/Files/Storage/Wrapper/Encryption.php',

lib/composer/composer/autoload_static.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1766,6 +1766,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
17661766
'OC\\Files\\Cache\\StorageGlobal' => __DIR__ . '/../../..' . '/lib/private/Files/Cache/StorageGlobal.php',
17671767
'OC\\Files\\Cache\\Updater' => __DIR__ . '/../../..' . '/lib/private/Files/Cache/Updater.php',
17681768
'OC\\Files\\Cache\\Watcher' => __DIR__ . '/../../..' . '/lib/private/Files/Cache/Watcher.php',
1769+
'OC\\Files\\Cache\\Wrapper\\CacheDirPermissionsMask' => __DIR__ . '/../../..' . '/lib/private/Files/Cache/Wrapper/CacheDirPermissionsMask.php',
17691770
'OC\\Files\\Cache\\Wrapper\\CacheJail' => __DIR__ . '/../../..' . '/lib/private/Files/Cache/Wrapper/CacheJail.php',
17701771
'OC\\Files\\Cache\\Wrapper\\CachePermissionsMask' => __DIR__ . '/../../..' . '/lib/private/Files/Cache/Wrapper/CachePermissionsMask.php',
17711772
'OC\\Files\\Cache\\Wrapper\\CacheWrapper' => __DIR__ . '/../../..' . '/lib/private/Files/Cache/Wrapper/CacheWrapper.php',
@@ -1851,6 +1852,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
18511852
'OC\\Files\\Storage\\StorageFactory' => __DIR__ . '/../../..' . '/lib/private/Files/Storage/StorageFactory.php',
18521853
'OC\\Files\\Storage\\Temporary' => __DIR__ . '/../../..' . '/lib/private/Files/Storage/Temporary.php',
18531854
'OC\\Files\\Storage\\Wrapper\\Availability' => __DIR__ . '/../../..' . '/lib/private/Files/Storage/Wrapper/Availability.php',
1855+
'OC\\Files\\Storage\\Wrapper\\DirPermissionsMask' => __DIR__ . '/../../..' . '/lib/private/Files/Storage/Wrapper/DirPermissionsMask.php',
18541856
'OC\\Files\\Storage\\Wrapper\\Encoding' => __DIR__ . '/../../..' . '/lib/private/Files/Storage/Wrapper/Encoding.php',
18551857
'OC\\Files\\Storage\\Wrapper\\EncodingDirectoryWrapper' => __DIR__ . '/../../..' . '/lib/private/Files/Storage/Wrapper/EncodingDirectoryWrapper.php',
18561858
'OC\\Files\\Storage\\Wrapper\\Encryption' => __DIR__ . '/../../..' . '/lib/private/Files/Storage/Wrapper/Encryption.php',
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
7+
* SPDX-License-Identifier: AGPL-3.0-or-later
8+
*/
9+
10+
namespace OC\Files\Cache\Wrapper;
11+
12+
use Closure;
13+
use OCP\Files\Cache\ICache;
14+
use OCP\Files\Cache\ICacheEntry;
15+
16+
class CacheDirPermissionsMask extends CachePermissionsMask {
17+
/**
18+
* @param Closure(string $path): bool $checkPath
19+
*/
20+
public function __construct(
21+
ICache $cache,
22+
int $mask,
23+
private readonly Closure $checkPath,
24+
) {
25+
parent::__construct($cache, $mask);
26+
}
27+
28+
protected function formatCacheEntry($entry): ICacheEntry|false {
29+
$checkPath = $this->checkPath;
30+
if ($checkPath($entry['path'])) {
31+
return parent::formatCacheEntry($entry);
32+
}
33+
34+
return $entry;
35+
}
36+
}
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
7+
* SPDX-License-Identifier: AGPL-3.0-only AND (AGPL-3.0-or-later OR AGPL-3.0-only)
8+
*/
9+
10+
namespace OC\Files\Storage\Wrapper;
11+
12+
use OC\Files\Cache\Wrapper\CacheDirPermissionsMask;
13+
use OC\Files\Storage\Storage;
14+
use OCP\Files\Cache\ICache;
15+
16+
/**
17+
* While PermissionMask can mask a whole storage this can
18+
* mask a certain directory inside a storage
19+
*/
20+
class DirPermissionsMask extends PermissionsMask {
21+
22+
/**
23+
* @var string the dir that should be masked
24+
*/
25+
private readonly string $path;
26+
27+
/**
28+
* @var int remember length
29+
*/
30+
private readonly int $pathLength;
31+
32+
/**
33+
* @param array{storage: Storage, mask: int, path: string, ...} $parameters
34+
* @psalm-suppress MoreSpecificImplementedParamType
35+
*
36+
* $storage: The storage the permissions mask should be applied on
37+
* $mask: The permission bits that should be kept, a combination of the \OCP\Constant::PERMISSION_ constants
38+
* $path: The path relative to the storage root that should be masked
39+
*/
40+
public function __construct($parameters) {
41+
parent::__construct($parameters);
42+
$this->path = rtrim((string)$parameters['path'], '/');
43+
$this->pathLength = strlen((string)$parameters['path']);
44+
}
45+
46+
protected function checkPath(string $path): bool {
47+
return $path === $this->path || substr($path, 0, $this->pathLength + 1) === $this->path . '/';
48+
}
49+
50+
public function isUpdatable($path): bool {
51+
if ($this->checkPath($path)) {
52+
return parent::isUpdatable($path);
53+
}
54+
55+
return $this->storage->isUpdatable($path);
56+
}
57+
58+
public function isCreatable($path): bool {
59+
if ($this->checkPath($path)) {
60+
return parent::isCreatable($path);
61+
}
62+
63+
return $this->storage->isCreatable($path);
64+
}
65+
66+
public function isDeletable($path): bool {
67+
if ($this->checkPath($path)) {
68+
return parent::isDeletable($path);
69+
}
70+
71+
return $this->storage->isDeletable($path);
72+
}
73+
74+
public function isSharable($path): bool {
75+
if ($this->checkPath($path)) {
76+
return parent::isSharable($path);
77+
}
78+
79+
return $this->storage->isSharable($path);
80+
}
81+
82+
public function getPermissions($path): int {
83+
if ($this->checkPath($path)) {
84+
return parent::getPermissions($path);
85+
}
86+
87+
return $this->storage->getPermissions($path);
88+
}
89+
90+
public function rename($source, $target): bool {
91+
if (!$this->isUpdatable($source)) {
92+
return false;
93+
}
94+
95+
if ($this->file_exists($target)) {
96+
if ($this->isUpdatable($target)) {
97+
return $this->storage->rename($source, $target);
98+
}
99+
} else {
100+
$parent = dirname($target);
101+
if ($parent === '.') {
102+
$parent = '';
103+
}
104+
105+
if ($this->isCreatable($parent)) {
106+
return $this->storage->rename($source, $target);
107+
}
108+
}
109+
110+
return false;
111+
}
112+
113+
public function copy($source, $target): bool {
114+
if (!$this->isReadable($source)) {
115+
return false;
116+
}
117+
118+
if ($this->file_exists($target)) {
119+
if ($this->isUpdatable($target)) {
120+
return $this->storage->copy($source, $target);
121+
}
122+
} else {
123+
$parent = dirname($target);
124+
if ($parent === '.') {
125+
$parent = '';
126+
}
127+
128+
if ($this->isCreatable($parent)) {
129+
return $this->storage->copy($source, $target);
130+
}
131+
}
132+
133+
return false;
134+
}
135+
136+
public function touch($path, $mtime = null): bool {
137+
if ($this->checkPath($path)) {
138+
return parent::touch($path);
139+
}
140+
141+
return $this->storage->touch($path);
142+
}
143+
144+
public function mkdir($path): bool {
145+
// Always allow creating the path of the dir mask.
146+
if ($path !== $this->path && $this->checkPath($path)) {
147+
return parent::mkdir($path);
148+
}
149+
150+
return $this->storage->mkdir($path);
151+
}
152+
153+
public function rmdir($path): bool {
154+
if ($this->checkPath($path)) {
155+
return parent::rmdir($path);
156+
}
157+
158+
return $this->storage->rmdir($path);
159+
}
160+
161+
public function unlink($path): bool {
162+
if ($this->checkPath($path)) {
163+
return parent::unlink($path);
164+
}
165+
166+
return $this->storage->unlink($path);
167+
}
168+
169+
public function file_put_contents($path, $data): int|float|false {
170+
if ($this->checkPath($path)) {
171+
return parent::file_put_contents($path, $data);
172+
}
173+
174+
return $this->storage->file_put_contents($path, $data);
175+
}
176+
177+
public function fopen($path, $mode) {
178+
if ($this->checkPath($path)) {
179+
return parent::fopen($path, $mode);
180+
}
181+
182+
return $this->storage->fopen($path, $mode);
183+
}
184+
185+
public function getCache($path = '', $storage = null): ICache {
186+
if (!$storage) {
187+
$storage = $this;
188+
}
189+
190+
$sourceCache = $this->storage->getCache($path, $storage);
191+
return new CacheDirPermissionsMask($sourceCache, $this->mask, $this->checkPath(...));
192+
}
193+
}

lib/private/Files/Storage/Wrapper/PermissionsMask.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
namespace OC\Files\Storage\Wrapper;
99

1010
use OC\Files\Cache\Wrapper\CachePermissionsMask;
11+
use OC\Files\Storage\Storage;
1112
use OCP\Constants;
1213
use OCP\Files\Storage\IStorage;
1314

@@ -22,10 +23,10 @@ class PermissionsMask extends Wrapper {
2223
/**
2324
* @var int the permissions bits we want to keep
2425
*/
25-
private $mask;
26+
protected readonly int $mask;
2627

2728
/**
28-
* @param array $parameters ['storage' => $storage, 'mask' => $mask]
29+
* @param array{storage: Storage, mask: int, ...} $parameters
2930
*
3031
* $storage: The storage the permissions mask should be applied on
3132
* $mask: The permission bits that should be kept, a combination of the \OCP\Constant::PERMISSION_ constants

0 commit comments

Comments
 (0)