1
0
mirror of https://github.com/bitwarden/server.git synced 2024-11-21 12:05:42 +01:00

[AC-2052] Block Manager role and AccessAll if using FlexibleCollections (#3671)

* Also don't assign AccessAll to the first orgUser if using Flexible Collections
This commit is contained in:
Thomas Rittson 2024-01-22 08:56:20 +10:00 committed by GitHub
parent 4b6299a055
commit 77698c3ee2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 291 additions and 63 deletions

View File

@ -515,7 +515,10 @@ public class ProviderService : IProviderService
new OrganizationUserInvite
{
Emails = new[] { clientOwnerEmail },
AccessAll = true,
// If using Flexible Collections, AccessAll is deprecated and set to false.
// If not using Flexible Collections, set AccessAll to true (previous behavior)
AccessAll = !organization.FlexibleCollections,
Type = OrganizationUserType.Owner,
Permissions = null,
Collections = Array.Empty<CollectionAccessSelection>(),

View File

@ -12,6 +12,7 @@ using Bit.Core.Exceptions;
using Bit.Core.Models.Business;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Test.AutoFixture.OrganizationFixtures;
using Bit.Core.Utilities;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
@ -513,7 +514,7 @@ public class ProviderServiceTests
await sutProvider.GetDependency<IEventService>().DidNotReceiveWithAnyArgs().LogProviderOrganizationEventsAsync(default);
}
[Theory, BitAutoData]
[Theory, OrganizationCustomize(FlexibleCollections = false), BitAutoData]
public async Task CreateOrganizationAsync_Success(Provider provider, OrganizationSignup organizationSignup,
Organization organization, string clientOwnerEmail, User user, SutProvider<ProviderService> sutProvider)
{
@ -541,6 +542,35 @@ public class ProviderServiceTests
t.First().Item2 == null));
}
[Theory, OrganizationCustomize(FlexibleCollections = true), BitAutoData]
public async Task CreateOrganizationAsync_WithFlexibleCollections_SetsAccessAllToFalse
(Provider provider, OrganizationSignup organizationSignup, Organization organization, string clientOwnerEmail,
User user, SutProvider<ProviderService> sutProvider)
{
organizationSignup.Plan = PlanType.EnterpriseAnnually;
sutProvider.GetDependency<IProviderRepository>().GetByIdAsync(provider.Id).Returns(provider);
var providerOrganizationRepository = sutProvider.GetDependency<IProviderOrganizationRepository>();
sutProvider.GetDependency<IOrganizationService>().SignUpAsync(organizationSignup, true)
.Returns(Tuple.Create(organization, null as OrganizationUser));
var providerOrganization =
await sutProvider.Sut.CreateOrganizationAsync(provider.Id, organizationSignup, clientOwnerEmail, user);
await providerOrganizationRepository.ReceivedWithAnyArgs().CreateAsync(default);
await sutProvider.GetDependency<IEventService>()
.Received().LogProviderOrganizationEventAsync(providerOrganization,
EventType.ProviderOrganization_Created);
await sutProvider.GetDependency<IOrganizationService>()
.Received().InviteUsersAsync(organization.Id, user.Id, Arg.Is<IEnumerable<(OrganizationUserInvite, string)>>(
t => t.Count() == 1 &&
t.First().Item1.Emails.Count() == 1 &&
t.First().Item1.Emails.First() == clientOwnerEmail &&
t.First().Item1.Type == OrganizationUserType.Owner &&
t.First().Item1.AccessAll == false &&
t.First().Item2 == null));
}
[Theory, BitAutoData]
public async Task AddOrganization_CreateAfterNov162023_PlanTypeDoesNotUpdated(Provider provider, Organization organization, string key,
SutProvider<ProviderService> sutProvider)

View File

@ -39,7 +39,7 @@ public class CreateGroupCommand : ICreateGroupCommand
IEnumerable<CollectionAccessSelection> collections = null,
IEnumerable<Guid> users = null)
{
Validate(organization);
Validate(organization, group);
await GroupRepositoryCreateGroupAsync(group, organization, collections);
if (users != null)
@ -54,7 +54,7 @@ public class CreateGroupCommand : ICreateGroupCommand
IEnumerable<CollectionAccessSelection> collections = null,
IEnumerable<Guid> users = null)
{
Validate(organization);
Validate(organization, group);
await GroupRepositoryCreateGroupAsync(group, organization, collections);
if (users != null)
@ -103,7 +103,7 @@ public class CreateGroupCommand : ICreateGroupCommand
}
}
private static void Validate(Organization organization)
private static void Validate(Organization organization, Group group)
{
if (organization == null)
{
@ -114,5 +114,10 @@ public class CreateGroupCommand : ICreateGroupCommand
{
throw new BadRequestException("This organization cannot use groups.");
}
if (organization.FlexibleCollections && group.AccessAll)
{
throw new BadRequestException("The AccessAll property has been deprecated by collection enhancements. Assign the group to collections instead.");
}
}
}

