diff --git a/apps/sharebymail/lib/ShareByMailProvider.php b/apps/sharebymail/lib/ShareByMailProvider.php
index b96a61288517f..d306177c85557 100644
--- a/apps/sharebymail/lib/ShareByMailProvider.php
+++ b/apps/sharebymail/lib/ShareByMailProvider.php
@@ -7,6 +7,7 @@
namespace OCA\ShareByMail;
+use DateTime;
use OC\Share20\DefaultShareProvider;
use OC\Share20\Exception\InvalidShare;
use OC\Share20\Share;
@@ -98,14 +99,10 @@ public function create(IShare $share): IShare {
// if the admin enforces a password for all mail shares we create a
// random password and send it to the recipient
- $password = $share->getPassword() ?: '';
- $passwordEnforced = $this->shareManager->shareApiLinkEnforcePassword();
- if ($passwordEnforced && empty($password)) {
+ $password = $share->getPassword();
+ if ($password === null && $this->shareManager->shareApiLinkEnforcePassword()) {
$password = $this->autoGeneratePassword($share);
- }
-
- if (!empty($password)) {
- $share->setPassword($this->hasher->hash($password));
+ $share->setPasswordHash($this->hasher->hash($password));
}
$shareId = $this->createMailShare($share);
@@ -116,9 +113,7 @@ public function create(IShare $share): IShare {
// Temporary set the clear password again to send it by mail
// This need to be done after the share was created in the database
// as the password is hashed in between.
- if (!empty($password)) {
- $data['password'] = $password;
- }
+ $data['password'] = $password;
return $this->createShareObject($data);
}
@@ -227,6 +222,11 @@ protected function createMailShare(IShare $share): string {
if ($share->getToken() === '') {
$share->setToken($this->generateToken());
}
+
+ if ($share->getPassword() !== null && !$share->isPasswordHashed()) {
+ throw new RuntimeException('The password must be hashed already.');
+ }
+
return $this->addShareToDB(
$share->getNodeId(),
$share->getNodeType(),
@@ -241,6 +241,7 @@ protected function createMailShare(IShare $share): string {
$share->getHideDownload(),
$share->getLabel(),
$share->getExpirationDate(),
+ $share->getShareTime(),
$share->getNote(),
$share->getAttributes(),
$share->getMailSend(),
@@ -699,6 +700,7 @@ protected function addShareToDB(
?bool $hideDownload,
?string $label,
?\DateTimeInterface $expirationTime,
+ ?DateTime $shareTime,
?string $note = '',
?IAttributes $attributes = null,
?bool $mailSend = true,
@@ -717,7 +719,7 @@ protected function addShareToDB(
->setValue('password', $qb->createNamedParameter($password))
->setValue('password_expiration_time', $qb->createNamedParameter($passwordExpirationTime, IQueryBuilder::PARAM_DATETIME_MUTABLE))
->setValue('password_by_talk', $qb->createNamedParameter($sendPasswordByTalk, IQueryBuilder::PARAM_BOOL))
- ->setValue('stime', $qb->createNamedParameter(time()))
+ ->setValue('stime', $qb->createNamedParameter($shareTime?->getTimestamp() ?? time()))
->setValue('hide_download', $qb->createNamedParameter((int)$hideDownload, IQueryBuilder::PARAM_INT))
->setValue('label', $qb->createNamedParameter($label))
->setValue('note', $qb->createNamedParameter($note))
@@ -767,6 +769,11 @@ public function update(IShare $share, ?string $plainTextPassword = null): IShare
$expiration = \DateTime::createFromInterface($expiration);
$expiration->setTimezone(new \DateTimeZone(date_default_timezone_get()));
}
+
+ if ($share->getPassword() !== null && !$share->isPasswordHashed()) {
+ throw new RuntimeException('The password must be hashed already.');
+ }
+
$qb->update('share')
->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
->set('item_source', $qb->createNamedParameter($share->getNodeId()))
@@ -1052,7 +1059,9 @@ protected function createShareObject(array $data): IShare {
$shareTime->setTimestamp((int)$data['stime']);
$share->setShareTime($shareTime);
$share->setSharedWith($data['share_with'] ?? '');
- $share->setPassword($data['password']);
+ if (($password = $data['password']) !== null) {
+ $share->setPasswordHash($password);
+ }
$passwordExpirationTime = \DateTime::createFromFormat('Y-m-d H:i:s', $data['password_expiration_time'] ?? '');
$share->setPasswordExpirationTime($passwordExpirationTime !== false ? $passwordExpirationTime : null);
$share->setLabel($data['label'] ?? '');
diff --git a/apps/sharing/appinfo/info.xml b/apps/sharing/appinfo/info.xml
index cd653bf56fa09..b46cd273f56f8 100644
--- a/apps/sharing/appinfo/info.xml
+++ b/apps/sharing/appinfo/info.xml
@@ -7,8 +7,8 @@
xsi:noNamespaceSchemaLocation="https://apps.nextcloud.com/schema/apps/info.xsd">
sharing
Sharing
- TODO
- TODO
+ This app provides APIs and occ commands to manage shares.
+ This app provides APIs and occ commands to manage shares.
2.0.0-dev.3
AGPL-3.0-or-later
Kate Döen
diff --git a/apps/sharing/lib/Command/AddShareRecipient.php b/apps/sharing/lib/Command/AddShareRecipient.php
index 5742b0816087b..e45a347943a1d 100644
--- a/apps/sharing/lib/Command/AddShareRecipient.php
+++ b/apps/sharing/lib/Command/AddShareRecipient.php
@@ -16,7 +16,6 @@
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
-// TODO: Initiator missing.
final class AddShareRecipient extends SharingBase {
#[\Override]
public function configure(): void {
diff --git a/apps/sharing/openapi.json b/apps/sharing/openapi.json
index 1073b158e7ac6..0a70734e06617 100644
--- a/apps/sharing/openapi.json
+++ b/apps/sharing/openapi.json
@@ -3,7 +3,7 @@
"info": {
"title": "sharing",
"version": "0.0.1",
- "description": "TODO",
+ "description": "This app provides APIs and occ commands to manage shares.",
"license": {
"name": "AGPL-3.0-or-later"
}
diff --git a/build/psalm-baseline.xml b/build/psalm-baseline.xml
index b6da12c8b93f5..ce4e99120819d 100644
--- a/build/psalm-baseline.xml
+++ b/build/psalm-baseline.xml
@@ -20,7 +20,6 @@
-
@@ -201,32 +200,17 @@
- CLASS]]>
- CLASS]]>
CLASS]]>
DTEND]]>
DTEND]]>
- DTEND]]>
- DTSTART]]>
DTSTART]]>
- DTSTART]]>
- DUE]]>
DUE]]>
DUE]]>
DURATION]]>
DURATION]]>
- DURATION]]>
- RDATE]]>
- RDATE]]>
RDATE]]>
RDATE]]>
- RDATE]]>
- RDATE]]>
- RRULE]]>
RRULE]]>
- RRULE]]>
- UID]]>
- UID]]>
UID]]>
@@ -2423,9 +2407,6 @@
-
-
-
diff --git a/core/Sharing/Recipient/GroupShareRecipientType.php b/core/Sharing/Recipient/GroupShareRecipientType.php
index d2539c72399b2..01490505dcce4 100644
--- a/core/Sharing/Recipient/GroupShareRecipientType.php
+++ b/core/Sharing/Recipient/GroupShareRecipientType.php
@@ -20,7 +20,7 @@
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\EventDispatcher\IEventListener;
-use OCP\Group\Events\GroupDeletedEvent;
+use OCP\Group\Events\BeforeGroupDeletedEvent;
use OCP\IDBConnection;
use OCP\IGroupManager;
use OCP\Interaction\InteractionReceiver;
@@ -30,7 +30,7 @@
use OCP\Share\IShare;
/**
- * @template-implements IEventListener
+ * @template-implements IEventListener
*/
final class GroupShareRecipientType extends AShareRecipientTypeSearchCollaborator implements IEventListener {
public function __construct(
@@ -39,7 +39,7 @@ public function __construct(
private readonly IGroupManager $groupManager,
private readonly ISharingManager $manager,
) {
- $eventDispatcher->addServiceListener(GroupDeletedEvent::class, self::class);
+ $eventDispatcher->addServiceListener(BeforeGroupDeletedEvent::class, self::class);
}
#[\Override]
diff --git a/core/Sharing/Recipient/UserShareRecipientType.php b/core/Sharing/Recipient/UserShareRecipientType.php
index 3c69a7ab804b1..61927b0b1f8c0 100644
--- a/core/Sharing/Recipient/UserShareRecipientType.php
+++ b/core/Sharing/Recipient/UserShareRecipientType.php
@@ -26,10 +26,10 @@
use OCP\IUserManager;
use OCP\L10N\IFactory;
use OCP\Share\IShare;
-use OCP\User\Events\UserDeletedEvent;
+use OCP\User\Events\BeforeUserDeletedEvent;
/**
- * @template-implements IEventListener
+ * @template-implements IEventListener
*/
final class UserShareRecipientType extends AShareRecipientTypeSearchCollaborator implements IEventListener {
@@ -39,7 +39,7 @@ public function __construct(
private readonly IUserManager $userManager,
private readonly ISharingManager $manager,
) {
- $eventDispatcher->addServiceListener(UserDeletedEvent::class, self::class);
+ $eventDispatcher->addServiceListener(BeforeUserDeletedEvent::class, self::class);
}
#[\Override]
diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php
index 255465c0ffb2e..01c8fc36d96b3 100644
--- a/lib/composer/composer/autoload_classmap.php
+++ b/lib/composer/composer/autoload_classmap.php
@@ -2366,7 +2366,6 @@
'OC\\Share20\\UserRemovedListener' => $baseDir . '/lib/private/Share20/UserRemovedListener.php',
'OC\\Share\\Constants' => $baseDir . '/lib/private/Share/Constants.php',
'OC\\Sharing\\ClassMapper' => $baseDir . '/lib/private/Sharing/ClassMapper.php',
- 'OC\\Sharing\\ISharingLegacyBackend' => $baseDir . '/lib/private/Sharing/ISharingLegacyBackend.php',
'OC\\Sharing\\SharingBackend' => $baseDir . '/lib/private/Sharing/SharingBackend.php',
'OC\\Sharing\\SharingManager' => $baseDir . '/lib/private/Sharing/SharingManager.php',
'OC\\Sharing\\SharingRegistry' => $baseDir . '/lib/private/Sharing/SharingRegistry.php',
diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php
index 046d2cd20b693..0687a45a56211 100644
--- a/lib/composer/composer/autoload_static.php
+++ b/lib/composer/composer/autoload_static.php
@@ -2407,7 +2407,6 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OC\\Share20\\UserRemovedListener' => __DIR__ . '/../../..' . '/lib/private/Share20/UserRemovedListener.php',
'OC\\Share\\Constants' => __DIR__ . '/../../..' . '/lib/private/Share/Constants.php',
'OC\\Sharing\\ClassMapper' => __DIR__ . '/../../..' . '/lib/private/Sharing/ClassMapper.php',
- 'OC\\Sharing\\ISharingLegacyBackend' => __DIR__ . '/../../..' . '/lib/private/Sharing/ISharingLegacyBackend.php',
'OC\\Sharing\\SharingBackend' => __DIR__ . '/../../..' . '/lib/private/Sharing/SharingBackend.php',
'OC\\Sharing\\SharingManager' => __DIR__ . '/../../..' . '/lib/private/Sharing/SharingManager.php',
'OC\\Sharing\\SharingRegistry' => __DIR__ . '/../../..' . '/lib/private/Sharing/SharingRegistry.php',
diff --git a/lib/private/Group/Group.php b/lib/private/Group/Group.php
index 7499db4046f07..a27c4ef89c536 100644
--- a/lib/private/Group/Group.php
+++ b/lib/private/Group/Group.php
@@ -98,11 +98,6 @@ public function setDisplayName(string $displayName): bool {
return false;
}
- /**
- * get all users in the group
- *
- * @return array
- */
#[\Override]
public function getUsers(): array {
if ($this->usersLoaded) {
diff --git a/lib/private/Share20/DefaultShareProvider.php b/lib/private/Share20/DefaultShareProvider.php
index c7a47188f587b..0fff3fd0644bc 100644
--- a/lib/private/Share20/DefaultShareProvider.php
+++ b/lib/private/Share20/DefaultShareProvider.php
@@ -46,6 +46,7 @@
use OCP\Share\IShareProviderWithNotification;
use OCP\Util;
use Psr\Log\LoggerInterface;
+use RuntimeException;
use function str_starts_with;
use function strlen;
@@ -110,7 +111,10 @@ public function create(IShare $share) {
if ($share->getShareType() === IShare::TYPE_USER) {
//Set the UID of the user we share with
$qb->setValue('share_with', $qb->createNamedParameter($share->getSharedWith()));
- $qb->setValue('accepted', $qb->createNamedParameter(IShare::STATUS_PENDING));
+ if ($share->getStatus() === null) {
+ $share->setStatus(IShare::STATUS_PENDING);
+ }
+ $qb->setValue('accepted', $qb->createNamedParameter($share->getStatus()));
//If an expiration date is set store it
if ($expirationDate !== null) {
@@ -133,8 +137,11 @@ public function create(IShare $share) {
$qb->setValue('token', $qb->createNamedParameter($share->getToken()));
//If a password is set store it
- if ($share->getPassword() !== null) {
- $qb->setValue('password', $qb->createNamedParameter($share->getPassword()));
+ if (($password = $share->getPassword()) !== null) {
+ if (!$share->isPasswordHashed()) {
+ throw new RuntimeException('The password must be hashed already.');
+ }
+ $qb->setValue('password', $qb->createNamedParameter($password));
}
$qb->setValue('password_by_talk', $qb->createNamedParameter($share->getSendPasswordByTalk(), IQueryBuilder::PARAM_BOOL));
@@ -185,9 +192,9 @@ public function create(IShare $share) {
$qb->setValue('note', $qb->createNamedParameter($share->getNote()));
}
- // Set the time this share was created
- $shareTime = $this->timeFactory->now();
+ $shareTime = $share->getShareTime() ?? \DateTime::createFromImmutable($this->timeFactory->now());
$qb->setValue('stime', $qb->createNamedParameter($shareTime->getTimestamp()));
+ $share->setShareTime($shareTime);
// insert the data and fetch the id of the share
$qb->executeStatement();
@@ -197,8 +204,6 @@ public function create(IShare $share) {
$share->setId((string)$id);
$share->setProviderId($this->identifier());
- $share->setShareTime(\DateTime::createFromImmutable($shareTime));
-
$mailSendValue = $share->getMailSend();
$share->setMailSend(($mailSendValue === null) ? true : $mailSendValue);
@@ -287,10 +292,15 @@ public function update(IShare $share) {
->set('attributes', $qb->createNamedParameter($shareAttributes))
->executeStatement();
} elseif ($share->getShareType() === IShare::TYPE_LINK) {
+ $password = $share->getPassword();
+ if ($password !== null && !$share->isPasswordHashed()) {
+ throw new RuntimeException('The password must be hashed already.');
+ }
+
$qb = $this->dbConn->getQueryBuilder();
$qb->update('share')
->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
- ->set('password', $qb->createNamedParameter($share->getPassword()))
+ ->set('password', $qb->createNamedParameter($password))
->set('password_by_talk', $qb->createNamedParameter($share->getSendPasswordByTalk(), IQueryBuilder::PARAM_BOOL))
->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
@@ -1129,7 +1139,9 @@ private function createShare($data): IShare {
$share->setSharedWith($data['share_with']);
$share->setSharedWithDisplayNameCallback(fn (IShare $share) => $this->groupManager->getDisplayName($share->getSharedWith()));
} elseif ($share->getShareType() === IShare::TYPE_LINK) {
- $share->setPassword($data['password']);
+ if (($password = $data['password']) !== null) {
+ $share->setPasswordHash($password);
+ }
$share->setSendPasswordByTalk((bool)$data['password_by_talk']);
$share->setToken($data['token']);
}
diff --git a/lib/private/Share20/Manager.php b/lib/private/Share20/Manager.php
index 7db6d8e4045da..5a0840693d736 100644
--- a/lib/private/Share20/Manager.php
+++ b/lib/private/Share20/Manager.php
@@ -567,9 +567,8 @@ public function createShare(IShare $share): IShare {
$this->verifyPassword($share->getPassword());
// If a password is set. Hash it!
- if ($share->getShareType() === IShare::TYPE_LINK
- && $share->getPassword() !== null) {
- $share->setPassword($this->hasher->hash($share->getPassword()));
+ if (($share->getShareType() === IShare::TYPE_LINK || $share->getShareType() === IShare::TYPE_EMAIL) && $share->getPassword() !== null && !$share->isPasswordHashed()) {
+ $share->setPasswordHash($this->hasher->hash($share->getPassword()));
}
}
@@ -588,9 +587,11 @@ public function createShare(IShare $share): IShare {
}
}
- $target = $shareFolder . '/' . $share->getNode()->getName();
- $target = Filesystem::normalizePath($target);
- $share->setTarget($target);
+ if ($share->getTarget() === null) {
+ $target = $shareFolder . '/' . $share->getNode()->getName();
+ $target = Filesystem::normalizePath($target);
+ $share->setTarget($target);
+ }
// Pre share event
$event = new BeforeShareCreatedEvent($share);
@@ -832,7 +833,7 @@ private function updateSharePasswordIfNeeded(IShare $share, IShare $originalShar
// If a password is set. Hash it!
if (!empty($share->getPassword())) {
- $share->setPassword($this->hasher->hash($share->getPassword()));
+ $share->setPasswordHash($this->hasher->hash($share->getPassword()));
if ($share->getShareType() === IShare::TYPE_EMAIL) {
// Shares shared by email have temporary passwords
$this->setSharePasswordExpirationTime($share);
@@ -850,7 +851,12 @@ private function updateSharePasswordIfNeeded(IShare $share, IShare $originalShar
} else {
// Reset the password to the original one, as it is either the same
// as the "new" password or a hashed version of it.
- $share->setPassword($originalShare->getPassword());
+ $password = $originalShare->getPassword();
+ if ($password !== null && $originalShare->isPasswordHashed()) {
+ $share->setPasswordHash($password);
+ } else {
+ $share->setPassword($password);
+ }
}
return false;
diff --git a/lib/private/Share20/Share.php b/lib/private/Share20/Share.php
index 3ea440a885a68..35c70a1464bfc 100644
--- a/lib/private/Share20/Share.php
+++ b/lib/private/Share20/Share.php
@@ -16,6 +16,7 @@
use OCP\Files\Node;
use OCP\Files\NotFoundException;
use OCP\IUserManager;
+use OCP\Security\IHasher;
use OCP\Server;
use OCP\Share\Exceptions\IllegalIDChangeException;
use OCP\Share\IAttributes;
@@ -57,6 +58,7 @@ class Share implements IShare {
private $expireDate;
/** @var string */
private $password;
+ private bool $isPasswordHashed = false;
private ?\DateTimeInterface $passwordExpirationTime = null;
/** @var bool */
private $sendPasswordByTalk = false;
@@ -501,6 +503,18 @@ public function getShareOwner() {
#[\Override]
public function setPassword($password) {
$this->password = $password;
+ $this->isPasswordHashed = false;
+ return $this;
+ }
+
+ #[\Override]
+ public function setPasswordHash(string $passwordHash): IShare {
+ if (!Server::get(IHasher::class)->validate($passwordHash)) {
+ throw new \InvalidArgumentException();
+ }
+
+ $this->password = $passwordHash;
+ $this->isPasswordHashed = true;
return $this;
}
@@ -512,6 +526,11 @@ public function getPassword() {
return $this->password;
}
+ #[\Override]
+ public function isPasswordHashed(): bool {
+ return $this->isPasswordHashed;
+ }
+
/**
* @inheritdoc
*/
diff --git a/lib/private/Sharing/ISharingLegacyBackend.php b/lib/private/Sharing/ISharingLegacyBackend.php
deleted file mode 100644
index 5aa9f45271e75..0000000000000
--- a/lib/private/Sharing/ISharingLegacyBackend.php
+++ /dev/null
@@ -1,59 +0,0 @@
->
- */
- public function getCompatibleSourceTypes(): array;
-
- /**
- * @return list>
- */
- public function getCompatibleRecipientTypes(): array;
-
- /**
- * Update a share.
- */
- public function updateShare(Share $share): void;
-
- /**
- * Delete a share.
- *
- * @throws ShareNotFoundException
- */
- public function deleteShare(string $id): void;
-
- /**
- * Get a share.
- *
- * @throws ShareNotFoundException
- */
- public function getShare(ShareAccessContext $accessContext, string $id): Share;
-
- /**
- * Get multiple shares.
- *
- * @param ?class-string $filterSourceTypeClass
- * @param ?positive-int $limit
- * @return list
- */
- public function getShares(ShareAccessContext $accessContext, ?string $filterSourceTypeClass, ?string $filterSourceTypeValue, ?string $lastShareID, ?int $limit): array;
-}
diff --git a/lib/private/Sharing/SharingManager.php b/lib/private/Sharing/SharingManager.php
index 328b919ec110f..5c44fe95f8204 100644
--- a/lib/private/Sharing/SharingManager.php
+++ b/lib/private/Sharing/SharingManager.php
@@ -50,8 +50,6 @@
use Random\Randomizer;
use RuntimeException;
-// TODO: Add accept/reject
-// TODO: Add permission masking (reshares)
// TODO: Test sharing to federated users, groups and circles
// TODO: Implement share transfers
// TODO: Cache share owner
@@ -188,14 +186,7 @@ public function onOwnerDeleted(ShareAccessContext $accessContext, ShareUser $own
// No need to update the last updated timestamp, because the share will be deleted anyway.
- $ids = $this->backend->onOwnerDeleted($owner);
-
- $legacyBackend = $this->registry->getLegacyBackend();
- if ($legacyBackend instanceof ISharingLegacyBackend) {
- foreach ($ids as $id) {
- $legacyBackend->deleteShare($id);
- }
- }
+ $this->backend->onOwnerDeleted($owner);
}
#[\Override]
@@ -759,11 +750,6 @@ public function deleteShare(ShareAccessContext $accessContext, Share $share): vo
$this->validateShareEditPermissions($accessContext, $share);
$this->backend->deleteShare($share->id);
-
- $legacyBackend = $this->registry->getLegacyBackend();
- if ($legacyBackend instanceof ISharingLegacyBackend) {
- $legacyBackend->deleteShare($share->id);
- }
}
#[\Override]
@@ -988,27 +974,6 @@ private function processShareUpdates(array $shares): array {
);
}
}
-
- $legacyBackend = $this->registry->getLegacyBackend();
- if ($legacyBackend instanceof ISharingLegacyBackend) {
- $compatibleSourceTypes = array_fill_keys($legacyBackend->getCompatibleSourceTypes(), true);
- foreach ($share->sources as $source) {
- if (!isset($compatibleSourceTypes[$source->class])) {
- throw new RuntimeException('The legacy backend ' . $legacyBackend::class . ' does not support this source type: ' . $source->class);
- }
- }
-
- $compatibleRecipientTypes = array_fill_keys($legacyBackend->getCompatibleRecipientTypes(), true);
- foreach ($share->recipients as $recipient) {
- if (!isset($compatibleRecipientTypes[$recipient->class])) {
- throw new RuntimeException(
- 'The legacy backend ' . $legacyBackend::class . ' does not support this recipient type: ' . $recipient->class
- );
- }
- }
-
- $legacyBackend->updateShare($share);
- }
}
return $shares;
diff --git a/lib/private/Sharing/SharingRegistry.php b/lib/private/Sharing/SharingRegistry.php
index d22e447f2c69a..d2c15b5ce9150 100644
--- a/lib/private/Sharing/SharingRegistry.php
+++ b/lib/private/Sharing/SharingRegistry.php
@@ -17,10 +17,7 @@
use NCU\Sharing\Source\IShareSourceType;
use RuntimeException;
-// TODO: Maybe add validate method to run all checks before using the manager
final class SharingRegistry implements ISharingRegistry {
- private ?ISharingLegacyBackend $legacyBackend = null;
-
/** @var array, IShareSourceType> */
private array $sourceTypes = [];
@@ -59,7 +56,6 @@ final class SharingRegistry implements ISharingRegistry {
#[\Override]
public function clear(): void {
- $this->legacyBackend = null;
$this->sourceTypes = [];
$this->recipientTypes = [];
$this->propertyTypes = [];
@@ -74,20 +70,6 @@ public function clear(): void {
$this->permissionPresetCompatiblePermissionTypes = [];
}
- #[\Override]
- public function registerLegacyBackend(ISharingLegacyBackend $legacyBackend): void {
- if ($this->legacyBackend instanceof ISharingLegacyBackend) {
- throw new RuntimeException('A sharing legacy backend is already registered');
- }
-
- $this->legacyBackend = $legacyBackend;
- }
-
- #[\Override]
- public function getLegacyBackend(): ?ISharingLegacyBackend {
- return $this->legacyBackend;
- }
-
#[\Override]
public function registerSourceType(IShareSourceType $sourceType): void {
$class = $sourceType::class;
diff --git a/lib/private/Snowflake/SnowflakeGenerator.php b/lib/private/Snowflake/SnowflakeGenerator.php
index 6e07bd72d6f61..a73253d246c7f 100644
--- a/lib/private/Snowflake/SnowflakeGenerator.php
+++ b/lib/private/Snowflake/SnowflakeGenerator.php
@@ -9,6 +9,7 @@
namespace OC\Snowflake;
+use DateTimeImmutable;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\IServerInfo;
use OCP\Snowflake\ISnowflakeGenerator;
@@ -31,9 +32,12 @@ public function __construct(
}
#[Override]
- public function nextId(): string {
+ public function nextId(?DateTimeImmutable $timestamp = null): string {
+ $timestamp ??= $this->timeFactory->now();
+
// Relative time
- [$seconds, $milliseconds] = $this->getCurrentTime();
+ $seconds = $timestamp->getTimestamp() - self::TS_OFFSET;
+ $milliseconds = (int)$timestamp->format('v');
$serverId = $this->serverInfo->getServerId();
$isCli = (int)$this->isCli(); // 1 bit
@@ -124,14 +128,6 @@ private function convertToDecimal(array $bytes): string {
return $digits;
}
- private function getCurrentTime(): array {
- $time = $this->timeFactory->now();
- return [
- $time->getTimestamp() - self::TS_OFFSET,
- (int)$time->format('v'),
- ];
- }
-
private function isCli(): bool {
return PHP_SAPI === 'cli';
}
diff --git a/lib/public/IGroup.php b/lib/public/IGroup.php
index 448b9f4c8f8ce..33da553d1ee38 100644
--- a/lib/public/IGroup.php
+++ b/lib/public/IGroup.php
@@ -41,7 +41,7 @@ public function setDisplayName(string $displayName): bool;
/**
* get all users in the group
*
- * @return IUser[]
+ * @return array
* @since 8.0.0
*/
public function getUsers(): array;
diff --git a/lib/public/Share/IShare.php b/lib/public/Share/IShare.php
index 5e5647f52aa10..6c92f30ef0ccd 100644
--- a/lib/public/Share/IShare.php
+++ b/lib/public/Share/IShare.php
@@ -453,6 +453,14 @@ public function getShareOwner();
*/
public function setPassword($password);
+ /**
+ * Sets the password for the shared, but in it's already hashed form.
+ * Use {@see isPasswordHashed} to check if the return value of {@see getPassword} is already hashed.
+ *
+ * @since 35.0.0
+ */
+ public function setPasswordHash(string $passwordHash): IShare;
+
/**
* Get the password of this share.
* If this share is obtained via a shareprovider the password is
@@ -463,6 +471,13 @@ public function setPassword($password);
*/
public function getPassword();
+ /**
+ * Returns whether the return value of {@see getPassword} is already hashed.
+ *
+ * @since 35.0.0
+ */
+ public function isPasswordHashed(): bool;
+
/**
* Returns whether the share is password protected by any means (e.g. password or OTP)
* @return bool
diff --git a/lib/public/Snowflake/ISnowflakeGenerator.php b/lib/public/Snowflake/ISnowflakeGenerator.php
index ee9a02bc486fa..475d376788313 100644
--- a/lib/public/Snowflake/ISnowflakeGenerator.php
+++ b/lib/public/Snowflake/ISnowflakeGenerator.php
@@ -9,6 +9,7 @@
namespace OCP\Snowflake;
+use DateTimeImmutable;
use OCP\AppFramework\Attribute\Consumable;
/**
@@ -39,11 +40,12 @@ interface ISnowflakeGenerator {
*
* Each call to this method is guaranteed to return a different ID.
*
+ * @param ?DateTimeImmutable $timestamp Generate the Snowflake ID for a specific time. This should only be used in very special cases.
* @return non-empty-string
*
* @since 33.0
*/
- public function nextId(): string;
+ public function nextId(?DateTimeImmutable $timestamp = null): string;
/**
* Return the smallest possible Snowflake ID for a given timestamp
diff --git a/lib/unstable/Sharing/ISharingRegistry.php b/lib/unstable/Sharing/ISharingRegistry.php
index 7e56c194f00c1..77bb566bdc59f 100644
--- a/lib/unstable/Sharing/ISharingRegistry.php
+++ b/lib/unstable/Sharing/ISharingRegistry.php
@@ -14,7 +14,6 @@
use NCU\Sharing\Property\ISharePropertyType;
use NCU\Sharing\Recipient\IShareRecipientType;
use NCU\Sharing\Source\IShareSourceType;
-use OC\Sharing\ISharingLegacyBackend;
use OCP\AppFramework\Attribute\Consumable;
/**
@@ -27,16 +26,6 @@ interface ISharingRegistry {
*/
public function clear(): void;
- /**
- * @experimental 35.0.0
- */
- public function registerLegacyBackend(ISharingLegacyBackend $legacyBackend): void;
-
- /**
- * @experimental 35.0.0
- */
- public function getLegacyBackend(): ?ISharingLegacyBackend;
-
/**
* @experimental 35.0.0
*/
diff --git a/tests/lib/Sharing/AbstractSharingManagerTests.php b/tests/lib/Sharing/AbstractSharingManagerTests.php
index 3b96003476921..349de677d0bfd 100644
--- a/tests/lib/Sharing/AbstractSharingManagerTests.php
+++ b/tests/lib/Sharing/AbstractSharingManagerTests.php
@@ -244,9 +244,10 @@ public function setUp(): void {
#[\Override]
protected function tearDown(): void {
+ $openTransaction = false;
if ($this->dbConnection->inTransaction()) {
$this->dbConnection->rollBack();
- $this->fail('Open transaction was not committed.');
+ $openTransaction = true;
}
$accessContext = new ShareAccessContext(overrideChecks: true);
@@ -257,11 +258,13 @@ protected function tearDown(): void {
$this->manager->deleteShare($accessContext, $share);
}
+ $this->dbConnection->commit();
+
$this->owner->delete();
$this->user1->delete();
$this->user2->delete();
- $this->dbConnection->commit();
+ $this->registry->clear();
foreach ([
'sharing_share',
@@ -279,9 +282,11 @@ protected function tearDown(): void {
$this->assertEquals(0, $qb->executeQuery()->fetchOne(), $table);
}
- $this->registry->clear();
-
parent::tearDown();
+
+ if ($openTransaction) {
+ $this->fail('Open transaction was not committed.');
+ }
}
private function reloadShare(ShareAccessContext $accessContext, Share $share): Share {
diff --git a/tests/lib/Snowflake/GeneratorTest.php b/tests/lib/Snowflake/GeneratorTest.php
index 3323773105a0d..a3d60f7b93efe 100644
--- a/tests/lib/Snowflake/GeneratorTest.php
+++ b/tests/lib/Snowflake/GeneratorTest.php
@@ -99,6 +99,18 @@ public function testGeneratorWithFixedTime(string $date, int $expectedSeconds, i
$this->assertEquals($this->serverInfo->getServerId(), $data->getServerId());
}
+ #[DataProvider('provideSnowflakeData')]
+ public function testGeneratorWithTimestampParameter(string $date, int $expectedSeconds, int $expectedMilliseconds): void {
+ $dt = new \DateTimeImmutable($date);
+
+ $generator = new SnowflakeGenerator(new TimeFactory(), $this->sequence, $this->serverInfo);
+ $data = $this->decoder->decode($generator->nextId($dt));
+
+ $this->assertEquals($expectedSeconds, $data->getCreatedAt()->format('U') - ISnowflakeGenerator::TS_OFFSET);
+ $this->assertEquals($expectedMilliseconds, (int)$data->getCreatedAt()->format('v'));
+ $this->assertEquals($this->serverInfo->getServerId(), $data->getServerId());
+ }
+
public static function provideSnowflakeData(): array {
$tests = [
['2025-10-01 00:00:00.000000', 0, 0],