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

[PM-17449] Add stored proc, EF query, and an integration test for them (#5413)

This commit is contained in:
Jimmy Vo 2025-02-20 15:08:06 -05:00 committed by GitHub
parent 93e5f7d0fe
commit 2f4d5283d3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 142 additions and 0 deletions

View File

@ -12,6 +12,7 @@ public interface IOrganizationDomainRepository : IRepository<OrganizationDomain,
Task<ICollection<OrganizationDomain>> GetManyByNextRunDateAsync(DateTime date); Task<ICollection<OrganizationDomain>> GetManyByNextRunDateAsync(DateTime date);
Task<OrganizationDomainSsoDetailsData?> GetOrganizationDomainSsoDetailsAsync(string email); Task<OrganizationDomainSsoDetailsData?> GetOrganizationDomainSsoDetailsAsync(string email);
Task<IEnumerable<VerifiedOrganizationDomainSsoDetail>> GetVerifiedOrganizationDomainSsoDetailsAsync(string email); Task<IEnumerable<VerifiedOrganizationDomainSsoDetail>> GetVerifiedOrganizationDomainSsoDetailsAsync(string email);
Task<IEnumerable<OrganizationDomain>> GetVerifiedDomainsByOrganizationIdsAsync(IEnumerable<Guid> organizationIds);
Task<OrganizationDomain?> GetDomainByIdOrganizationIdAsync(Guid id, Guid organizationId); Task<OrganizationDomain?> GetDomainByIdOrganizationIdAsync(Guid id, Guid organizationId);
Task<OrganizationDomain?> GetDomainByOrgIdAndDomainNameAsync(Guid orgId, string domainName); Task<OrganizationDomain?> GetDomainByOrgIdAndDomainNameAsync(Guid orgId, string domainName);
Task<ICollection<OrganizationDomain>> GetExpiredOrganizationDomainsAsync(); Task<ICollection<OrganizationDomain>> GetExpiredOrganizationDomainsAsync();

View File

@ -46,6 +46,20 @@ public class OrganizationDomainRepository : Repository<OrganizationDomain, Guid>
} }
} }
public async Task<IEnumerable<OrganizationDomain>> GetVerifiedDomainsByOrganizationIdsAsync(IEnumerable<Guid> organizationIds)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<OrganizationDomain>(
$"[{Schema}].[OrganizationDomain_ReadByOrganizationIds]",
new { OrganizationIds = organizationIds.ToGuidIdArrayTVP() },
commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
public async Task<ICollection<OrganizationDomain>> GetManyByNextRunDateAsync(DateTime date) public async Task<ICollection<OrganizationDomain>> GetManyByNextRunDateAsync(DateTime date)
{ {
using var connection = new SqlConnection(ConnectionString); using var connection = new SqlConnection(ConnectionString);

View File

@ -157,4 +157,25 @@ public class OrganizationDomainRepository : Repository<Core.Entities.Organizatio
dbContext.OrganizationDomains.RemoveRange(expiredDomains); dbContext.OrganizationDomains.RemoveRange(expiredDomains);
return await dbContext.SaveChangesAsync() > 0; return await dbContext.SaveChangesAsync() > 0;
} }
public async Task<IEnumerable<Core.Entities.OrganizationDomain>> GetVerifiedDomainsByOrganizationIdsAsync(
IEnumerable<Guid> organizationIds)
{
using var scope = ServiceScopeFactory.CreateScope();
var dbContext = GetDatabaseContext(scope);
var verifiedDomains = await (from d in dbContext.OrganizationDomains
where organizationIds.Contains(d.OrganizationId) && d.VerifiedDate != null
select new OrganizationDomain
{
OrganizationId = d.OrganizationId,
DomainName = d.DomainName
})
.AsNoTracking()
.ToListAsync();
return Mapper.Map<List<OrganizationDomain>>(verifiedDomains);
}
} }

View File

