mirror of
https://github.com/bitwarden/server.git
synced 2024-11-27 13:05:23 +01:00
[PS-1928] Add BumpAccountRevisionDate
methods (#2458)
* Move RevisionDate Bumps to Extension Class * Add Tests against live databases * Run Formatting * Fix Typo * Fix Test Solution Typo * Await ReplaceAsync
This commit is contained in:
parent
41db511872
commit
efe91fd0d8
@ -19,6 +19,12 @@
|
|||||||
"commands": [
|
"commands": [
|
||||||
"reportgenerator"
|
"reportgenerator"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"dotnet-ef": {
|
||||||
|
"version": "6.0.11",
|
||||||
|
"commands": [
|
||||||
|
"dotnet-ef"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -104,6 +104,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MicroBenchmarks", "perf\Mic
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Scim.Test", "bitwarden_license\test\Scim.Test\Scim.Test.csproj", "{B1595DA3-4C60-41AA-8BF0-499A5F75A885}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Scim.Test", "bitwarden_license\test\Scim.Test\Scim.Test.csproj", "{B1595DA3-4C60-41AA-8BF0-499A5F75A885}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Infrastructure.IntegrationTest", "test\Infrastructure.IntegrationTest\Infrastructure.IntegrationTest.csproj", "{7E9A7DD5-EB78-4AC5-BFD5-64573FD2533B}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@ -252,6 +254,10 @@ Global
|
|||||||
{B1595DA3-4C60-41AA-8BF0-499A5F75A885}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{B1595DA3-4C60-41AA-8BF0-499A5F75A885}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{B1595DA3-4C60-41AA-8BF0-499A5F75A885}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{B1595DA3-4C60-41AA-8BF0-499A5F75A885}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{B1595DA3-4C60-41AA-8BF0-499A5F75A885}.Release|Any CPU.Build.0 = Release|Any CPU
|
{B1595DA3-4C60-41AA-8BF0-499A5F75A885}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{7E9A7DD5-EB78-4AC5-BFD5-64573FD2533B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{7E9A7DD5-EB78-4AC5-BFD5-64573FD2533B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{7E9A7DD5-EB78-4AC5-BFD5-64573FD2533B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{7E9A7DD5-EB78-4AC5-BFD5-64573FD2533B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
@ -292,6 +298,7 @@ Global
|
|||||||
{FE998849-5FC8-41A2-B7C9-9227901471A0} = {287CFF34-BBDB-4BC4-AF88-1E19A5A4679B}
|
{FE998849-5FC8-41A2-B7C9-9227901471A0} = {287CFF34-BBDB-4BC4-AF88-1E19A5A4679B}
|
||||||
{9C8F8255-5F74-4085-AB9C-9075CF6DDC61} = {EC2D422A-6060-48E2-AAD2-37220D759F03}
|
{9C8F8255-5F74-4085-AB9C-9075CF6DDC61} = {EC2D422A-6060-48E2-AAD2-37220D759F03}
|
||||||
{B1595DA3-4C60-41AA-8BF0-499A5F75A885} = {287CFF34-BBDB-4BC4-AF88-1E19A5A4679B}
|
{B1595DA3-4C60-41AA-8BF0-499A5F75A885} = {287CFF34-BBDB-4BC4-AF88-1E19A5A4679B}
|
||||||
|
{7E9A7DD5-EB78-4AC5-BFD5-64573FD2533B} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84F}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {E01CBF68-2E20-425F-9EDB-E0A6510CA92F}
|
SolutionGuid = {E01CBF68-2E20-425F-9EDB-E0A6510CA92F}
|
||||||
|
@ -1,13 +1,10 @@
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using AutoMapper;
|
using AutoMapper;
|
||||||
using Bit.Core.Enums;
|
|
||||||
using Bit.Core.Enums.Provider;
|
|
||||||
using Bit.Infrastructure.EntityFramework.Models;
|
using Bit.Infrastructure.EntityFramework.Models;
|
||||||
using Bit.Infrastructure.EntityFramework.Repositories.Queries;
|
using Bit.Infrastructure.EntityFramework.Repositories.Queries;
|
||||||
using LinqToDB.Data;
|
using LinqToDB.Data;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Cipher = Bit.Core.Entities.Cipher;
|
|
||||||
using User = Bit.Core.Entities.User;
|
using User = Bit.Core.Entities.User;
|
||||||
|
|
||||||
namespace Bit.Infrastructure.EntityFramework.Repositories;
|
namespace Bit.Infrastructure.EntityFramework.Repositories;
|
||||||
@ -51,68 +48,6 @@ public abstract class BaseEntityFrameworkRepository
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async Task UserBumpAccountRevisionDateByCipherId(Cipher cipher)
|
|
||||||
{
|
|
||||||
var list = new List<Cipher> { cipher };
|
|
||||||
await UserBumpAccountRevisionDateByCipherId(list);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async Task UserBumpAccountRevisionDateByCipherId(IEnumerable<Cipher> ciphers)
|
|
||||||
{
|
|
||||||
using (var scope = ServiceScopeFactory.CreateScope())
|
|
||||||
{
|
|
||||||
foreach (var cipher in ciphers)
|
|
||||||
{
|
|
||||||
var dbContext = GetDatabaseContext(scope);
|
|
||||||
var query = new UserBumpAccountRevisionDateByCipherIdQuery(cipher);
|
|
||||||
var users = query.Run(dbContext);
|
|
||||||
|
|
||||||
await users.ForEachAsync(e =>
|
|
||||||
{
|
|
||||||
dbContext.Attach(e);
|
|
||||||
e.RevisionDate = DateTime.UtcNow;
|
|
||||||
});
|
|
||||||
await dbContext.SaveChangesAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async Task UserBumpAccountRevisionDateByOrganizationId(Guid organizationId)
|
|
||||||
{
|
|
||||||
using (var scope = ServiceScopeFactory.CreateScope())
|
|
||||||
{
|
|
||||||
var dbContext = GetDatabaseContext(scope);
|
|
||||||
var query = new UserBumpAccountRevisionDateByOrganizationIdQuery(organizationId);
|
|
||||||
var users = query.Run(dbContext);
|
|
||||||
await users.ForEachAsync(e =>
|
|
||||||
{
|
|
||||||
dbContext.Attach(e);
|
|
||||||
e.RevisionDate = DateTime.UtcNow;
|
|
||||||
});
|
|
||||||
await dbContext.SaveChangesAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async Task UserBumpAccountRevisionDate(Guid userId)
|
|
||||||
{
|
|
||||||
await UserBumpManyAccountRevisionDates(new[] { userId });
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async Task UserBumpManyAccountRevisionDates(ICollection<Guid> userIds)
|
|
||||||
{
|
|
||||||
using (var scope = ServiceScopeFactory.CreateScope())
|
|
||||||
{
|
|
||||||
var dbContext = GetDatabaseContext(scope);
|
|
||||||
var users = dbContext.Users.Where(u => userIds.Contains(u.Id));
|
|
||||||
await users.ForEachAsync(u =>
|
|
||||||
{
|
|
||||||
dbContext.Attach(u);
|
|
||||||
u.RevisionDate = DateTime.UtcNow;
|
|
||||||
});
|
|
||||||
await dbContext.SaveChangesAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async Task OrganizationUpdateStorage(Guid organizationId)
|
protected async Task OrganizationUpdateStorage(Guid organizationId)
|
||||||
{
|
{
|
||||||
using (var scope = ServiceScopeFactory.CreateScope())
|
using (var scope = ServiceScopeFactory.CreateScope())
|
||||||
@ -197,81 +132,4 @@ public abstract class BaseEntityFrameworkRepository
|
|||||||
await dbContext.SaveChangesAsync();
|
await dbContext.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async Task UserBumpAccountRevisionDateByCollectionId(Guid collectionId, Guid organizationId)
|
|
||||||
{
|
|
||||||
using (var scope = ServiceScopeFactory.CreateScope())
|
|
||||||
{
|
|
||||||
var dbContext = GetDatabaseContext(scope);
|
|
||||||
var query = from u in dbContext.Users
|
|
||||||
join ou in dbContext.OrganizationUsers
|
|
||||||
on u.Id equals ou.UserId
|
|
||||||
join cu in dbContext.CollectionUsers
|
|
||||||
on ou.Id equals cu.OrganizationUserId into cu_g
|
|
||||||
from cu in cu_g.DefaultIfEmpty()
|
|
||||||
where !ou.AccessAll && cu.CollectionId.Equals(collectionId)
|
|
||||||
join gu in dbContext.GroupUsers
|
|
||||||
on ou.Id equals gu.OrganizationUserId into gu_g
|
|
||||||
from gu in gu_g.DefaultIfEmpty()
|
|
||||||
where cu.CollectionId == default(Guid) && !ou.AccessAll
|
|
||||||
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 gu.GroupId equals cg.GroupId into cg_g
|
|
||||||
from cg in cg_g.DefaultIfEmpty()
|
|
||||||
where !g.AccessAll && cg.CollectionId == collectionId &&
|
|
||||||
(ou.OrganizationId == organizationId && ou.Status == OrganizationUserStatusType.Confirmed &&
|
|
||||||
(cu.CollectionId != default(Guid) || cg.CollectionId != default(Guid) || ou.AccessAll || g.AccessAll))
|
|
||||||
select new { u, ou, cu, gu, g, cg };
|
|
||||||
var users = query.Select(x => x.u);
|
|
||||||
await users.ForEachAsync(u =>
|
|
||||||
{
|
|
||||||
dbContext.Attach(u);
|
|
||||||
u.RevisionDate = DateTime.UtcNow;
|
|
||||||
});
|
|
||||||
await dbContext.SaveChangesAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async Task UserBumpAccountRevisionDateByOrganizationUserId(Guid organizationUserId)
|
|
||||||
{
|
|
||||||
using (var scope = ServiceScopeFactory.CreateScope())
|
|
||||||
{
|
|
||||||
var dbContext = GetDatabaseContext(scope);
|
|
||||||
var query = from u in dbContext.Users
|
|
||||||
join ou in dbContext.OrganizationUsers
|
|
||||||
on u.Id equals ou.UserId
|
|
||||||
where ou.Id.Equals(organizationUserId) && ou.Status.Equals(OrganizationUserStatusType.Confirmed)
|
|
||||||
select new { u, ou };
|
|
||||||
var users = query.Select(x => x.u);
|
|
||||||
await users.ForEachAsync(u =>
|
|
||||||
{
|
|
||||||
dbContext.Attach(u);
|
|
||||||
u.AccountRevisionDate = DateTime.UtcNow;
|
|
||||||
});
|
|
||||||
await dbContext.SaveChangesAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async Task UserBumpAccountRevisionDateByProviderUserIds(ICollection<Guid> providerUserIds)
|
|
||||||
{
|
|
||||||
using (var scope = ServiceScopeFactory.CreateScope())
|
|
||||||
{
|
|
||||||
var dbContext = GetDatabaseContext(scope);
|
|
||||||
var query = from pu in dbContext.ProviderUsers
|
|
||||||
join u in dbContext.Users
|
|
||||||
on pu.UserId equals u.Id
|
|
||||||
where pu.Status.Equals(ProviderUserStatusType.Confirmed) &&
|
|
||||||
providerUserIds.Contains(pu.Id)
|
|
||||||
select new { pu, u };
|
|
||||||
var users = query.Select(x => x.u);
|
|
||||||
await users.ForEachAsync(u =>
|
|
||||||
{
|
|
||||||
dbContext.Attach(u);
|
|
||||||
u.AccountRevisionDate = DateTime.UtcNow;
|
|
||||||
});
|
|
||||||
await dbContext.SaveChangesAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -29,40 +29,59 @@ public class CipherRepository : Repository<Core.Entities.Cipher, Cipher, Guid>,
|
|||||||
var dbContext = GetDatabaseContext(scope);
|
var dbContext = GetDatabaseContext(scope);
|
||||||
if (cipher.OrganizationId.HasValue)
|
if (cipher.OrganizationId.HasValue)
|
||||||
{
|
{
|
||||||
await UserBumpAccountRevisionDateByCipherId(cipher);
|
await dbContext.UserBumpAccountRevisionDateByCipherIdAsync(cipher.Id, cipher.OrganizationId);
|
||||||
}
|
}
|
||||||
else if (cipher.UserId.HasValue)
|
else if (cipher.UserId.HasValue)
|
||||||
{
|
{
|
||||||
await UserBumpAccountRevisionDate(cipher.UserId.Value);
|
await dbContext.UserBumpAccountRevisionDateAsync(cipher.UserId.Value);
|
||||||
}
|
}
|
||||||
|
await dbContext.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
return cipher;
|
return cipher;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IQueryable<User> GetBumpedAccountsByCipherId(Core.Entities.Cipher cipher)
|
public override async Task DeleteAsync(Core.Entities.Cipher cipher)
|
||||||
{
|
{
|
||||||
using (var scope = ServiceScopeFactory.CreateScope())
|
using (var scope = ServiceScopeFactory.CreateScope())
|
||||||
{
|
{
|
||||||
var dbContext = GetDatabaseContext(scope);
|
var dbContext = GetDatabaseContext(scope);
|
||||||
var query = new UserBumpAccountRevisionDateByCipherIdQuery(cipher);
|
var cipherInfo = await dbContext.Ciphers
|
||||||
return query.Run(dbContext);
|
.Where(c => c.Id == cipher.Id)
|
||||||
|
.Select(c => new { c.UserId, c.OrganizationId, HasAttachments = c.Attachments != null })
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
|
||||||
|
await base.DeleteAsync(cipher);
|
||||||
|
|
||||||
|
if (cipherInfo?.OrganizationId != null)
|
||||||
|
{
|
||||||
|
if (cipherInfo.HasAttachments == true)
|
||||||
|
{
|
||||||
|
await OrganizationUpdateStorage(cipherInfo.OrganizationId.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
await dbContext.UserBumpAccountRevisionDateByCipherIdAsync(cipher.Id, cipherInfo.OrganizationId);
|
||||||
|
}
|
||||||
|
else if (cipherInfo?.UserId != null)
|
||||||
|
{
|
||||||
|
if (cipherInfo.HasAttachments)
|
||||||
|
{
|
||||||
|
await UserUpdateStorage(cipherInfo.UserId.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
await dbContext.UserBumpAccountRevisionDateAsync(cipherInfo.UserId.Value);
|
||||||
|
}
|
||||||
|
await dbContext.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task CreateAsync(Core.Entities.Cipher cipher, IEnumerable<Guid> collectionIds)
|
public async Task CreateAsync(Core.Entities.Cipher cipher, IEnumerable<Guid> collectionIds)
|
||||||
{
|
{
|
||||||
cipher = await base.CreateAsync(cipher);
|
cipher = await CreateAsync(cipher);
|
||||||
await UpdateCollections(cipher, collectionIds);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task UpdateCollections(Core.Entities.Cipher cipher, IEnumerable<Guid> collectionIds)
|
|
||||||
{
|
|
||||||
using (var scope = ServiceScopeFactory.CreateScope())
|
using (var scope = ServiceScopeFactory.CreateScope())
|
||||||
{
|
{
|
||||||
var dbContext = GetDatabaseContext(scope);
|
var dbContext = GetDatabaseContext(scope);
|
||||||
var cipherEntity = await dbContext.Ciphers.FindAsync(cipher.Id);
|
await UpdateCollectionsAsync(dbContext, cipher.Id,
|
||||||
var query = new CipherUpdateCollectionsQuery(cipherEntity, collectionIds).Run(dbContext);
|
cipher.UserId, cipher.OrganizationId, collectionIds);
|
||||||
await dbContext.AddRangeAsync(query);
|
|
||||||
await dbContext.SaveChangesAsync();
|
await dbContext.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -88,16 +107,22 @@ public class CipherRepository : Repository<Core.Entities.Cipher, Cipher, Guid>,
|
|||||||
null;
|
null;
|
||||||
var entity = Mapper.Map<Cipher>((Core.Entities.Cipher)cipher);
|
var entity = Mapper.Map<Cipher>((Core.Entities.Cipher)cipher);
|
||||||
await dbContext.AddAsync(entity);
|
await dbContext.AddAsync(entity);
|
||||||
|
await dbContext.UserBumpAccountRevisionDateByCipherIdAsync(cipher.Id, cipher.OrganizationId.GetValueOrDefault());
|
||||||
await dbContext.SaveChangesAsync();
|
await dbContext.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
await UserBumpAccountRevisionDateByCipherId(cipher);
|
|
||||||
return cipher;
|
return cipher;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task CreateAsync(CipherDetails cipher, IEnumerable<Guid> collectionIds)
|
public async Task CreateAsync(CipherDetails cipher, IEnumerable<Guid> collectionIds)
|
||||||
{
|
{
|
||||||
cipher = await CreateAsyncReturnCipher(cipher);
|
cipher = await CreateAsyncReturnCipher(cipher);
|
||||||
await UpdateCollections(cipher, collectionIds);
|
using (var scope = ServiceScopeFactory.CreateScope())
|
||||||
|
{
|
||||||
|
var dbContext = GetDatabaseContext(scope);
|
||||||
|
await UpdateCollectionsAsync(dbContext, cipher.Id,
|
||||||
|
cipher.UserId, cipher.OrganizationId, collectionIds);
|
||||||
|
await dbContext.SaveChangesAsync();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task CreateAsync(IEnumerable<Core.Entities.Cipher> ciphers, IEnumerable<Core.Entities.Folder> folders)
|
public async Task CreateAsync(IEnumerable<Core.Entities.Cipher> ciphers, IEnumerable<Core.Entities.Folder> folders)
|
||||||
@ -114,7 +139,8 @@ public class CipherRepository : Repository<Core.Entities.Cipher, Cipher, Guid>,
|
|||||||
await dbContext.BulkCopyAsync(base.DefaultBulkCopyOptions, folderEntities);
|
await dbContext.BulkCopyAsync(base.DefaultBulkCopyOptions, folderEntities);
|
||||||
var cipherEntities = Mapper.Map<List<Cipher>>(ciphers);
|
var cipherEntities = Mapper.Map<List<Cipher>>(ciphers);
|
||||||
await dbContext.BulkCopyAsync(base.DefaultBulkCopyOptions, cipherEntities);
|
await dbContext.BulkCopyAsync(base.DefaultBulkCopyOptions, cipherEntities);
|
||||||
await UserBumpAccountRevisionDateByCipherId(ciphers);
|
await dbContext.UserBumpAccountRevisionDateByCipherIdAsync(ciphers);
|
||||||
|
await dbContext.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,7 +166,8 @@ public class CipherRepository : Repository<Core.Entities.Cipher, Cipher, Guid>,
|
|||||||
await dbContext.BulkCopyAsync(base.DefaultBulkCopyOptions, collectionCipherEntities);
|
await dbContext.BulkCopyAsync(base.DefaultBulkCopyOptions, collectionCipherEntities);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await UserBumpAccountRevisionDateByOrganizationId(ciphers.First().OrganizationId.Value);
|
await dbContext.UserBumpAccountRevisionDateByOrganizationIdAsync(ciphers.First().OrganizationId.Value);
|
||||||
|
await dbContext.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,13 +190,14 @@ public class CipherRepository : Repository<Core.Entities.Cipher, Cipher, Guid>,
|
|||||||
if (cipher.OrganizationId.HasValue)
|
if (cipher.OrganizationId.HasValue)
|
||||||
{
|
{
|
||||||
await OrganizationUpdateStorage(cipher.OrganizationId.Value);
|
await OrganizationUpdateStorage(cipher.OrganizationId.Value);
|
||||||
await UserBumpAccountRevisionDateByCipherId(cipher);
|
await dbContext.UserBumpAccountRevisionDateByCipherIdAsync(cipher.Id, cipher.OrganizationId.Value);
|
||||||
}
|
}
|
||||||
else if (cipher.UserId.HasValue)
|
else if (cipher.UserId.HasValue)
|
||||||
{
|
{
|
||||||
await UserUpdateStorage(cipher.UserId.Value);
|
await UserUpdateStorage(cipher.UserId.Value);
|
||||||
await UserBumpAccountRevisionDate(cipher.UserId.Value);
|
await dbContext.UserBumpAccountRevisionDateAsync(cipher.UserId.Value);
|
||||||
}
|
}
|
||||||
|
await dbContext.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,9 +212,10 @@ public class CipherRepository : Repository<Core.Entities.Cipher, Cipher, Guid>,
|
|||||||
select c;
|
select c;
|
||||||
dbContext.RemoveRange(ciphers);
|
dbContext.RemoveRange(ciphers);
|
||||||
await dbContext.SaveChangesAsync();
|
await dbContext.SaveChangesAsync();
|
||||||
|
await OrganizationUpdateStorage(organizationId);
|
||||||
|
await dbContext.UserBumpAccountRevisionDateByOrganizationIdAsync(organizationId);
|
||||||
|
await dbContext.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
await OrganizationUpdateStorage(organizationId);
|
|
||||||
await UserBumpAccountRevisionDateByOrganizationId(organizationId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DeleteByOrganizationIdAsync(Guid organizationId)
|
public async Task DeleteByOrganizationIdAsync(Guid organizationId)
|
||||||
@ -207,10 +236,10 @@ public class CipherRepository : Repository<Core.Entities.Cipher, Cipher, Guid>,
|
|||||||
select c;
|
select c;
|
||||||
dbContext.RemoveRange(ciphers);
|
dbContext.RemoveRange(ciphers);
|
||||||
|
|
||||||
|
await OrganizationUpdateStorage(organizationId);
|
||||||
|
await dbContext.UserBumpAccountRevisionDateByOrganizationIdAsync(organizationId);
|
||||||
await dbContext.SaveChangesAsync();
|
await dbContext.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
await OrganizationUpdateStorage(organizationId);
|
|
||||||
await UserBumpAccountRevisionDateByOrganizationId(organizationId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DeleteByUserIdAsync(Guid userId)
|
public async Task DeleteByUserIdAsync(Guid userId)
|
||||||
@ -228,7 +257,8 @@ public class CipherRepository : Repository<Core.Entities.Cipher, Cipher, Guid>,
|
|||||||
dbContext.RemoveRange(folders);
|
dbContext.RemoveRange(folders);
|
||||||
await dbContext.SaveChangesAsync();
|
await dbContext.SaveChangesAsync();
|
||||||
await UserUpdateStorage(userId);
|
await UserUpdateStorage(userId);
|
||||||
await UserBumpAccountRevisionDate(userId);
|
await dbContext.UserBumpAccountRevisionDateAsync(userId);
|
||||||
|
await dbContext.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -364,8 +394,8 @@ public class CipherRepository : Repository<Core.Entities.Cipher, Cipher, Guid>,
|
|||||||
dbContext.Attach(cipher);
|
dbContext.Attach(cipher);
|
||||||
cipher.Folders = JsonConvert.SerializeObject(foldersJson);
|
cipher.Folders = JsonConvert.SerializeObject(foldersJson);
|
||||||
});
|
});
|
||||||
|
await dbContext.UserBumpAccountRevisionDateAsync(userId);
|
||||||
await dbContext.SaveChangesAsync();
|
await dbContext.SaveChangesAsync();
|
||||||
await UserBumpAccountRevisionDate(userId);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -427,26 +457,100 @@ public class CipherRepository : Repository<Core.Entities.Cipher, Cipher, Guid>,
|
|||||||
}
|
}
|
||||||
var mappedEntity = Mapper.Map<Cipher>((Core.Entities.Cipher)cipher);
|
var mappedEntity = Mapper.Map<Cipher>((Core.Entities.Cipher)cipher);
|
||||||
dbContext.Entry(entity).CurrentValues.SetValues(mappedEntity);
|
dbContext.Entry(entity).CurrentValues.SetValues(mappedEntity);
|
||||||
await UserBumpAccountRevisionDateByCipherId(cipher);
|
await dbContext.UserBumpAccountRevisionDateByCipherIdAsync(cipher.Id, cipher.OrganizationId.GetValueOrDefault());
|
||||||
await dbContext.SaveChangesAsync();
|
await dbContext.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> ReplaceAsync(Core.Entities.Cipher obj, IEnumerable<Guid> collectionIds)
|
private static async Task<int> UpdateCollectionsAsync(DatabaseContext context, Guid id, Guid? userId, Guid? organizationId, IEnumerable<Guid> collectionIds)
|
||||||
|
{
|
||||||
|
if (!organizationId.HasValue || !collectionIds.Any())
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
IQueryable<Guid> availableCollectionsQuery;
|
||||||
|
|
||||||
|
if (!userId.HasValue)
|
||||||
|
{
|
||||||
|
availableCollectionsQuery = context.Collections
|
||||||
|
.Where(c => c.OrganizationId == organizationId.Value)
|
||||||
|
.Select(c => c.Id);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
availableCollectionsQuery = from c in context.Collections
|
||||||
|
join o in context.Organizations
|
||||||
|
on c.OrganizationId equals o.Id
|
||||||
|
join ou in context.OrganizationUsers
|
||||||
|
on new { OrganizationId = o.Id, UserId = (Guid?)userId.Value } equals
|
||||||
|
new { ou.OrganizationId, ou.UserId }
|
||||||
|
join cu in context.CollectionUsers
|
||||||
|
on new { ou.AccessAll, CollectionId = c.Id, OrganizationUserId = ou.Id } equals
|
||||||
|
new { AccessAll = false, cu.CollectionId, cu.OrganizationUserId } into cu_g
|
||||||
|
from cu in cu_g.DefaultIfEmpty()
|
||||||
|
join gu in context.GroupUsers
|
||||||
|
on new { CollectionId = (Guid?)cu.CollectionId, ou.AccessAll, OrganizationUserId = ou.Id } equals
|
||||||
|
new { CollectionId = (Guid?)null, AccessAll = false, gu.OrganizationUserId } into gu_g
|
||||||
|
from gu in gu_g.DefaultIfEmpty()
|
||||||
|
join g in context.Groups
|
||||||
|
on gu.GroupId equals g.Id into g_g
|
||||||
|
from g in g_g.DefaultIfEmpty()
|
||||||
|
join cg in context.CollectionGroups
|
||||||
|
on new { g.AccessAll, CollectionId = c.Id, gu.GroupId } equals
|
||||||
|
new { AccessAll = false, cg.CollectionId, cg.GroupId }
|
||||||
|
where o.Id == organizationId &&
|
||||||
|
o.Enabled &&
|
||||||
|
ou.Status == OrganizationUserStatusType.Confirmed &&
|
||||||
|
(ou.AccessAll || !cu.ReadOnly || g.AccessAll || !cg.ReadOnly)
|
||||||
|
select c.Id;
|
||||||
|
}
|
||||||
|
|
||||||
|
var availableCollections = await availableCollectionsQuery.ToListAsync();
|
||||||
|
|
||||||
|
if (!availableCollections.Any())
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
var collectionCiphers = collectionIds
|
||||||
|
.Where(collectionId => availableCollections.Contains(collectionId))
|
||||||
|
.Select(collectionId => new CollectionCipher
|
||||||
|
{
|
||||||
|
CollectionId = collectionId,
|
||||||
|
CipherId = id,
|
||||||
|
});
|
||||||
|
context.CollectionCiphers.AddRange(collectionCiphers);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> ReplaceAsync(Core.Entities.Cipher cipher, IEnumerable<Guid> collectionIds)
|
||||||
{
|
{
|
||||||
await UpdateCollections(obj, collectionIds);
|
|
||||||
using (var scope = ServiceScopeFactory.CreateScope())
|
using (var scope = ServiceScopeFactory.CreateScope())
|
||||||
{
|
{
|
||||||
var dbContext = GetDatabaseContext(scope);
|
var dbContext = GetDatabaseContext(scope);
|
||||||
var cipher = await dbContext.Ciphers.FindAsync(obj.Id);
|
var transaction = await dbContext.Database.BeginTransactionAsync();
|
||||||
cipher.UserId = null;
|
var successes = await UpdateCollectionsAsync(
|
||||||
cipher.OrganizationId = obj.OrganizationId;
|
dbContext, cipher.Id, cipher.UserId,
|
||||||
cipher.Data = obj.Data;
|
cipher.OrganizationId, collectionIds);
|
||||||
cipher.Attachments = obj.Attachments;
|
|
||||||
cipher.RevisionDate = obj.RevisionDate;
|
if (successes < 0)
|
||||||
cipher.DeletedDate = obj.DeletedDate;
|
{
|
||||||
await dbContext.SaveChangesAsync();
|
await transaction.CommitAsync();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var trackedCipher = await dbContext.Ciphers.FindAsync(cipher.Id);
|
||||||
|
|
||||||
|
trackedCipher.UserId = null;
|
||||||
|
trackedCipher.OrganizationId = cipher.OrganizationId;
|
||||||
|
trackedCipher.Data = cipher.Data;
|
||||||
|
trackedCipher.Attachments = cipher.Attachments;
|
||||||
|
trackedCipher.RevisionDate = cipher.RevisionDate;
|
||||||
|
trackedCipher.DeletedDate = cipher.DeletedDate;
|
||||||
|
|
||||||
|
await transaction.CommitAsync();
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(cipher.Attachments))
|
if (!string.IsNullOrWhiteSpace(cipher.Attachments))
|
||||||
{
|
{
|
||||||
@ -460,7 +564,8 @@ public class CipherRepository : Repository<Core.Entities.Cipher, Cipher, Guid>,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await UserBumpAccountRevisionDateByCipherId(cipher);
|
await dbContext.UserBumpAccountRevisionDateByCipherIdAsync(cipher.Id, cipher.OrganizationId.GetValueOrDefault());
|
||||||
|
await dbContext.SaveChangesAsync();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -522,13 +627,13 @@ public class CipherRepository : Repository<Core.Entities.Cipher, Cipher, Guid>,
|
|||||||
foreach (var orgId in orgIds)
|
foreach (var orgId in orgIds)
|
||||||
{
|
{
|
||||||
await OrganizationUpdateStorage(orgId.Value);
|
await OrganizationUpdateStorage(orgId.Value);
|
||||||
await UserBumpAccountRevisionDateByOrganizationId(orgId.Value);
|
await dbContext.UserBumpAccountRevisionDateByOrganizationIdAsync(orgId.Value);
|
||||||
}
|
}
|
||||||
if (query.Any(c => c.UserId.HasValue && !string.IsNullOrWhiteSpace(c.Attachments)))
|
if (query.Any(c => c.UserId.HasValue && !string.IsNullOrWhiteSpace(c.Attachments)))
|
||||||
{
|
{
|
||||||
await UserUpdateStorage(userId);
|
await UserUpdateStorage(userId);
|
||||||
}
|
}
|
||||||
await UserBumpAccountRevisionDate(userId);
|
await dbContext.UserBumpAccountRevisionDateAsync(userId);
|
||||||
await dbContext.SaveChangesAsync();
|
await dbContext.SaveChangesAsync();
|
||||||
return utcNow;
|
return utcNow;
|
||||||
}
|
}
|
||||||
@ -547,9 +652,9 @@ public class CipherRepository : Repository<Core.Entities.Cipher, Cipher, Guid>,
|
|||||||
cipher.DeletedDate = utcNow;
|
cipher.DeletedDate = utcNow;
|
||||||
cipher.RevisionDate = utcNow;
|
cipher.RevisionDate = utcNow;
|
||||||
});
|
});
|
||||||
await dbContext.SaveChangesAsync();
|
|
||||||
await OrganizationUpdateStorage(organizationId);
|
await OrganizationUpdateStorage(organizationId);
|
||||||
await UserBumpAccountRevisionDateByOrganizationId(organizationId);
|
await dbContext.UserBumpAccountRevisionDateByOrganizationIdAsync(organizationId);
|
||||||
|
await dbContext.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -570,13 +675,14 @@ public class CipherRepository : Repository<Core.Entities.Cipher, Cipher, Guid>,
|
|||||||
if (attachment.OrganizationId.HasValue)
|
if (attachment.OrganizationId.HasValue)
|
||||||
{
|
{
|
||||||
await OrganizationUpdateStorage(cipher.OrganizationId.Value);
|
await OrganizationUpdateStorage(cipher.OrganizationId.Value);
|
||||||
await UserBumpAccountRevisionDateByCipherId(new List<Core.Entities.Cipher> { cipher });
|
await dbContext.UserBumpAccountRevisionDateByCipherIdAsync(cipher.Id, cipher.OrganizationId);
|
||||||
}
|
}
|
||||||
else if (attachment.UserId.HasValue)
|
else if (attachment.UserId.HasValue)
|
||||||
{
|
{
|
||||||
await UserUpdateStorage(attachment.UserId.Value);
|
await UserUpdateStorage(attachment.UserId.Value);
|
||||||
await UserBumpAccountRevisionDate(attachment.UserId.Value);
|
await dbContext.UserBumpAccountRevisionDateAsync(attachment.UserId.Value);
|
||||||
}
|
}
|
||||||
|
await dbContext.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -591,7 +697,8 @@ public class CipherRepository : Repository<Core.Entities.Cipher, Cipher, Guid>,
|
|||||||
var dbContext = GetDatabaseContext(scope);
|
var dbContext = GetDatabaseContext(scope);
|
||||||
var entities = Mapper.Map<List<Cipher>>(ciphers);
|
var entities = Mapper.Map<List<Cipher>>(ciphers);
|
||||||
await dbContext.BulkCopyAsync(base.DefaultBulkCopyOptions, entities);
|
await dbContext.BulkCopyAsync(base.DefaultBulkCopyOptions, entities);
|
||||||
await UserBumpAccountRevisionDate(userId);
|
await dbContext.UserBumpAccountRevisionDateAsync(userId);
|
||||||
|
await dbContext.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -626,8 +733,8 @@ public class CipherRepository : Repository<Core.Entities.Cipher, Cipher, Guid>,
|
|||||||
favoritesJson.Remove(userId.ToString());
|
favoritesJson.Remove(userId.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await dbContext.UserBumpAccountRevisionDateAsync(userId);
|
||||||
await dbContext.SaveChangesAsync();
|
await dbContext.SaveChangesAsync();
|
||||||
await UserBumpAccountRevisionDate(userId);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,7 +25,8 @@ public class CollectionCipherRepository : BaseEntityFrameworkRepository, ICollec
|
|||||||
var organizationId = (await dbContext.Ciphers.FirstOrDefaultAsync(c => c.Id.Equals(obj.CipherId))).OrganizationId;
|
var organizationId = (await dbContext.Ciphers.FirstOrDefaultAsync(c => c.Id.Equals(obj.CipherId))).OrganizationId;
|
||||||
if (organizationId.HasValue)
|
if (organizationId.HasValue)
|
||||||
{
|
{
|
||||||
await UserBumpAccountRevisionDateByCollectionId(obj.CollectionId, organizationId.Value);
|
await dbContext.UserBumpAccountRevisionDateByCollectionIdAsync(obj.CollectionId, organizationId.Value);
|
||||||
|
await dbContext.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
@ -132,12 +133,12 @@ public class CollectionCipherRepository : BaseEntityFrameworkRepository, ICollec
|
|||||||
});
|
});
|
||||||
await dbContext.AddRangeAsync(insert);
|
await dbContext.AddRangeAsync(insert);
|
||||||
dbContext.RemoveRange(delete);
|
dbContext.RemoveRange(delete);
|
||||||
await dbContext.SaveChangesAsync();
|
|
||||||
|
|
||||||
if (organizationId.HasValue)
|
if (organizationId.HasValue)
|
||||||
{
|
{
|
||||||
await UserBumpAccountRevisionDateByOrganizationId(organizationId.Value);
|
await dbContext.UserBumpAccountRevisionDateByOrganizationIdAsync(organizationId.Value);
|
||||||
}
|
}
|
||||||
|
await dbContext.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,8 +183,8 @@ public class CollectionCipherRepository : BaseEntityFrameworkRepository, ICollec
|
|||||||
});
|
});
|
||||||
await dbContext.AddRangeAsync(insert);
|
await dbContext.AddRangeAsync(insert);
|
||||||
dbContext.RemoveRange(delete);
|
dbContext.RemoveRange(delete);
|
||||||
|
await dbContext.UserBumpAccountRevisionDateByOrganizationIdAsync(organizationId);
|
||||||
await dbContext.SaveChangesAsync();
|
await dbContext.SaveChangesAsync();
|
||||||
await UserBumpAccountRevisionDateByOrganizationId(organizationId);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -231,7 +232,8 @@ public class CollectionCipherRepository : BaseEntityFrameworkRepository, ICollec
|
|||||||
CipherId = cipherId,
|
CipherId = cipherId,
|
||||||
};
|
};
|
||||||
await dbContext.AddRangeAsync(insertData);
|
await dbContext.AddRangeAsync(insertData);
|
||||||
await UserBumpAccountRevisionDateByOrganizationId(organizationId);
|
await dbContext.UserBumpAccountRevisionDateByOrganizationIdAsync(organizationId);
|
||||||
|
await dbContext.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,16 +14,43 @@ public class CollectionRepository : Repository<Core.Entities.Collection, Collect
|
|||||||
: base(serviceScopeFactory, mapper, (DatabaseContext context) => context.Collections)
|
: base(serviceScopeFactory, mapper, (DatabaseContext context) => context.Collections)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
public override async Task<Core.Entities.Collection> CreateAsync(Core.Entities.Collection obj)
|
public override async Task<Core.Entities.Collection> CreateAsync(Core.Entities.Collection collection)
|
||||||
{
|
{
|
||||||
await base.CreateAsync(obj);
|
await base.CreateAsync(collection);
|
||||||
await UserBumpAccountRevisionDateByCollectionId(obj.Id, obj.OrganizationId);
|
using (var scope = ServiceScopeFactory.CreateScope())
|
||||||
return obj;
|
{
|
||||||
|
var dbContext = GetDatabaseContext(scope);
|
||||||
|
await dbContext.UserBumpAccountRevisionDateByCollectionIdAsync(collection.Id, collection.OrganizationId);
|
||||||
|
await dbContext.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
return collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task DeleteAsync(Core.Entities.Collection collection)
|
||||||
|
{
|
||||||
|
using (var scope = ServiceScopeFactory.CreateScope())
|
||||||
|
{
|
||||||
|
var dbContext = GetDatabaseContext(scope);
|
||||||
|
await dbContext.UserBumpAccountRevisionDateByCollectionIdAsync(collection.Id, collection.OrganizationId);
|
||||||
|
await dbContext.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
await base.DeleteAsync(collection);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task UpsertAsync(Core.Entities.Collection collection)
|
||||||
|
{
|
||||||
|
await base.UpsertAsync(collection);
|
||||||
|
using (var scope = ServiceScopeFactory.CreateScope())
|
||||||
|
{
|
||||||
|
var dbContext = GetDatabaseContext(scope);
|
||||||
|
await dbContext.UserBumpAccountRevisionDateByCollectionIdAsync(collection.Id, collection.OrganizationId);
|
||||||
|
await dbContext.SaveChangesAsync();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task CreateAsync(Core.Entities.Collection obj, IEnumerable<SelectionReadOnly> groups)
|
public async Task CreateAsync(Core.Entities.Collection obj, IEnumerable<SelectionReadOnly> groups)
|
||||||
{
|
{
|
||||||
await base.CreateAsync(obj);
|
await CreateAsync(obj);
|
||||||
using (var scope = ServiceScopeFactory.CreateScope())
|
using (var scope = ServiceScopeFactory.CreateScope())
|
||||||
{
|
{
|
||||||
var dbContext = GetDatabaseContext(scope);
|
var dbContext = GetDatabaseContext(scope);
|
||||||
@ -40,8 +67,8 @@ public class CollectionRepository : Repository<Core.Entities.Collection, Collect
|
|||||||
HidePasswords = g.HidePasswords,
|
HidePasswords = g.HidePasswords,
|
||||||
});
|
});
|
||||||
await dbContext.AddRangeAsync(collectionGroups);
|
await dbContext.AddRangeAsync(collectionGroups);
|
||||||
|
await dbContext.UserBumpAccountRevisionDateByOrganizationIdAsync(obj.OrganizationId);
|
||||||
await dbContext.SaveChangesAsync();
|
await dbContext.SaveChangesAsync();
|
||||||
await UserBumpAccountRevisionDateByOrganizationId(obj.OrganizationId);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,8 +82,8 @@ public class CollectionRepository : Repository<Core.Entities.Collection, Collect
|
|||||||
cu.OrganizationUserId == organizationUserId
|
cu.OrganizationUserId == organizationUserId
|
||||||
select cu;
|
select cu;
|
||||||
dbContext.RemoveRange(await query.ToListAsync());
|
dbContext.RemoveRange(await query.ToListAsync());
|
||||||
|
await dbContext.UserBumpAccountRevisionDateByOrganizationUserIdAsync(organizationUserId);
|
||||||
await dbContext.SaveChangesAsync();
|
await dbContext.SaveChangesAsync();
|
||||||
await UserBumpAccountRevisionDateByOrganizationUserId(organizationUserId);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,7 +194,7 @@ public class CollectionRepository : Repository<Core.Entities.Collection, Collect
|
|||||||
|
|
||||||
public async Task ReplaceAsync(Core.Entities.Collection collection, IEnumerable<SelectionReadOnly> groups)
|
public async Task ReplaceAsync(Core.Entities.Collection collection, IEnumerable<SelectionReadOnly> groups)
|
||||||
{
|
{
|
||||||
await base.ReplaceAsync(collection);
|
await UpsertAsync(collection);
|
||||||
using (var scope = ServiceScopeFactory.CreateScope())
|
using (var scope = ServiceScopeFactory.CreateScope())
|
||||||
{
|
{
|
||||||
var dbContext = GetDatabaseContext(scope);
|
var dbContext = GetDatabaseContext(scope);
|
||||||
@ -228,8 +255,8 @@ public class CollectionRepository : Repository<Core.Entities.Collection, Collect
|
|||||||
await dbContext.AddRangeAsync(insert);
|
await dbContext.AddRangeAsync(insert);
|
||||||
dbContext.UpdateRange(update);
|
dbContext.UpdateRange(update);
|
||||||
dbContext.RemoveRange(delete);
|
dbContext.RemoveRange(delete);
|
||||||
|
await dbContext.UserBumpAccountRevisionDateByCollectionIdAsync(collection.Id, collection.OrganizationId);
|
||||||
await dbContext.SaveChangesAsync();
|
await dbContext.SaveChangesAsync();
|
||||||
await UserBumpAccountRevisionDateByCollectionId(collection.Id, collection.OrganizationId);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -273,7 +300,7 @@ public class CollectionRepository : Repository<Core.Entities.Collection, Collect
|
|||||||
// Remove all existing ones that are no longer requested
|
// Remove all existing ones that are no longer requested
|
||||||
var requestedUserIds = requestedUsers.Select(u => u.Id);
|
var requestedUserIds = requestedUsers.Select(u => u.Id);
|
||||||
dbContext.CollectionUsers.RemoveRange(existingCollectionUsers.Where(cu => !requestedUserIds.Contains(cu.OrganizationUserId)));
|
dbContext.CollectionUsers.RemoveRange(existingCollectionUsers.Where(cu => !requestedUserIds.Contains(cu.OrganizationUserId)));
|
||||||
await UserBumpAccountRevisionDateByCollectionId(id, organizationId);
|
await dbContext.UserBumpAccountRevisionDateByCollectionIdAsync(id, organizationId);
|
||||||
await dbContext.SaveChangesAsync();
|
await dbContext.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,146 @@
|
|||||||
|
using Bit.Core.Entities;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Enums.Provider;
|
||||||
|
using Bit.Infrastructure.EntityFramework.Repositories.Queries;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace Bit.Infrastructure.EntityFramework.Repositories;
|
||||||
|
|
||||||
|
public static class DatabaseContextExtensions
|
||||||
|
{
|
||||||
|
public static async Task UserBumpAccountRevisionDateAsync(this DatabaseContext context, Guid userId)
|
||||||
|
{
|
||||||
|
var user = await context.Users.FindAsync(userId);
|
||||||
|
user.AccountRevisionDate = DateTime.UtcNow;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task UserBumpManyAccountRevisionDatesAsync(this DatabaseContext context, ICollection<Guid> userIds)
|
||||||
|
{
|
||||||
|
var users = context.Users.Where(u => userIds.Contains(u.Id));
|
||||||
|
var currentTime = DateTime.UtcNow;
|
||||||
|
await users.ForEachAsync(u =>
|
||||||
|
{
|
||||||
|
context.Attach(u);
|
||||||
|
u.AccountRevisionDate = currentTime;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task UserBumpAccountRevisionDateByOrganizationIdAsync(this DatabaseContext context, Guid organizationId)
|
||||||
|
{
|
||||||
|
var users = await (from u in context.Users
|
||||||
|
join ou in context.OrganizationUsers on u.Id equals ou.UserId
|
||||||
|
where ou.OrganizationId == organizationId && ou.Status == OrganizationUserStatusType.Confirmed
|
||||||
|
select u).ToListAsync();
|
||||||
|
|
||||||
|
UpdateUserRevisionDate(users);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task UserBumpAccountRevisionDateByCipherIdAsync(this DatabaseContext context, Guid cipherId, Guid? organizationId)
|
||||||
|
{
|
||||||
|
var query = new UserBumpAccountRevisionDateByCipherIdQuery(cipherId, organizationId);
|
||||||
|
var users = await query.Run(context).ToListAsync();
|
||||||
|
UpdateUserRevisionDate(users);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task UserBumpAccountRevisionDateByCipherIdAsync(this DatabaseContext context, IEnumerable<Cipher> ciphers)
|
||||||
|
{
|
||||||
|
foreach (var cipher in ciphers)
|
||||||
|
{
|
||||||
|
await context.UserBumpAccountRevisionDateByCipherIdAsync(cipher.Id, cipher.OrganizationId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task UserBumpAccountRevisionDateByCollectionIdAsync(this DatabaseContext context, Guid collectionId, Guid organizationId)
|
||||||
|
{
|
||||||
|
var query = from u in context.Users
|
||||||
|
join ou in context.OrganizationUsers
|
||||||
|
on u.Id equals ou.UserId
|
||||||
|
join cu in context.CollectionUsers
|
||||||
|
on new { ou.AccessAll, OrganizationUserId = ou.Id, CollectionId = collectionId } equals
|
||||||
|
new { AccessAll = false, cu.OrganizationUserId, cu.CollectionId } into cu_g
|
||||||
|
from cu in cu_g.DefaultIfEmpty()
|
||||||
|
join gu in context.GroupUsers
|
||||||
|
on new { CollectionId = (Guid?)cu.CollectionId, ou.AccessAll, OrganizationUserId = ou.Id } equals
|
||||||
|
new { CollectionId = (Guid?)null, AccessAll = false, gu.OrganizationUserId } into gu_g
|
||||||
|
from gu in gu_g.DefaultIfEmpty()
|
||||||
|
join g in context.Groups
|
||||||
|
on gu.GroupId equals g.Id into g_g
|
||||||
|
from g in g_g.DefaultIfEmpty()
|
||||||
|
join cg in context.CollectionGroups
|
||||||
|
on new { g.AccessAll, gu.GroupId, CollectionId = collectionId } equals
|
||||||
|
new { AccessAll = false, cg.GroupId, cg.CollectionId } into cg_g
|
||||||
|
from cg in cg_g.DefaultIfEmpty()
|
||||||
|
where ou.OrganizationId == organizationId &&
|
||||||
|
ou.Status == OrganizationUserStatusType.Confirmed &&
|
||||||
|
cg.CollectionId != null &&
|
||||||
|
ou.AccessAll == true &&
|
||||||
|
g.AccessAll == true
|
||||||
|
select u;
|
||||||
|
|
||||||
|
var users = await query.ToListAsync();
|
||||||
|
UpdateUserRevisionDate(users);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task UserBumpAccountRevisionDateByOrganizationUserIdAsync(this DatabaseContext context, Guid organizationUserId)
|
||||||
|
{
|
||||||
|
var query = from u in context.Users
|
||||||
|
join ou in context.OrganizationUsers
|
||||||
|
on u.Id equals ou.UserId
|
||||||
|
where ou.Id == organizationUserId && ou.Status == OrganizationUserStatusType.Confirmed
|
||||||
|
select u;
|
||||||
|
|
||||||
|
var users = await query.ToListAsync();
|
||||||
|
UpdateUserRevisionDate(users);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task UserBumpAccountRevisionDateByOrganizationUserIdsAsync(this DatabaseContext context, IEnumerable<Guid> organizationUserIds)
|
||||||
|
{
|
||||||
|
foreach (var organizationUserId in organizationUserIds)
|
||||||
|
{
|
||||||
|
await context.UserBumpAccountRevisionDateByOrganizationUserIdAsync(organizationUserId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task UserBumpAccountRevisionDateByEmergencyAccessGranteeIdAsync(this DatabaseContext context, Guid emergencyAccessId)
|
||||||
|
{
|
||||||
|
var query = from u in context.Users
|
||||||
|
join ea in context.EmergencyAccesses on u.Id equals ea.GranteeId
|
||||||
|
where ea.Id == emergencyAccessId && ea.Status == EmergencyAccessStatusType.Confirmed
|
||||||
|
select u;
|
||||||
|
|
||||||
|
var users = await query.ToListAsync();
|
||||||
|
|
||||||
|
UpdateUserRevisionDate(users);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task UserBumpAccountRevisionDateByProviderIdAsync(this DatabaseContext context, Guid providerId)
|
||||||
|
{
|
||||||
|
var query = from u in context.Users
|
||||||
|
join pu in context.ProviderUsers on u.Id equals pu.UserId
|
||||||
|
where pu.ProviderId == providerId && pu.Status == ProviderUserStatusType.Confirmed
|
||||||
|
select u;
|
||||||
|
|
||||||
|
var users = await query.ToListAsync();
|
||||||
|
UpdateUserRevisionDate(users);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task UserBumpAccountRevisionDateByProviderUserIdAsync(this DatabaseContext context, Guid providerUserId)
|
||||||
|
{
|
||||||
|
var query = from u in context.Users
|
||||||
|
join pu in context.ProviderUsers on u.Id equals pu.UserId
|
||||||
|
where pu.ProviderId == providerUserId && pu.Status == ProviderUserStatusType.Confirmed
|
||||||
|
select u;
|
||||||
|
|
||||||
|
var users = await query.ToListAsync();
|
||||||
|
UpdateUserRevisionDate(users);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void UpdateUserRevisionDate(List<Models.User> users)
|
||||||
|
{
|
||||||
|
var time = DateTime.UtcNow;
|
||||||
|
foreach (var user in users)
|
||||||
|
{
|
||||||
|
user.AccountRevisionDate = time;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -21,6 +21,17 @@ public class EmergencyAccessRepository : Repository<Core.Entities.EmergencyAcces
|
|||||||
return await GetCountFromQuery(query);
|
return await GetCountFromQuery(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override async Task DeleteAsync(Core.Entities.EmergencyAccess emergencyAccess)
|
||||||
|
{
|
||||||
|
using (var scope = ServiceScopeFactory.CreateScope())
|
||||||
|
{
|
||||||
|
var dbContext = GetDatabaseContext(scope);
|
||||||
|
await dbContext.UserBumpAccountRevisionDateByEmergencyAccessGranteeIdAsync(emergencyAccess.Id);
|
||||||
|
await dbContext.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
await base.DeleteAsync(emergencyAccess);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<EmergencyAccessDetails> GetDetailsByIdGrantorIdAsync(Guid id, Guid grantorId)
|
public async Task<EmergencyAccessDetails> GetDetailsByIdGrantorIdAsync(Guid id, Guid grantorId)
|
||||||
{
|
{
|
||||||
using (var scope = ServiceScopeFactory.CreateScope())
|
using (var scope = ServiceScopeFactory.CreateScope())
|
||||||
|
@ -46,6 +46,7 @@ public class GroupRepository : Repository<Core.Entities.Group, Group, Guid>, IGr
|
|||||||
gu.OrganizationUserId == organizationUserId
|
gu.OrganizationUserId == organizationUserId
|
||||||
select gu;
|
select gu;
|
||||||
dbContext.RemoveRange(await query.ToListAsync());
|
dbContext.RemoveRange(await query.ToListAsync());
|
||||||
|
await dbContext.UserBumpAccountRevisionDateByOrganizationUserIdAsync(organizationUserId);
|
||||||
await dbContext.SaveChangesAsync();
|
await dbContext.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -134,7 +135,8 @@ public class GroupRepository : Repository<Core.Entities.Group, Group, Guid>, IGr
|
|||||||
using (var scope = ServiceScopeFactory.CreateScope())
|
using (var scope = ServiceScopeFactory.CreateScope())
|
||||||
{
|
{
|
||||||
var dbContext = GetDatabaseContext(scope);
|
var dbContext = GetDatabaseContext(scope);
|
||||||
await UserBumpAccountRevisionDateByOrganizationId(obj.OrganizationId);
|
await dbContext.UserBumpAccountRevisionDateByOrganizationIdAsync(obj.OrganizationId);
|
||||||
|
await dbContext.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,7 +163,8 @@ public class GroupRepository : Repository<Core.Entities.Group, Group, Guid>, IGr
|
|||||||
select gu;
|
select gu;
|
||||||
dbContext.RemoveRange(delete);
|
dbContext.RemoveRange(delete);
|
||||||
await dbContext.SaveChangesAsync();
|
await dbContext.SaveChangesAsync();
|
||||||
await UserBumpAccountRevisionDateByOrganizationId(orgId);
|
await dbContext.UserBumpAccountRevisionDateByOrganizationIdAsync(orgId);
|
||||||
|
await dbContext.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,9 +70,39 @@ public class OrganizationUserRepository : Repository<Core.Entities.OrganizationU
|
|||||||
using (var scope = ServiceScopeFactory.CreateScope())
|
using (var scope = ServiceScopeFactory.CreateScope())
|
||||||
{
|
{
|
||||||
var dbContext = GetDatabaseContext(scope);
|
var dbContext = GetDatabaseContext(scope);
|
||||||
var orgUser = await dbContext.FindAsync<OrganizationUser>(organizationUserId);
|
await dbContext.UserBumpAccountRevisionDateByOrganizationUserIdAsync(organizationUserId);
|
||||||
|
var orgUser = await dbContext.OrganizationUsers
|
||||||
|
.Where(ou => ou.Id == organizationUserId)
|
||||||
|
.FirstAsync();
|
||||||
|
|
||||||
dbContext.Remove(orgUser);
|
var organizationId = orgUser?.OrganizationId;
|
||||||
|
var userId = orgUser?.UserId;
|
||||||
|
|
||||||
|
if (orgUser?.OrganizationId != null && orgUser?.UserId != null)
|
||||||
|
{
|
||||||
|
var ssoUsers = dbContext.SsoUsers
|
||||||
|
.Where(su => su.UserId == userId && su.OrganizationId == organizationId);
|
||||||
|
dbContext.SsoUsers.RemoveRange(ssoUsers);
|
||||||
|
}
|
||||||
|
|
||||||
|
var collectionUsers = dbContext.CollectionUsers
|
||||||
|
.Where(cu => cu.OrganizationUserId == organizationUserId);
|
||||||
|
dbContext.CollectionUsers.RemoveRange(collectionUsers);
|
||||||
|
|
||||||
|
var groupUsers = dbContext.GroupUsers
|
||||||
|
.Where(gu => gu.OrganizationUserId == organizationUserId);
|
||||||
|
dbContext.GroupUsers.RemoveRange(groupUsers);
|
||||||
|
|
||||||
|
var orgSponsorships = await dbContext.OrganizationSponsorships
|
||||||
|
.Where(os => os.SponsoringOrganizationUserId == organizationUserId)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
foreach (var orgSponsorship in orgSponsorships)
|
||||||
|
{
|
||||||
|
orgSponsorship.ToDelete = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
dbContext.OrganizationUsers.Remove(orgUser);
|
||||||
await dbContext.SaveChangesAsync();
|
await dbContext.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -82,7 +112,9 @@ public class OrganizationUserRepository : Repository<Core.Entities.OrganizationU
|
|||||||
using (var scope = ServiceScopeFactory.CreateScope())
|
using (var scope = ServiceScopeFactory.CreateScope())
|
||||||
{
|
{
|
||||||
var dbContext = GetDatabaseContext(scope);
|
var dbContext = GetDatabaseContext(scope);
|
||||||
|
await dbContext.UserBumpAccountRevisionDateByOrganizationUserIdsAsync(organizationUserIds);
|
||||||
var entities = await dbContext.OrganizationUsers
|
var entities = await dbContext.OrganizationUsers
|
||||||
|
// TODO: Does this work?
|
||||||
.Where(ou => organizationUserIds.Contains(ou.Id))
|
.Where(ou => organizationUserIds.Contains(ou.Id))
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
@ -309,9 +341,20 @@ public class OrganizationUserRepository : Repository<Core.Entities.OrganizationU
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async override Task ReplaceAsync(Core.Entities.OrganizationUser organizationUser)
|
||||||
|
{
|
||||||
|
await base.ReplaceAsync(organizationUser);
|
||||||
|
using (var scope = ServiceScopeFactory.CreateScope())
|
||||||
|
{
|
||||||
|
var dbContext = GetDatabaseContext(scope);
|
||||||
|
await dbContext.UserBumpAccountRevisionDateAsync(organizationUser.UserId.GetValueOrDefault());
|
||||||
|
await dbContext.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async Task ReplaceAsync(Core.Entities.OrganizationUser obj, IEnumerable<SelectionReadOnly> requestedCollections)
|
public async Task ReplaceAsync(Core.Entities.OrganizationUser obj, IEnumerable<SelectionReadOnly> requestedCollections)
|
||||||
{
|
{
|
||||||
await base.ReplaceAsync(obj);
|
await ReplaceAsync(obj);
|
||||||
using (var scope = ServiceScopeFactory.CreateScope())
|
using (var scope = ServiceScopeFactory.CreateScope())
|
||||||
{
|
{
|
||||||
var dbContext = GetDatabaseContext(scope);
|
var dbContext = GetDatabaseContext(scope);
|
||||||
@ -356,7 +399,7 @@ public class OrganizationUserRepository : Repository<Core.Entities.OrganizationU
|
|||||||
var dbContext = GetDatabaseContext(scope);
|
var dbContext = GetDatabaseContext(scope);
|
||||||
dbContext.UpdateRange(organizationUsers);
|
dbContext.UpdateRange(organizationUsers);
|
||||||
await dbContext.SaveChangesAsync();
|
await dbContext.SaveChangesAsync();
|
||||||
await UserBumpManyAccountRevisionDates(organizationUsers
|
await dbContext.UserBumpManyAccountRevisionDatesAsync(organizationUsers
|
||||||
.Where(ou => ou.UserId.HasValue)
|
.Where(ou => ou.UserId.HasValue)
|
||||||
.Select(ou => ou.UserId.Value).ToArray());
|
.Select(ou => ou.UserId.Value).ToArray());
|
||||||
}
|
}
|
||||||
@ -400,7 +443,7 @@ public class OrganizationUserRepository : Repository<Core.Entities.OrganizationU
|
|||||||
var delete = procedure.Delete.Run(dbContext);
|
var delete = procedure.Delete.Run(dbContext);
|
||||||
var deleteData = await delete.ToListAsync();
|
var deleteData = await delete.ToListAsync();
|
||||||
dbContext.RemoveRange(deleteData);
|
dbContext.RemoveRange(deleteData);
|
||||||
await UserBumpAccountRevisionDateByOrganizationUserId(orgUserId);
|
await dbContext.UserBumpAccountRevisionDateByOrganizationUserIdAsync(orgUserId);
|
||||||
await dbContext.SaveChangesAsync();
|
await dbContext.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -449,17 +492,15 @@ public class OrganizationUserRepository : Repository<Core.Entities.OrganizationU
|
|||||||
using (var scope = ServiceScopeFactory.CreateScope())
|
using (var scope = ServiceScopeFactory.CreateScope())
|
||||||
{
|
{
|
||||||
var dbContext = GetDatabaseContext(scope);
|
var dbContext = GetDatabaseContext(scope);
|
||||||
var orgUser = await GetDbSet(dbContext).FindAsync(id);
|
var orgUser = await dbContext.OrganizationUsers.FindAsync(id);
|
||||||
if (orgUser != null)
|
if (orgUser == null)
|
||||||
{
|
{
|
||||||
dbContext.Update(orgUser);
|
return;
|
||||||
orgUser.Status = OrganizationUserStatusType.Revoked;
|
|
||||||
await dbContext.SaveChangesAsync();
|
|
||||||
if (orgUser.UserId.HasValue)
|
|
||||||
{
|
|
||||||
await UserBumpAccountRevisionDate(orgUser.UserId.Value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
orgUser.Status = OrganizationUserStatusType.Revoked;
|
||||||
|
await dbContext.UserBumpAccountRevisionDateByOrganizationUserIdAsync(id);
|
||||||
|
await dbContext.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -468,17 +509,17 @@ public class OrganizationUserRepository : Repository<Core.Entities.OrganizationU
|
|||||||
using (var scope = ServiceScopeFactory.CreateScope())
|
using (var scope = ServiceScopeFactory.CreateScope())
|
||||||
{
|
{
|
||||||
var dbContext = GetDatabaseContext(scope);
|
var dbContext = GetDatabaseContext(scope);
|
||||||
var orgUser = await GetDbSet(dbContext).FindAsync(id);
|
var orgUser = await dbContext.OrganizationUsers
|
||||||
if (orgUser != null)
|
.FirstOrDefaultAsync(ou => ou.Id == id && ou.Status == OrganizationUserStatusType.Revoked);
|
||||||
|
|
||||||
|
if (orgUser == null)
|
||||||
{
|
{
|
||||||
dbContext.Update(orgUser);
|
return;
|
||||||
orgUser.Status = status;
|
|
||||||
await dbContext.SaveChangesAsync();
|
|
||||||
if (orgUser.UserId.HasValue)
|
|
||||||
{
|
|
||||||
await UserBumpAccountRevisionDate(orgUser.UserId.Value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
orgUser.Status = status;
|
||||||
|
await dbContext.UserBumpAccountRevisionDateByOrganizationUserIdAsync(id);
|
||||||
|
await dbContext.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,17 @@ public class ProviderRepository : Repository<Provider, Models.Provider, Guid>, I
|
|||||||
: base(serviceScopeFactory, mapper, context => context.Providers)
|
: base(serviceScopeFactory, mapper, context => context.Providers)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
|
public override async Task DeleteAsync(Provider provider)
|
||||||
|
{
|
||||||
|
using (var scope = ServiceScopeFactory.CreateScope())
|
||||||
|
{
|
||||||
|
var dbContext = GetDatabaseContext(scope);
|
||||||
|
await dbContext.UserBumpAccountRevisionDateByProviderIdAsync(provider.Id);
|
||||||
|
await dbContext.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
await base.DeleteAsync(provider);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<ICollection<Provider>> SearchAsync(string name, string userEmail, int skip, int take)
|
public async Task<ICollection<Provider>> SearchAsync(string name, string userEmail, int skip, int take)
|
||||||
{
|
{
|
||||||
using (var scope = ServiceScopeFactory.CreateScope())
|
using (var scope = ServiceScopeFactory.CreateScope())
|
||||||
|
@ -16,6 +16,17 @@ public class ProviderUserRepository :
|
|||||||
: base(serviceScopeFactory, mapper, (DatabaseContext context) => context.ProviderUsers)
|
: base(serviceScopeFactory, mapper, (DatabaseContext context) => context.ProviderUsers)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
|
public override async Task DeleteAsync(ProviderUser providerUser)
|
||||||
|
{
|
||||||
|
using (var scope = ServiceScopeFactory.CreateScope())
|
||||||
|
{
|
||||||
|
var dbContext = GetDatabaseContext(scope);
|
||||||
|
await dbContext.UserBumpAccountRevisionDateByProviderUserIdAsync(providerUser.Id);
|
||||||
|
await dbContext.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
await base.DeleteAsync(providerUser);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<int> GetCountByProviderAsync(Guid providerId, string email, bool onlyRegisteredUsers)
|
public async Task<int> GetCountByProviderAsync(Guid providerId, string email, bool onlyRegisteredUsers)
|
||||||
{
|
{
|
||||||
using (var scope = ServiceScopeFactory.CreateScope())
|
using (var scope = ServiceScopeFactory.CreateScope())
|
||||||
@ -59,7 +70,10 @@ public class ProviderUserRepository :
|
|||||||
using (var scope = ServiceScopeFactory.CreateScope())
|
using (var scope = ServiceScopeFactory.CreateScope())
|
||||||
{
|
{
|
||||||
var dbContext = GetDatabaseContext(scope);
|
var dbContext = GetDatabaseContext(scope);
|
||||||
await UserBumpAccountRevisionDateByProviderUserIds(providerUserIds.ToArray());
|
foreach (var providerUserId in providerUserIds)
|
||||||
|
{
|
||||||
|
await dbContext.UserBumpAccountRevisionDateByProviderUserIdAsync(providerUserId);
|
||||||
|
}
|
||||||
var entities = dbContext.ProviderUsers.Where(pu => providerUserIds.Contains(pu.Id));
|
var entities = dbContext.ProviderUsers.Where(pu => providerUserIds.Contains(pu.Id));
|
||||||
dbContext.ProviderUsers.RemoveRange(entities);
|
dbContext.ProviderUsers.RemoveRange(entities);
|
||||||
await dbContext.SaveChangesAsync();
|
await dbContext.SaveChangesAsync();
|
||||||
|
@ -1,73 +0,0 @@
|
|||||||
using Bit.Core.Entities;
|
|
||||||
using Bit.Core.Enums;
|
|
||||||
using CollectionCipher = Bit.Infrastructure.EntityFramework.Models.CollectionCipher;
|
|
||||||
|
|
||||||
namespace Bit.Infrastructure.EntityFramework.Repositories.Queries;
|
|
||||||
|
|
||||||
public class CipherUpdateCollectionsQuery : IQuery<CollectionCipher>
|
|
||||||
{
|
|
||||||
private readonly Cipher _cipher;
|
|
||||||
private readonly IEnumerable<Guid> _collectionIds;
|
|
||||||
|
|
||||||
public CipherUpdateCollectionsQuery(Cipher cipher, IEnumerable<Guid> collectionIds)
|
|
||||||
{
|
|
||||||
_cipher = cipher;
|
|
||||||
_collectionIds = collectionIds;
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual IQueryable<CollectionCipher> Run(DatabaseContext dbContext)
|
|
||||||
{
|
|
||||||
if (!_cipher.OrganizationId.HasValue || !_collectionIds.Any())
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var availibleCollections = !_cipher.UserId.HasValue ?
|
|
||||||
|
|
||||||
from c in dbContext.Collections
|
|
||||||
where c.OrganizationId == _cipher.OrganizationId
|
|
||||||
select c.Id :
|
|
||||||
|
|
||||||
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, _cipher.UserId } equals new { ou.OrganizationId, ou.UserId }
|
|
||||||
|
|
||||||
join cu in dbContext.CollectionUsers
|
|
||||||
on new { ou.AccessAll, CollectionId = c.Id, OrganizationUserId = ou.Id } equals
|
|
||||||
new { AccessAll = false, cu.CollectionId, cu.OrganizationUserId } into cu_g
|
|
||||||
from cu in cu_g.DefaultIfEmpty()
|
|
||||||
|
|
||||||
join gu in dbContext.GroupUsers
|
|
||||||
on new { CollectionId = (Guid?)cu.CollectionId, ou.AccessAll, OrganizationUserId = ou.Id } equals
|
|
||||||
new { CollectionId = (Guid?)null, AccessAll = false, 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 { g.AccessAll, CollectionId = c.Id, gu.GroupId } equals
|
|
||||||
new { AccessAll = false, cg.CollectionId, cg.GroupId } into cg_g
|
|
||||||
from cg in cg_g.DefaultIfEmpty()
|
|
||||||
|
|
||||||
where o.Id == _cipher.OrganizationId &&
|
|
||||||
o.Enabled &&
|
|
||||||
ou.Status == OrganizationUserStatusType.Confirmed &&
|
|
||||||
(ou.AccessAll || !cu.ReadOnly || g.AccessAll || !cg.ReadOnly)
|
|
||||||
select c.Id;
|
|
||||||
|
|
||||||
if (!availibleCollections.Any())
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var query = from c in availibleCollections
|
|
||||||
select new CollectionCipher { CollectionId = c, CipherId = _cipher.Id };
|
|
||||||
return query;
|
|
||||||
}
|
|
||||||
}
|
|
@ -6,11 +6,19 @@ namespace Bit.Infrastructure.EntityFramework.Repositories.Queries;
|
|||||||
|
|
||||||
public class UserBumpAccountRevisionDateByCipherIdQuery : IQuery<User>
|
public class UserBumpAccountRevisionDateByCipherIdQuery : IQuery<User>
|
||||||
{
|
{
|
||||||
private readonly Cipher _cipher;
|
private readonly Guid _cipherId;
|
||||||
|
private readonly Guid? _organizationId;
|
||||||
|
|
||||||
public UserBumpAccountRevisionDateByCipherIdQuery(Cipher cipher)
|
public UserBumpAccountRevisionDateByCipherIdQuery(Cipher cipher)
|
||||||
{
|
{
|
||||||
_cipher = cipher;
|
_cipherId = cipher.Id;
|
||||||
|
_organizationId = cipher.OrganizationId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserBumpAccountRevisionDateByCipherIdQuery(Guid cipherId, Guid? organizationId)
|
||||||
|
{
|
||||||
|
_cipherId = cipherId;
|
||||||
|
_organizationId = organizationId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IQueryable<User> Run(DatabaseContext dbContext)
|
public IQueryable<User> Run(DatabaseContext dbContext)
|
||||||
@ -21,7 +29,7 @@ public class UserBumpAccountRevisionDateByCipherIdQuery : IQuery<User>
|
|||||||
on u.Id equals ou.UserId
|
on u.Id equals ou.UserId
|
||||||
|
|
||||||
join collectionCipher in dbContext.CollectionCiphers
|
join collectionCipher in dbContext.CollectionCiphers
|
||||||
on _cipher.Id equals collectionCipher.CipherId into cc_g
|
on _cipherId equals collectionCipher.CipherId into cc_g
|
||||||
from cc in cc_g.DefaultIfEmpty()
|
from cc in cc_g.DefaultIfEmpty()
|
||||||
|
|
||||||
join collectionUser in dbContext.CollectionUsers
|
join collectionUser in dbContext.CollectionUsers
|
||||||
@ -43,7 +51,7 @@ public class UserBumpAccountRevisionDateByCipherIdQuery : IQuery<User>
|
|||||||
new { AccessAll = false, collectionGroup.GroupId, collectionGroup.CollectionId } into cg_g
|
new { AccessAll = false, collectionGroup.GroupId, collectionGroup.CollectionId } into cg_g
|
||||||
from cg in cg_g.DefaultIfEmpty()
|
from cg in cg_g.DefaultIfEmpty()
|
||||||
|
|
||||||
where ou.OrganizationId == _cipher.OrganizationId &&
|
where ou.OrganizationId == _organizationId &&
|
||||||
ou.Status == OrganizationUserStatusType.Confirmed &&
|
ou.Status == OrganizationUserStatusType.Confirmed &&
|
||||||
(cu.CollectionId != null ||
|
(cu.CollectionId != null ||
|
||||||
cg.CollectionId != null ||
|
cg.CollectionId != null ||
|
||||||
|
@ -15,11 +15,17 @@ public class SendRepository : Repository<Core.Entities.Send, Send, Guid>, ISendR
|
|||||||
public override async Task<Core.Entities.Send> CreateAsync(Core.Entities.Send send)
|
public override async Task<Core.Entities.Send> CreateAsync(Core.Entities.Send send)
|
||||||
{
|
{
|
||||||
send = await base.CreateAsync(send);
|
send = await base.CreateAsync(send);
|
||||||
if (send.UserId.HasValue)
|
using (var scope = ServiceScopeFactory.CreateScope())
|
||||||
{
|
{
|
||||||
await UserUpdateStorage(send.UserId.Value);
|
var dbContext = GetDatabaseContext(scope);
|
||||||
await UserBumpAccountRevisionDate(send.UserId.Value);
|
if (send.UserId.HasValue)
|
||||||
|
{
|
||||||
|
await UserUpdateStorage(send.UserId.Value);
|
||||||
|
await dbContext.UserBumpAccountRevisionDateAsync(send.UserId.Value);
|
||||||
|
await dbContext.SaveChangesAsync();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return send;
|
return send;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,17 @@
|
|||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
|
||||||
|
namespace Bit.Infrastructure.IntegrationTest;
|
||||||
|
|
||||||
|
public static class ConfigurationExtensions
|
||||||
|
{
|
||||||
|
public static bool TryGetConnectionString(this IConfiguration config, string key, out string connectionString)
|
||||||
|
{
|
||||||
|
connectionString = config[key];
|
||||||
|
if (string.IsNullOrEmpty(connectionString))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
84
test/Infrastructure.IntegrationTest/DatabaseDataAttribute.cs
Normal file
84
test/Infrastructure.IntegrationTest/DatabaseDataAttribute.cs
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Settings;
|
||||||
|
using Bit.Infrastructure.Dapper;
|
||||||
|
using Bit.Infrastructure.EntityFramework;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Xunit.Sdk;
|
||||||
|
|
||||||
|
namespace Bit.Infrastructure.IntegrationTest;
|
||||||
|
|
||||||
|
public class DatabaseDataAttribute : DataAttribute
|
||||||
|
{
|
||||||
|
public bool SelfHosted { get; set; }
|
||||||
|
|
||||||
|
public override IEnumerable<object[]> GetData(MethodInfo testMethod)
|
||||||
|
{
|
||||||
|
var parameters = testMethod.GetParameters();
|
||||||
|
|
||||||
|
var config = DatabaseTheoryAttribute.GetConfiguration();
|
||||||
|
|
||||||
|
var serviceProviders = GetDatabaseProviders(config);
|
||||||
|
|
||||||
|
foreach (var provider in serviceProviders)
|
||||||
|
{
|
||||||
|
var objects = new object[parameters.Length];
|
||||||
|
for (var i = 0; i < parameters.Length; i++)
|
||||||
|
{
|
||||||
|
objects[i] = provider.GetRequiredService(parameters[i].ParameterType);
|
||||||
|
}
|
||||||
|
yield return objects;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<IServiceProvider> GetDatabaseProviders(IConfiguration config)
|
||||||
|
{
|
||||||
|
var configureLogging = (ILoggingBuilder builder) =>
|
||||||
|
{
|
||||||
|
if (!config.GetValue<bool>("Quiet"))
|
||||||
|
{
|
||||||
|
builder.AddConfiguration(config);
|
||||||
|
builder.AddConsole();
|
||||||
|
builder.AddDebug();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (config.TryGetConnectionString(DatabaseTheoryAttribute.DapperSqlServerKey, out var dapperSqlServerConnectionString))
|
||||||
|
{
|
||||||
|
var dapperSqlServerCollection = new ServiceCollection();
|
||||||
|
dapperSqlServerCollection.AddLogging(configureLogging);
|
||||||
|
dapperSqlServerCollection.AddDapperRepositories(SelfHosted);
|
||||||
|
var globalSettings = new GlobalSettings
|
||||||
|
{
|
||||||
|
DatabaseProvider = "sqlServer",
|
||||||
|
SqlServer = new GlobalSettings.SqlSettings
|
||||||
|
{
|
||||||
|
ConnectionString = dapperSqlServerConnectionString,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
dapperSqlServerCollection.AddSingleton(globalSettings);
|
||||||
|
dapperSqlServerCollection.AddSingleton<IGlobalSettings>(globalSettings);
|
||||||
|
yield return dapperSqlServerCollection.BuildServiceProvider();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.TryGetConnectionString(DatabaseTheoryAttribute.EfPostgresKey, out var efPostgresConnectionString))
|
||||||
|
{
|
||||||
|
var efPostgresCollection = new ServiceCollection();
|
||||||
|
efPostgresCollection.AddLogging(configureLogging);
|
||||||
|
efPostgresCollection.AddEFRepositories(SelfHosted, efPostgresConnectionString, SupportedDatabaseProviders.Postgres);
|
||||||
|
efPostgresCollection.AddTransient<ITestDatabaseHelper, EfTestDatabaseHelper>();
|
||||||
|
yield return efPostgresCollection.BuildServiceProvider();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.TryGetConnectionString(DatabaseTheoryAttribute.EfMySqlKey, out var efMySqlConnectionString))
|
||||||
|
{
|
||||||
|
var efMySqlCollection = new ServiceCollection();
|
||||||
|
efMySqlCollection.AddLogging(configureLogging);
|
||||||
|
efMySqlCollection.AddEFRepositories(SelfHosted, efMySqlConnectionString, SupportedDatabaseProviders.MySql);
|
||||||
|
efMySqlCollection.AddTransient<ITestDatabaseHelper, EfTestDatabaseHelper>();
|
||||||
|
yield return efMySqlCollection.BuildServiceProvider();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Bit.Infrastructure.IntegrationTest;
|
||||||
|
|
||||||
|
public class DatabaseTheoryAttribute : TheoryAttribute
|
||||||
|
{
|
||||||
|
private static IConfiguration? _cachedConfiguration;
|
||||||
|
|
||||||
|
public const string DapperSqlServerKey = "Dapper:SqlServer";
|
||||||
|
public const string EfPostgresKey = "Ef:Postgres";
|
||||||
|
public const string EfMySqlKey = "Ef:MySql";
|
||||||
|
|
||||||
|
public DatabaseTheoryAttribute()
|
||||||
|
{
|
||||||
|
if (!HasAnyDatabaseSetup())
|
||||||
|
{
|
||||||
|
Skip = "No database connections strings setup.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool HasAnyDatabaseSetup()
|
||||||
|
{
|
||||||
|
var config = GetConfiguration();
|
||||||
|
return config.TryGetConnectionString(DapperSqlServerKey, out _) ||
|
||||||
|
config.TryGetConnectionString(EfPostgresKey, out _) ||
|
||||||
|
config.TryGetConnectionString(EfMySqlKey, out _);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IConfiguration GetConfiguration()
|
||||||
|
{
|
||||||
|
return _cachedConfiguration ??= new ConfigurationBuilder()
|
||||||
|
.AddUserSecrets<DatabaseDataAttribute>(optional: true, reloadOnChange: false)
|
||||||
|
.AddEnvironmentVariables("BW_TEST_")
|
||||||
|
.AddCommandLine(Environment.GetCommandLineArgs())
|
||||||
|
.Build();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<IsPackable>false</IsPackable>
|
||||||
|
<UserSecretsId>6570f288-5c2c-47ad-8978-f3da255079c2</UserSecretsId>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Configuration" Version="6.0.1" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.1" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
|
||||||
|
<PackageReference Include="xunit" Version="2.4.1" />
|
||||||
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="coverlet.collector" Version="3.1.0">
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
</PackageReference>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\src\Infrastructure.Dapper\Infrastructure.Dapper.csproj" />
|
||||||
|
<ProjectReference Include="..\..\src\Infrastructure.EntityFramework\Infrastructure.EntityFramework.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
@ -0,0 +1,97 @@
|
|||||||
|
using Bit.Core.Entities;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Repositories;
|
||||||
|
using Core.Models.Data;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Bit.Infrastructure.IntegrationTest.Repositories;
|
||||||
|
|
||||||
|
public class CipherRepositoryTests
|
||||||
|
{
|
||||||
|
[DatabaseTheory, DatabaseData]
|
||||||
|
public async Task DeleteAsync_UpdatesUserRevisionDate(
|
||||||
|
IUserRepository userRepository,
|
||||||
|
ICipherRepository cipherRepository,
|
||||||
|
ITestDatabaseHelper helper)
|
||||||
|
{
|
||||||
|
var user = await userRepository.CreateAsync(new User
|
||||||
|
{
|
||||||
|
Name = "Test User",
|
||||||
|
Email = "test@email.com",
|
||||||
|
ApiKey = "TEST",
|
||||||
|
SecurityStamp = "stamp",
|
||||||
|
});
|
||||||
|
|
||||||
|
var cipher = await cipherRepository.CreateAsync(new Cipher
|
||||||
|
{
|
||||||
|
Type = CipherType.Login,
|
||||||
|
UserId = user.Id,
|
||||||
|
});
|
||||||
|
|
||||||
|
helper.ClearTracker();
|
||||||
|
|
||||||
|
await cipherRepository.DeleteAsync(cipher);
|
||||||
|
|
||||||
|
var deletedCipher = await cipherRepository.GetByIdAsync(cipher.Id);
|
||||||
|
|
||||||
|
Assert.Null(deletedCipher);
|
||||||
|
var updatedUser = await userRepository.GetByIdAsync(user.Id);
|
||||||
|
Assert.NotEqual(updatedUser.AccountRevisionDate, user.AccountRevisionDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
[DatabaseTheory, DatabaseData]
|
||||||
|
public async Task CreateAsync_UpdateWithCollecitons_Works(
|
||||||
|
IUserRepository userRepository,
|
||||||
|
IOrganizationRepository organizationRepository,
|
||||||
|
IOrganizationUserRepository organizationUserRepository,
|
||||||
|
ICollectionRepository collectionRepository,
|
||||||
|
ICipherRepository cipherRepository,
|
||||||
|
ICollectionCipherRepository collectionCipherRepository,
|
||||||
|
ITestDatabaseHelper helper)
|
||||||
|
{
|
||||||
|
var user = await userRepository.CreateAsync(new User
|
||||||
|
{
|
||||||
|
Name = "Test User",
|
||||||
|
Email = "test@email.com",
|
||||||
|
ApiKey = "TEST",
|
||||||
|
SecurityStamp = "stamp",
|
||||||
|
});
|
||||||
|
|
||||||
|
var organization = await organizationRepository.CreateAsync(new Organization
|
||||||
|
{
|
||||||
|
Name = "Test Organization",
|
||||||
|
});
|
||||||
|
|
||||||
|
await organizationUserRepository.CreateAsync(new OrganizationUser
|
||||||
|
{
|
||||||
|
UserId = user.Id,
|
||||||
|
OrganizationId = organization.Id,
|
||||||
|
Status = OrganizationUserStatusType.Accepted,
|
||||||
|
Type = OrganizationUserType.Owner,
|
||||||
|
});
|
||||||
|
|
||||||
|
var collection = await collectionRepository.CreateAsync(new Collection
|
||||||
|
{
|
||||||
|
Name = "Test Collection",
|
||||||
|
OrganizationId = organization.Id
|
||||||
|
});
|
||||||
|
|
||||||
|
helper.ClearTracker();
|
||||||
|
|
||||||
|
await cipherRepository.CreateAsync(new CipherDetails
|
||||||
|
{
|
||||||
|
Type = CipherType.Login,
|
||||||
|
OrganizationId = organization.Id,
|
||||||
|
}, new List<Guid>
|
||||||
|
{
|
||||||
|
collection.Id,
|
||||||
|
});
|
||||||
|
|
||||||
|
var updatedUser = await userRepository.GetByIdAsync(user.Id);
|
||||||
|
|
||||||
|
Assert.NotEqual(updatedUser.AccountRevisionDate, user.AccountRevisionDate);
|
||||||
|
|
||||||
|
var collectionCiphers = await collectionCipherRepository.GetManyByOrganizationIdAsync(organization.Id);
|
||||||
|
Assert.NotEmpty(collectionCiphers);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
using Bit.Core.Entities;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Repositories;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Bit.Infrastructure.IntegrationTest.Repositories;
|
||||||
|
|
||||||
|
public class EmergencyAccessRepositoriesTests
|
||||||
|
{
|
||||||
|
[DatabaseTheory, DatabaseData]
|
||||||
|
public async Task DeleteAsync_UpdatesRevisionDate(IUserRepository userRepository,
|
||||||
|
IEmergencyAccessRepository emergencyAccessRepository,
|
||||||
|
ITestDatabaseHelper helper)
|
||||||
|
{
|
||||||
|
var grantorUser = await userRepository.CreateAsync(new User
|
||||||
|
{
|
||||||
|
Name = "Test Grantor User",
|
||||||
|
Email = "test+grantor@email.com",
|
||||||
|
ApiKey = "TEST",
|
||||||
|
SecurityStamp = "stamp",
|
||||||
|
});
|
||||||
|
|
||||||
|
var granteeUser = await userRepository.CreateAsync(new User
|
||||||
|
{
|
||||||
|
Name = "Test Grantee User",
|
||||||
|
Email = "test+grantee@email.com",
|
||||||
|
ApiKey = "TEST",
|
||||||
|
SecurityStamp = "stamp",
|
||||||
|
});
|
||||||
|
|
||||||
|
var emergencyAccess = await emergencyAccessRepository.CreateAsync(new EmergencyAccess
|
||||||
|
{
|
||||||
|
GrantorId = grantorUser.Id,
|
||||||
|
GranteeId = granteeUser.Id,
|
||||||
|
Status = EmergencyAccessStatusType.Confirmed,
|
||||||
|
});
|
||||||
|
|
||||||
|
helper.ClearTracker();
|
||||||
|
|
||||||
|
await emergencyAccessRepository.DeleteAsync(emergencyAccess);
|
||||||
|
|
||||||
|
var updatedGrantee = await userRepository.GetByIdAsync(granteeUser.Id);
|
||||||
|
|
||||||
|
Assert.NotEqual(updatedGrantee.AccountRevisionDate, granteeUser.AccountRevisionDate);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,97 @@
|
|||||||
|
using Bit.Core.Entities;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Repositories;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Bit.Infrastructure.IntegrationTest.Repositories;
|
||||||
|
|
||||||
|
public class OrganizationUserRepositoryTests
|
||||||
|
{
|
||||||
|
[DatabaseTheory, DatabaseData]
|
||||||
|
public async Task DeleteAsync_Works(IUserRepository userRepository,
|
||||||
|
IOrganizationRepository organizationRepository,
|
||||||
|
IOrganizationUserRepository organizationUserRepository,
|
||||||
|
ITestDatabaseHelper helper)
|
||||||
|
{
|
||||||
|
var user = await userRepository.CreateAsync(new User
|
||||||
|
{
|
||||||
|
Name = "Test User",
|
||||||
|
Email = "test@email.com",
|
||||||
|
ApiKey = "TEST",
|
||||||
|
SecurityStamp = "stamp",
|
||||||
|
});
|
||||||
|
|
||||||
|
var organization = await organizationRepository.CreateAsync(new Organization
|
||||||
|
{
|
||||||
|
Name = "Test Org",
|
||||||
|
});
|
||||||
|
|
||||||
|
var orgUser = await organizationUserRepository.CreateAsync(new OrganizationUser
|
||||||
|
{
|
||||||
|
OrganizationId = organization.Id,
|
||||||
|
UserId = user.Id,
|
||||||
|
Status = OrganizationUserStatusType.Confirmed,
|
||||||
|
});
|
||||||
|
|
||||||
|
helper.ClearTracker();
|
||||||
|
|
||||||
|
await organizationUserRepository.DeleteAsync(orgUser);
|
||||||
|
|
||||||
|
var newUser = await userRepository.GetByIdAsync(user.Id);
|
||||||
|
Assert.NotEqual(newUser.AccountRevisionDate, user.AccountRevisionDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
[DatabaseTheory, DatabaseData]
|
||||||
|
public async Task DeleteManyAsync_Works(IUserRepository userRepository,
|
||||||
|
IOrganizationRepository organizationRepository,
|
||||||
|
IOrganizationUserRepository organizationUserRepository,
|
||||||
|
ITestDatabaseHelper helper)
|
||||||
|
{
|
||||||
|
var user1 = await userRepository.CreateAsync(new User
|
||||||
|
{
|
||||||
|
Name = "Test User 1",
|
||||||
|
Email = "test1@email.com",
|
||||||
|
ApiKey = "TEST",
|
||||||
|
SecurityStamp = "stamp",
|
||||||
|
});
|
||||||
|
|
||||||
|
var user2 = await userRepository.CreateAsync(new User
|
||||||
|
{
|
||||||
|
Name = "Test User 2",
|
||||||
|
Email = "test1@email.com",
|
||||||
|
ApiKey = "TEST",
|
||||||
|
SecurityStamp = "stamp",
|
||||||
|
});
|
||||||
|
|
||||||
|
var organization = await organizationRepository.CreateAsync(new Organization
|
||||||
|
{
|
||||||
|
Name = "Test Org",
|
||||||
|
});
|
||||||
|
|
||||||
|
var orgUser1 = await organizationUserRepository.CreateAsync(new OrganizationUser
|
||||||
|
{
|
||||||
|
OrganizationId = organization.Id,
|
||||||
|
UserId = user1.Id,
|
||||||
|
});
|
||||||
|
|
||||||
|
var orgUser2 = await organizationUserRepository.CreateAsync(new OrganizationUser
|
||||||
|
{
|
||||||
|
OrganizationId = organization.Id,
|
||||||
|
UserId = user2.Id,
|
||||||
|
});
|
||||||
|
|
||||||
|
helper.ClearTracker();
|
||||||
|
|
||||||
|
await organizationUserRepository.DeleteManyAsync(new List<Guid>
|
||||||
|
{
|
||||||
|
orgUser1.Id,
|
||||||
|
orgUser2.Id,
|
||||||
|
});
|
||||||
|
|
||||||
|
var updatedUser1 = await userRepository.GetByIdAsync(user1.Id);
|
||||||
|
var updatedUser2 = await userRepository.GetByIdAsync(user2.Id);
|
||||||
|
|
||||||
|
Assert.NotEqual(updatedUser1.AccountRevisionDate, user1.AccountRevisionDate);
|
||||||
|
Assert.NotEqual(updatedUser2.AccountRevisionDate, user2.AccountRevisionDate);
|
||||||
|
}
|
||||||
|
}
|
36
test/Infrastructure.IntegrationTest/TestDatabaseHelpers.cs
Normal file
36
test/Infrastructure.IntegrationTest/TestDatabaseHelpers.cs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
using Bit.Infrastructure.EntityFramework.Repositories;
|
||||||
|
|
||||||
|
namespace Bit.Infrastructure.IntegrationTest;
|
||||||
|
|
||||||
|
public interface ITestDatabaseHelper
|
||||||
|
{
|
||||||
|
void ClearTracker();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class EfTestDatabaseHelper : ITestDatabaseHelper
|
||||||
|
{
|
||||||
|
private readonly DatabaseContext _databaseContext;
|
||||||
|
|
||||||
|
public EfTestDatabaseHelper(DatabaseContext databaseContext)
|
||||||
|
{
|
||||||
|
_databaseContext = databaseContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ClearTracker()
|
||||||
|
{
|
||||||
|
_databaseContext.ChangeTracker.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DapperSqlServerTestDatabaseHelper : ITestDatabaseHelper
|
||||||
|
{
|
||||||
|
public DapperSqlServerTestDatabaseHelper()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ClearTracker()
|
||||||
|
{
|
||||||
|
// There are no tracked entities in Dapper SQL Server
|
||||||
|
}
|
||||||
|
}
|
2845
test/Infrastructure.IntegrationTest/packages.lock.json
Normal file
2845
test/Infrastructure.IntegrationTest/packages.lock.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -21,6 +21,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Infrastructure.EFIntegratio
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.IntegrationTest", "Api.IntegrationTest\Api.IntegrationTest.csproj", "{6ED94433-3423-498C-96C9-F24756357D95}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.IntegrationTest", "Api.IntegrationTest\Api.IntegrationTest.csproj", "{6ED94433-3423-498C-96C9-F24756357D95}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Infrastructure.IntegrationTest", "Infrastructure.IntegrationTest\Infrastructure.IntegrationTest.csproj", "{5827E256-D1C5-4BBE-BB74-ED28A83578FA}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@ -142,5 +144,17 @@ Global
|
|||||||
{6ED94433-3423-498C-96C9-F24756357D95}.Release|x64.Build.0 = Release|Any CPU
|
{6ED94433-3423-498C-96C9-F24756357D95}.Release|x64.Build.0 = Release|Any CPU
|
||||||
{6ED94433-3423-498C-96C9-F24756357D95}.Release|x86.ActiveCfg = Release|Any CPU
|
{6ED94433-3423-498C-96C9-F24756357D95}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
{6ED94433-3423-498C-96C9-F24756357D95}.Release|x86.Build.0 = Release|Any CPU
|
{6ED94433-3423-498C-96C9-F24756357D95}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{5827E256-D1C5-4BBE-BB74-ED28A83578FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{5827E256-D1C5-4BBE-BB74-ED28A83578FA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{5827E256-D1C5-4BBE-BB74-ED28A83578FA}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{5827E256-D1C5-4BBE-BB74-ED28A83578FA}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{5827E256-D1C5-4BBE-BB74-ED28A83578FA}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{5827E256-D1C5-4BBE-BB74-ED28A83578FA}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{5827E256-D1C5-4BBE-BB74-ED28A83578FA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{5827E256-D1C5-4BBE-BB74-ED28A83578FA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{5827E256-D1C5-4BBE-BB74-ED28A83578FA}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{5827E256-D1C5-4BBE-BB74-ED28A83578FA}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{5827E256-D1C5-4BBE-BB74-ED28A83578FA}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{5827E256-D1C5-4BBE-BB74-ED28A83578FA}.Release|x86.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
Loading…
Reference in New Issue
Block a user