mirror of
https://github.com/bitwarden/server.git
synced 2025-06-28 06:36:15 -05:00
Refactor ICollectionService and CollectionService to remove SaveAsync method
* Removed the SaveAsync method from ICollectionService and its implementation in CollectionService. * Updated related tests in CollectionServiceTests to reflect the removal of SaveAsync, ensuring existing functionality remains intact.
This commit is contained in:
parent
ba9fee9ca5
commit
ff475afc2f
@ -1,10 +1,8 @@
|
|||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Models.Data;
|
|
||||||
|
|
||||||
namespace Bit.Core.Services;
|
namespace Bit.Core.Services;
|
||||||
|
|
||||||
public interface ICollectionService
|
public interface ICollectionService
|
||||||
{
|
{
|
||||||
Task SaveAsync(Collection collection, IEnumerable<CollectionAccessSelection> groups = null, IEnumerable<CollectionAccessSelection> users = null);
|
|
||||||
Task DeleteUserAsync(Collection collection, Guid organizationUserId);
|
Task DeleteUserAsync(Collection collection, Guid organizationUserId);
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Models.Data;
|
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
|
|
||||||
namespace Bit.Core.Services;
|
namespace Bit.Core.Services;
|
||||||
@ -10,71 +9,20 @@ namespace Bit.Core.Services;
|
|||||||
public class CollectionService : ICollectionService
|
public class CollectionService : ICollectionService
|
||||||
{
|
{
|
||||||
private readonly IEventService _eventService;
|
private readonly IEventService _eventService;
|
||||||
private readonly IOrganizationRepository _organizationRepository;
|
|
||||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||||
private readonly ICollectionRepository _collectionRepository;
|
private readonly ICollectionRepository _collectionRepository;
|
||||||
|
|
||||||
public CollectionService(
|
public CollectionService(
|
||||||
IEventService eventService,
|
IEventService eventService,
|
||||||
IOrganizationRepository organizationRepository,
|
|
||||||
IOrganizationUserRepository organizationUserRepository,
|
IOrganizationUserRepository organizationUserRepository,
|
||||||
ICollectionRepository collectionRepository)
|
ICollectionRepository collectionRepository)
|
||||||
{
|
{
|
||||||
_eventService = eventService;
|
_eventService = eventService;
|
||||||
_organizationRepository = organizationRepository;
|
|
||||||
_organizationUserRepository = organizationUserRepository;
|
_organizationUserRepository = organizationUserRepository;
|
||||||
_collectionRepository = collectionRepository;
|
_collectionRepository = collectionRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SaveAsync(Collection collection, IEnumerable<CollectionAccessSelection>? groups = null,
|
|
||||||
IEnumerable<CollectionAccessSelection>? users = null)
|
|
||||||
{
|
|
||||||
var org = await _organizationRepository.GetByIdAsync(collection.OrganizationId);
|
|
||||||
if (org == null)
|
|
||||||
{
|
|
||||||
throw new BadRequestException("Organization not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
var groupsList = groups?.ToList();
|
|
||||||
var usersList = users?.ToList();
|
|
||||||
|
|
||||||
// Cannot use Manage with ReadOnly/HidePasswords permissions
|
|
||||||
var invalidAssociations = groupsList?.Where(cas => cas.Manage && (cas.ReadOnly || cas.HidePasswords));
|
|
||||||
if (invalidAssociations?.Any() ?? false)
|
|
||||||
{
|
|
||||||
throw new BadRequestException("The Manage property is mutually exclusive and cannot be true while the ReadOnly or HidePasswords properties are also true.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// A collection should always have someone with Can Manage permissions
|
|
||||||
var groupHasManageAccess = groupsList?.Any(g => g.Manage) ?? false;
|
|
||||||
var userHasManageAccess = usersList?.Any(u => u.Manage) ?? false;
|
|
||||||
if (!groupHasManageAccess && !userHasManageAccess && !org.AllowAdminAccessToAllCollectionItems)
|
|
||||||
{
|
|
||||||
throw new BadRequestException(
|
|
||||||
"At least one member or group must have can manage permission.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (collection.Id == default(Guid))
|
|
||||||
{
|
|
||||||
if (org.MaxCollections.HasValue)
|
|
||||||
{
|
|
||||||
var collectionCount = await _collectionRepository.GetCountByOrganizationIdAsync(org.Id);
|
|
||||||
if (org.MaxCollections.Value <= collectionCount)
|
|
||||||
{
|
|
||||||
throw new BadRequestException("You have reached the maximum number of collections " +
|
|
||||||
$"({org.MaxCollections.Value}) for this organization.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await _collectionRepository.CreateAsync(collection, org.UseGroups ? groupsList : null, usersList);
|
|
||||||
await _eventService.LogCollectionEventAsync(collection, Enums.EventType.Collection_Created);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await _collectionRepository.ReplaceAsync(collection, org.UseGroups ? groupsList : null, usersList);
|
|
||||||
await _eventService.LogCollectionEventAsync(collection, Enums.EventType.Collection_Updated);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task DeleteUserAsync(Collection collection, Guid organizationUserId)
|
public async Task DeleteUserAsync(Collection collection, Guid organizationUserId)
|
||||||
{
|
{
|
||||||
|
@ -2,10 +2,8 @@
|
|||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Models.Data;
|
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
using Bit.Core.Test.AutoFixture;
|
|
||||||
using Bit.Core.Test.AutoFixture.OrganizationFixtures;
|
using Bit.Core.Test.AutoFixture.OrganizationFixtures;
|
||||||
using Bit.Test.Common.AutoFixture;
|
using Bit.Test.Common.AutoFixture;
|
||||||
using Bit.Test.Common.AutoFixture.Attributes;
|
using Bit.Test.Common.AutoFixture.Attributes;
|
||||||
@ -18,128 +16,6 @@ namespace Bit.Core.Test.Services;
|
|||||||
[OrganizationCustomize]
|
[OrganizationCustomize]
|
||||||
public class CollectionServiceTest
|
public class CollectionServiceTest
|
||||||
{
|
{
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task SaveAsync_DefaultIdWithUsers_CreatesCollectionInTheRepository(Collection collection, Organization organization, [CollectionAccessSelectionCustomize(true)] IEnumerable<CollectionAccessSelection> users, SutProvider<CollectionService> sutProvider)
|
|
||||||
{
|
|
||||||
collection.Id = default;
|
|
||||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
|
||||||
var utcNow = DateTime.UtcNow;
|
|
||||||
|
|
||||||
await sutProvider.Sut.SaveAsync(collection, null, users);
|
|
||||||
|
|
||||||
await sutProvider.GetDependency<ICollectionRepository>().Received()
|
|
||||||
.CreateAsync(collection, Arg.Is<List<CollectionAccessSelection>>(l => l == null),
|
|
||||||
Arg.Is<List<CollectionAccessSelection>>(l => l.Any(i => i.Manage == true)));
|
|
||||||
await sutProvider.GetDependency<IEventService>().Received()
|
|
||||||
.LogCollectionEventAsync(collection, EventType.Collection_Created);
|
|
||||||
Assert.True(collection.CreationDate - utcNow < TimeSpan.FromSeconds(1));
|
|
||||||
Assert.True(collection.RevisionDate - utcNow < TimeSpan.FromSeconds(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task SaveAsync_DefaultIdWithGroupsAndUsers_CreateCollectionWithGroupsAndUsersInRepository(Collection collection,
|
|
||||||
[CollectionAccessSelectionCustomize(true)] IEnumerable<CollectionAccessSelection> groups, IEnumerable<CollectionAccessSelection> users, Organization organization, SutProvider<CollectionService> sutProvider)
|
|
||||||
{
|
|
||||||
collection.Id = default;
|
|
||||||
organization.UseGroups = true;
|
|
||||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
|
||||||
var utcNow = DateTime.UtcNow;
|
|
||||||
|
|
||||||
await sutProvider.Sut.SaveAsync(collection, groups, users);
|
|
||||||
|
|
||||||
await sutProvider.GetDependency<ICollectionRepository>().Received()
|
|
||||||
.CreateAsync(collection, Arg.Is<List<CollectionAccessSelection>>(l => l.Any(i => i.Manage == true)),
|
|
||||||
Arg.Any<List<CollectionAccessSelection>>());
|
|
||||||
await sutProvider.GetDependency<IEventService>().Received()
|
|
||||||
.LogCollectionEventAsync(collection, EventType.Collection_Created);
|
|
||||||
Assert.True(collection.CreationDate - utcNow < TimeSpan.FromSeconds(1));
|
|
||||||
Assert.True(collection.RevisionDate - utcNow < TimeSpan.FromSeconds(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task SaveAsync_NonDefaultId_ReplacesCollectionInRepository(Collection collection, Organization organization, [CollectionAccessSelectionCustomize(true)] IEnumerable<CollectionAccessSelection> users, SutProvider<CollectionService> sutProvider)
|
|
||||||
{
|
|
||||||
var creationDate = collection.CreationDate;
|
|
||||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
|
||||||
var utcNow = DateTime.UtcNow;
|
|
||||||
|
|
||||||
await sutProvider.Sut.SaveAsync(collection, null, users);
|
|
||||||
|
|
||||||
await sutProvider.GetDependency<ICollectionRepository>().Received().ReplaceAsync(collection,
|
|
||||||
Arg.Is<List<CollectionAccessSelection>>(l => l == null),
|
|
||||||
Arg.Is<List<CollectionAccessSelection>>(l => l.Any(i => i.Manage == true)));
|
|
||||||
await sutProvider.GetDependency<IEventService>().Received()
|
|
||||||
.LogCollectionEventAsync(collection, EventType.Collection_Updated);
|
|
||||||
Assert.Equal(collection.CreationDate, creationDate);
|
|
||||||
Assert.True(collection.RevisionDate - utcNow < TimeSpan.FromSeconds(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task SaveAsync_OrganizationNotUseGroup_CreateCollectionWithoutGroupsInRepository(Collection collection,
|
|
||||||
[CollectionAccessSelectionCustomize] IEnumerable<CollectionAccessSelection> groups, [CollectionAccessSelectionCustomize(true)] IEnumerable<CollectionAccessSelection> users,
|
|
||||||
Organization organization, SutProvider<CollectionService> sutProvider)
|
|
||||||
{
|
|
||||||
collection.Id = default;
|
|
||||||
organization.UseGroups = false;
|
|
||||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
|
||||||
var utcNow = DateTime.UtcNow;
|
|
||||||
|
|
||||||
await sutProvider.Sut.SaveAsync(collection, groups, users);
|
|
||||||
|
|
||||||
await sutProvider.GetDependency<ICollectionRepository>().Received().CreateAsync(collection,
|
|
||||||
Arg.Is<List<CollectionAccessSelection>>(l => l == null),
|
|
||||||
Arg.Is<List<CollectionAccessSelection>>(l => l.Any(i => i.Manage == true)));
|
|
||||||
await sutProvider.GetDependency<IEventService>().Received()
|
|
||||||
.LogCollectionEventAsync(collection, EventType.Collection_Created);
|
|
||||||
Assert.True(collection.CreationDate - utcNow < TimeSpan.FromSeconds(1));
|
|
||||||
Assert.True(collection.RevisionDate - utcNow < TimeSpan.FromSeconds(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task SaveAsync_NonExistingOrganizationId_ThrowsBadRequest(Collection collection, SutProvider<CollectionService> sutProvider)
|
|
||||||
{
|
|
||||||
var ex = await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.SaveAsync(collection));
|
|
||||||
Assert.Contains("Organization not found", ex.Message);
|
|
||||||
await sutProvider.GetDependency<ICollectionRepository>().DidNotReceiveWithAnyArgs().CreateAsync(default);
|
|
||||||
await sutProvider.GetDependency<ICollectionRepository>().DidNotReceiveWithAnyArgs().CreateAsync(default, default, default);
|
|
||||||
await sutProvider.GetDependency<ICollectionRepository>().DidNotReceiveWithAnyArgs().ReplaceAsync(default);
|
|
||||||
await sutProvider.GetDependency<IEventService>().DidNotReceiveWithAnyArgs().LogCollectionEventAsync(default, default);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task SaveAsync_NoManageAccess_ThrowsBadRequest(Collection collection, Organization organization,
|
|
||||||
[CollectionAccessSelectionCustomize] IEnumerable<CollectionAccessSelection> users, SutProvider<CollectionService> sutProvider)
|
|
||||||
{
|
|
||||||
collection.Id = default;
|
|
||||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
|
||||||
organization.AllowAdminAccessToAllCollectionItems = false;
|
|
||||||
|
|
||||||
var ex = await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.SaveAsync(collection, null, users));
|
|
||||||
Assert.Contains("At least one member or group must have can manage permission.", ex.Message);
|
|
||||||
await sutProvider.GetDependency<ICollectionRepository>().DidNotReceiveWithAnyArgs().CreateAsync(default);
|
|
||||||
await sutProvider.GetDependency<ICollectionRepository>().DidNotReceiveWithAnyArgs().CreateAsync(default, default, default);
|
|
||||||
await sutProvider.GetDependency<ICollectionRepository>().DidNotReceiveWithAnyArgs().ReplaceAsync(default);
|
|
||||||
await sutProvider.GetDependency<IEventService>().DidNotReceiveWithAnyArgs().LogCollectionEventAsync(default, default);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
|
||||||
public async Task SaveAsync_ExceedsOrganizationMaxCollections_ThrowsBadRequest(Collection collection,
|
|
||||||
Organization organization, [CollectionAccessSelectionCustomize(true)] IEnumerable<CollectionAccessSelection> users,
|
|
||||||
SutProvider<CollectionService> sutProvider)
|
|
||||||
{
|
|
||||||
collection.Id = default;
|
|
||||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
|
||||||
sutProvider.GetDependency<ICollectionRepository>().GetCountByOrganizationIdAsync(organization.Id)
|
|
||||||
.Returns(organization.MaxCollections.Value);
|
|
||||||
|
|
||||||
var ex = await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.SaveAsync(collection, null, users));
|
|
||||||
Assert.Equal($@"You have reached the maximum number of collections ({organization.MaxCollections.Value}) for this organization.", ex.Message);
|
|
||||||
await sutProvider.GetDependency<ICollectionRepository>().DidNotReceiveWithAnyArgs().CreateAsync(default);
|
|
||||||
await sutProvider.GetDependency<ICollectionRepository>().DidNotReceiveWithAnyArgs().CreateAsync(default, default, default);
|
|
||||||
await sutProvider.GetDependency<ICollectionRepository>().DidNotReceiveWithAnyArgs().ReplaceAsync(default);
|
|
||||||
await sutProvider.GetDependency<IEventService>().DidNotReceiveWithAnyArgs().LogCollectionEventAsync(default, default);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
[Theory, BitAutoData]
|
||||||
public async Task DeleteUserAsync_DeletesValidUserWhoBelongsToCollection(Collection collection,
|
public async Task DeleteUserAsync_DeletesValidUserWhoBelongsToCollection(Collection collection,
|
||||||
Organization organization, OrganizationUser organizationUser, SutProvider<CollectionService> sutProvider)
|
Organization organization, OrganizationUser organizationUser, SutProvider<CollectionService> sutProvider)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user