1
0
mirror of https://github.com/bitwarden/server.git synced 2025-04-04 20:50:21 -05:00

SM-561: Update Secret Revision Dates (#2770)

* SM-561: Update secret revision date on restore

* SM-561: Update secret revision dates when a project is deleted

* SM-561: Fix bug when updating revision dates for secrets when their parent project is deleted

* SM-561: Handle case when there are no secrets in the projects that are being deleted

* SM-561: Rename func to GetManyWithSecretsByIds and move UpdateRevisionDates call from ProjectsController to projects delete command

* SM-561: update secret ids before project deletion

* SM-561: Refactor out command in command call to follow CQRS pattern

* SM-561: Remove null check
This commit is contained in:
Colton Hurst 2023-03-10 11:54:19 -05:00 committed by GitHub
parent 250509c7ac
commit 397f3d6865
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 42 additions and 12 deletions

View File

@ -11,11 +11,13 @@ public class DeleteProjectCommand : IDeleteProjectCommand
{
private readonly IProjectRepository _projectRepository;
private readonly ICurrentContext _currentContext;
private readonly ISecretRepository _secretRepository;
public DeleteProjectCommand(IProjectRepository projectRepository, ICurrentContext currentContext)
public DeleteProjectCommand(IProjectRepository projectRepository, ICurrentContext currentContext, ISecretRepository secretRepository)
{
_projectRepository = projectRepository;
_currentContext = currentContext;
_secretRepository = secretRepository;
}
public async Task<List<Tuple<Project, string>>> DeleteProjects(List<Guid> ids, Guid userId)
@ -25,7 +27,7 @@ public class DeleteProjectCommand : IDeleteProjectCommand
throw new ArgumentNullException();
}
var projects = (await _projectRepository.GetManyByIds(ids))?.ToList();
var projects = (await _projectRepository.GetManyWithSecretsByIds(ids))?.ToList();
if (projects?.Any() != true || projects.Count != ids.Count)
{
@ -72,9 +74,16 @@ public class DeleteProjectCommand : IDeleteProjectCommand
if (deleteIds.Count > 0)
{
var secretIds = results.SelectMany(projTuple => projTuple.Item1?.Secrets?.Select(s => s.Id) ?? Array.Empty<Guid>()).ToList();
if (secretIds.Count > 0)
{
await _secretRepository.UpdateRevisionDates(secretIds);
}
await _projectRepository.DeleteManyByIdAsync(deleteIds);
}
return results;
}
}

View File

@ -91,12 +91,13 @@ public class ProjectRepository : Repository<Core.SecretsManager.Entities.Project
}
}
public async Task<IEnumerable<Core.SecretsManager.Entities.Project>> GetManyByIds(IEnumerable<Guid> ids)
public async Task<IEnumerable<Core.SecretsManager.Entities.Project>> GetManyWithSecretsByIds(IEnumerable<Guid> ids)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var projects = await dbContext.Project
.Include(p => p.Secrets)
.Where(c => ids.Contains(c.Id) && c.DeletedDate == null)
.ToListAsync();
return Mapper.Map<List<Core.SecretsManager.Entities.Project>>(projects);

View File

@ -212,11 +212,13 @@ public class SecretRepository : Repository<Core.SecretsManager.Entities.Secret,
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var utcNow = DateTime.UtcNow;
var secrets = dbContext.Secret.Where(c => ids.Contains(c.Id));
await secrets.ForEachAsync(secret =>
{
dbContext.Attach(secret);
secret.DeletedDate = null;
secret.RevisionDate = utcNow;
});
await dbContext.SaveChangesAsync();
}
@ -252,4 +254,22 @@ public class SecretRepository : Repository<Core.SecretsManager.Entities.Secret,
}
return secrets;
}
public async Task UpdateRevisionDates(IEnumerable<Guid> ids)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var utcNow = DateTime.UtcNow;
var secrets = dbContext.Secret.Where(s => ids.Contains(s.Id));
await secrets.ForEachAsync(secret =>
{
dbContext.Attach(secret);
secret.RevisionDate = utcNow;
});
await dbContext.SaveChangesAsync();
}
}
}

View File

