-
-
Notifications
You must be signed in to change notification settings - Fork 4.7k
Fix federated reshares #58689
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Fix federated reshares #58689
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -128,30 +128,30 @@ public function create(IShare $share): IShare { | |
| $share->setSharedWith($cloudId->getId()); | ||
|
|
||
| try { | ||
| $remoteShare = $this->getShareFromExternalShareTable($share); | ||
| $remoteShare = $this->getShareFromExternalShareTable($share->getShareOwner(), $share->getTarget()); | ||
| } catch (ShareNotFound $e) { | ||
| $remoteShare = null; | ||
| } | ||
|
|
||
| if ($remoteShare) { | ||
| try { | ||
| $ownerCloudId = $this->cloudIdManager->getCloudId($remoteShare['owner'], $remoteShare['remote']); | ||
| $shareId = $this->addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $ownerCloudId->getId(), $permissions, 'tmp_token_' . time(), $shareType, $expirationDate); | ||
| $share->setId($shareId); | ||
| [$token, $remoteId] = $this->askOwnerToReShare($shareWith, $share, $shareId); | ||
| // remote share was create successfully if we get a valid token as return | ||
| $send = is_string($token) && $token !== ''; | ||
| } catch (\Exception $e) { | ||
| // fall back to old re-share behavior if the remote server | ||
| // doesn't support flat re-shares (was introduced with Nextcloud 9.1) | ||
| $this->removeShareFromTable($share); | ||
| $shareId = $this->createFederatedShare($share); | ||
provokateurin marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
| if ($send) { | ||
| $ownerCloudId = $this->cloudIdManager->getCloudId($remoteShare['owner'], $remoteShare['remote']); | ||
| $shareId = $this->addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $ownerCloudId->getId(), $permissions, 'tmp_token_' . time(), $shareType, $expirationDate); | ||
| [$token, $remoteId] = $this->notifications->requestReShare( | ||
| $remoteShare['share_token'], | ||
| $remoteShare['remote_id'], | ||
| $shareId, | ||
| $remoteShare['remote'], | ||
| $shareWith, | ||
| $permissions, | ||
| $share->getNode()->getName(), | ||
| $shareType, | ||
| ); | ||
| // remote share was create successfully if we get a valid token as return | ||
| if (is_string($token) && $token !== '') { | ||
| $this->updateSuccessfulReshare($shareId, $token); | ||
| $this->storeRemoteId($shareId, $remoteId); | ||
| } else { | ||
| $this->removeShareFromTable($share); | ||
| $this->removeShareFromTable($shareId); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. comment: noticing that there is no handling for the case where the oc_share entry was missing but the remote had a matching share. This could happen if the current server's DB would be restored to a state without that specific row. I don't see any code that would handle a "share already exists on the remote, track it on this server" but I may have missed it. |
||
| $message_t = $this->l->t('File is already shared with %s', [$shareWith]); | ||
| throw new \Exception($message_t); | ||
| } | ||
|
|
@@ -216,7 +216,7 @@ protected function createFederatedShare(IShare $share): string { | |
| } | ||
|
|
||
| if ($failure) { | ||
| $this->removeShareFromTableById($shareId); | ||
| $this->removeShareFromTable($shareId); | ||
| $message_t = $this->l->t('Sharing %1$s failed, could not find %2$s, maybe the server is currently unreachable or uses a self-signed certificate.', | ||
| [$share->getNode()->getName(), $share->getSharedWith()]); | ||
| throw new \Exception($message_t); | ||
|
|
@@ -225,45 +225,17 @@ protected function createFederatedShare(IShare $share): string { | |
| return $shareId; | ||
| } | ||
|
|
||
| /** | ||
| * @param string $shareWith | ||
| * @param IShare $share | ||
| * @param string $shareId internal share Id | ||
| * @return array | ||
| * @throws \Exception | ||
| */ | ||
| protected function askOwnerToReShare($shareWith, IShare $share, $shareId) { | ||
| $remoteShare = $this->getShareFromExternalShareTable($share); | ||
| $token = $remoteShare['share_token']; | ||
| $remoteId = $remoteShare['remote_id']; | ||
| $remote = $remoteShare['remote']; | ||
|
|
||
| [$token, $remoteId] = $this->notifications->requestReShare( | ||
| $token, | ||
| $remoteId, | ||
| $shareId, | ||
| $remote, | ||
| $shareWith, | ||
| $share->getPermissions(), | ||
| $share->getNode()->getName(), | ||
| $share->getShareType(), | ||
| ); | ||
|
|
||
| return [$token, $remoteId]; | ||
| } | ||
|
|
||
| /** | ||
| * get federated share from the share_external table but exclude mounted link shares | ||
| * | ||
| * @param IShare $share | ||
| * @return array | ||
| * @throws ShareNotFound | ||
| */ | ||
| protected function getShareFromExternalShareTable(IShare $share) { | ||
| protected function getShareFromExternalShareTable(string $owner, string $target) { | ||
| $query = $this->dbConnection->getQueryBuilder(); | ||
| $query->select('*')->from($this->externalShareTable) | ||
| ->where($query->expr()->eq('user', $query->createNamedParameter($share->getShareOwner()))) | ||
| ->andWhere($query->expr()->eq('mountpoint', $query->createNamedParameter($share->getTarget()))); | ||
| ->where($query->expr()->eq('user', $query->createNamedParameter($owner))) | ||
| ->andWhere($query->expr()->eq('mountpoint', $query->createNamedParameter($target))); | ||
| $qResult = $query->executeQuery(); | ||
| $result = $qResult->fetchAllAssociative(); | ||
| $qResult->closeCursor(); | ||
|
|
@@ -453,7 +425,7 @@ public function delete(IShare $share) { | |
|
|
||
| // only remove the share when all messages are send to not lose information | ||
| // about the share to early | ||
| $this->removeShareFromTable($share); | ||
| $this->removeShareFromTable($share->getId()); | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -483,19 +455,10 @@ protected function revokeShare($share, $isOwner) { | |
| } | ||
| } | ||
|
|
||
| /** | ||
| * remove share from table | ||
| * | ||
| * @param IShare $share | ||
| */ | ||
| public function removeShareFromTable(IShare $share) { | ||
| $this->removeShareFromTableById($share->getId()); | ||
| } | ||
|
|
||
| /** | ||
| * Remove share from table. | ||
| */ | ||
| private function removeShareFromTableById(string $shareId): void { | ||
| public function removeShareFromTable(string $shareId): void { | ||
| $qb = $this->dbConnection->getQueryBuilder(); | ||
| $qb->delete('share') | ||
| ->where($qb->expr()->eq('id', $qb->createNamedParameter($shareId))) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fix: I think that removing this will lead to a stale entry in oc_share if the remote call fails for any reason with an Exception
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO this was always wrong. Basically when resharing fails, the original share was also deleted. This was a separate bug besides the fact that resharing didn't work in the first place.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The original share was deleted because the code causing the exception was
setId, but the row inoc_sharefor Server B to Server C got created in the line above. If the request to the original server fails, we should remove that row, otherwise Server B displays that the file is shared with Server C, but that might not be true.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There could still be a case where the remote server managed to store the share information but failed afterwards, so removing the share here could lead to an inconsistency in that case, which in connection with my other comment could lead to still having a broken situation 🤔