Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/cypress.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ env:
# Usually it's the base branch of the PR, but for pushes it's the branch itself.
# e.g. 'main', 'stable27' or 'feature/my-feature'
# n.b. server will use head_ref, as we want to test the PR branch.
BRANCH: ${{ github.base_ref || github.ref_name }}
BRANCH: stable30


permissions:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/phpunit-mysql-sharding.yml
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ jobs:
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
repository: nextcloud/circles
ref: master
ref: stable30
path: apps/circles

- name: Checkout app
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/phpunit-mysql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ jobs:
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
repository: nextcloud/circles
ref: stable32
ref: stable30
path: apps/circles

- name: Checkout app
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/phpunit-oci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ jobs:
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
repository: nextcloud/circles
ref: stable32
ref: stable30
path: apps/circles

- name: Checkout app
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/phpunit-pgsql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ jobs:
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
repository: nextcloud/circles
ref: stable32
ref: stable30
path: apps/circles

- name: Checkout app
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/phpunit-sqlite-s3.yml
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ jobs:
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
repository: nextcloud/circles
ref: master
ref: stable30
path: apps/circles

- name: Checkout app
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/phpunit-sqlite.yml
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ jobs:
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
repository: nextcloud/circles
ref: stable32
ref: stable30
path: apps/circles

- name: Checkout app
Expand Down
2 changes: 1 addition & 1 deletion appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ As of Hub 10/Nextcloud 31, the admin needs to be a part of the team to be able t
<screenshot>https://raw.githubusercontent.com/nextcloud/groupfolders/master/screenshots/permissions.png</screenshot>

<dependencies>
<nextcloud min-version="32" max-version="32" />
<nextcloud min-version="30" max-version="30" />
</dependencies>

