1
0
mirror of https://github.com/bitwarden/server.git synced 2025-06-30 15:42:48 -05:00

[AC-1116] Assign new imported collections to the importing user with Manage permission (#3424)

* [AC-1116] Assigning imported collections to the importing user with Manage permission

* [AC-1116] Added unit tests
This commit is contained in:
Rui Tomé
2023-11-23 12:21:20 +00:00
committed by GitHub
parent 4e8284cf81
commit e2d644f136
5 changed files with 148 additions and 9 deletions

View File

@ -32,7 +32,7 @@ public interface ICipherRepository : IRepository<Cipher, Guid>
Task UpdateCiphersAsync(Guid userId, IEnumerable<Cipher> ciphers);
Task CreateAsync(IEnumerable<Cipher> ciphers, IEnumerable<Folder> folders);
Task CreateAsync(IEnumerable<Cipher> ciphers, IEnumerable<Collection> collections,
IEnumerable<CollectionCipher> collectionCiphers);
IEnumerable<CollectionCipher> collectionCiphers, IEnumerable<CollectionUser> collectionUsers);
Task SoftDeleteAsync(IEnumerable<Guid> ids, Guid userId);
Task SoftDeleteByIdsOrganizationIdAsync(IEnumerable<Guid> ids, Guid organizationId);
Task<DateTime> RestoreAsync(IEnumerable<Guid> ids, Guid userId);

View File

@ -27,6 +27,7 @@ public class CipherService : ICipherService
private readonly ICollectionRepository _collectionRepository;
private readonly IUserRepository _userRepository;
private readonly IOrganizationRepository _organizationRepository;
private readonly IOrganizationUserRepository _organizationUserRepository;
private readonly ICollectionCipherRepository _collectionCipherRepository;
private readonly IPushNotificationService _pushService;
private readonly IAttachmentStorageService _attachmentStorageService;
@ -34,7 +35,7 @@ public class CipherService : ICipherService
private readonly IUserService _userService;
private readonly IPolicyService _policyService;
private readonly GlobalSettings _globalSettings;
private const long _fileSizeLeeway = 1024L * 1024L; // 1MB
private const long _fileSizeLeeway = 1024L * 1024L; // 1MB
private readonly IReferenceEventService _referenceEventService;
private readonly ICurrentContext _currentContext;
@ -44,6 +45,7 @@ public class CipherService : ICipherService
ICollectionRepository collectionRepository,
IUserRepository userRepository,
IOrganizationRepository organizationRepository,
IOrganizationUserRepository organizationUserRepository,
ICollectionCipherRepository collectionCipherRepository,
IPushNotificationService pushService,
IAttachmentStorageService attachmentStorageService,
@ -59,6 +61,7 @@ public class CipherService : ICipherService
_collectionRepository = collectionRepository;
_userRepository = userRepository;
_organizationRepository = organizationRepository;
_organizationUserRepository = organizationUserRepository;
_collectionCipherRepository = collectionCipherRepository;
_pushService = pushService;
_attachmentStorageService = attachmentStorageService;
@ -652,7 +655,7 @@ public class CipherService : ICipherService
cipher.RevisionDate = DateTime.UtcNow;
// The sprocs will validate that all collections belong to this org/user and that they have
// The sprocs will validate that all collections belong to this org/user and that they have
// proper write permissions.
if (orgAdmin)
{
@ -747,6 +750,7 @@ public class CipherService : ICipherService
var org = collections.Count > 0 ?
await _organizationRepository.GetByIdAsync(collections[0].OrganizationId) :
await _organizationRepository.GetByIdAsync(ciphers.FirstOrDefault(c => c.OrganizationId.HasValue).OrganizationId.Value);
var importingOrgUser = await _organizationUserRepository.GetByOrganizationAsync(org.Id, importingUserId);
if (collections.Count > 0 && org != null && org.MaxCollections.HasValue)
{
@ -764,18 +768,25 @@ public class CipherService : ICipherService
cipher.SetNewId();
}
var userCollectionsIds = (await _collectionRepository.GetManyByOrganizationIdAsync(org.Id)).Select(c => c.Id).ToList();
var organizationCollectionsIds = (await _collectionRepository.GetManyByOrganizationIdAsync(org.Id)).Select(c => c.Id).ToList();
//Assign id to the ones that don't exist in DB
//Need to keep the list order to create the relationships
List<Collection> newCollections = new List<Collection>();
var newCollections = new List<Collection>();
var newCollectionUsers = new List<CollectionUser>();
foreach (var collection in collections)
{
if (!userCollectionsIds.Contains(collection.Id))
if (!organizationCollectionsIds.Contains(collection.Id))
{
collection.SetNewId();
newCollections.Add(collection);
newCollectionUsers.Add(new CollectionUser
{
CollectionId = collection.Id,
OrganizationUserId = importingOrgUser.Id,
Manage = true
});
}
}
@ -799,7 +810,7 @@ public class CipherService : ICipherService
}
// Create it all
await _cipherRepository.CreateAsync(ciphers, newCollections, collectionCiphers);
await _cipherRepository.CreateAsync(ciphers, newCollections, collectionCiphers, newCollectionUsers);
// push
await _pushService.PushSyncVaultAsync(importingUserId);