View File

@ -29,7 +29,7 @@ public class UpdateGroupCommand : IUpdateGroupCommand
IEnumerable<CollectionAccessSelection> collections = null,
IEnumerable<Guid> userIds = null)
{
Validate(organization);
Validate(organization, group);
await GroupRepositoryUpdateGroupAsync(group, collections);
if (userIds != null)
@ -44,7 +44,7 @@ public class UpdateGroupCommand : IUpdateGroupCommand
IEnumerable<CollectionAccessSelection> collections = null,
IEnumerable<Guid> userIds = null)
{
Validate(organization);
Validate(organization, group);
await GroupRepositoryUpdateGroupAsync(group, collections);
if (userIds != null)
@ -97,7 +97,7 @@ public class UpdateGroupCommand : IUpdateGroupCommand
}
}
private static void Validate(Organization organization)
private static void Validate(Organization organization, Group group)
{
if (organization == null)
{
@ -108,5 +108,10 @@ public class UpdateGroupCommand : IUpdateGroupCommand
{
throw new BadRequestException("This organization cannot use groups.");
}
if (organization.FlexibleCollections && group.AccessAll)
{
throw new BadRequestException("The AccessAll property has been deprecated by collection enhancements. Assign the group to collections instead.");
}
}
}

View File

@ -673,7 +673,10 @@ public class OrganizationService : IOrganizationService
AccessSecretsManager = organization.UseSecretsManager,
Type = OrganizationUserType.Owner,
Status = OrganizationUserStatusType.Confirmed,
AccessAll = true,
// If using Flexible Collections, AccessAll is deprecated and set to false.
// If not using Flexible Collections, set AccessAll to true (previous behavior)
AccessAll = !organization.FlexibleCollections,
CreationDate = organization.CreationDate,
RevisionDate = organization.CreationDate
};
@ -885,6 +888,18 @@ public class OrganizationService : IOrganizationService
throw new NotFoundException();
}
// If the organization is using Flexible Collections, prevent use of any deprecated permissions
if (organization.FlexibleCollections && invites.Any(i => i.invite.Type is OrganizationUserType.Manager))
{
throw new BadRequestException("The Manager role has been deprecated by collection enhancements. Use the collection Can Manage permission instead.");
}
if (organization.FlexibleCollections && invites.Any(i => i.invite.AccessAll))
{
throw new BadRequestException("The AccessAll property has been deprecated by collection enhancements. Assign the user to collections instead.");
}
// End Flexible Collections
var existingEmails = new HashSet<string>(await _organizationUserRepository.SelectKnownEmailsAsync(
organizationId, invites.SelectMany(i => i.invite.Emails), false), StringComparer.InvariantCultureIgnoreCase);
@ -1377,6 +1392,19 @@ public class OrganizationService : IOrganizationService
throw new BadRequestException("Organization must have at least one confirmed owner.");
}
// If the organization is using Flexible Collections, prevent use of any deprecated permissions
var organizationAbility = await _applicationCacheService.GetOrganizationAbilityAsync(user.OrganizationId);
if (organizationAbility?.FlexibleCollections == true && user.Type == OrganizationUserType.Manager)
{
throw new BadRequestException("The Manager role has been deprecated by collection enhancements. Use the collection Can Manage permission instead.");
}
if (organizationAbility?.FlexibleCollections == true && user.AccessAll)
{
throw new BadRequestException("The AccessAll property has been deprecated by collection enhancements. Assign the user to collections instead.");
}
// End Flexible Collections
// Only autoscale (if required) after all validation has passed so that we know it's a valid request before
// updating Stripe
if (!originalUser.AccessSecretsManager && user.AccessSecretsManager)
@ -2027,15 +2055,6 @@ public class OrganizationService : IOrganizationService
{
throw new BadRequestException("Custom users can only grant the same custom permissions that they have.");
}
// TODO: pass in the whole organization object when this is refactored into a command/query
// See AC-2036
var organizationAbility = await _applicationCacheService.GetOrganizationAbilityAsync(organizationId);
var flexibleCollectionsEnabled = organizationAbility?.FlexibleCollections ?? false;
if (flexibleCollectionsEnabled && newType == OrganizationUserType.Manager && oldType is not OrganizationUserType.Manager)
{
throw new BadRequestException("Manager role is deprecated after Flexible Collections.");
}
}
private async Task ValidateOrganizationCustomPermissionsEnabledAsync(Guid organizationId, OrganizationUserType newType)
@ -2451,7 +2470,10 @@ public class OrganizationService : IOrganizationService
Key = null,
Type = OrganizationUserType.Owner,
Status = OrganizationUserStatusType.Invited,
AccessAll = true
// If using Flexible Collections, AccessAll is deprecated and set to false.
// If not using Flexible Collections, set AccessAll to true (previous behavior)
AccessAll = !organization.FlexibleCollections,
};
await _organizationUserRepository.CreateAsync(ownerOrganizationUser);