@ -0,0 +1,14 @@
CREATE PROCEDURE [dbo].[OrganizationDomain_ReadByOrganizationIds]
@OrganizationIds AS [dbo].[GuidIdArray] READONLY
AS
BEGIN
SET NOCOUNT ON
SELECT
d.OrganizationId,
d.DomainName
FROM dbo.OrganizationDomainView AS d
WHERE d.OrganizationId IN (SELECT [Id] FROM @OrganizationIds)
AND d.VerifiedDate IS NOT NULL;
END

View File

@ -306,4 +306,81 @@ public class OrganizationDomainRepositoryTests
var expectedDomain = domains.FirstOrDefault(domain => domain.DomainName == organizationDomain.DomainName); var expectedDomain = domains.FirstOrDefault(domain => domain.DomainName == organizationDomain.DomainName);
Assert.Null(expectedDomain); Assert.Null(expectedDomain);
} }
[DatabaseTheory, DatabaseData]
public async Task GetVerifiedDomainsByOrganizationIdsAsync_ShouldVerifiedDomainsMatchesOrganizationIds(
IOrganizationRepository organizationRepository,
IOrganizationDomainRepository organizationDomainRepository)
{
// Arrange
var guid1 = Guid.NewGuid();
var guid2 = Guid.NewGuid();
var organization1 = await organizationRepository.CreateAsync(new Organization
{
Name = $"Test Org {guid1}",
BillingEmail = $"test+{guid1}@example.com",
Plan = "Test",
PrivateKey = "privatekey",
});
var organization1Domain1 = new OrganizationDomain
{
OrganizationId = organization1.Id,
DomainName = $"domain1+{guid1}@example.com",
Txt = "btw+12345"
};
const int arbitraryNextIteration = 1;
organization1Domain1.SetNextRunDate(arbitraryNextIteration);
organization1Domain1.SetVerifiedDate();
await organizationDomainRepository.CreateAsync(organization1Domain1);
var organization1Domain2 = new OrganizationDomain
{
OrganizationId = organization1.Id,
DomainName = $"domain2+{guid1}@example.com",
Txt = "btw+12345"
};
organization1Domain2.SetNextRunDate(arbitraryNextIteration);
await organizationDomainRepository.CreateAsync(organization1Domain2);
var organization2 = await organizationRepository.CreateAsync(new Organization
{
Name = $"Test Org {guid2}",
BillingEmail = $"test+{guid2}@example.com",
Plan = "Test",
PrivateKey = "privatekey",
});
var organization2Domain1 = new OrganizationDomain
{
OrganizationId = organization2.Id,
DomainName = $"domain+{guid2}@example.com",
Txt = "btw+12345"
};
organization2Domain1.SetVerifiedDate();
organization2Domain1.SetNextRunDate(arbitraryNextIteration);
await organizationDomainRepository.CreateAsync(organization2Domain1);
// Act
var domains = await organizationDomainRepository.GetVerifiedDomainsByOrganizationIdsAsync(new[] { organization1.Id });
// Assert
var expectedDomain = domains.FirstOrDefault(domain => domain.DomainName == organization1Domain1.DomainName);
Assert.NotNull(expectedDomain);
var unverifiedDomain = domains.FirstOrDefault(domain => domain.DomainName == organization1Domain2.DomainName);
var otherOrganizationDomain = domains.FirstOrDefault(domain => domain.DomainName == organization2Domain1.DomainName);
Assert.Null(otherOrganizationDomain);
Assert.Null(unverifiedDomain);
}
} }

View File

@ -0,0 +1,15 @@
CREATE OR ALTER PROCEDURE [dbo].[OrganizationDomain_ReadByOrganizationIds]
@OrganizationIds AS [dbo].[GuidIdArray] READONLY
AS
BEGIN
SET NOCOUNT ON
SELECT
d.OrganizationId,
d.DomainName
FROM dbo.OrganizationDomainView AS d
WHERE d.OrganizationId IN (SELECT [Id] FROM @OrganizationIds)
AND d.VerifiedDate IS NOT NULL;
END