mirror of
https://github.com/bitwarden/server.git
synced 2024-11-25 12:45:18 +01:00
[AC-1330] [AC-1816] Deprecate AccessAll in CollectionCipher sprocs (#3480)
This commit is contained in:
parent
59879f913b
commit
dbf8907bfc
@ -106,7 +106,7 @@ public class CiphersController : Controller
|
|||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var collectionCiphers = await _collectionCipherRepository.GetManyByUserIdCipherIdAsync(userId, id);
|
var collectionCiphers = await _collectionCipherRepository.GetManyByUserIdCipherIdAsync(userId, id, UseFlexibleCollections);
|
||||||
return new CipherDetailsResponseModel(cipher, _globalSettings, collectionCiphers);
|
return new CipherDetailsResponseModel(cipher, _globalSettings, collectionCiphers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,7 +120,7 @@ public class CiphersController : Controller
|
|||||||
Dictionary<Guid, IGrouping<Guid, CollectionCipher>> collectionCiphersGroupDict = null;
|
Dictionary<Guid, IGrouping<Guid, CollectionCipher>> collectionCiphersGroupDict = null;
|
||||||
if (hasOrgs)
|
if (hasOrgs)
|
||||||
{
|
{
|
||||||
var collectionCiphers = await _collectionCipherRepository.GetManyByUserIdAsync(userId);
|
var collectionCiphers = await _collectionCipherRepository.GetManyByUserIdAsync(userId, UseFlexibleCollections);
|
||||||
collectionCiphersGroupDict = collectionCiphers.GroupBy(c => c.CipherId).ToDictionary(s => s.Key);
|
collectionCiphersGroupDict = collectionCiphers.GroupBy(c => c.CipherId).ToDictionary(s => s.Key);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,7 +189,7 @@ public class CiphersController : Controller
|
|||||||
ValidateClientVersionForItemLevelEncryptionSupport(cipher);
|
ValidateClientVersionForItemLevelEncryptionSupport(cipher);
|
||||||
ValidateClientVersionForFido2CredentialSupport(cipher);
|
ValidateClientVersionForFido2CredentialSupport(cipher);
|
||||||
|
|
||||||
var collectionIds = (await _collectionCipherRepository.GetManyByUserIdCipherIdAsync(userId, id)).Select(c => c.CollectionId).ToList();
|
var collectionIds = (await _collectionCipherRepository.GetManyByUserIdCipherIdAsync(userId, id, UseFlexibleCollections)).Select(c => c.CollectionId).ToList();
|
||||||
var modelOrgId = string.IsNullOrWhiteSpace(model.OrganizationId) ?
|
var modelOrgId = string.IsNullOrWhiteSpace(model.OrganizationId) ?
|
||||||
(Guid?)null : new Guid(model.OrganizationId);
|
(Guid?)null : new Guid(model.OrganizationId);
|
||||||
if (cipher.OrganizationId != modelOrgId)
|
if (cipher.OrganizationId != modelOrgId)
|
||||||
@ -220,7 +220,7 @@ public class CiphersController : Controller
|
|||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var collectionIds = (await _collectionCipherRepository.GetManyByUserIdCipherIdAsync(userId, id)).Select(c => c.CollectionId).ToList();
|
var collectionIds = (await _collectionCipherRepository.GetManyByUserIdCipherIdAsync(userId, id, UseFlexibleCollections)).Select(c => c.CollectionId).ToList();
|
||||||
// object cannot be a descendant of CipherDetails, so let's clone it.
|
// object cannot be a descendant of CipherDetails, so let's clone it.
|
||||||
var cipherClone = model.ToCipher(cipher).Clone();
|
var cipherClone = model.ToCipher(cipher).Clone();
|
||||||
await _cipherService.SaveAsync(cipherClone, userId, model.LastKnownRevisionDate, collectionIds, true, false);
|
await _cipherService.SaveAsync(cipherClone, userId, model.LastKnownRevisionDate, collectionIds, true, false);
|
||||||
|
@ -95,7 +95,7 @@ public class SyncController : Controller
|
|||||||
if (hasEnabledOrgs)
|
if (hasEnabledOrgs)
|
||||||
{
|
{
|
||||||
collections = await _collectionRepository.GetManyByUserIdAsync(user.Id);
|
collections = await _collectionRepository.GetManyByUserIdAsync(user.Id);
|
||||||
var collectionCiphers = await _collectionCipherRepository.GetManyByUserIdAsync(user.Id);
|
var collectionCiphers = await _collectionCipherRepository.GetManyByUserIdAsync(user.Id, UseFlexibleCollections);
|
||||||
collectionCiphersGroupDict = collectionCiphers.GroupBy(c => c.CipherId).ToDictionary(s => s.Key);
|
collectionCiphersGroupDict = collectionCiphers.GroupBy(c => c.CipherId).ToDictionary(s => s.Key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,11 +4,11 @@ namespace Bit.Core.Repositories;
|
|||||||
|
|
||||||
public interface ICollectionCipherRepository
|
public interface ICollectionCipherRepository
|
||||||
{
|
{
|
||||||
Task<ICollection<CollectionCipher>> GetManyByUserIdAsync(Guid userId);
|
Task<ICollection<CollectionCipher>> GetManyByUserIdAsync(Guid userId, bool useFlexibleCollections);
|
||||||
Task<ICollection<CollectionCipher>> GetManyByOrganizationIdAsync(Guid organizationId);
|
Task<ICollection<CollectionCipher>> GetManyByOrganizationIdAsync(Guid organizationId);
|
||||||
Task<ICollection<CollectionCipher>> GetManyByUserIdCipherIdAsync(Guid userId, Guid cipherId);
|
Task<ICollection<CollectionCipher>> GetManyByUserIdCipherIdAsync(Guid userId, Guid cipherId, bool useFlexibleCollections);
|
||||||
Task UpdateCollectionsAsync(Guid cipherId, Guid userId, IEnumerable<Guid> collectionIds);
|
Task UpdateCollectionsAsync(Guid cipherId, Guid userId, IEnumerable<Guid> collectionIds, bool useFlexibleCollections);
|
||||||
Task UpdateCollectionsForAdminAsync(Guid cipherId, Guid organizationId, IEnumerable<Guid> collectionIds);
|
Task UpdateCollectionsForAdminAsync(Guid cipherId, Guid organizationId, IEnumerable<Guid> collectionIds);
|
||||||
Task UpdateCollectionsForCiphersAsync(IEnumerable<Guid> cipherIds, Guid userId, Guid organizationId,
|
Task UpdateCollectionsForCiphersAsync(IEnumerable<Guid> cipherIds, Guid userId, Guid organizationId,
|
||||||
IEnumerable<Guid> collectionIds);
|
IEnumerable<Guid> collectionIds, bool useFlexibleCollections);
|
||||||
}
|
}
|
||||||
|
@ -585,11 +585,11 @@ public class CipherService : ICipherService
|
|||||||
originalCipher.SetAttachments(originalAttachments);
|
originalCipher.SetAttachments(originalAttachments);
|
||||||
}
|
}
|
||||||
|
|
||||||
var currentCollectionsForCipher = await _collectionCipherRepository.GetManyByUserIdCipherIdAsync(sharingUserId, originalCipher.Id);
|
var currentCollectionsForCipher = await _collectionCipherRepository.GetManyByUserIdCipherIdAsync(sharingUserId, originalCipher.Id, UseFlexibleCollections);
|
||||||
var currentCollectionIdsForCipher = currentCollectionsForCipher.Select(c => c.CollectionId).ToList();
|
var currentCollectionIdsForCipher = currentCollectionsForCipher.Select(c => c.CollectionId).ToList();
|
||||||
currentCollectionIdsForCipher.RemoveAll(id => collectionIds.Contains(id));
|
currentCollectionIdsForCipher.RemoveAll(id => collectionIds.Contains(id));
|
||||||
|
|
||||||
await _collectionCipherRepository.UpdateCollectionsAsync(originalCipher.Id, sharingUserId, currentCollectionIdsForCipher);
|
await _collectionCipherRepository.UpdateCollectionsAsync(originalCipher.Id, sharingUserId, currentCollectionIdsForCipher, UseFlexibleCollections);
|
||||||
await _cipherRepository.ReplaceAsync(originalCipher);
|
await _cipherRepository.ReplaceAsync(originalCipher);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -634,7 +634,7 @@ public class CipherService : ICipherService
|
|||||||
|
|
||||||
await _cipherRepository.UpdateCiphersAsync(sharingUserId, cipherInfos.Select(c => c.cipher));
|
await _cipherRepository.UpdateCiphersAsync(sharingUserId, cipherInfos.Select(c => c.cipher));
|
||||||
await _collectionCipherRepository.UpdateCollectionsForCiphersAsync(cipherIds, sharingUserId,
|
await _collectionCipherRepository.UpdateCollectionsForCiphersAsync(cipherIds, sharingUserId,
|
||||||
organizationId, collectionIds);
|
organizationId, collectionIds, UseFlexibleCollections);
|
||||||
|
|
||||||
var events = cipherInfos.Select(c =>
|
var events = cipherInfos.Select(c =>
|
||||||
new Tuple<Cipher, EventType, DateTime?>(c.cipher, EventType.Cipher_Shared, null));
|
new Tuple<Cipher, EventType, DateTime?>(c.cipher, EventType.Cipher_Shared, null));
|
||||||
@ -675,7 +675,7 @@ public class CipherService : ICipherService
|
|||||||
{
|
{
|
||||||
throw new BadRequestException("You do not have permissions to edit this.");
|
throw new BadRequestException("You do not have permissions to edit this.");
|
||||||
}
|
}
|
||||||
await _collectionCipherRepository.UpdateCollectionsAsync(cipher.Id, savingUserId, collectionIds);
|
await _collectionCipherRepository.UpdateCollectionsAsync(cipher.Id, savingUserId, collectionIds, UseFlexibleCollections);
|
||||||
}
|
}
|
||||||
|
|
||||||
await _eventService.LogCipherEventAsync(cipher, Bit.Core.Enums.EventType.Cipher_UpdatedCollections);
|
await _eventService.LogCipherEventAsync(cipher, Bit.Core.Enums.EventType.Cipher_UpdatedCollections);
|
||||||
|
@ -17,12 +17,16 @@ public class CollectionCipherRepository : BaseRepository, ICollectionCipherRepos
|
|||||||
: base(connectionString, readOnlyConnectionString)
|
: base(connectionString, readOnlyConnectionString)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
public async Task<ICollection<CollectionCipher>> GetManyByUserIdAsync(Guid userId)
|
public async Task<ICollection<CollectionCipher>> GetManyByUserIdAsync(Guid userId, bool useFlexibleCollections)
|
||||||
{
|
{
|
||||||
|
var sprocName = useFlexibleCollections
|
||||||
|
? "[dbo].[CollectionCipher_ReadByUserId_V2]"
|
||||||
|
: "[dbo].[CollectionCipher_ReadByUserId]";
|
||||||
|
|
||||||
using (var connection = new SqlConnection(ConnectionString))
|
using (var connection = new SqlConnection(ConnectionString))
|
||||||
{
|
{
|
||||||
var results = await connection.QueryAsync<CollectionCipher>(
|
var results = await connection.QueryAsync<CollectionCipher>(
|
||||||
"[dbo].[CollectionCipher_ReadByUserId]",
|
sprocName,
|
||||||
new { UserId = userId },
|
new { UserId = userId },
|
||||||
commandType: CommandType.StoredProcedure);
|
commandType: CommandType.StoredProcedure);
|
||||||
|
|
||||||
@ -43,12 +47,16 @@ public class CollectionCipherRepository : BaseRepository, ICollectionCipherRepos
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ICollection<CollectionCipher>> GetManyByUserIdCipherIdAsync(Guid userId, Guid cipherId)
|
public async Task<ICollection<CollectionCipher>> GetManyByUserIdCipherIdAsync(Guid userId, Guid cipherId, bool useFlexibleCollections)
|
||||||
{
|
{
|
||||||
|
var sprocName = useFlexibleCollections
|
||||||
|
? "[dbo].[CollectionCipher_ReadByUserIdCipherId_V2]"
|
||||||
|
: "[dbo].[CollectionCipher_ReadByUserIdCipherId]";
|
||||||
|
|
||||||
using (var connection = new SqlConnection(ConnectionString))
|
using (var connection = new SqlConnection(ConnectionString))
|
||||||
{
|
{
|
||||||
var results = await connection.QueryAsync<CollectionCipher>(
|
var results = await connection.QueryAsync<CollectionCipher>(
|
||||||
"[dbo].[CollectionCipher_ReadByUserIdCipherId]",
|
sprocName,
|
||||||
new { UserId = userId, CipherId = cipherId },
|
new { UserId = userId, CipherId = cipherId },
|
||||||
commandType: CommandType.StoredProcedure);
|
commandType: CommandType.StoredProcedure);
|
||||||
|
|
||||||
@ -56,12 +64,16 @@ public class CollectionCipherRepository : BaseRepository, ICollectionCipherRepos
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task UpdateCollectionsAsync(Guid cipherId, Guid userId, IEnumerable<Guid> collectionIds)
|
public async Task UpdateCollectionsAsync(Guid cipherId, Guid userId, IEnumerable<Guid> collectionIds, bool useFlexibleCollections)
|
||||||
{
|
{
|
||||||
|
var sprocName = useFlexibleCollections
|
||||||
|
? "[dbo].[CollectionCipher_UpdateCollections_V2]"
|
||||||
|
: "[dbo].[CollectionCipher_UpdateCollections]";
|
||||||
|
|
||||||
using (var connection = new SqlConnection(ConnectionString))
|
using (var connection = new SqlConnection(ConnectionString))
|
||||||
{
|
{
|
||||||
var results = await connection.ExecuteAsync(
|
var results = await connection.ExecuteAsync(
|
||||||
"[dbo].[CollectionCipher_UpdateCollections]",
|
sprocName,
|
||||||
new { CipherId = cipherId, UserId = userId, CollectionIds = collectionIds.ToGuidIdArrayTVP() },
|
new { CipherId = cipherId, UserId = userId, CollectionIds = collectionIds.ToGuidIdArrayTVP() },
|
||||||
commandType: CommandType.StoredProcedure);
|
commandType: CommandType.StoredProcedure);
|
||||||
}
|
}
|
||||||
@ -79,12 +91,16 @@ public class CollectionCipherRepository : BaseRepository, ICollectionCipherRepos
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async Task UpdateCollectionsForCiphersAsync(IEnumerable<Guid> cipherIds, Guid userId,
|
public async Task UpdateCollectionsForCiphersAsync(IEnumerable<Guid> cipherIds, Guid userId,
|
||||||
Guid organizationId, IEnumerable<Guid> collectionIds)
|
Guid organizationId, IEnumerable<Guid> collectionIds, bool useFlexibleCollections)
|
||||||
{
|
{
|
||||||
|
var sprocName = useFlexibleCollections
|
||||||
|
? "[dbo].[CollectionCipher_UpdateCollectionsForCiphers_V2]"
|
||||||
|
: "[dbo].[CollectionCipher_UpdateCollectionsForCiphers]";
|
||||||
|
|
||||||
using (var connection = new SqlConnection(ConnectionString))
|
using (var connection = new SqlConnection(ConnectionString))
|
||||||
{
|
{
|
||||||
var results = await connection.ExecuteAsync(
|
var results = await connection.ExecuteAsync(
|
||||||
"[dbo].[CollectionCipher_UpdateCollectionsForCiphers]",
|
sprocName,
|
||||||
new
|
new
|
||||||
{
|
{
|
||||||
CipherIds = cipherIds.ToGuidIdArrayTVP(),
|
CipherIds = cipherIds.ToGuidIdArrayTVP(),
|
||||||
|
@ -46,31 +46,31 @@ public class CollectionCipherRepository : BaseEntityFrameworkRepository, ICollec
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ICollection<CollectionCipher>> GetManyByUserIdAsync(Guid userId)
|
public async Task<ICollection<CollectionCipher>> GetManyByUserIdAsync(Guid userId, bool useFlexibleCollections)
|
||||||
{
|
{
|
||||||
using (var scope = ServiceScopeFactory.CreateScope())
|
using (var scope = ServiceScopeFactory.CreateScope())
|
||||||
{
|
{
|
||||||
var dbContext = GetDatabaseContext(scope);
|
var dbContext = GetDatabaseContext(scope);
|
||||||
var data = await new CollectionCipherReadByUserIdQuery(userId)
|
var data = await new CollectionCipherReadByUserIdQuery(userId, useFlexibleCollections)
|
||||||
.Run(dbContext)
|
.Run(dbContext)
|
||||||
.ToArrayAsync();
|
.ToArrayAsync();
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ICollection<CollectionCipher>> GetManyByUserIdCipherIdAsync(Guid userId, Guid cipherId)
|
public async Task<ICollection<CollectionCipher>> GetManyByUserIdCipherIdAsync(Guid userId, Guid cipherId, bool useFlexibleCollections)
|
||||||
{
|
{
|
||||||
using (var scope = ServiceScopeFactory.CreateScope())
|
using (var scope = ServiceScopeFactory.CreateScope())
|
||||||
{
|
{
|
||||||
var dbContext = GetDatabaseContext(scope);
|
var dbContext = GetDatabaseContext(scope);
|
||||||
var data = await new CollectionCipherReadByUserIdCipherIdQuery(userId, cipherId)
|
var data = await new CollectionCipherReadByUserIdCipherIdQuery(userId, cipherId, useFlexibleCollections)
|
||||||
.Run(dbContext)
|
.Run(dbContext)
|
||||||
.ToArrayAsync();
|
.ToArrayAsync();
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task UpdateCollectionsAsync(Guid cipherId, Guid userId, IEnumerable<Guid> collectionIds)
|
public async Task UpdateCollectionsAsync(Guid cipherId, Guid userId, IEnumerable<Guid> collectionIds, bool useFlexibleCollections)
|
||||||
{
|
{
|
||||||
using (var scope = ServiceScopeFactory.CreateScope())
|
using (var scope = ServiceScopeFactory.CreateScope())
|
||||||
{
|
{
|
||||||
@ -81,7 +81,17 @@ public class CollectionCipherRepository : BaseEntityFrameworkRepository, ICollec
|
|||||||
.Select(c => c.OrganizationId)
|
.Select(c => c.OrganizationId)
|
||||||
.FirstAsync();
|
.FirstAsync();
|
||||||
|
|
||||||
var availableCollections = await (from c in dbContext.Collections
|
List<Guid> availableCollections;
|
||||||
|
if (useFlexibleCollections)
|
||||||
|
{
|
||||||
|
var availableCollectionsQuery = new CollectionsReadByOrganizationIdUserIdQuery(organizationId, userId);
|
||||||
|
availableCollections = await availableCollectionsQuery
|
||||||
|
.Run(dbContext)
|
||||||
|
.Select(c => c.Id).ToListAsync();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
availableCollections = await (from c in dbContext.Collections
|
||||||
join o in dbContext.Organizations on c.OrganizationId equals o.Id
|
join o in dbContext.Organizations on c.OrganizationId equals o.Id
|
||||||
join ou in dbContext.OrganizationUsers
|
join ou in dbContext.OrganizationUsers
|
||||||
on new { OrganizationId = o.Id, UserId = (Guid?)userId } equals
|
on new { OrganizationId = o.Id, UserId = (Guid?)userId } equals
|
||||||
@ -104,6 +114,8 @@ public class CollectionCipherRepository : BaseEntityFrameworkRepository, ICollec
|
|||||||
&& (ou.AccessAll || !cu.ReadOnly || g.AccessAll || !cg.ReadOnly)
|
&& (ou.AccessAll || !cu.ReadOnly || g.AccessAll || !cg.ReadOnly)
|
||||||
select c.Id).ToListAsync();
|
select c.Id).ToListAsync();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
var collectionCiphers = await (from cc in dbContext.CollectionCiphers
|
var collectionCiphers = await (from cc in dbContext.CollectionCiphers
|
||||||
where cc.CipherId == cipherId
|
where cc.CipherId == cipherId
|
||||||
select cc).ToListAsync();
|
select cc).ToListAsync();
|
||||||
@ -176,12 +188,22 @@ public class CollectionCipherRepository : BaseEntityFrameworkRepository, ICollec
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task UpdateCollectionsForCiphersAsync(IEnumerable<Guid> cipherIds, Guid userId, Guid organizationId, IEnumerable<Guid> collectionIds)
|
public async Task UpdateCollectionsForCiphersAsync(IEnumerable<Guid> cipherIds, Guid userId, Guid organizationId, IEnumerable<Guid> collectionIds, bool useFlexibleCollections)
|
||||||
{
|
{
|
||||||
using (var scope = ServiceScopeFactory.CreateScope())
|
using (var scope = ServiceScopeFactory.CreateScope())
|
||||||
{
|
{
|
||||||
var dbContext = GetDatabaseContext(scope);
|
var dbContext = GetDatabaseContext(scope);
|
||||||
var availableCollections = from c in dbContext.Collections
|
|
||||||
|
IQueryable<Models.Collection> availableCollections;
|
||||||
|
if (useFlexibleCollections)
|
||||||
|
{
|
||||||
|
var availableCollectionsQuery = new CollectionsReadByOrganizationIdUserIdQuery(organizationId, userId);
|
||||||
|
availableCollections = availableCollectionsQuery
|
||||||
|
.Run(dbContext);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
availableCollections = from c in dbContext.Collections
|
||||||
join o in dbContext.Organizations
|
join o in dbContext.Organizations
|
||||||
on c.OrganizationId equals o.Id
|
on c.OrganizationId equals o.Id
|
||||||
join ou in dbContext.OrganizationUsers
|
join ou in dbContext.OrganizationUsers
|
||||||
@ -204,8 +226,10 @@ public class CollectionCipherRepository : BaseEntityFrameworkRepository, ICollec
|
|||||||
where !g.AccessAll && cg.CollectionId == c.Id &&
|
where !g.AccessAll && cg.CollectionId == c.Id &&
|
||||||
(o.Id == organizationId && o.Enabled && ou.Status == OrganizationUserStatusType.Confirmed &&
|
(o.Id == organizationId && o.Enabled && ou.Status == OrganizationUserStatusType.Confirmed &&
|
||||||
(ou.AccessAll || !cu.ReadOnly || g.AccessAll || !cg.ReadOnly))
|
(ou.AccessAll || !cu.ReadOnly || g.AccessAll || !cg.ReadOnly))
|
||||||
select new { c, o, ou, cu, gu, g, cg };
|
select c;
|
||||||
var count = await availableCollections.CountAsync();
|
|
||||||
|
}
|
||||||
|
|
||||||
if (await availableCollections.CountAsync() < 1)
|
if (await availableCollections.CountAsync() < 1)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@ -213,7 +237,7 @@ public class CollectionCipherRepository : BaseEntityFrameworkRepository, ICollec
|
|||||||
|
|
||||||
var insertData = from collectionId in collectionIds
|
var insertData = from collectionId in collectionIds
|
||||||
from cipherId in cipherIds
|
from cipherId in cipherIds
|
||||||
where availableCollections.Select(x => x.c.Id).Contains(collectionId)
|
where availableCollections.Select(c => c.Id).Contains(collectionId)
|
||||||
select new Models.CollectionCipher
|
select new Models.CollectionCipher
|
||||||
{
|
{
|
||||||
CollectionId = collectionId,
|
CollectionId = collectionId,
|
||||||
|
@ -6,7 +6,7 @@ public class CollectionCipherReadByUserIdCipherIdQuery : CollectionCipherReadByU
|
|||||||
{
|
{
|
||||||
private readonly Guid _cipherId;
|
private readonly Guid _cipherId;
|
||||||
|
|
||||||
public CollectionCipherReadByUserIdCipherIdQuery(Guid userId, Guid cipherId) : base(userId)
|
public CollectionCipherReadByUserIdCipherIdQuery(Guid userId, Guid cipherId, bool useFlexibleCollections) : base(userId, useFlexibleCollections)
|
||||||
{
|
{
|
||||||
_cipherId = cipherId;
|
_cipherId = cipherId;
|
||||||
}
|
}
|
||||||
|
@ -6,13 +6,58 @@ namespace Bit.Infrastructure.EntityFramework.Repositories.Queries;
|
|||||||
public class CollectionCipherReadByUserIdQuery : IQuery<CollectionCipher>
|
public class CollectionCipherReadByUserIdQuery : IQuery<CollectionCipher>
|
||||||
{
|
{
|
||||||
private readonly Guid _userId;
|
private readonly Guid _userId;
|
||||||
|
private readonly bool _useFlexibleCollections;
|
||||||
|
|
||||||
public CollectionCipherReadByUserIdQuery(Guid userId)
|
public CollectionCipherReadByUserIdQuery(Guid userId, bool useFlexibleCollections)
|
||||||
{
|
{
|
||||||
_userId = userId;
|
_userId = userId;
|
||||||
|
_useFlexibleCollections = useFlexibleCollections;
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual IQueryable<CollectionCipher> Run(DatabaseContext dbContext)
|
public virtual IQueryable<CollectionCipher> Run(DatabaseContext dbContext)
|
||||||
|
{
|
||||||
|
return _useFlexibleCollections
|
||||||
|
? Run_VNext(dbContext)
|
||||||
|
: Run_VCurrent(dbContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IQueryable<CollectionCipher> Run_VNext(DatabaseContext dbContext)
|
||||||
|
{
|
||||||
|
var query = from cc in dbContext.CollectionCiphers
|
||||||
|
|
||||||
|
join c in dbContext.Collections
|
||||||
|
on cc.CollectionId equals c.Id
|
||||||
|
|
||||||
|
join ou in dbContext.OrganizationUsers
|
||||||
|
on new { c.OrganizationId, UserId = (Guid?)_userId } equals
|
||||||
|
new { ou.OrganizationId, ou.UserId }
|
||||||
|
|
||||||
|
join cu in dbContext.CollectionUsers
|
||||||
|
on new { CollectionId = c.Id, OrganizationUserId = ou.Id } equals
|
||||||
|
new { cu.CollectionId, cu.OrganizationUserId } into cu_g
|
||||||
|
from cu in cu_g.DefaultIfEmpty()
|
||||||
|
|
||||||
|
join gu in dbContext.GroupUsers
|
||||||
|
on new { CollectionId = (Guid?)cu.CollectionId, OrganizationUserId = ou.Id } equals
|
||||||
|
new { CollectionId = (Guid?)null, gu.OrganizationUserId } into gu_g
|
||||||
|
from gu in gu_g.DefaultIfEmpty()
|
||||||
|
|
||||||
|
join g in dbContext.Groups
|
||||||
|
on gu.GroupId equals g.Id into g_g
|
||||||
|
from g in g_g.DefaultIfEmpty()
|
||||||
|
|
||||||
|
join cg in dbContext.CollectionGroups
|
||||||
|
on new { CollectionId = c.Id, gu.GroupId } equals
|
||||||
|
new { cg.CollectionId, cg.GroupId } into cg_g
|
||||||
|
from cg in cg_g.DefaultIfEmpty()
|
||||||
|
|
||||||
|
where ou.Status == OrganizationUserStatusType.Confirmed &&
|
||||||
|
(cu.CollectionId != null || cg.CollectionId != null)
|
||||||
|
select cc;
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IQueryable<CollectionCipher> Run_VCurrent(DatabaseContext dbContext)
|
||||||
{
|
{
|
||||||
var query = from cc in dbContext.CollectionCiphers
|
var query = from cc in dbContext.CollectionCiphers
|
||||||
|
|
||||||
|
@ -0,0 +1,44 @@
|
|||||||
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Infrastructure.EntityFramework.Models;
|
||||||
|
|
||||||
|
namespace Bit.Infrastructure.EntityFramework.Repositories.Queries;
|
||||||
|
|
||||||
|
public class CollectionsReadByOrganizationIdUserIdQuery : IQuery<Collection>
|
||||||
|
{
|
||||||
|
private readonly Guid? _organizationId;
|
||||||
|
private readonly Guid _userId;
|
||||||
|
|
||||||
|
public CollectionsReadByOrganizationIdUserIdQuery(Guid? organizationId, Guid userId)
|
||||||
|
{
|
||||||
|
_organizationId = organizationId;
|
||||||
|
_userId = userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual IQueryable<Collection> Run(DatabaseContext dbContext)
|
||||||
|
{
|
||||||
|
var query = from c in dbContext.Collections
|
||||||
|
join o in dbContext.Organizations on c.OrganizationId equals o.Id
|
||||||
|
join ou in dbContext.OrganizationUsers
|
||||||
|
on new { OrganizationId = o.Id, UserId = (Guid?)_userId } equals
|
||||||
|
new { ou.OrganizationId, ou.UserId }
|
||||||
|
join cu in dbContext.CollectionUsers
|
||||||
|
on new { CollectionId = c.Id, OrganizationUserId = ou.Id } equals
|
||||||
|
new { cu.CollectionId, cu.OrganizationUserId } into cu_g
|
||||||
|
from cu in cu_g.DefaultIfEmpty()
|
||||||
|
join gu in dbContext.GroupUsers
|
||||||
|
on new { CollectionId = (Guid?)cu.CollectionId, OrganizationUserId = ou.Id } equals
|
||||||
|
new { CollectionId = (Guid?)null, gu.OrganizationUserId } into gu_g
|
||||||
|
from gu in gu_g.DefaultIfEmpty()
|
||||||
|
join g in dbContext.Groups on gu.GroupId equals g.Id into g_g
|
||||||
|
from g in g_g.DefaultIfEmpty()
|
||||||
|
join cg in dbContext.CollectionGroups
|
||||||
|
on new { CollectionId = c.Id, gu.GroupId } equals
|
||||||
|
new { cg.CollectionId, cg.GroupId } into cg_g
|
||||||
|
from cg in cg_g.DefaultIfEmpty()
|
||||||
|
where o.Id == _organizationId && o.Enabled && ou.Status == OrganizationUserStatusType.Confirmed
|
||||||
|
&& (!cu.ReadOnly || !cg.ReadOnly)
|
||||||
|
select c;
|
||||||
|
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
CREATE PROCEDURE [dbo].[CollectionCipher_ReadByUserIdCipherId_V2]
|
||||||
|
@UserId UNIQUEIDENTIFIER,
|
||||||
|
@CipherId UNIQUEIDENTIFIER
|
||||||
|
AS
|
||||||
|
BEGIN
|
||||||
|
SET NOCOUNT ON
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
CC.*
|
||||||
|
FROM
|
||||||
|
[dbo].[CollectionCipher] CC
|
||||||
|
INNER JOIN
|
||||||
|
[dbo].[Collection] S ON S.[Id] = CC.[CollectionId]
|
||||||
|
INNER JOIN
|
||||||
|
[dbo].[OrganizationUser] OU ON OU.[OrganizationId] = S.[OrganizationId] AND OU.[UserId] = @UserId
|
||||||
|
LEFT JOIN
|
||||||
|
[dbo].[CollectionUser] CU ON CU.[CollectionId] = S.[Id] AND CU.[OrganizationUserId] = OU.[Id]
|
||||||
|
LEFT JOIN
|
||||||
|
[dbo].[GroupUser] GU ON CU.[CollectionId] IS NULL AND GU.[OrganizationUserId] = OU.[Id]
|
||||||
|
LEFT JOIN
|
||||||
|
[dbo].[Group] G ON G.[Id] = GU.[GroupId]
|
||||||
|
LEFT JOIN
|
||||||
|
[dbo].[CollectionGroup] CG ON CG.[CollectionId] = CC.[CollectionId] AND CG.[GroupId] = GU.[GroupId]
|
||||||
|
WHERE
|
||||||
|
CC.[CipherId] = @CipherId
|
||||||
|
AND OU.[Status] = 2 -- Confirmed
|
||||||
|
AND (
|
||||||
|
CU.[CollectionId] IS NOT NULL
|
||||||
|
OR CG.[CollectionId] IS NOT NULL
|
||||||
|
)
|
||||||
|
END
|
@ -0,0 +1,29 @@
|
|||||||
|
CREATE PROCEDURE [dbo].[CollectionCipher_ReadByUserId_V2]
|
||||||
|
@UserId UNIQUEIDENTIFIER
|
||||||
|
AS
|
||||||
|
BEGIN
|
||||||
|
SET NOCOUNT ON
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
CC.*
|
||||||
|
FROM
|
||||||
|
[dbo].[CollectionCipher] CC
|
||||||
|
INNER JOIN
|
||||||
|
[dbo].[Collection] S ON S.[Id] = CC.[CollectionId]
|
||||||
|
INNER JOIN
|
||||||
|
[dbo].[OrganizationUser] OU ON OU.[OrganizationId] = S.[OrganizationId] AND OU.[UserId] = @UserId
|
||||||
|
LEFT JOIN
|
||||||
|
[dbo].[CollectionUser] CU ON CU.[CollectionId] = S.[Id] AND CU.[OrganizationUserId] = OU.[Id]
|
||||||
|
LEFT JOIN
|
||||||
|
[dbo].[GroupUser] GU ON CU.[CollectionId] IS NULL AND GU.[OrganizationUserId] = OU.[Id]
|
||||||
|
LEFT JOIN
|
||||||
|
[dbo].[Group] G ON G.[Id] = GU.[GroupId]
|
||||||
|
LEFT JOIN
|
||||||
|
[dbo].[CollectionGroup] CG ON CG.[CollectionId] = CC.[CollectionId] AND CG.[GroupId] = GU.[GroupId]
|
||||||
|
WHERE
|
||||||
|
OU.[Status] = 2 -- Confirmed
|
||||||
|
AND (
|
||||||
|
CU.[CollectionId] IS NOT NULL
|
||||||
|
OR CG.[CollectionId] IS NOT NULL
|
||||||
|
)
|
||||||
|
END
|
@ -0,0 +1,62 @@
|
|||||||
|
CREATE PROCEDURE [dbo].[CollectionCipher_UpdateCollectionsForCiphers_V2]
|
||||||
|
@CipherIds AS [dbo].[GuidIdArray] READONLY,
|
||||||
|
@OrganizationId UNIQUEIDENTIFIER,
|
||||||
|
@UserId UNIQUEIDENTIFIER,
|
||||||
|
@CollectionIds AS [dbo].[GuidIdArray] READONLY
|
||||||
|
AS
|
||||||
|
BEGIN
|
||||||
|
SET NOCOUNT ON
|
||||||
|
|
||||||
|
CREATE TABLE #AvailableCollections (
|
||||||
|
[Id] UNIQUEIDENTIFIER
|
||||||
|
)
|
||||||
|
|
||||||
|
INSERT INTO #AvailableCollections
|
||||||
|
SELECT
|
||||||
|
C.[Id]
|
||||||
|
FROM
|
||||||
|
[dbo].[Collection] C
|
||||||
|
INNER JOIN
|
||||||
|
[Organization] O ON O.[Id] = C.[OrganizationId]
|
||||||
|
INNER JOIN
|
||||||
|
[dbo].[OrganizationUser] OU ON OU.[OrganizationId] = O.[Id] AND OU.[UserId] = @UserId
|
||||||
|
LEFT JOIN
|
||||||
|
[dbo].[CollectionUser] CU ON CU.[CollectionId] = C.[Id] AND CU.[OrganizationUserId] = OU.[Id]
|
||||||
|
LEFT JOIN
|
||||||
|
[dbo].[GroupUser] GU ON CU.[CollectionId] IS NULL AND GU.[OrganizationUserId] = OU.[Id]
|
||||||
|
LEFT JOIN
|
||||||
|
[dbo].[Group] G ON G.[Id] = GU.[GroupId]
|
||||||
|
LEFT JOIN
|
||||||
|
[dbo].[CollectionGroup] CG ON CG.[CollectionId] = C.[Id] AND CG.[GroupId] = GU.[GroupId]
|
||||||
|
WHERE
|
||||||
|
O.[Id] = @OrganizationId
|
||||||
|
AND O.[Enabled] = 1
|
||||||
|
AND OU.[Status] = 2 -- Confirmed
|
||||||
|
AND (
|
||||||
|
CU.[ReadOnly] = 0
|
||||||
|
OR CG.[ReadOnly] = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
IF (SELECT COUNT(1) FROM #AvailableCollections) < 1
|
||||||
|
BEGIN
|
||||||
|
-- No writable collections available to share with in this organization.
|
||||||
|
RETURN
|
||||||
|
END
|
||||||
|
|
||||||
|
INSERT INTO [dbo].[CollectionCipher]
|
||||||
|
(
|
||||||
|
[CollectionId],
|
||||||
|
[CipherId]
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
[Collection].[Id],
|
||||||
|
[Cipher].[Id]
|
||||||
|
FROM
|
||||||
|
@CollectionIds [Collection]
|
||||||
|
INNER JOIN
|
||||||
|
@CipherIds [Cipher] ON 1 = 1
|
||||||
|
WHERE
|
||||||
|
[Collection].[Id] IN (SELECT [Id] FROM #AvailableCollections)
|
||||||
|
|
||||||
|
EXEC [dbo].[User_BumpAccountRevisionDateByOrganizationId] @OrganizationId
|
||||||
|
END
|
@ -0,0 +1,77 @@
|
|||||||
|
CREATE PROCEDURE [dbo].[CollectionCipher_UpdateCollections_V2]
|
||||||
|
@CipherId UNIQUEIDENTIFIER,
|
||||||
|
@UserId UNIQUEIDENTIFIER,
|
||||||
|
@CollectionIds AS [dbo].[GuidIdArray] READONLY
|
||||||
|
AS
|
||||||
|
BEGIN
|
||||||
|
SET NOCOUNT ON
|
||||||
|
|
||||||
|
DECLARE @OrgId UNIQUEIDENTIFIER = (
|
||||||
|
SELECT TOP 1
|
||||||
|
[OrganizationId]
|
||||||
|
FROM
|
||||||
|
[dbo].[Cipher]
|
||||||
|
WHERE
|
||||||
|
[Id] = @CipherId
|
||||||
|
)
|
||||||
|
|
||||||
|
;WITH [AvailableCollectionsCTE] AS(
|
||||||
|
SELECT
|
||||||
|
C.[Id]
|
||||||
|
FROM
|
||||||
|
[dbo].[Collection] C
|
||||||
|
INNER JOIN
|
||||||
|
[Organization] O ON O.[Id] = C.[OrganizationId]
|
||||||
|
INNER JOIN
|
||||||
|
[dbo].[OrganizationUser] OU ON OU.[OrganizationId] = O.[Id] AND OU.[UserId] = @UserId
|
||||||
|
LEFT JOIN
|
||||||
|
[dbo].[CollectionUser] CU ON CU.[CollectionId] = C.[Id] AND CU.[OrganizationUserId] = OU.[Id]
|
||||||
|
LEFT JOIN
|
||||||
|
[dbo].[GroupUser] GU ON CU.[CollectionId] IS NULL AND GU.[OrganizationUserId] = OU.[Id]
|
||||||
|
LEFT JOIN
|
||||||
|
[dbo].[Group] G ON G.[Id] = GU.[GroupId]
|
||||||
|
LEFT JOIN
|
||||||
|
[dbo].[CollectionGroup] CG ON CG.[CollectionId] = C.[Id] AND CG.[GroupId] = GU.[GroupId]
|
||||||
|
WHERE
|
||||||
|
O.[Id] = @OrgId
|
||||||
|
AND O.[Enabled] = 1
|
||||||
|
AND OU.[Status] = 2 -- Confirmed
|
||||||
|
AND (
|
||||||
|
CU.[ReadOnly] = 0
|
||||||
|
OR CG.[ReadOnly] = 0
|
||||||
|
)
|
||||||
|
),
|
||||||
|
[CollectionCiphersCTE] AS(
|
||||||
|
SELECT
|
||||||
|
[CollectionId],
|
||||||
|
[CipherId]
|
||||||
|
FROM
|
||||||
|
[dbo].[CollectionCipher]
|
||||||
|
WHERE
|
||||||
|
[CipherId] = @CipherId
|
||||||
|
)
|
||||||
|
MERGE
|
||||||
|
[CollectionCiphersCTE] AS [Target]
|
||||||
|
USING
|
||||||
|
@CollectionIds AS [Source]
|
||||||
|
ON
|
||||||
|
[Target].[CollectionId] = [Source].[Id]
|
||||||
|
AND [Target].[CipherId] = @CipherId
|
||||||
|
WHEN NOT MATCHED BY TARGET
|
||||||
|
AND [Source].[Id] IN (SELECT [Id] FROM [AvailableCollectionsCTE]) THEN
|
||||||
|
INSERT VALUES
|
||||||
|
(
|
||||||
|
[Source].[Id],
|
||||||
|
@CipherId
|
||||||
|
)
|
||||||
|
WHEN NOT MATCHED BY SOURCE
|
||||||
|
AND [Target].[CipherId] = @CipherId
|
||||||
|
AND [Target].[CollectionId] IN (SELECT [Id] FROM [AvailableCollectionsCTE]) THEN
|
||||||
|
DELETE
|
||||||
|
;
|
||||||
|
|
||||||
|
IF @OrgId IS NOT NULL
|
||||||
|
BEGIN
|
||||||
|
EXEC [dbo].[User_BumpAccountRevisionDateByOrganizationId] @OrgId
|
||||||
|
END
|
||||||
|
END
|
@ -116,7 +116,7 @@ public class SyncControllerTests
|
|||||||
|
|
||||||
// Returns for methods only called if we have enabled orgs
|
// Returns for methods only called if we have enabled orgs
|
||||||
collectionRepository.GetManyByUserIdAsync(user.Id).Returns(collections);
|
collectionRepository.GetManyByUserIdAsync(user.Id).Returns(collections);
|
||||||
collectionCipherRepository.GetManyByUserIdAsync(user.Id).Returns(new List<CollectionCipher>());
|
collectionCipherRepository.GetManyByUserIdAsync(user.Id, Arg.Any<bool>()).Returns(new List<CollectionCipher>());
|
||||||
|
|
||||||
// Back to standard test setup
|
// Back to standard test setup
|
||||||
userService.TwoFactorIsEnabledAsync(user).Returns(false);
|
userService.TwoFactorIsEnabledAsync(user).Returns(false);
|
||||||
@ -281,7 +281,7 @@ public class SyncControllerTests
|
|||||||
|
|
||||||
// Returns for methods only called if we have enabled orgs
|
// Returns for methods only called if we have enabled orgs
|
||||||
collectionRepository.GetManyByUserIdAsync(user.Id).Returns(collections);
|
collectionRepository.GetManyByUserIdAsync(user.Id).Returns(collections);
|
||||||
collectionCipherRepository.GetManyByUserIdAsync(user.Id).Returns(new List<CollectionCipher>());
|
collectionCipherRepository.GetManyByUserIdAsync(user.Id, Arg.Any<bool>()).Returns(new List<CollectionCipher>());
|
||||||
|
|
||||||
// Back to standard test setup
|
// Back to standard test setup
|
||||||
userService.TwoFactorIsEnabledAsync(user).Returns(false);
|
userService.TwoFactorIsEnabledAsync(user).Returns(false);
|
||||||
@ -346,7 +346,7 @@ public class SyncControllerTests
|
|||||||
await collectionRepository.ReceivedWithAnyArgs(1)
|
await collectionRepository.ReceivedWithAnyArgs(1)
|
||||||
.GetManyByUserIdAsync(default);
|
.GetManyByUserIdAsync(default);
|
||||||
await collectionCipherRepository.ReceivedWithAnyArgs(1)
|
await collectionCipherRepository.ReceivedWithAnyArgs(1)
|
||||||
.GetManyByUserIdAsync(default);
|
.GetManyByUserIdAsync(default, default);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -354,7 +354,7 @@ public class SyncControllerTests
|
|||||||
await collectionRepository.ReceivedWithAnyArgs(0)
|
await collectionRepository.ReceivedWithAnyArgs(0)
|
||||||
.GetManyByUserIdAsync(default);
|
.GetManyByUserIdAsync(default);
|
||||||
await collectionCipherRepository.ReceivedWithAnyArgs(0)
|
await collectionCipherRepository.ReceivedWithAnyArgs(0)
|
||||||
.GetManyByUserIdAsync(default);
|
.GetManyByUserIdAsync(default, default);
|
||||||
}
|
}
|
||||||
|
|
||||||
await userService.ReceivedWithAnyArgs(1)
|
await userService.ReceivedWithAnyArgs(1)
|
||||||
|
@ -432,7 +432,7 @@ public class CipherServiceTests
|
|||||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
||||||
var attachmentStorageService = sutProvider.GetDependency<IAttachmentStorageService>();
|
var attachmentStorageService = sutProvider.GetDependency<IAttachmentStorageService>();
|
||||||
var collectionCipherRepository = sutProvider.GetDependency<ICollectionCipherRepository>();
|
var collectionCipherRepository = sutProvider.GetDependency<ICollectionCipherRepository>();
|
||||||
collectionCipherRepository.GetManyByUserIdCipherIdAsync(cipher.UserId.Value, cipher.Id).Returns(
|
collectionCipherRepository.GetManyByUserIdCipherIdAsync(cipher.UserId.Value, cipher.Id, Arg.Any<bool>()).Returns(
|
||||||
Task.FromResult((ICollection<CollectionCipher>)new List<CollectionCipher>
|
Task.FromResult((ICollection<CollectionCipher>)new List<CollectionCipher>
|
||||||
{
|
{
|
||||||
new CollectionCipher
|
new CollectionCipher
|
||||||
@ -512,7 +512,7 @@ public class CipherServiceTests
|
|||||||
Assert.Contains("ex from StartShareAttachmentAsync", exception.Message);
|
Assert.Contains("ex from StartShareAttachmentAsync", exception.Message);
|
||||||
|
|
||||||
await collectionCipherRepository.Received().UpdateCollectionsAsync(cipher.Id, cipher.UserId.Value,
|
await collectionCipherRepository.Received().UpdateCollectionsAsync(cipher.Id, cipher.UserId.Value,
|
||||||
Arg.Is<List<Guid>>(ids => ids.Count == 1 && ids[0] != collectionIds[0]));
|
Arg.Is<List<Guid>>(ids => ids.Count == 1 && ids[0] != collectionIds[0]), Arg.Any<bool>());
|
||||||
|
|
||||||
await cipherRepository.Received().ReplaceAsync(Arg.Is<Cipher>(c =>
|
await cipherRepository.Received().ReplaceAsync(Arg.Is<Cipher>(c =>
|
||||||
c.GetAttachments()[v0AttachmentId].Key == null
|
c.GetAttachments()[v0AttachmentId].Key == null
|
||||||
@ -537,7 +537,7 @@ public class CipherServiceTests
|
|||||||
var attachmentStorageService = sutProvider.GetDependency<IAttachmentStorageService>();
|
var attachmentStorageService = sutProvider.GetDependency<IAttachmentStorageService>();
|
||||||
var userRepository = sutProvider.GetDependency<IUserRepository>();
|
var userRepository = sutProvider.GetDependency<IUserRepository>();
|
||||||
var collectionCipherRepository = sutProvider.GetDependency<ICollectionCipherRepository>();
|
var collectionCipherRepository = sutProvider.GetDependency<ICollectionCipherRepository>();
|
||||||
collectionCipherRepository.GetManyByUserIdCipherIdAsync(cipher.UserId.Value, cipher.Id).Returns(
|
collectionCipherRepository.GetManyByUserIdCipherIdAsync(cipher.UserId.Value, cipher.Id, Arg.Any<bool>()).Returns(
|
||||||
Task.FromResult((ICollection<CollectionCipher>)new List<CollectionCipher>
|
Task.FromResult((ICollection<CollectionCipher>)new List<CollectionCipher>
|
||||||
{
|
{
|
||||||
new CollectionCipher
|
new CollectionCipher
|
||||||
@ -644,7 +644,7 @@ public class CipherServiceTests
|
|||||||
Assert.Contains("ex from StartShareAttachmentAsync", exception.Message);
|
Assert.Contains("ex from StartShareAttachmentAsync", exception.Message);
|
||||||
|
|
||||||
await collectionCipherRepository.Received().UpdateCollectionsAsync(cipher.Id, cipher.UserId.Value,
|
await collectionCipherRepository.Received().UpdateCollectionsAsync(cipher.Id, cipher.UserId.Value,
|
||||||
Arg.Is<List<Guid>>(ids => ids.Count == 1 && ids[0] != collectionIds[0]));
|
Arg.Is<List<Guid>>(ids => ids.Count == 1 && ids[0] != collectionIds[0]), Arg.Any<bool>());
|
||||||
|
|
||||||
await cipherRepository.Received().ReplaceAsync(Arg.Is<Cipher>(c =>
|
await cipherRepository.Received().ReplaceAsync(Arg.Is<Cipher>(c =>
|
||||||
c.GetAttachments()[v0AttachmentId1].Key == null
|
c.GetAttachments()[v0AttachmentId1].Key == null
|
||||||
|
@ -0,0 +1,212 @@
|
|||||||
|
-- Flexible Collections: create new CollectionCipher sprocs that don't use AccessAll logic
|
||||||
|
|
||||||
|
-- CollectionCipher_ReadByUserId_V2
|
||||||
|
CREATE OR ALTER PROCEDURE [dbo].[CollectionCipher_ReadByUserId_V2]
|
||||||
|
@UserId UNIQUEIDENTIFIER
|
||||||
|
AS
|
||||||
|
BEGIN
|
||||||
|
SET NOCOUNT ON
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
CC.*
|
||||||
|
FROM
|
||||||
|
[dbo].[CollectionCipher] CC
|
||||||
|
INNER JOIN
|
||||||
|
[dbo].[Collection] S ON S.[Id] = CC.[CollectionId]
|
||||||
|
INNER JOIN
|
||||||
|
[dbo].[OrganizationUser] OU ON OU.[OrganizationId] = S.[OrganizationId] AND OU.[UserId] = @UserId
|
||||||
|
LEFT JOIN
|
||||||
|
[dbo].[CollectionUser] CU ON CU.[CollectionId] = S.[Id] AND CU.[OrganizationUserId] = OU.[Id]
|
||||||
|
LEFT JOIN
|
||||||
|
[dbo].[GroupUser] GU ON CU.[CollectionId] IS NULL AND GU.[OrganizationUserId] = OU.[Id]
|
||||||
|
LEFT JOIN
|
||||||
|
[dbo].[Group] G ON G.[Id] = GU.[GroupId]
|
||||||
|
LEFT JOIN
|
||||||
|
[dbo].[CollectionGroup] CG ON CG.[CollectionId] = CC.[CollectionId] AND CG.[GroupId] = GU.[GroupId]
|
||||||
|
WHERE
|
||||||
|
OU.[Status] = 2 -- Confirmed
|
||||||
|
AND (
|
||||||
|
CU.[CollectionId] IS NOT NULL
|
||||||
|
OR CG.[CollectionId] IS NOT NULL
|
||||||
|
)
|
||||||
|
END
|
||||||
|
GO
|
||||||
|
|
||||||
|
-- CollectionCipher_ReadByUserIdCipherId_V2
|
||||||
|
CREATE OR ALTER PROCEDURE [dbo].[CollectionCipher_ReadByUserIdCipherId_V2]
|
||||||
|
@UserId UNIQUEIDENTIFIER,
|
||||||
|
@CipherId UNIQUEIDENTIFIER
|
||||||
|
AS
|
||||||
|
BEGIN
|
||||||
|
SET NOCOUNT ON
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
CC.*
|
||||||
|
FROM
|
||||||
|
[dbo].[CollectionCipher] CC
|
||||||
|
INNER JOIN
|
||||||
|
[dbo].[Collection] S ON S.[Id] = CC.[CollectionId]
|
||||||
|
INNER JOIN
|
||||||
|
[dbo].[OrganizationUser] OU ON OU.[OrganizationId] = S.[OrganizationId] AND OU.[UserId] = @UserId
|
||||||
|
LEFT JOIN
|
||||||
|
[dbo].[CollectionUser] CU ON CU.[CollectionId] = S.[Id] AND CU.[OrganizationUserId] = OU.[Id]
|
||||||
|
LEFT JOIN
|
||||||
|
[dbo].[GroupUser] GU ON CU.[CollectionId] IS NULL AND GU.[OrganizationUserId] = OU.[Id]
|
||||||
|
LEFT JOIN
|
||||||
|
[dbo].[Group] G ON G.[Id] = GU.[GroupId]
|
||||||
|
LEFT JOIN
|
||||||
|
[dbo].[CollectionGroup] CG ON CG.[CollectionId] = CC.[CollectionId] AND CG.[GroupId] = GU.[GroupId]
|
||||||
|
WHERE
|
||||||
|
CC.[CipherId] = @CipherId
|
||||||
|
AND OU.[Status] = 2 -- Confirmed
|
||||||
|
AND (
|
||||||
|
CU.[CollectionId] IS NOT NULL
|
||||||
|
OR CG.[CollectionId] IS NOT NULL
|
||||||
|
)
|
||||||
|
END
|
||||||
|
GO
|
||||||
|
|
||||||
|
-- CollectionCipher_UpdateCollections_V2
|
||||||
|
CREATE OR ALTER PROCEDURE [dbo].[CollectionCipher_UpdateCollections_V2]
|
||||||
|
@CipherId UNIQUEIDENTIFIER,
|
||||||
|
@UserId UNIQUEIDENTIFIER,
|
||||||
|
@CollectionIds AS [dbo].[GuidIdArray] READONLY
|
||||||
|
AS
|
||||||
|
BEGIN
|
||||||
|
SET NOCOUNT ON
|
||||||
|
|
||||||
|
DECLARE @OrgId UNIQUEIDENTIFIER = (
|
||||||
|
SELECT TOP 1
|
||||||
|
[OrganizationId]
|
||||||
|
FROM
|
||||||
|
[dbo].[Cipher]
|
||||||
|
WHERE
|
||||||
|
[Id] = @CipherId
|
||||||
|
)
|
||||||
|
|
||||||
|
;WITH [AvailableCollectionsCTE] AS(
|
||||||
|
SELECT
|
||||||
|
C.[Id]
|
||||||
|
FROM
|
||||||
|
[dbo].[Collection] C
|
||||||
|
INNER JOIN
|
||||||
|
[Organization] O ON O.[Id] = C.[OrganizationId]
|
||||||
|
INNER JOIN
|
||||||
|
[dbo].[OrganizationUser] OU ON OU.[OrganizationId] = O.[Id] AND OU.[UserId] = @UserId
|
||||||
|
LEFT JOIN
|
||||||
|
[dbo].[CollectionUser] CU ON CU.[CollectionId] = C.[Id] AND CU.[OrganizationUserId] = OU.[Id]
|
||||||
|
LEFT JOIN
|
||||||
|
[dbo].[GroupUser] GU ON CU.[CollectionId] IS NULL AND GU.[OrganizationUserId] = OU.[Id]
|
||||||
|
LEFT JOIN
|
||||||
|
[dbo].[Group] G ON G.[Id] = GU.[GroupId]
|
||||||
|
LEFT JOIN
|
||||||
|
[dbo].[CollectionGroup] CG ON CG.[CollectionId] = C.[Id] AND CG.[GroupId] = GU.[GroupId]
|
||||||
|
WHERE
|
||||||
|
O.[Id] = @OrgId
|
||||||
|
AND O.[Enabled] = 1
|
||||||
|
AND OU.[Status] = 2 -- Confirmed
|
||||||
|
AND (
|
||||||
|
CU.[ReadOnly] = 0
|
||||||
|
OR CG.[ReadOnly] = 0
|
||||||
|
)
|
||||||
|
),
|
||||||
|
[CollectionCiphersCTE] AS(
|
||||||
|
SELECT
|
||||||
|
[CollectionId],
|
||||||
|
[CipherId]
|
||||||
|
FROM
|
||||||
|
[dbo].[CollectionCipher]
|
||||||
|
WHERE
|
||||||
|
[CipherId] = @CipherId
|
||||||
|
)
|
||||||
|
MERGE
|
||||||
|
[CollectionCiphersCTE] AS [Target]
|
||||||
|
USING
|
||||||
|
@CollectionIds AS [Source]
|
||||||
|
ON
|
||||||
|
[Target].[CollectionId] = [Source].[Id]
|
||||||
|
AND [Target].[CipherId] = @CipherId
|
||||||
|
WHEN NOT MATCHED BY TARGET
|
||||||
|
AND [Source].[Id] IN (SELECT [Id] FROM [AvailableCollectionsCTE]) THEN
|
||||||
|
INSERT VALUES
|
||||||
|
(
|
||||||
|
[Source].[Id],
|
||||||
|
@CipherId
|
||||||
|
)
|
||||||
|
WHEN NOT MATCHED BY SOURCE
|
||||||
|
AND [Target].[CipherId] = @CipherId
|
||||||
|
AND [Target].[CollectionId] IN (SELECT [Id] FROM [AvailableCollectionsCTE]) THEN
|
||||||
|
DELETE
|
||||||
|
;
|
||||||
|
|
||||||
|
IF @OrgId IS NOT NULL
|
||||||
|
BEGIN
|
||||||
|
EXEC [dbo].[User_BumpAccountRevisionDateByOrganizationId] @OrgId
|
||||||
|
END
|
||||||
|
END
|
||||||
|
GO
|
||||||
|
|
||||||
|
-- CollectionCipher_UpdateCollectionsForCiphers_V2
|
||||||
|
CREATE OR ALTER PROCEDURE [dbo].[CollectionCipher_UpdateCollectionsForCiphers_V2]
|
||||||
|
@CipherIds AS [dbo].[GuidIdArray] READONLY,
|
||||||
|
@OrganizationId UNIQUEIDENTIFIER,
|
||||||
|
@UserId UNIQUEIDENTIFIER,
|
||||||
|
@CollectionIds AS [dbo].[GuidIdArray] READONLY
|
||||||
|
AS
|
||||||
|
BEGIN
|
||||||
|
SET NOCOUNT ON
|
||||||
|
|
||||||
|
CREATE TABLE #AvailableCollections (
|
||||||
|
[Id] UNIQUEIDENTIFIER
|
||||||
|
)
|
||||||
|
|
||||||
|
INSERT INTO #AvailableCollections
|
||||||
|
SELECT
|
||||||
|
C.[Id]
|
||||||
|
FROM
|
||||||
|
[dbo].[Collection] C
|
||||||
|
INNER JOIN
|
||||||
|
[Organization] O ON O.[Id] = C.[OrganizationId]
|
||||||
|
INNER JOIN
|
||||||
|
[dbo].[OrganizationUser] OU ON OU.[OrganizationId] = O.[Id] AND OU.[UserId] = @UserId
|
||||||
|
LEFT JOIN
|
||||||
|
[dbo].[CollectionUser] CU ON CU.[CollectionId] = C.[Id] AND CU.[OrganizationUserId] = OU.[Id]
|
||||||
|
LEFT JOIN
|
||||||
|
[dbo].[GroupUser] GU ON CU.[CollectionId] IS NULL AND GU.[OrganizationUserId] = OU.[Id]
|
||||||
|
LEFT JOIN
|
||||||
|
[dbo].[Group] G ON G.[Id] = GU.[GroupId]
|
||||||
|
LEFT JOIN
|
||||||
|
[dbo].[CollectionGroup] CG ON CG.[CollectionId] = C.[Id] AND CG.[GroupId] = GU.[GroupId]
|
||||||
|
WHERE
|
||||||
|
O.[Id] = @OrganizationId
|
||||||
|
AND O.[Enabled] = 1
|
||||||
|
AND OU.[Status] = 2 -- Confirmed
|
||||||
|
AND (
|
||||||
|
CU.[ReadOnly] = 0
|
||||||
|
OR CG.[ReadOnly] = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
IF (SELECT COUNT(1) FROM #AvailableCollections) < 1
|
||||||
|
BEGIN
|
||||||
|
-- No writable collections available to share with in this organization.
|
||||||
|
RETURN
|
||||||
|
END
|
||||||
|
|
||||||
|
INSERT INTO [dbo].[CollectionCipher]
|
||||||
|
(
|
||||||
|
[CollectionId],
|
||||||
|
[CipherId]
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
[Collection].[Id],
|
||||||
|
[Cipher].[Id]
|
||||||
|
FROM
|
||||||
|
@CollectionIds [Collection]
|
||||||
|
INNER JOIN
|
||||||
|
@CipherIds [Cipher] ON 1 = 1
|
||||||
|
WHERE
|
||||||
|
[Collection].[Id] IN (SELECT [Id] FROM #AvailableCollections)
|
||||||
|
|
||||||
|
EXEC [dbo].[User_BumpAccountRevisionDateByOrganizationId] @OrganizationId
|
||||||
|
END
|
||||||
|
GO
|
Loading…
Reference in New Issue
Block a user