View File

@ -589,7 +589,7 @@ public class CipherRepository : Repository<Cipher, Guid>, ICipherRepository
}
public async Task CreateAsync(IEnumerable<Cipher> ciphers, IEnumerable<Collection> collections,
IEnumerable<CollectionCipher> collectionCiphers)
IEnumerable<CollectionCipher> collectionCiphers, IEnumerable<CollectionUser> collectionUsers)
{
if (!ciphers.Any())
{
@ -631,6 +631,16 @@ public class CipherRepository : Repository<Cipher, Guid>, ICipherRepository
}
}
if (collectionUsers.Any())
{
using (var bulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.KeepIdentity, transaction))
{
bulkCopy.DestinationTableName = "[dbo].[CollectionUser]";
var dataTable = BuildCollectionUsersTable(bulkCopy, collectionUsers);
bulkCopy.WriteToServer(dataTable);
}
}
await connection.ExecuteAsync(
$"[{Schema}].[User_BumpAccountRevisionDateByOrganizationId]",
new { OrganizationId = ciphers.First().OrganizationId },
@ -896,6 +906,53 @@ public class CipherRepository : Repository<Cipher, Guid>, ICipherRepository
return collectionCiphersTable;
}
private DataTable BuildCollectionUsersTable(SqlBulkCopy bulkCopy, IEnumerable<CollectionUser> collectionUsers)
{
var cu = collectionUsers.FirstOrDefault();
if (cu == null)
{
throw new ApplicationException("Must have some collectionUsers to bulk import.");
}
var collectionUsersTable = new DataTable("CollectionUserDataTable");
var collectionIdColumn = new DataColumn(nameof(cu.CollectionId), cu.CollectionId.GetType());
collectionUsersTable.Columns.Add(collectionIdColumn);
var organizationUserIdColumn = new DataColumn(nameof(cu.OrganizationUserId), cu.OrganizationUserId.GetType());
collectionUsersTable.Columns.Add(organizationUserIdColumn);
var readOnlyColumn = new DataColumn(nameof(cu.ReadOnly), cu.ReadOnly.GetType());
collectionUsersTable.Columns.Add(readOnlyColumn);
var hidePasswordsColumn = new DataColumn(nameof(cu.HidePasswords), cu.HidePasswords.GetType());
collectionUsersTable.Columns.Add(hidePasswordsColumn);
var manageColumn = new DataColumn(nameof(cu.Manage), cu.Manage.GetType());
collectionUsersTable.Columns.Add(manageColumn);
foreach (DataColumn col in collectionUsersTable.Columns)
{
bulkCopy.ColumnMappings.Add(col.ColumnName, col.ColumnName);
}
var keys = new DataColumn[2];
keys[0] = collectionIdColumn;
keys[1] = organizationUserIdColumn;
collectionUsersTable.PrimaryKey = keys;
foreach (var collectionUser in collectionUsers)
{
var row = collectionUsersTable.NewRow();
row[collectionIdColumn] = collectionUser.CollectionId;
row[organizationUserIdColumn] = collectionUser.OrganizationUserId;
row[readOnlyColumn] = collectionUser.ReadOnly;
row[hidePasswordsColumn] = collectionUser.HidePasswords;
row[manageColumn] = collectionUser.Manage;
collectionUsersTable.Rows.Add(row);
}
return collectionUsersTable;
}
private DataTable BuildSendsTable(SqlBulkCopy bulkCopy, IEnumerable<Send> sends)
{
var s = sends.FirstOrDefault();

View File

@ -161,7 +161,10 @@ public class CipherRepository : Repository<Core.Vault.Entities.Cipher, Cipher, G
}
}
public async Task CreateAsync(IEnumerable<Core.Vault.Entities.Cipher> ciphers, IEnumerable<Core.Entities.Collection> collections, IEnumerable<Core.Entities.CollectionCipher> collectionCiphers)
public async Task CreateAsync(IEnumerable<Core.Vault.Entities.Cipher> ciphers,
IEnumerable<Core.Entities.Collection> collections,
IEnumerable<Core.Entities.CollectionCipher> collectionCiphers,
IEnumerable<Core.Entities.CollectionUser> collectionUsers)
{
if (!ciphers.Any())
{
@ -184,6 +187,13 @@ public class CipherRepository : Repository<Core.Vault.Entities.Cipher, Cipher, G
var collectionCipherEntities = Mapper.Map<List<CollectionCipher>>(collectionCiphers);
await dbContext.BulkCopyAsync(base.DefaultBulkCopyOptions, collectionCipherEntities);
}
if (collectionUsers.Any())
{
var collectionUserEntities = Mapper.Map<List<CollectionUser>>(collectionUsers);
await dbContext.BulkCopyAsync(base.DefaultBulkCopyOptions, collectionUserEntities);
}
await dbContext.UserBumpAccountRevisionDateByOrganizationIdAsync(ciphers.First().OrganizationId.Value);
await dbContext.SaveChangesAsync();
}