From c2d36cb28bbf67a891c732f42e2bbdc727dda83a Mon Sep 17 00:00:00 2001 From: Jared Snider <116684653+JaredSnider-Bitwarden@users.noreply.github.com> Date: Mon, 18 Dec 2023 19:34:56 -0500 Subject: [PATCH] PM-5340 - Fix bug where new enterprise orgs without an SSO config couldn't invite new users as I was missing null SSO config handling. (#3593) --- .../Implementations/OrganizationService.cs | 2 +- .../Services/OrganizationServiceTests.cs | 49 +++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/src/Core/AdminConsole/Services/Implementations/OrganizationService.cs b/src/Core/AdminConsole/Services/Implementations/OrganizationService.cs index 4a46c4200..e9eca14a9 100644 --- a/src/Core/AdminConsole/Services/Implementations/OrganizationService.cs +++ b/src/Core/AdminConsole/Services/Implementations/OrganizationService.cs @@ -1118,7 +1118,7 @@ public class OrganizationService : IOrganizationService // Determine if org has SSO enabled and if user is required to login with SSO // Note: we only want to call the DB after checking if the org can use SSO per plan and if they have any policies enabled. - var orgSsoEnabled = organization.UseSso && (await _ssoConfigRepository.GetByOrganizationIdAsync(organization.Id)).Enabled; + var orgSsoEnabled = organization.UseSso && (await _ssoConfigRepository.GetByOrganizationIdAsync(organization.Id))?.Enabled == true; // Even though the require SSO policy can be turned on regardless of SSO being enabled, for this logic, we only // need to check the policy if the org has SSO enabled. var orgSsoLoginRequiredPolicyEnabled = orgSsoEnabled && diff --git a/test/Core.Test/AdminConsole/Services/OrganizationServiceTests.cs b/test/Core.Test/AdminConsole/Services/OrganizationServiceTests.cs index dd78133d2..b6b7ac56c 100644 --- a/test/Core.Test/AdminConsole/Services/OrganizationServiceTests.cs +++ b/test/Core.Test/AdminConsole/Services/OrganizationServiceTests.cs @@ -432,6 +432,55 @@ public class OrganizationServiceTests } + [Theory] + [OrganizationInviteCustomize, BitAutoData] + public async Task InviteUser_SsoOrgWithNullSsoConfig_Passes(Organization organization, OrganizationUser invitor, + [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner, + OrganizationUserInvite invite, SutProvider sutProvider) + { + // Setup FakeDataProtectorTokenFactory for creating new tokens - this must come first in order to avoid resetting mocks + sutProvider.SetDependency(_orgUserInviteTokenDataFactory, "orgUserInviteTokenDataFactory"); + sutProvider.Create(); + + // Org must be able to use SSO to trigger this proper test case as we currently only call to retrieve + // an org's SSO config if the org can use SSO + organization.UseSso = true; + + // Return null for sso config + sutProvider.GetDependency().GetByOrganizationIdAsync(organization.Id).ReturnsNull(); + + sutProvider.GetDependency().GetByIdAsync(organization.Id).Returns(organization); + sutProvider.GetDependency().OrganizationOwner(organization.Id).Returns(true); + sutProvider.GetDependency().ManageUsers(organization.Id).Returns(true); + var organizationUserRepository = sutProvider.GetDependency(); + organizationUserRepository.GetManyByOrganizationAsync(organization.Id, OrganizationUserType.Owner) + .Returns(new[] { owner }); + + // Must set guids in order for dictionary of guids to not throw aggregate exceptions + SetupOrgUserRepositoryCreateManyAsyncMock(organizationUserRepository); + + // Mock tokenable factory to return a token that expires in 5 days + sutProvider.GetDependency() + .CreateToken(Arg.Any()) + .Returns( + info => new OrgUserInviteTokenable(info.Arg()) + { + ExpirationDate = DateTime.UtcNow.Add(TimeSpan.FromDays(5)) + } + ); + + + + await sutProvider.Sut.InviteUsersAsync(organization.Id, invitor.UserId, new (OrganizationUserInvite, string)[] { (invite, null) }); + + await sutProvider.GetDependency().Received(1) + .SendOrganizationInviteEmailsAsync(Arg.Is(info => + info.OrgUserTokenPairs.Count() == invite.Emails.Distinct().Count() && + info.IsFreeOrg == (organization.PlanType == PlanType.Free) && + info.OrganizationName == organization.Name)); + + } + [Theory] [OrganizationInviteCustomize( InviteeUserType = OrganizationUserType.Admin,