View File

@ -18,6 +18,7 @@ namespace Bit.Core.Test.AutoFixture.OrganizationFixtures;
public class OrganizationCustomization : ICustomization
{
public bool UseGroups { get; set; }
public bool FlexibleCollections { get; set; }
public void Customize(IFixture fixture)
{
@ -27,7 +28,8 @@ public class OrganizationCustomization : ICustomization
fixture.Customize<Organization>(composer => composer
.With(o => o.Id, organizationId)
.With(o => o.MaxCollections, maxCollections)
.With(o => o.UseGroups, UseGroups));
.With(o => o.UseGroups, UseGroups)
.With(o => o.FlexibleCollections, FlexibleCollections));
fixture.Customize<Collection>(composer =>
composer
@ -181,10 +183,15 @@ internal class TeamsMonthlyWithAddOnsOrganizationCustomization : ICustomization
}
}
internal class OrganizationCustomizeAttribute : BitCustomizeAttribute
public class OrganizationCustomizeAttribute : BitCustomizeAttribute
{
public bool UseGroups { get; set; }
public override ICustomization GetCustomization() => new OrganizationCustomization() { UseGroups = UseGroups };
public bool FlexibleCollections { get; set; }
public override ICustomization GetCustomization() => new OrganizationCustomization()
{
UseGroups = UseGroups,
FlexibleCollections = FlexibleCollections
};
}
internal class PaidOrganizationCustomizeAttribute : BitCustomizeAttribute

View File

@ -20,7 +20,7 @@ namespace Bit.Core.Test.AdminConsole.OrganizationFeatures.Groups;
[SutProviderCustomize]
public class CreateGroupCommandTests
{
[Theory, OrganizationCustomize(UseGroups = true), BitAutoData]
[Theory, OrganizationCustomize(UseGroups = true, FlexibleCollections = false), BitAutoData]
public async Task CreateGroup_Success(SutProvider<CreateGroupCommand> sutProvider, Organization organization, Group group)
{
await sutProvider.Sut.CreateGroupAsync(group, organization);
@ -32,7 +32,7 @@ public class CreateGroupCommandTests
AssertHelper.AssertRecent(group.RevisionDate);
}
[Theory, OrganizationCustomize(UseGroups = true), BitAutoData]
[Theory, OrganizationCustomize(UseGroups = true, FlexibleCollections = false), BitAutoData]
public async Task CreateGroup_WithCollections_Success(SutProvider<CreateGroupCommand> sutProvider, Organization organization, Group group, List<CollectionAccessSelection> collections)
{
await sutProvider.Sut.CreateGroupAsync(group, organization, collections);
@ -44,7 +44,7 @@ public class CreateGroupCommandTests
AssertHelper.AssertRecent(group.RevisionDate);
}
[Theory, OrganizationCustomize(UseGroups = true), BitAutoData]
[Theory, OrganizationCustomize(UseGroups = true, FlexibleCollections = false), BitAutoData]
public async Task CreateGroup_WithEventSystemUser_Success(SutProvider<CreateGroupCommand> sutProvider, Organization organization, Group group, EventSystemUser eventSystemUser)
{
await sutProvider.Sut.CreateGroupAsync(group, organization, eventSystemUser);
@ -56,7 +56,7 @@ public class CreateGroupCommandTests
AssertHelper.AssertRecent(group.RevisionDate);
}
[Theory, OrganizationCustomize(UseGroups = true), BitAutoData]
[Theory, OrganizationCustomize(UseGroups = true, FlexibleCollections = false), BitAutoData]
public async Task CreateGroup_WithNullOrganization_Throws(SutProvider<CreateGroupCommand> sutProvider, Group group, EventSystemUser eventSystemUser)
{
var exception = await Assert.ThrowsAsync<BadRequestException>(async () => await sutProvider.Sut.CreateGroupAsync(group, null, eventSystemUser));
@ -68,7 +68,7 @@ public class CreateGroupCommandTests
await sutProvider.GetDependency<IReferenceEventService>().DidNotReceiveWithAnyArgs().RaiseEventAsync(default);
}
[Theory, OrganizationCustomize(UseGroups = false), BitAutoData]
[Theory, OrganizationCustomize(UseGroups = false, FlexibleCollections = false), BitAutoData]
public async Task CreateGroup_WithUseGroupsAsFalse_Throws(SutProvider<CreateGroupCommand> sutProvider, Organization organization, Group group, EventSystemUser eventSystemUser)
{
var exception = await Assert.ThrowsAsync<BadRequestException>(async () => await sutProvider.Sut.CreateGroupAsync(group, organization, eventSystemUser));
@ -79,4 +79,20 @@ public class CreateGroupCommandTests
await sutProvider.GetDependency<IEventService>().DidNotReceiveWithAnyArgs().LogGroupEventAsync(default, default, default);
await sutProvider.GetDependency<IReferenceEventService>().DidNotReceiveWithAnyArgs().RaiseEventAsync(default);
}
[Theory, OrganizationCustomize(UseGroups = true, FlexibleCollections = true), BitAutoData]
public async Task CreateGroup_WithFlexibleCollections_WithAccessAll_Throws(
SutProvider<CreateGroupCommand> sutProvider, Organization organization, Group group)
{
group.AccessAll = true;
organization.FlexibleCollections = true;
var exception =
await Assert.ThrowsAsync<BadRequestException>(async () => await sutProvider.Sut.CreateGroupAsync(group, organization));
Assert.Contains("AccessAll property has been deprecated", exception.Message);
await sutProvider.GetDependency<IGroupRepository>().DidNotReceiveWithAnyArgs().CreateAsync(default);
await sutProvider.GetDependency<IEventService>().DidNotReceiveWithAnyArgs().LogGroupEventAsync(default, default, default);
await sutProvider.GetDependency<IReferenceEventService>().DidNotReceiveWithAnyArgs().RaiseEventAsync(default);
}
}

