diff --git a/src/App/Pages/Vault/SharePageViewModel.cs b/src/App/Pages/Vault/SharePageViewModel.cs index 159da9007..ccea0ee4a 100644 --- a/src/App/Pages/Vault/SharePageViewModel.cs +++ b/src/App/Pages/Vault/SharePageViewModel.cs @@ -113,8 +113,15 @@ namespace Bit.App.Pages try { await _deviceActionService.ShowLoadingAsync(AppResources.Saving); - await _cipherService.ShareWithServerAsync(cipherView, OrganizationId, checkedCollectionIds); + var error = await _cipherService.ShareWithServerAsync(cipherView, OrganizationId, checkedCollectionIds); await _deviceActionService.HideLoadingAsync(); + + if (error == ICipherService.ShareWithServerError.DuplicatedPasskeyInOrg) + { + _platformUtilsService.ShowToast(null, null, AppResources.ThisItemCannotBeSharedWithTheOrganizationBecauseThereIsOneAlreadyWithTheSamePasskey); + return false; + } + var movedItemToOrgText = string.Format(AppResources.MovedItemToOrg, cipherView.Name, (await _organizationService.GetAsync(OrganizationId)).Name); _platformUtilsService.ShowToast("success", null, movedItemToOrgText); diff --git a/src/App/Resources/AppResources.Designer.cs b/src/App/Resources/AppResources.Designer.cs index 1ffb3ce6f..546083c39 100644 --- a/src/App/Resources/AppResources.Designer.cs +++ b/src/App/Resources/AppResources.Designer.cs @@ -6236,6 +6236,16 @@ namespace Bit.App.Resources { } } + /// + /// Looks up a localized string similar to This item cannot be shared with the organization because there is one already with the same passkey.. + /// + public static string ThisItemCannotBeSharedWithTheOrganizationBecauseThereIsOneAlreadyWithTheSamePasskey { + get { + return ResourceManager.GetString("ThisItemCannotBeSharedWithTheOrganizationBecauseThereIsOneAlreadyWithTheSamePassk" + + "ey", resourceCulture); + } + } + /// /// Looks up a localized string similar to This request is no longer valid. /// diff --git a/src/App/Resources/AppResources.resx b/src/App/Resources/AppResources.resx index f1f08c3a1..bf567ca16 100644 --- a/src/App/Resources/AppResources.resx +++ b/src/App/Resources/AppResources.resx @@ -2671,4 +2671,7 @@ Do you want to switch to this account? Invalid API token + + This item cannot be shared with the organization because there is one already with the same passkey. + diff --git a/src/Core/Abstractions/ICipherService.cs b/src/Core/Abstractions/ICipherService.cs index e34aaa068..d68b0dc93 100644 --- a/src/Core/Abstractions/ICipherService.cs +++ b/src/Core/Abstractions/ICipherService.cs @@ -10,6 +10,12 @@ namespace Bit.Core.Abstractions { public interface ICipherService { + public enum ShareWithServerError + { + None, + DuplicatedPasskeyInOrg + } + Task ClearAsync(string userId); Task ClearCacheAsync(); Task DeleteAsync(List ids); @@ -31,7 +37,7 @@ namespace Bit.Core.Abstractions Task SaveCollectionsWithServerAsync(Cipher cipher); Task SaveNeverDomainAsync(string domain); Task SaveWithServerAsync(Cipher cipher); - Task ShareWithServerAsync(CipherView cipher, string organizationId, HashSet collectionIds); + Task ShareWithServerAsync(CipherView cipher, string organizationId, HashSet collectionIds); Task UpdateLastUsedDateAsync(string id); Task UpsertAsync(CipherData cipher); Task UpsertAsync(List cipher); diff --git a/src/Core/Services/CipherService.cs b/src/Core/Services/CipherService.cs index 5eb344c2f..757e017ce 100644 --- a/src/Core/Services/CipherService.cs +++ b/src/Core/Services/CipherService.cs @@ -532,8 +532,13 @@ namespace Bit.Core.Services await UpsertAsync(data); } - public async Task ShareWithServerAsync(CipherView cipher, string organizationId, HashSet collectionIds) + public async Task ShareWithServerAsync(CipherView cipher, string organizationId, HashSet collectionIds) { + if (!await ValidateCanBeSharedWithOrgAsync(cipher, organizationId)) + { + return ICipherService.ShareWithServerError.DuplicatedPasskeyInOrg; + } + var attachmentTasks = new List(); if (cipher.Attachments != null) { @@ -554,6 +559,30 @@ namespace Bit.Core.Services var userId = await _stateService.GetActiveUserIdAsync(); var data = new CipherData(response, userId, collectionIds); await UpsertAsync(data); + + return ICipherService.ShareWithServerError.None; + } + + private async Task ValidateCanBeSharedWithOrgAsync(CipherView cipher, string organizationId) + { + if (cipher.Login?.Fido2Key is null && cipher.Fido2Key is null) + { + return true; + } + + var decCiphers = await GetAllDecryptedAsync(); + var orgCiphers = decCiphers.Where(c => c.OrganizationId == organizationId); + if (cipher.Login?.Fido2Key != null) + { + return !orgCiphers.Any(c => c.Login?.Fido2Key?.RpId == cipher.Login.Fido2Key.RpId); + } + + if (cipher.Fido2Key != null) + { + return !orgCiphers.Any(c => c.Fido2Key?.RpId == cipher.Fido2Key.RpId); + } + + return true; } public async Task SaveAttachmentRawWithServerAsync(Cipher cipher, string filename, byte[] data)