From 1bb7bbb9e59444d0a2f8e9149db2d6e527757755 Mon Sep 17 00:00:00 2001 From: Laurent Cozic Date: Sun, 21 Nov 2021 14:35:01 +0000 Subject: [PATCH] Desktop: Fixed sharing notebook when recipient is not allowed to share --- .../MainScreen/commands/leaveSharedFolder.ts | 23 ++++++++++- .../ShareFolderDialog/ShareFolderDialog.tsx | 38 ++++++++++++++++--- 2 files changed, 54 insertions(+), 7 deletions(-) diff --git a/packages/app-desktop/gui/MainScreen/commands/leaveSharedFolder.ts b/packages/app-desktop/gui/MainScreen/commands/leaveSharedFolder.ts index 7664ec2653..1ea03df0db 100644 --- a/packages/app-desktop/gui/MainScreen/commands/leaveSharedFolder.ts +++ b/packages/app-desktop/gui/MainScreen/commands/leaveSharedFolder.ts @@ -1,6 +1,10 @@ import { CommandRuntime, CommandDeclaration, CommandContext } from '@joplin/lib/services/CommandService'; import { _ } from '@joplin/lib/locale'; import ShareService from '@joplin/lib/services/share/ShareService'; +import Setting from '@joplin/lib/models/Setting'; +import Logger from '@joplin/lib/Logger'; + +const logger = Logger.create('leaveSharedFolder'); export const declaration: CommandDeclaration = { name: 'leaveSharedFolder', @@ -12,7 +16,24 @@ export const runtime = (): CommandRuntime => { execute: async (_context: CommandContext, folderId: string = null) => { const answer = confirm(_('This will remove the notebook from your collection and you will no longer have access to its content. Do you wish to continue?')); if (!answer) return; - await ShareService.instance().leaveSharedFolder(folderId); + + try { + // Since we are going to delete the notebook, do some extra safety checks. In particular: + // - Check that the notebook is indeed being shared. + // - Check that it does **not** belong to the current user. + + const shares = await ShareService.instance().refreshShares(); + const share = shares.find(s => s.folder_id === folderId); + if (!share) throw new Error(_('Could not verify the share status of this notebook - aborting. Please try again when you are connected to the internet.')); + + const userId = Setting.value('sync.userId'); + if (share.user.id === userId) throw new Error('Cannot leave own notebook'); + + await ShareService.instance().leaveSharedFolder(folderId); + } catch (error) { + logger.error(error); + alert(_('Error: %s', error.message)); + } }, enabledCondition: 'joplinServerConnected && folderIsShareRootAndNotOwnedByUser', }; diff --git a/packages/app-desktop/gui/ShareFolderDialog/ShareFolderDialog.tsx b/packages/app-desktop/gui/ShareFolderDialog/ShareFolderDialog.tsx index a13ede553e..63edae9176 100644 --- a/packages/app-desktop/gui/ShareFolderDialog/ShareFolderDialog.tsx +++ b/packages/app-desktop/gui/ShareFolderDialog/ShareFolderDialog.tsx @@ -5,7 +5,7 @@ import { _ } from '@joplin/lib/locale'; import { useEffect, useState } from 'react'; import { FolderEntity } from '@joplin/lib/services/database/types'; import Folder from '@joplin/lib/models/Folder'; -import ShareService from '@joplin/lib/services/share/ShareService'; +import ShareService, { ApiShare } from '@joplin/lib/services/share/ShareService'; import styled from 'styled-components'; import StyledFormLabel from '../style/StyledFormLabel'; import StyledInput from '../style/StyledInput'; @@ -167,11 +167,38 @@ function ShareFolderDialog(props: Props) { async function shareRecipient_click() { setShareState(ShareState.Creating); + setLatestError(null); + + let errorSet = false; + + const handleError = (error: any) => { + if (!errorSet) setLatestError(error); + errorSet = true; + logger.error(error); + }; + + const defer = (error: any) => { + if (error) handleError(error); + setShareState(ShareState.Idle); + }; + + let share: ApiShare = null; + + try { + share = await ShareService.instance().shareFolder(props.folderId); + } catch (error) { + return defer(error); + } try { - setLatestError(null); - const share = await ShareService.instance().shareFolder(props.folderId); await ShareService.instance().addShareRecipient(share.id, share.master_key_id, recipientEmail); + } catch (error) { + // Handle the error but continue the process because we need to at + // least refresh the shares since one has been created above. + handleError(error); + } + + try { await Promise.all([ ShareService.instance().refreshShares(), ShareService.instance().refreshShareUsers(share.id), @@ -180,10 +207,9 @@ function ShareFolderDialog(props: Props) { await synchronize(); } catch (error) { - logger.error(error); - setLatestError(error); + handleError(error); } finally { - setShareState(ShareState.Idle); + defer(null); } }