View File

@ -17,7 +17,7 @@ namespace Bit.Core.Test.AdminConsole.OrganizationFeatures.Groups;
[SutProviderCustomize]
public class UpdateGroupCommandTests
{
[Theory, OrganizationCustomize(UseGroups = true), BitAutoData]
[Theory, OrganizationCustomize(UseGroups = true, FlexibleCollections = false), BitAutoData]
public async Task UpdateGroup_Success(SutProvider<UpdateGroupCommand> sutProvider, Group group, Organization organization)
{
await sutProvider.Sut.UpdateGroupAsync(group, organization);
@ -27,7 +27,7 @@ public class UpdateGroupCommandTests
AssertHelper.AssertRecent(group.RevisionDate);
}
[Theory, OrganizationCustomize(UseGroups = true), BitAutoData]
[Theory, OrganizationCustomize(UseGroups = true, FlexibleCollections = false), BitAutoData]
public async Task UpdateGroup_WithCollections_Success(SutProvider<UpdateGroupCommand> sutProvider, Group group, Organization organization, List<CollectionAccessSelection> collections)
{
await sutProvider.Sut.UpdateGroupAsync(group, organization, collections);
@ -37,7 +37,7 @@ public class UpdateGroupCommandTests
AssertHelper.AssertRecent(group.RevisionDate);
}
[Theory, OrganizationCustomize(UseGroups = true), BitAutoData]
[Theory, OrganizationCustomize(UseGroups = true, FlexibleCollections = false), BitAutoData]
public async Task UpdateGroup_WithEventSystemUser_Success(SutProvider<UpdateGroupCommand> sutProvider, Group group, Organization organization, EventSystemUser eventSystemUser)
{
await sutProvider.Sut.UpdateGroupAsync(group, organization, eventSystemUser);
@ -47,7 +47,7 @@ public class UpdateGroupCommandTests
AssertHelper.AssertRecent(group.RevisionDate);
}
[Theory, OrganizationCustomize(UseGroups = true), BitAutoData]
[Theory, OrganizationCustomize(UseGroups = true, FlexibleCollections = false), BitAutoData]
public async Task UpdateGroup_WithNullOrganization_Throws(SutProvider<UpdateGroupCommand> sutProvider, Group group, EventSystemUser eventSystemUser)
{
var exception = await Assert.ThrowsAsync<BadRequestException>(async () => await sutProvider.Sut.UpdateGroupAsync(group, null, eventSystemUser));
@ -58,7 +58,7 @@ public class UpdateGroupCommandTests
await sutProvider.GetDependency<IEventService>().DidNotReceiveWithAnyArgs().LogGroupEventAsync(default, default, default);
}
[Theory, OrganizationCustomize(UseGroups = false), BitAutoData]
[Theory, OrganizationCustomize(UseGroups = false, FlexibleCollections = false), BitAutoData]
public async Task UpdateGroup_WithUseGroupsAsFalse_Throws(SutProvider<UpdateGroupCommand> sutProvider, Organization organization, Group group, EventSystemUser eventSystemUser)
{
var exception = await Assert.ThrowsAsync<BadRequestException>(async () => await sutProvider.Sut.UpdateGroupAsync(group, organization, eventSystemUser));
@ -68,4 +68,19 @@ public class UpdateGroupCommandTests
await sutProvider.GetDependency<IGroupRepository>().DidNotReceiveWithAnyArgs().CreateAsync(default);
await sutProvider.GetDependency<IEventService>().DidNotReceiveWithAnyArgs().LogGroupEventAsync(default, default, default);
}
[Theory, OrganizationCustomize(UseGroups = true, FlexibleCollections = true), BitAutoData]
public async Task UpdateGroup_WithFlexibleCollections_WithAccessAll_Throws(
SutProvider<UpdateGroupCommand> sutProvider, Organization organization, Group group)
{
group.AccessAll = true;
organization.FlexibleCollections = true;
var exception =
await Assert.ThrowsAsync<BadRequestException>(async () => await sutProvider.Sut.UpdateGroupAsync(group, organization));
Assert.Contains("AccessAll property has been deprecated", exception.Message);
await sutProvider.GetDependency<IGroupRepository>().DidNotReceiveWithAnyArgs().CreateAsync(default);
await sutProvider.GetDependency<IEventService>().DidNotReceiveWithAnyArgs().LogGroupEventAsync(default, default, default);
}
}