<background-jobs>
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"roave/security-advisories": "dev-latest",
"sabre/dav": "^4.1",
"sabre/xml": "^2.2",
"nextcloud/ocp": "dev-stable32"
"nextcloud/ocp": "dev-stable30"
},
"require": {
"bamarni/composer-bin-plugin": "^1.8"
Expand Down
534 changes: 352 additions & 182 deletions composer.lock

Large diffs are not rendered by default.

74 changes: 37 additions & 37 deletions lib/ACL/ACLStorageWrapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@
use Icewind\Streams\IteratorDirectory;
use OC\Files\Storage\Wrapper\Wrapper;
use OCP\Constants;
use OCP\Files\Cache\ICache;
use OCP\Files\Cache\IScanner;
use OCP\Files\Storage\IConstructableStorage;
use OCP\Files\Storage\IStorage;

class ACLStorageWrapper extends Wrapper implements IConstructableStorage {
/**
* @psalm-suppress DeprecatedInterface
* @psalm-suppress MissingParamType
*/
class ACLStorageWrapper extends Wrapper {
private readonly ACLManager $aclManager;
private readonly bool $inShare;
private int $storageId;
Expand All @@ -41,37 +41,37 @@ private function getACLPermissionsForPath(string $path): int {
return $canRead ? $permissions : 0;
}

private function checkPermissions(string $path, int $permissions): bool {
private function checkPermissions($path, $permissions): bool {
return ($this->getACLPermissionsForPath($path) & $permissions) === $permissions;
}

public function isReadable(string $path): bool {
public function isReadable($path) {
return $this->checkPermissions($path, Constants::PERMISSION_READ) && parent::isReadable($path);
}

public function isUpdatable(string $path): bool {
public function isUpdatable($path) {
return $this->checkPermissions($path, Constants::PERMISSION_UPDATE) && parent::isUpdatable($path);
}

public function isCreatable(string $path): bool {
public function isCreatable($path) {
return $this->checkPermissions($path, Constants::PERMISSION_CREATE) && parent::isCreatable($path);
}

public function isDeletable(string $path): bool {
public function isDeletable($path) {
return $this->checkPermissions($path, Constants::PERMISSION_DELETE)
&& $this->canDeleteTree($path)
&& parent::isDeletable($path);
}

public function isSharable(string $path): bool {
public function isSharable($path) {
return $this->checkPermissions($path, Constants::PERMISSION_SHARE) && parent::isSharable($path);
}

public function getPermissions(string $path): int {
public function getPermissions($path): int {
return $this->storage->getPermissions($path) & $this->getACLPermissionsForPath($path);
}

public function rename(string $source, string $target): bool {
public function rename($source, $target): bool {
if (str_starts_with($source, $target)) {
$part = substr($source, strlen($target));
//This is a rename of the transfer file to the original file
Expand All @@ -98,7 +98,7 @@ public function rename(string $source, string $target): bool {
&& parent::rename($source, $target);
}

public function opendir(string $path) {
public function opendir($path) {
if (!$this->checkPermissions($path, Constants::PERMISSION_READ)) {
return false;
}
Expand All @@ -120,29 +120,29 @@ public function opendir(string $path) {
return IteratorDirectory::wrap($items);
}

public function copy(string $source, string $target): bool {
public function copy($source, $target): bool {
$permissions = $this->file_exists($target) ? Constants::PERMISSION_UPDATE : Constants::PERMISSION_CREATE;
return $this->checkPermissions($target, $permissions)
&& $this->checkPermissions($source, Constants::PERMISSION_READ)
&& parent::copy($source, $target);
}

public function touch(string $path, ?int $mtime = null): bool {
public function touch($path, $mtime = null): bool {
$permissions = $this->file_exists($path) ? Constants::PERMISSION_UPDATE : Constants::PERMISSION_CREATE;
return $this->checkPermissions($path, $permissions) && parent::touch($path, $mtime);
}

public function mkdir(string $path): bool {
public function mkdir($path): bool {
return $this->checkPermissions($path, Constants::PERMISSION_CREATE) && parent::mkdir($path);
}

public function rmdir(string $path): bool {
public function rmdir($path): bool {
return $this->checkPermissions($path, Constants::PERMISSION_DELETE)
&& $this->canDeleteTree($path)
&& parent::rmdir($path);
}

public function unlink(string $path): bool {
public function unlink($path): bool {
return $this->checkPermissions($path, Constants::PERMISSION_DELETE)
&& $this->canDeleteTree($path)
&& parent::unlink($path);
Expand All @@ -156,12 +156,12 @@ private function canDeleteTree(string $path): int {
return $this->aclManager->getPermissionsForTree($this->storageId, $path) & Constants::PERMISSION_DELETE;
}

public function file_put_contents(string $path, mixed $data): int|float|false {
public function file_put_contents($path, $data) {
$permissions = $this->file_exists($path) ? Constants::PERMISSION_UPDATE : Constants::PERMISSION_CREATE;
return $this->checkPermissions($path, $permissions) ? parent::file_put_contents($path, $data) : false;
}

public function fopen(string $path, string $mode) {
public function fopen($path, $mode) {
if ($mode === 'r' or $mode === 'rb') {
$permissions = Constants::PERMISSION_READ;
} else {
Expand All @@ -179,7 +179,7 @@ public function writeStream(string $path, $stream, ?int $size = null): int {
/**
* @inheritDoc
*/
public function getCache(string $path = '', ?IStorage $storage = null): ICache {
public function getCache($path = '', $storage = null) {
if (!$storage) {
$storage = $this;
}
Expand All @@ -189,7 +189,7 @@ public function getCache(string $path = '', ?IStorage $storage = null): ICache {
return new ACLCacheWrapper($sourceCache, $this->aclManager, $this->inShare);
}

public function getMetaData(string $path): ?array {
public function getMetaData($path): ?array {
$data = parent::getMetaData($path);

if ($data && isset($data['permissions'])) {
Expand All @@ -203,102 +203,102 @@ public function getMetaData(string $path): ?array {
/**
* @inheritDoc
*/
public function getScanner(string $path = '', ?IStorage $storage = null): IScanner {
public function getScanner($path = '', $storage = null) {
if (!$storage) {
$storage = $this->storage;
}

return parent::getScanner($path, $storage);
}

public function is_dir(string $path): bool {
public function is_dir($path) {
return $this->checkPermissions($path, Constants::PERMISSION_READ)
&& parent::is_dir($path);
}

public function is_file(string $path): bool {
public function is_file($path) {
return $this->checkPermissions($path, Constants::PERMISSION_READ)
&& parent::is_file($path);
}

public function stat(string $path): array|false {
public function stat($path) {
if (!$this->checkPermissions($path, Constants::PERMISSION_READ)) {
return false;
}

return parent::stat($path);
}

public function filetype(string $path): string|false {
public function filetype($path) {
if (!$this->checkPermissions($path, Constants::PERMISSION_READ)) {
return false;
}

return parent::filetype($path);
}

public function filesize(string $path): false|int|float {
public function filesize($path): float|false|int {
if (!$this->checkPermissions($path, Constants::PERMISSION_READ)) {
return false;
}

return parent::filesize($path);
}

public function file_exists(string $path): bool {
public function file_exists($path): bool {
return $this->checkPermissions($path, Constants::PERMISSION_READ)
&& parent::file_exists($path);
}

public function filemtime(string $path): int|false {
public function filemtime($path): bool|int {
if (!$this->checkPermissions($path, Constants::PERMISSION_READ)) {
return false;
}

return parent::filemtime($path);
}

public function file_get_contents(string $path): string|false {
public function file_get_contents($path): bool|string {
if (!$this->checkPermissions($path, Constants::PERMISSION_READ)) {
return false;
}

return parent::file_get_contents($path);
}

public function getMimeType(string $path): string|false {
public function getMimeType($path): bool|string {
if (!$this->checkPermissions($path, Constants::PERMISSION_READ)) {
return false;
}

return parent::getMimeType($path);
}

public function hash(string $type, string $path, bool $raw = false): string|false {
public function hash($type, $path, $raw = false): bool|string {
if (!$this->checkPermissions($path, Constants::PERMISSION_READ)) {
return false;
}

return parent::hash($type, $path, $raw);
}

public function getETag(string $path): string|false {
public function getETag($path): bool|string {
if (!$this->checkPermissions($path, Constants::PERMISSION_READ)) {
return false;
}

return parent::getETag($path);
}

public function getDirectDownload(string $path): array|false {
public function getDirectDownload($path): bool|array {
if (!$this->checkPermissions($path, Constants::PERMISSION_READ)) {
return false;
}

return parent::getDirectDownload($path);
}

public function getDirectoryContent(string $directory): \Traversable {
public function getDirectoryContent($directory): \Traversable {
$content = $this->getWrapperStorage()->getDirectoryContent($directory);
foreach ($content as $data) {
$data['scan_permissions'] ??= $data['permissions'];
Expand Down
16 changes: 16 additions & 0 deletions lib/BackCompat/InvalidObjectStoreConfigurationException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Robin Appelman <[email protected]>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCA\GroupFolders\BackCompat;

/**
* Copy of \OC\Files\ObjectStore\InvalidObjectStoreConfigurationException from NC 32
*/
class InvalidObjectStoreConfigurationException extends \Exception {

}
35 changes: 35 additions & 0 deletions lib/BackCompat/Mapper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
* SPDX-License-Identifier: AGPL-3.0-only
*/
namespace OCA\GroupFolders\BackCompat;

use OCP\IUser;

/**
* Copy of \OC\Files\ObjectStore\Mapper from NC 32
*
* Map a user to a bucket.
*/
class Mapper {
public function __construct(
private readonly IUser $user,
private readonly array $config,
) {
}

public function getBucket(int $numBuckets = 64): string {
// Get the bucket config and shift if provided.
// Allow us to prevent writing in old filled buckets
$minBucket = isset($this->config['arguments']['min_bucket'])
? (int)$this->config['arguments']['min_bucket']
: 0;

$hash = md5($this->user->getUID());
$num = hexdec(substr($hash, 0, 4));
return (string)(($num % ($numBuckets - $minBucket)) + $minBucket);
}
}
Loading
Loading