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)