View File

@ -251,6 +251,64 @@ public class OrganizationServiceTests
);
}
[Theory]
[BitAutoData(PlanType.FamiliesAnnually)]
public async Task SignUp_WithFlexibleCollections_SetsAccessAllToFalse
(PlanType planType, OrganizationSignup signup, SutProvider<OrganizationService> sutProvider)
{
signup.Plan = planType;
var plan = StaticStore.GetPlan(signup.Plan);
signup.AdditionalSeats = 0;
signup.PaymentMethodType = PaymentMethodType.Card;
signup.PremiumAccessAddon = false;
signup.UseSecretsManager = false;
sutProvider.GetDependency<IFeatureService>()
.IsEnabled(FeatureFlagKeys.FlexibleCollectionsSignup)
.Returns(true);
var result = await sutProvider.Sut.SignUpAsync(signup);
await sutProvider.GetDependency<IOrganizationUserRepository>().Received(1).CreateAsync(
Arg.Is<OrganizationUser>(o =>
o.UserId == signup.Owner.Id &&
o.AccessAll == false));
Assert.NotNull(result);
Assert.NotNull(result.Item1);
Assert.NotNull(result.Item2);
Assert.IsType<Tuple<Organization, OrganizationUser>>(result);
}
[Theory]
[BitAutoData(PlanType.FamiliesAnnually)]
public async Task SignUp_WithoutFlexibleCollections_SetsAccessAllToTrue
(PlanType planType, OrganizationSignup signup, SutProvider<OrganizationService> sutProvider)
{
signup.Plan = planType;
var plan = StaticStore.GetPlan(signup.Plan);
signup.AdditionalSeats = 0;
signup.PaymentMethodType = PaymentMethodType.Card;
signup.PremiumAccessAddon = false;
signup.UseSecretsManager = false;
sutProvider.GetDependency<IFeatureService>()
.IsEnabled(FeatureFlagKeys.FlexibleCollectionsSignup)
.Returns(false);
var result = await sutProvider.Sut.SignUpAsync(signup);
await sutProvider.GetDependency<IOrganizationUserRepository>().Received(1).CreateAsync(
Arg.Is<OrganizationUser>(o =>
o.UserId == signup.Owner.Id &&
o.AccessAll == true));
Assert.NotNull(result);
Assert.NotNull(result.Item1);
Assert.NotNull(result.Item2);
Assert.IsType<Tuple<Organization, OrganizationUser>>(result);
}
[Theory]
[BitAutoData(PlanType.EnterpriseAnnually)]
[BitAutoData(PlanType.EnterpriseMonthly)]
@ -378,7 +436,7 @@ public class OrganizationServiceTests
[Theory]
[OrganizationInviteCustomize(InviteeUserType = OrganizationUserType.User,
InvitorUserType = OrganizationUserType.Owner), BitAutoData]
InvitorUserType = OrganizationUserType.Owner), OrganizationCustomize(FlexibleCollections = false), BitAutoData]
public async Task InviteUser_NoEmails_Throws(Organization organization, OrganizationUser invitor,
OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
{
@ -391,7 +449,7 @@ public class OrganizationServiceTests
}
[Theory]
[OrganizationInviteCustomize, BitAutoData]
[OrganizationInviteCustomize, OrganizationCustomize(FlexibleCollections = false), BitAutoData]
public async Task InviteUser_DuplicateEmails_PassesWithoutDuplicates(Organization organization, OrganizationUser invitor,
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
@ -434,7 +492,7 @@ public class OrganizationServiceTests
}
[Theory]
[OrganizationInviteCustomize, BitAutoData]
[OrganizationInviteCustomize, OrganizationCustomize(FlexibleCollections = false), BitAutoData]
public async Task InviteUser_SsoOrgWithNullSsoConfig_Passes(Organization organization, OrganizationUser invitor,
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
@ -483,7 +541,7 @@ public class OrganizationServiceTests
}
[Theory]
[OrganizationInviteCustomize, BitAutoData]
[OrganizationInviteCustomize, OrganizationCustomize(FlexibleCollections = false), BitAutoData]
public async Task InviteUser_SsoOrgWithNeverEnabledRequireSsoPolicy_Passes(Organization organization, SsoConfig ssoConfig, OrganizationUser invitor,
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
@ -537,7 +595,7 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
[OrganizationInviteCustomize(
InviteeUserType = OrganizationUserType.Admin,
InvitorUserType = OrganizationUserType.Owner
), BitAutoData]
), OrganizationCustomize(FlexibleCollections = false), BitAutoData]
public async Task InviteUser_NoOwner_Throws(Organization organization, OrganizationUser invitor,
OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
{
@ -553,7 +611,7 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
[OrganizationInviteCustomize(
InviteeUserType = OrganizationUserType.Owner,
InvitorUserType = OrganizationUserType.Admin
), BitAutoData]
), OrganizationCustomize(FlexibleCollections = false), BitAutoData]
public async Task InviteUser_NonOwnerConfiguringOwner_Throws(Organization organization, OrganizationUserInvite invite,
OrganizationUser invitor, SutProvider<OrganizationService> sutProvider)
{
@ -572,7 +630,7 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
[OrganizationInviteCustomize(
InviteeUserType = OrganizationUserType.Custom,
InvitorUserType = OrganizationUserType.User
), BitAutoData]
), OrganizationCustomize(FlexibleCollections = false), BitAutoData]
public async Task InviteUser_NonAdminConfiguringAdmin_Throws(Organization organization, OrganizationUserInvite invite,
OrganizationUser invitor, SutProvider<OrganizationService> sutProvider)
{
@ -593,7 +651,7 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
[OrganizationInviteCustomize(
InviteeUserType = OrganizationUserType.Custom,
InvitorUserType = OrganizationUserType.Admin
), BitAutoData]
), OrganizationCustomize(FlexibleCollections = false), BitAutoData]
public async Task InviteUser_WithCustomType_WhenUseCustomPermissionsIsFalse_Throws(Organization organization, OrganizationUserInvite invite,
OrganizationUser invitor, SutProvider<OrganizationService> sutProvider)
{
@ -620,7 +678,7 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
[OrganizationInviteCustomize(
InviteeUserType = OrganizationUserType.Custom,
InvitorUserType = OrganizationUserType.Admin
), BitAutoData]
), OrganizationCustomize(FlexibleCollections = false), BitAutoData]
public async Task InviteUser_WithCustomType_WhenUseCustomPermissionsIsTrue_Passes(Organization organization, OrganizationUserInvite invite,
OrganizationUser invitor, SutProvider<OrganizationService> sutProvider)
{
@ -646,6 +704,7 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
}
[Theory]
[OrganizationCustomize(FlexibleCollections = false)]
[BitAutoData(OrganizationUserType.Admin)]
[BitAutoData(OrganizationUserType.Manager)]
[BitAutoData(OrganizationUserType.Owner)]
@ -679,7 +738,7 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
[OrganizationInviteCustomize(
InviteeUserType = OrganizationUserType.Manager,
InvitorUserType = OrganizationUserType.Custom
), BitAutoData]
), OrganizationCustomize(FlexibleCollections = false), BitAutoData]
public async Task InviteUser_CustomUserWithoutManageUsersConfiguringUser_Throws(Organization organization, OrganizationUserInvite invite,
OrganizationUser invitor, SutProvider<OrganizationService> sutProvider)
{
@ -707,7 +766,7 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
[OrganizationInviteCustomize(
InviteeUserType = OrganizationUserType.Admin,
InvitorUserType = OrganizationUserType.Custom
), BitAutoData]
), OrganizationCustomize(FlexibleCollections = false), BitAutoData]
public async Task InviteUser_CustomUserConfiguringAdmin_Throws(Organization organization, OrganizationUserInvite invite,
OrganizationUser invitor, SutProvider<OrganizationService> sutProvider)
{
@ -733,7 +792,7 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
[OrganizationInviteCustomize(
InviteeUserType = OrganizationUserType.User,
InvitorUserType = OrganizationUserType.Owner
), BitAutoData]
), OrganizationCustomize(FlexibleCollections = false), BitAutoData]
public async Task InviteUser_NoPermissionsObject_Passes(Organization organization, OrganizationUserInvite invite,
OrganizationUser invitor, SutProvider<OrganizationService> sutProvider)
{
@ -759,7 +818,7 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
[OrganizationInviteCustomize(
InviteeUserType = OrganizationUserType.User,
InvitorUserType = OrganizationUserType.Custom
), BitAutoData]
), OrganizationCustomize(FlexibleCollections = false), BitAutoData]
public async Task InviteUser_Passes(Organization organization, IEnumerable<(OrganizationUserInvite invite, string externalId)> invites,
OrganizationUser invitor,
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
@ -832,7 +891,7 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
[OrganizationInviteCustomize(
InviteeUserType = OrganizationUserType.User,
InvitorUserType = OrganizationUserType.Custom
), BitAutoData]
), OrganizationCustomize(FlexibleCollections = false), BitAutoData]
public async Task InviteUser_WithEventSystemUser_Passes(Organization organization, EventSystemUser eventSystemUser, IEnumerable<(OrganizationUserInvite invite, string externalId)> invites,
OrganizationUser invitor,
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
@ -882,7 +941,7 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
await sutProvider.GetDependency<IEventService>().Received(1).LogOrganizationUserEventsAsync(Arg.Any<IEnumerable<(OrganizationUser, EventType, EventSystemUser, DateTime?)>>());
}
[Theory, BitAutoData, OrganizationInviteCustomize]
[Theory, BitAutoData, OrganizationCustomize(FlexibleCollections = false), OrganizationInviteCustomize]
public async Task InviteUser_WithSecretsManager_Passes(Organization organization,
IEnumerable<(OrganizationUserInvite invite, string externalId)> invites,
OrganizationUser savingUser, SutProvider<OrganizationService> sutProvider)
@ -916,7 +975,7 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
!update.MaxAutoscaleSmSeatsChanged));
}
[Theory, BitAutoData, OrganizationInviteCustomize]
[Theory, BitAutoData, OrganizationCustomize(FlexibleCollections = false), OrganizationInviteCustomize]
public async Task InviteUser_WithSecretsManager_WhenErrorIsThrown_RevertsAutoscaling(Organization organization,
IEnumerable<(OrganizationUserInvite invite, string externalId)> invites,
OrganizationUser savingUser, SutProvider<OrganizationService> sutProvider)
@ -972,26 +1031,48 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
});
}
[Theory, BitAutoData]
public async Task InviteUser_WithFCEnabled_WhenInvitingManager_Throws(OrganizationAbility organizationAbility,
[Theory, OrganizationCustomize(FlexibleCollections = true), BitAutoData]
public async Task InviteUser_WithFlexibleCollections_WhenInvitingManager_Throws(Organization organization,
OrganizationUserInvite invite, OrganizationUser invitor, SutProvider<OrganizationService> sutProvider)
{
invite.Type = OrganizationUserType.Manager;
organizationAbility.FlexibleCollections = true;
organization.FlexibleCollections = true;
sutProvider.GetDependency<IApplicationCacheService>()
.GetOrganizationAbilityAsync(organizationAbility.Id)
.Returns(organizationAbility);
sutProvider.GetDependency<IOrganizationRepository>()
.GetByIdAsync(organization.Id)
.Returns(organization);
sutProvider.GetDependency<ICurrentContext>()
.ManageUsers(organizationAbility.Id)
.ManageUsers(organization.Id)
.Returns(true);
var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.InviteUsersAsync(organizationAbility.Id, invitor.UserId,
() => sutProvider.Sut.InviteUsersAsync(organization.Id, invitor.UserId,
new (OrganizationUserInvite, string)[] { (invite, null) }));
Assert.Contains("manager role is deprecated", exception.Message.ToLowerInvariant());
Assert.Contains("manager role has been deprecated", exception.Message.ToLowerInvariant());
}
[Theory, OrganizationCustomize(FlexibleCollections = true), BitAutoData]
public async Task InviteUser_WithFlexibleCollections_WithAccessAll_Throws(Organization organization,
OrganizationUserInvite invite, OrganizationUser invitor, SutProvider<OrganizationService> sutProvider)
{
invite.Type = OrganizationUserType.User;
invite.AccessAll = true;
sutProvider.GetDependency<IOrganizationRepository>()
.GetByIdAsync(organization.Id)
.Returns(organization);
sutProvider.GetDependency<ICurrentContext>()
.ManageUsers(organization.Id)
.Returns(true);
var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.InviteUsersAsync(organization.Id, invitor.UserId,
new (OrganizationUserInvite, string)[] { (invite, null) }));
Assert.Contains("accessall property has been deprecated", exception.Message.ToLowerInvariant());
}
private void InviteUserHelper_ArrangeValidPermissions(Organization organization, OrganizationUser savingUser,
@ -1275,15 +1356,21 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
}
[Theory, BitAutoData]
public async Task SaveUser_WithFCEnabled_WhenUpgradingToManager_Throws(
public async Task SaveUser_WithFlexibleCollections_WhenUpgradingToManager_Throws(
OrganizationAbility organizationAbility,
[OrganizationUser(type: OrganizationUserType.User)] OrganizationUser oldUserData,
[OrganizationUser(type: OrganizationUserType.Manager)] OrganizationUser newUserData,
[OrganizationUser(type: OrganizationUserType.Owner, status: OrganizationUserStatusType.Confirmed)] OrganizationUser savingUser,
IEnumerable<CollectionAccessSelection> collections,
IEnumerable<Guid> groups,
SutProvider<OrganizationService> sutProvider)
{
organizationAbility.FlexibleCollections = true;
newUserData.Id = oldUserData.Id;
newUserData.UserId = oldUserData.UserId;
newUserData.OrganizationId = oldUserData.OrganizationId = savingUser.OrganizationId = organizationAbility.Id;
newUserData.Permissions = CoreHelpers.ClassToJsonData(new Permissions());
sutProvider.GetDependency<IApplicationCacheService>()
.GetOrganizationAbilityAsync(organizationAbility.Id)
.Returns(organizationAbility);
@ -1296,15 +1383,53 @@ OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
.GetByIdAsync(oldUserData.Id)
.Returns(oldUserData);
newUserData.Id = oldUserData.Id;
newUserData.UserId = oldUserData.UserId;
newUserData.OrganizationId = oldUserData.OrganizationId = organizationAbility.Id;
newUserData.Permissions = CoreHelpers.ClassToJsonData(new Permissions());
sutProvider.GetDependency<IOrganizationUserRepository>()
.GetManyByOrganizationAsync(organizationAbility.Id, OrganizationUserType.Owner)
.Returns(new List<OrganizationUser> { savingUser });
var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.SaveUserAsync(newUserData, oldUserData.UserId, collections, groups));
Assert.Contains("manager role is deprecated", exception.Message.ToLowerInvariant());
Assert.Contains("manager role has been deprecated", exception.Message.ToLowerInvariant());
}
[Theory, BitAutoData]
public async Task SaveUser_WithFlexibleCollections_WithAccessAll_Throws(
OrganizationAbility organizationAbility,
[OrganizationUser(type: OrganizationUserType.User)] OrganizationUser oldUserData,
[OrganizationUser(type: OrganizationUserType.User)] OrganizationUser newUserData,
[OrganizationUser(type: OrganizationUserType.Owner, status: OrganizationUserStatusType.Confirmed)] OrganizationUser savingUser,
IEnumerable<CollectionAccessSelection> collections,
IEnumerable<Guid> groups,
SutProvider<OrganizationService> sutProvider)
{
organizationAbility.FlexibleCollections = true;
newUserData.Id = oldUserData.Id;
newUserData.UserId = oldUserData.UserId;
newUserData.OrganizationId = oldUserData.OrganizationId = savingUser.OrganizationId = organizationAbility.Id;
newUserData.Permissions = CoreHelpers.ClassToJsonData(new Permissions());
newUserData.AccessAll = true;
sutProvider.GetDependency<IApplicationCacheService>()
.GetOrganizationAbilityAsync(organizationAbility.Id)
.Returns(organizationAbility);
sutProvider.GetDependency<ICurrentContext>()
.ManageUsers(organizationAbility.Id)
.Returns(true);
sutProvider.GetDependency<IOrganizationUserRepository>()
.GetByIdAsync(oldUserData.Id)
.Returns(oldUserData);
sutProvider.GetDependency<IOrganizationUserRepository>()
.GetManyByOrganizationAsync(organizationAbility.Id, OrganizationUserType.Owner)
.Returns(new List<OrganizationUser> { savingUser });
var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.SaveUserAsync(newUserData, oldUserData.UserId, collections, groups));
Assert.Contains("the accessall property has been deprecated", exception.Message.ToLowerInvariant());
}
[Theory, BitAutoData]