using System.Text.Json; using Bit.Core.Entities; using Bit.Core.Entities.Provider; using Bit.Core.Enums; using Bit.Core.Models.Data; using Bit.Core.Models.Data.Organizations.OrganizationUsers; using Bit.Core.Repositories; using Bit.Core.Test.AutoFixture.Attributes; using Bit.Infrastructure.EFIntegration.Test.AutoFixture; using Bit.Infrastructure.EFIntegration.Test.Repositories.EqualityComparers; using Xunit; using EfRepo = Bit.Infrastructure.EntityFramework.Repositories; using OrganizationUser = Bit.Core.Entities.OrganizationUser; using SqlRepo = Bit.Infrastructure.Dapper.Repositories; namespace Bit.Infrastructure.EFIntegration.Test.Repositories; public class OrganizationUserRepositoryTests { [CiSkippedTheory, EfOrganizationUserAutoData] public async void CreateAsync_Works_DataMatches(OrganizationUser orgUser, User user, Organization org, OrganizationUserCompare equalityComparer, List suts, List efOrgRepos, List efUserRepos, SqlRepo.OrganizationUserRepository sqlOrgUserRepo, SqlRepo.UserRepository sqlUserRepo, SqlRepo.OrganizationRepository sqlOrgRepo) { var savedOrgUsers = new List(); foreach (var sut in suts) { var i = suts.IndexOf(sut); var postEfUser = await efUserRepos[i].CreateAsync(user); var postEfOrg = await efOrgRepos[i].CreateAsync(org); sut.ClearChangeTracking(); orgUser.UserId = postEfUser.Id; orgUser.OrganizationId = postEfOrg.Id; var postEfOrgUser = await sut.CreateAsync(orgUser); sut.ClearChangeTracking(); var savedOrgUser = await sut.GetByIdAsync(postEfOrgUser.Id); savedOrgUsers.Add(savedOrgUser); } var postSqlUser = await sqlUserRepo.CreateAsync(user); var postSqlOrg = await sqlOrgRepo.CreateAsync(org); orgUser.UserId = postSqlUser.Id; orgUser.OrganizationId = postSqlOrg.Id; var sqlOrgUser = await sqlOrgUserRepo.CreateAsync(orgUser); var savedSqlOrgUser = await sqlOrgUserRepo.GetByIdAsync(sqlOrgUser.Id); savedOrgUsers.Add(savedSqlOrgUser); var distinctItems = savedOrgUsers.Distinct(equalityComparer); Assert.True(!distinctItems.Skip(1).Any()); } [CiSkippedTheory, EfOrganizationUserAutoData] public async void ReplaceAsync_Works_DataMatches( OrganizationUser postOrgUser, OrganizationUser replaceOrgUser, User user, Organization org, OrganizationUserCompare equalityComparer, List suts, List efUserRepos, List efOrgRepos, SqlRepo.OrganizationUserRepository sqlOrgUserRepo, SqlRepo.UserRepository sqlUserRepo, SqlRepo.OrganizationRepository sqlOrgRepo ) { var savedOrgUsers = new List(); foreach (var sut in suts) { var i = suts.IndexOf(sut); var postEfUser = await efUserRepos[i].CreateAsync(user); var postEfOrg = await efOrgRepos[i].CreateAsync(org); sut.ClearChangeTracking(); postOrgUser.UserId = replaceOrgUser.UserId = postEfUser.Id; postOrgUser.OrganizationId = replaceOrgUser.OrganizationId = postEfOrg.Id; var postEfOrgUser = await sut.CreateAsync(postOrgUser); sut.ClearChangeTracking(); replaceOrgUser.Id = postOrgUser.Id; await sut.ReplaceAsync(replaceOrgUser); sut.ClearChangeTracking(); var replacedOrganizationUser = await sut.GetByIdAsync(replaceOrgUser.Id); savedOrgUsers.Add(replacedOrganizationUser); } var postSqlUser = await sqlUserRepo.CreateAsync(user); var postSqlOrg = await sqlOrgRepo.CreateAsync(org); postOrgUser.UserId = replaceOrgUser.UserId = postSqlUser.Id; postOrgUser.OrganizationId = replaceOrgUser.OrganizationId = postSqlOrg.Id; var postSqlOrgUser = await sqlOrgUserRepo.CreateAsync(postOrgUser); replaceOrgUser.Id = postSqlOrgUser.Id; await sqlOrgUserRepo.ReplaceAsync(replaceOrgUser); var replacedSqlUser = await sqlOrgUserRepo.GetByIdAsync(replaceOrgUser.Id); var distinctItems = savedOrgUsers.Distinct(equalityComparer); Assert.True(!distinctItems.Skip(1).Any()); } [CiSkippedTheory, EfOrganizationUserAutoData] public async void DeleteAsync_Works_DataMatches(OrganizationUser orgUser, User user, Organization org, List suts, List efUserRepos, List efOrgRepos, SqlRepo.OrganizationUserRepository sqlOrgUserRepo, SqlRepo.UserRepository sqlUserRepo, SqlRepo.OrganizationRepository sqlOrgRepo) { foreach (var sut in suts) { var i = suts.IndexOf(sut); var postEfUser = await efUserRepos[i].CreateAsync(user); var postEfOrg = await efOrgRepos[i].CreateAsync(org); sut.ClearChangeTracking(); orgUser.UserId = postEfUser.Id; orgUser.OrganizationId = postEfOrg.Id; var postEfOrgUser = await sut.CreateAsync(orgUser); sut.ClearChangeTracking(); var savedEfOrgUser = await sut.GetByIdAsync(postEfOrgUser.Id); Assert.True(savedEfOrgUser != null); sut.ClearChangeTracking(); await sut.DeleteAsync(savedEfOrgUser); sut.ClearChangeTracking(); savedEfOrgUser = await sut.GetByIdAsync(savedEfOrgUser.Id); Assert.True(savedEfOrgUser == null); } var postSqlUser = await sqlUserRepo.CreateAsync(user); var postSqlOrg = await sqlOrgRepo.CreateAsync(org); orgUser.UserId = postSqlUser.Id; orgUser.OrganizationId = postSqlOrg.Id; var postSqlOrgUser = await sqlOrgUserRepo.CreateAsync(orgUser); var savedSqlOrgUser = await sqlOrgUserRepo.GetByIdAsync(postSqlOrgUser.Id); Assert.True(savedSqlOrgUser != null); await sqlOrgUserRepo.DeleteAsync(postSqlOrgUser); savedSqlOrgUser = await sqlOrgUserRepo.GetByIdAsync(postSqlOrgUser.Id); Assert.True(savedSqlOrgUser == null); } [CiSkippedTheory] [EfPolicyApplicableToUserInlineAutoData(OrganizationUserType.User, false, OrganizationUserStatusType.Confirmed, true, false)] // Ordinary user [EfPolicyApplicableToUserInlineAutoData(OrganizationUserType.User, false, OrganizationUserStatusType.Invited, true, false)] // Invited user [EfPolicyApplicableToUserInlineAutoData(OrganizationUserType.Owner, false, OrganizationUserStatusType.Confirmed, true, false)] // Owner [EfPolicyApplicableToUserInlineAutoData(OrganizationUserType.Admin, false, OrganizationUserStatusType.Confirmed, true, false)] // Admin [EfPolicyApplicableToUserInlineAutoData(OrganizationUserType.User, true, OrganizationUserStatusType.Confirmed, true, false)] // canManagePolicies [EfPolicyApplicableToUserInlineAutoData(OrganizationUserType.User, false, OrganizationUserStatusType.Confirmed, true, true)] // Provider [EfPolicyApplicableToUserInlineAutoData(OrganizationUserType.User, false, OrganizationUserStatusType.Confirmed, false, false)] // Policy disabled [EfPolicyApplicableToUserInlineAutoData(OrganizationUserType.User, false, OrganizationUserStatusType.Confirmed, true, false)] // No policy of Type [EfPolicyApplicableToUserInlineAutoData(OrganizationUserType.User, false, OrganizationUserStatusType.Invited, true, false)] // User not minStatus public async void GetByUserIdWithPolicyDetailsAsync_Works_DataMatches( // Inline data OrganizationUserType userType, bool canManagePolicies, OrganizationUserStatusType orgUserStatus, bool policyEnabled, bool isProvider, // Auto data - models Policy policy, User user, Organization organization, OrganizationUser orgUser, Provider provider, ProviderOrganization providerOrganization, ProviderUser providerUser, OrganizationUserPolicyDetailsCompare equalityComparer, // Auto data - EF repos List efPolicyRepository, List efUserRepository, List efOrganizationRepository, List suts, List efProviderRepository, List efProviderOrganizationRepository, List efProviderUserRepository, // Auto data - SQL repos SqlRepo.PolicyRepository sqlPolicyRepo, SqlRepo.UserRepository sqlUserRepo, SqlRepo.OrganizationRepository sqlOrganizationRepo, SqlRepo.ProviderRepository sqlProviderRepo, SqlRepo.OrganizationUserRepository sqlOrganizationUserRepo, SqlRepo.ProviderOrganizationRepository sqlProviderOrganizationRepo, SqlRepo.ProviderUserRepository sqlProviderUserRepo ) { // Combine EF and SQL repos into one list per type var policyRepos = efPolicyRepository.ToList(); policyRepos.Add(sqlPolicyRepo); var userRepos = efUserRepository.ToList(); userRepos.Add(sqlUserRepo); var orgRepos = efOrganizationRepository.ToList(); orgRepos.Add(sqlOrganizationRepo); var orgUserRepos = suts.ToList(); orgUserRepos.Add(sqlOrganizationUserRepo); var providerRepos = efProviderRepository.ToList(); providerRepos.Add(sqlProviderRepo); var providerOrgRepos = efProviderOrganizationRepository.ToList(); providerOrgRepos.Add(sqlProviderOrganizationRepo); var providerUserRepos = efProviderUserRepository.ToList(); providerUserRepos.Add(sqlProviderUserRepo); // Arrange data var savedPolicyType = PolicyType.SingleOrg; orgUser.Type = userType; orgUser.Status = orgUserStatus; var permissionsData = new Permissions { ManagePolicies = canManagePolicies }; orgUser.Permissions = JsonSerializer.Serialize(permissionsData, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, }); policy.Enabled = policyEnabled; policy.Type = savedPolicyType; var results = new List(); foreach (var policyRepo in policyRepos) { var i = policyRepos.IndexOf(policyRepo); // Seed database user.CreationDate = user.RevisionDate = DateTime.Now; var savedUser = await userRepos[i].CreateAsync(user); var savedOrg = await orgRepos[i].CreateAsync(organization); // Invited orgUsers are not associated with an account yet, so they are identified by Email not UserId if (orgUserStatus == OrganizationUserStatusType.Invited) { orgUser.Email = savedUser.Email; orgUser.UserId = null; } else { orgUser.UserId = savedUser.Id; } orgUser.OrganizationId = savedOrg.Id; await orgUserRepos[i].CreateAsync(orgUser); if (isProvider) { var savedProvider = await providerRepos[i].CreateAsync(provider); providerOrganization.OrganizationId = savedOrg.Id; providerOrganization.ProviderId = savedProvider.Id; await providerOrgRepos[i].CreateAsync(providerOrganization); providerUser.UserId = savedUser.Id; providerUser.ProviderId = savedProvider.Id; await providerUserRepos[i].CreateAsync(providerUser); } policy.OrganizationId = savedOrg.Id; await policyRepo.CreateAsync(policy); if (efPolicyRepository.Contains(policyRepo)) { (policyRepo as EfRepo.BaseEntityFrameworkRepository).ClearChangeTracking(); } // Act var result = await orgUserRepos[i].GetByUserIdWithPolicyDetailsAsync(savedUser.Id, policy.Type); results.Add(result.FirstOrDefault()); } // Assert var distinctItems = results.Distinct(equalityComparer); Assert.Single(distinctItems); } }