@ -19,7 +19,7 @@ public class DeleteProjectCommandTests
public async Task DeleteProjects_Throws_NotFoundException(List<Guid> data, Guid userId,
SutProvider<DeleteProjectCommand> sutProvider)
{
sutProvider.GetDependency<IProjectRepository>().GetManyByIds(data).Returns(new List<Project>());
sutProvider.GetDependency<IProjectRepository>().GetManyWithSecretsByIds(data).Returns(new List<Project>());
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.DeleteProjects(data, userId));
@ -35,7 +35,7 @@ public class DeleteProjectCommandTests
{
Id = Guid.NewGuid()
};
sutProvider.GetDependency<IProjectRepository>().GetManyByIds(data).Returns(new List<Project>() { project });
sutProvider.GetDependency<IProjectRepository>().GetManyWithSecretsByIds(data).Returns(new List<Project>() { project });
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.DeleteProjects(data, userId));
@ -51,7 +51,7 @@ public class DeleteProjectCommandTests
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(organizationId).Returns(true);
sutProvider.GetDependency<ICurrentContext>().ClientType = ClientType.User;
sutProvider.GetDependency<IProjectRepository>().GetManyByIds(data).Returns(projects);
sutProvider.GetDependency<IProjectRepository>().GetManyWithSecretsByIds(data).Returns(projects);
sutProvider.GetDependency<IProjectRepository>().UserHasWriteAccessToProject(Arg.Any<Guid>(), userId).Returns(true);
var results = await sutProvider.Sut.DeleteProjects(data, userId);
@ -73,7 +73,7 @@ public class DeleteProjectCommandTests
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(organizationId).Returns(true);
sutProvider.GetDependency<ICurrentContext>().ClientType = ClientType.User;
sutProvider.GetDependency<IProjectRepository>().GetManyByIds(data).Returns(projects);
sutProvider.GetDependency<IProjectRepository>().GetManyWithSecretsByIds(data).Returns(projects);
sutProvider.GetDependency<IProjectRepository>().UserHasWriteAccessToProject(userId, userId).Returns(false);
var results = await sutProvider.Sut.DeleteProjects(data, userId);
@ -95,7 +95,7 @@ public class DeleteProjectCommandTests
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(organizationId).Returns(true);
sutProvider.GetDependency<ICurrentContext>().OrganizationAdmin(organizationId).Returns(true);
sutProvider.GetDependency<IProjectRepository>().GetManyByIds(data).Returns(projects);
sutProvider.GetDependency<IProjectRepository>().GetManyWithSecretsByIds(data).Returns(projects);
var results = await sutProvider.Sut.DeleteProjects(data, userId);

View File

@ -111,7 +111,6 @@ public class ProjectsController : Controller
public async Task<ListResponseModel<BulkDeleteResponseModel>> BulkDeleteAsync([FromBody] List<Guid> ids)
{
var userId = _userService.GetProperUserId(User).Value;
var results = await _deleteProjectCommand.DeleteProjects(ids, userId);
var responses = results.Select(r => new BulkDeleteResponseModel(r.Item1.Id, r.Item2));
return new ListResponseModel<BulkDeleteResponseModel>(responses);

View File

@ -7,7 +7,7 @@ public interface IProjectRepository
{
Task<IEnumerable<Project>> GetManyByOrganizationIdAsync(Guid organizationId, Guid userId, AccessClientType accessType);
Task<IEnumerable<Project>> GetManyByOrganizationIdWriteAccessAsync(Guid organizationId, Guid userId, AccessClientType accessType);
Task<IEnumerable<Project>> GetManyByIds(IEnumerable<Guid> ids);
Task<IEnumerable<Project>> GetManyWithSecretsByIds(IEnumerable<Guid> ids);
Task<Project> GetByIdAsync(Guid id);
Task<Project> CreateAsync(Project project);
Task ReplaceAsync(Project project);

View File

@ -17,4 +17,5 @@ public interface ISecretRepository
Task HardDeleteManyByIdAsync(IEnumerable<Guid> ids);
Task RestoreManyByIdAsync(IEnumerable<Guid> ids);
Task<IEnumerable<Secret>> ImportAsync(IEnumerable<Secret> secrets);
Task UpdateRevisionDates(IEnumerable<Guid> ids);
}

View File

@ -361,7 +361,7 @@ public class ProjectsControllerTest : IClassFixture<ApiApplicationFactory>, IAsy
results!.Data.Select(x => x.Id).OrderBy(x => x));
Assert.DoesNotContain(results.Data, x => x.Error != null);
var projects = await _projectRepository.GetManyByIds(projectIds);
var projects = await _projectRepository.GetManyWithSecretsByIds(projectIds);
Assert.Empty(projects);
}