From 97fa55ad59a12c989d6451b3f352564ae39fcb09 Mon Sep 17 00:00:00 2001 From: Patrick Pimentel Date: Thu, 19 Dec 2024 12:22:31 -0500 Subject: [PATCH] feat(device): Devices with Auth Request Migrated tests to appropriate place. Fixed up some of the comments discussed with Ike. --- .../DeviceAuthRequestResponseModel.cs | 5 - ...dActiveWithPendingAuthRequestsByUserId.sql | 26 +- .../Repositories/DeviceRepositoryTests.cs | 316 +----------------- .../AuthRequestRepositoryTests.cs | 14 +- .../Repositories/DeviceRepositoryTests.cs | 195 +++++++++++ .../DatabaseDataAttribute.cs | 4 +- ...-04_00_AddActiveDeviceWithPendingAuth.sql} | 2 +- 7 files changed, 219 insertions(+), 343 deletions(-) create mode 100644 test/Infrastructure.IntegrationTest/Auth/Repositories/DeviceRepositoryTests.cs rename util/Migrator/DbScripts/{2024-12-04_00_AddActiveDeviceWithPendighAuth.sql => 2024-12-04_00_AddActiveDeviceWithPendingAuth.sql} (92%) diff --git a/src/Core/Auth/Models/Api/Response/DeviceAuthRequestResponseModel.cs b/src/Core/Auth/Models/Api/Response/DeviceAuthRequestResponseModel.cs index 10202e923d6b..3cbecb318872 100644 --- a/src/Core/Auth/Models/Api/Response/DeviceAuthRequestResponseModel.cs +++ b/src/Core/Auth/Models/Api/Response/DeviceAuthRequestResponseModel.cs @@ -30,10 +30,6 @@ public DeviceAuthRequestResponseModel( } } - /** - * Is there a better way to do this for Dapper so that I don't need to explicitly - * enumerate all the properties in the constructor for mapping? - */ public DeviceAuthRequestResponseModel( Guid id, Guid userId, @@ -73,7 +69,6 @@ public DeviceAuthRequestResponseModel( } } - public Guid Id { get; set; } public string Name { get; set; } public DeviceType Type { get; set; } diff --git a/src/Sql/Auth/dbo/Stored Procedures/Device_ReadActiveWithPendingAuthRequestsByUserId.sql b/src/Sql/Auth/dbo/Stored Procedures/Device_ReadActiveWithPendingAuthRequestsByUserId.sql index 7040a013090a..80cea91095c1 100644 --- a/src/Sql/Auth/dbo/Stored Procedures/Device_ReadActiveWithPendingAuthRequestsByUserId.sql +++ b/src/Sql/Auth/dbo/Stored Procedures/Device_ReadActiveWithPendingAuthRequestsByUserId.sql @@ -1,4 +1,4 @@ -CREATE PROCEDURE [dbo].[Device_ReadActiveWithPendingAuthRequestsByUserId] +CREATE OR ALTER PROCEDURE [dbo].[Device_ReadActiveWithPendingAuthRequestsByUserId] @UserId UNIQUEIDENTIFIER, @ExpirationMinutes INT AS @@ -8,18 +8,18 @@ BEGIN AR.Id as AuthRequestId, AR.CreationDate as AuthRequestCreationDate FROM [dbo].[DeviceView] D - LEFT OUTER JOIN ( - SELECT TOP 1 -- Take only the top record sorted by auth request creation date - AR.Id, - AR.CreationDate, - AR.RequestDeviceIdentifier - FROM [dbo].[AuthRequestView] AR - WHERE AR.Type IN (0, 1) -- Include only AuthenticateAndUnlock and Unlock types, excluding Admin Approval (type 2) - AND DATEADD(mi, @ExpirationMinutes, AR.CreationDate) > GETUTCDATE() -- Ensure the request hasn't expired - AND AR.Approved IS NULL -- Include only requests that haven't been acknowledged or approved - ORDER BY AR.CreationDate DESC - ) AR ON D.Identifier = AR.RequestDeviceIdentifier + LEFT OUTER JOIN ( + SELECT TOP 1 -- Take only the top record sorted by auth request creation date + AR.Id, + AR.CreationDate, + AR.RequestDeviceIdentifier + FROM [dbo].[AuthRequestView] AR + WHERE AR.Type IN (0, 1) -- Include only AuthenticateAndUnlock and Unlock types, excluding Admin Approval (type 2) + AND DATEADD(mi, @ExpirationMinutes, AR.CreationDate) > GETUTCDATE() -- Ensure the request hasn't expired + AND AR.Approved IS NULL -- Include only requests that haven't been acknowledged or approved + ORDER BY AR.CreationDate DESC + ) AR ON D.Identifier = AR.RequestDeviceIdentifier WHERE D.UserId = @UserId - AND D.Active = 1 -- Include only active devices + AND D.Active = 1 -- Include only active devices END diff --git a/test/Infrastructure.EFIntegration.Test/Repositories/DeviceRepositoryTests.cs b/test/Infrastructure.EFIntegration.Test/Repositories/DeviceRepositoryTests.cs index 2e946e5cb20d..cc914d9aae55 100644 --- a/test/Infrastructure.EFIntegration.Test/Repositories/DeviceRepositoryTests.cs +++ b/test/Infrastructure.EFIntegration.Test/Repositories/DeviceRepositoryTests.cs @@ -1,14 +1,9 @@ -using Bit.Core.Auth.Entities; -using Bit.Core.Auth.Enums; -using Bit.Core.Auth.Models.Api.Response; -using Bit.Core.Entities; +using Bit.Core.Entities; using Bit.Core.Test.AutoFixture.Attributes; using Bit.Infrastructure.EFIntegration.Test.AutoFixture; using Bit.Infrastructure.EFIntegration.Test.Repositories.EqualityComparers; using Xunit; -using EfAuthRepo = Bit.Infrastructure.EntityFramework.Auth.Repositories; using EfRepo = Bit.Infrastructure.EntityFramework.Repositories; -using SqlAuthRepo = Bit.Infrastructure.Dapper.Auth.Repositories; using SqlRepo = Bit.Infrastructure.Dapper.Repositories; namespace Bit.Infrastructure.EFIntegration.Test.Repositories; @@ -51,313 +46,4 @@ public async Task CreateAsync_Works_DataMatches( var distinctItems = savedDevices.Distinct(equalityComparer); Assert.False(distinctItems.Skip(1).Any()); } - - [CiSkippedTheory, EfDeviceAutoData] - public async Task GetManyByUserIdWithDeviceAuth_Works_ReturnsExpectedResults( - Device device, - User user, - AuthRequest authRequest, - List efSuts, - List efUserRepos, - List efAuthRequestRepos, - SqlRepo.DeviceRepository sqlSut, - SqlRepo.UserRepository sqlUserRepo, - SqlAuthRepo.AuthRequestRepository sqlAuthRequestRepository - ) - { - // Arrange - var allResponses = new List>(); - var userIdsToSearchOn = new List(); - var expirationTime = 15; - var correctAuthRequestsToRetrieve = new List(); - - // Configure data for successful responses. - device.Active = true; - - authRequest.ResponseDeviceId = null; - authRequest.Approved = null; - authRequest.Type = AuthRequestType.AuthenticateAndUnlock; - authRequest.OrganizationId = null; - - // Entity Framework Repo - foreach (var efSut in efSuts) - { - var i = efSuts.IndexOf(efSut); - - // Create user - var efUser = await efUserRepos[i].CreateAsync(user); - efSut.ClearChangeTracking(); - - userIdsToSearchOn.Add(efUser.Id); - - // Create device - device.UserId = efUser.Id; - device.Name = "test-ef-chrome"; - - var efDevice = await efSuts[i].CreateAsync(device); - efSut.ClearChangeTracking(); - - // Create auth request - authRequest.UserId = efUser.Id; - authRequest.RequestDeviceIdentifier = efDevice.Identifier; - - // Old auth request - authRequest.CreationDate = DateTime.UtcNow.AddMinutes(-10); - await efAuthRequestRepos[i].CreateAsync(authRequest); - - // Fresher auth request - authRequest.CreationDate = DateTime.UtcNow; - correctAuthRequestsToRetrieve.Add((await efAuthRequestRepos[i].CreateAsync(authRequest)).Id); - } - - // Dapper Repo - // Create user - var sqlUser = await sqlUserRepo.CreateAsync(user); - - userIdsToSearchOn.Add(sqlUser.Id); - - // Create device - device.UserId = sqlUser.Id; - device.Name = "test-sql-chrome"; - var sqlDevice = await sqlSut.CreateAsync(device); - - // Create auth request - authRequest.UserId = sqlUser.Id; - authRequest.Type = AuthRequestType.Unlock; - authRequest.RequestDeviceIdentifier = sqlDevice.Identifier; - - // Old auth request - authRequest.CreationDate = DateTime.UtcNow.AddMinutes(-10); - await sqlAuthRequestRepository.CreateAsync(authRequest); - - // Fresher auth request - authRequest.CreationDate = DateTime.UtcNow; - correctAuthRequestsToRetrieve.Add((await sqlAuthRequestRepository.CreateAsync(authRequest)).Id); - - // Act - - // Entity Framework Responses - foreach (var efSut in efSuts) - { - var i = efSuts.IndexOf(efSut); - allResponses.Add(await efSut.GetManyByUserIdWithDeviceAuth(userIdsToSearchOn[i], expirationTime)); - } - - // Sql Responses - allResponses.Add(await sqlSut.GetManyByUserIdWithDeviceAuth(userIdsToSearchOn.Last(), expirationTime)); - - // Assert - - // Test all responses to not have a device pending auth request. - // All but n-1 are EF responses. n is the stored procedure. - foreach (var response in allResponses) - { - Assert.NotNull(response.First().DevicePendingAuthRequest); - - // Remove auth request id from the correct auth request pool. - correctAuthRequestsToRetrieve.Remove(response.First().DevicePendingAuthRequest.Id); - } - - // After we iterate through all our devices with auth requests and remove them from the expected list, there - // should be none left. - Assert.True(correctAuthRequestsToRetrieve.Count == 0); - } - - [CiSkippedTheory, EfDeviceAutoData] - public async Task GetManyByUserIdWithDeviceAuth_WorksWithNoAuthRequestAndMultipleDevices_ReturnsExpectedResults( - Device device, - User user, - List efSuts, - List efUserRepos, - SqlRepo.DeviceRepository sqlSut, - SqlRepo.UserRepository sqlUserRepo) - { - // Arrange - var allResponses = new List>(); - var userIdsToSearchOn = new List(); - var expirationTime = 15; - - // Configure data for successful responses. - device.Active = true; - - // Entity Framework Repo - foreach (var efSut in efSuts) - { - var i = efSuts.IndexOf(efSut); - - // Create user - var efUser = await efUserRepos[i].CreateAsync(user); - efSut.ClearChangeTracking(); - - userIdsToSearchOn.Add(efUser.Id); - - // Create device - device.UserId = efUser.Id; - - device.Identifier = Guid.NewGuid().ToString(); - device.Name = "test-ef-chrome"; - await efSuts[i].CreateAsync(device); - - device.Identifier = Guid.NewGuid().ToString(); - device.Name = "test-ef-chrome-2"; - await efSuts[i].CreateAsync(device); - } - - // Dapper Repo - // Create user - var sqlUser = await sqlUserRepo.CreateAsync(user); - - userIdsToSearchOn.Add(sqlUser.Id); - - // Create device - device.UserId = sqlUser.Id; - - device.Identifier = Guid.NewGuid().ToString(); - device.Name = "test-sql-chrome"; - await sqlSut.CreateAsync(device); - - device.Identifier = Guid.NewGuid().ToString(); - device.Name = "test-ef-chrome-2"; - await sqlSut.CreateAsync(device); - - // Act - - // Entity Framework Responses - foreach (var efSut in efSuts) - { - var i = efSuts.IndexOf(efSut); - allResponses.Add(await efSut.GetManyByUserIdWithDeviceAuth(userIdsToSearchOn[i], expirationTime)); - } - - // Sql Responses - allResponses.Add(await sqlSut.GetManyByUserIdWithDeviceAuth(userIdsToSearchOn.Last(), expirationTime)); - - // Assert - - // Test all responses to not have a device pending auth request. - // All but n-1 are EF responses. n is the stored procedure. - foreach (var response in allResponses) - { - Assert.NotNull(response.First()); - Assert.Null(response.First().DevicePendingAuthRequest); - Assert.True(response.Count == 2); - } - } - - [CiSkippedTheory, EfDeviceAutoData] - public async Task GetManyByUserIdWithDeviceAuth_Fails_ReturnsExpectedResults( - Device device, - User user, - AuthRequest authRequest, - List efSuts, - List efUserRepos, - List efAuthRequestRepos, - SqlRepo.DeviceRepository sqlSut, - SqlRepo.UserRepository sqlUserRepo, - SqlAuthRepo.AuthRequestRepository sqlAuthRequestRepository - ) - { - var casesThatCauseNoAuthDataInResponse = new[] - { - new - { - authRequestType = AuthRequestType.AdminApproval, // Device typing is wrong - authRequestApproved = (bool?)null, - expirey = DateTime.UtcNow.AddMinutes(0), - }, - new - { - authRequestType = AuthRequestType.AuthenticateAndUnlock, - authRequestApproved = (bool?)true, // Auth request is already approved - expirey = DateTime.UtcNow.AddMinutes(0), - }, - new - { - authRequestType = AuthRequestType.AuthenticateAndUnlock, - authRequestApproved = (bool?)null, - expirey = DateTime.UtcNow.AddMinutes(-30), // Past the point of expiring - } - }; - - foreach (var testCase in casesThatCauseNoAuthDataInResponse) - { - // Arrange - var allResponses = new List>(); - var userIdsToSearchOn = new List(); - const int expirationTime = 15; - - // Configure data for successful responses. - user.Email = $"{user.Id.ToString().Substring(0, 5)}@test.com"; - - device.Active = true; - - authRequest.ResponseDeviceId = null; - authRequest.Type = testCase.authRequestType; - authRequest.Approved = testCase.authRequestApproved; - authRequest.OrganizationId = null; - - // Entity Framework Repo - foreach (var efSut in efSuts) - { - var i = efSuts.IndexOf(efSut); - - // Create user - var efUser = await efUserRepos[i].CreateAsync(user); - efSut.ClearChangeTracking(); - - userIdsToSearchOn.Add(efUser.Id); - - // Create device - device.UserId = efUser.Id; - device.Name = "test-ef-chrome"; - - var efDevice = await efSuts[i].CreateAsync(device); - efSut.ClearChangeTracking(); - - // Create auth request - authRequest.UserId = efUser.Id; - authRequest.RequestDeviceIdentifier = efDevice.Identifier; - authRequest.CreationDate = testCase.expirey; - await efAuthRequestRepos[i].CreateAsync(authRequest); - } - - // Dapper Repo - // Create user - var sqlUser = await sqlUserRepo.CreateAsync(user); - - userIdsToSearchOn.Add(sqlUser.Id); - - // Create device - device.UserId = sqlUser.Id; - device.Name = "test-sql-chrome"; - var sqlDevice = await sqlSut.CreateAsync(device); - - // Create auth request - authRequest.UserId = sqlUser.Id; - authRequest.RequestDeviceIdentifier = sqlDevice.Identifier; - authRequest.CreationDate = testCase.expirey; - await sqlAuthRequestRepository.CreateAsync(authRequest); - - // Act - - // Entity Framework Responses - foreach (var efSut in efSuts) - { - var i = efSuts.IndexOf(efSut); - allResponses.Add(await efSut.GetManyByUserIdWithDeviceAuth(userIdsToSearchOn[i], expirationTime)); - } - - // Sql Responses - allResponses.Add(await sqlSut.GetManyByUserIdWithDeviceAuth(userIdsToSearchOn.Last(), expirationTime)); - - // Assert - - // Test all responses to not have a device pending auth request. - // All but n-1 are EF responses. n is the stored procedure. - foreach (var response in allResponses) - { - Assert.Null(response.First().DevicePendingAuthRequest); - } - } - } } diff --git a/test/Infrastructure.IntegrationTest/Auth/Repositories/AuthRequestRepositoryTests.cs b/test/Infrastructure.IntegrationTest/Auth/Repositories/AuthRequestRepositoryTests.cs index 9fddb571b9d1..23754499f9a9 100644 --- a/test/Infrastructure.IntegrationTest/Auth/Repositories/AuthRequestRepositoryTests.cs +++ b/test/Infrastructure.IntegrationTest/Auth/Repositories/AuthRequestRepositoryTests.cs @@ -8,9 +8,9 @@ namespace Bit.Infrastructure.IntegrationTest.Auth.Repositories; public class AuthRequestRepositoryTests { - private readonly static TimeSpan _userRequestExpiration = TimeSpan.FromMinutes(15); - private readonly static TimeSpan _adminRequestExpiration = TimeSpan.FromDays(6); - private readonly static TimeSpan _afterAdminApprovalExpiration = TimeSpan.FromHours(12); + private static readonly TimeSpan _userRequestExpiration = TimeSpan.FromMinutes(15); + private static readonly TimeSpan _adminRequestExpiration = TimeSpan.FromDays(6); + private static readonly TimeSpan _afterAdminApprovalExpiration = TimeSpan.FromHours(12); [DatabaseTheory, DatabaseData] public async Task DeleteExpiredAsync_Works( @@ -25,11 +25,11 @@ public async Task DeleteExpiredAsync_Works( SecurityStamp = "stamp", }); - // A user auth request type that has passed it's expiration time, should be deleted. + // A user auth request type that has passed its expiration time, should be deleted. var userExpiredAuthRequest = await authRequestRepository.CreateAsync( CreateAuthRequest(user.Id, AuthRequestType.AuthenticateAndUnlock, CreateExpiredDate(_userRequestExpiration))); - // An AdminApproval request that hasn't had any action taken on it and has passed it's expiration time, should be deleted. + // An AdminApproval request that hasn't had any action taken on it and has passed its expiration time, should be deleted. var adminApprovalExpiredAuthRequest = await authRequestRepository.CreateAsync( CreateAuthRequest(user.Id, AuthRequestType.AdminApproval, CreateExpiredDate(_adminRequestExpiration))); @@ -37,7 +37,7 @@ public async Task DeleteExpiredAsync_Works( var adminApprovedExpiredAuthRequest = await authRequestRepository.CreateAsync( CreateAuthRequest(user.Id, AuthRequestType.AdminApproval, DateTime.UtcNow.AddDays(-6), true, CreateExpiredDate(_afterAdminApprovalExpiration))); - // An AdminApproval request that was rejected within it's allowed lifetime but has no gone past it's expiration time, should be deleted. + // An AdminApproval request that was rejected within it's allowed lifetime but has not gone past its expiration time, should be deleted. var adminRejectedExpiredAuthRequest = await authRequestRepository.CreateAsync( CreateAuthRequest(user.Id, AuthRequestType.AdminApproval, CreateExpiredDate(_adminRequestExpiration), false, DateTime.UtcNow.AddHours(-1))); @@ -45,7 +45,7 @@ public async Task DeleteExpiredAsync_Works( var notExpiredUserAuthRequest = await authRequestRepository.CreateAsync( CreateAuthRequest(user.Id, AuthRequestType.Unlock, DateTime.UtcNow.AddMinutes(-1))); - // An AdminApproval AuthRequest that was create 6 days 23 hours 59 minutes 59 seconds ago which is right on the edge of still being valid + // An AdminApproval AuthRequest that was created 6 days 23 hours 59 minutes 59 seconds ago which is right on the edge of still being valid var notExpiredAdminApprovalRequest = await authRequestRepository.CreateAsync( CreateAuthRequest(user.Id, AuthRequestType.AdminApproval, DateTime.UtcNow.Add(new TimeSpan(days: 6, hours: 23, minutes: 59, seconds: 59)))); diff --git a/test/Infrastructure.IntegrationTest/Auth/Repositories/DeviceRepositoryTests.cs b/test/Infrastructure.IntegrationTest/Auth/Repositories/DeviceRepositoryTests.cs new file mode 100644 index 000000000000..9b87c0d9d1fa --- /dev/null +++ b/test/Infrastructure.IntegrationTest/Auth/Repositories/DeviceRepositoryTests.cs @@ -0,0 +1,195 @@ +using Bit.Core.Auth.Entities; +using Bit.Core.Auth.Enums; +using Bit.Core.Entities; +using Bit.Core.Enums; +using Bit.Core.Repositories; +using Xunit; + +namespace Bit.Infrastructure.IntegrationTest.Auth.Repositories; + +public class DeviceRepositoryTests +{ + [DatabaseTheory] + [DatabaseData] + public async Task GetManyByUserIdWithDeviceAuth_Works_ReturnsExpectedResults( + IDeviceRepository deviceRepository, + IUserRepository userRepository, + IAuthRequestRepository authRequestRepository) + { + const int expirationMinutes = 15; + + // Arrange + var user = await userRepository.CreateAsync(new User + { + Name = "Test User", + Email = $"test+{Guid.NewGuid()}@email.com", + ApiKey = "TEST", + SecurityStamp = "stamp", + }); + + var device = await deviceRepository.CreateAsync(new Device + { + Active = true, + Name = "chrome-test", + UserId = user.Id, + Type = DeviceType.ChromeBrowser, + Identifier = Guid.NewGuid().ToString(), + }); + + var staleAuthRequest = await authRequestRepository.CreateAsync(new AuthRequest + { + ResponseDeviceId = null, + Approved = null, + Type = AuthRequestType.AuthenticateAndUnlock, + OrganizationId = null, + UserId = user.Id, + RequestIpAddress = ":1", + RequestDeviceIdentifier = device.Identifier, + AccessCode = "AccessCode_1234", + PublicKey = "PublicKey_1234" + }); + staleAuthRequest.CreationDate = DateTime.UtcNow.AddMinutes(-10); + await authRequestRepository.ReplaceAsync(staleAuthRequest); + + var freshAuthRequest = await authRequestRepository.CreateAsync(new AuthRequest + { + ResponseDeviceId = null, + Approved = null, + Type = AuthRequestType.AuthenticateAndUnlock, + OrganizationId = null, + UserId = user.Id, + RequestIpAddress = ":1", + RequestDeviceIdentifier = device.Identifier, + AccessCode = "AccessCode_1234", + PublicKey = "PublicKey_1234", + Key = "Key_1234", + MasterPasswordHash = "MasterPasswordHash_1234" + }); + + // Act + var response = await deviceRepository.GetManyByUserIdWithDeviceAuth(user.Id, expirationMinutes); + + // Assert + Assert.NotNull(response.First().DevicePendingAuthRequest); + Assert.Equal(response.First().DevicePendingAuthRequest.Id, freshAuthRequest.Id); + } + + [DatabaseTheory] + [DatabaseData] + public async Task GetManyByUserIdWithDeviceAuth_WorksWithNoAuthRequestAndMultipleDevices_ReturnsExpectedResults( + IDeviceRepository deviceRepository, + IUserRepository userRepository) + { + const int expirationMinutes = 15; + + // Arrange + var user = await userRepository.CreateAsync(new User + { + Name = "Test User", + Email = $"test+{Guid.NewGuid()}@email.com", + ApiKey = "TEST", + SecurityStamp = "stamp", + }); + + await deviceRepository.CreateAsync(new Device + { + Active = true, + Name = "chrome-test", + UserId = user.Id, + Type = DeviceType.ChromeBrowser, + Identifier = Guid.NewGuid().ToString(), + }); + + await deviceRepository.CreateAsync(new Device + { + Active = true, + Name = "macos-test", + UserId = user.Id, + Type = DeviceType.MacOsDesktop, + Identifier = Guid.NewGuid().ToString(), + }); + + // Act + var response = await deviceRepository.GetManyByUserIdWithDeviceAuth(user.Id, expirationMinutes); + + // Assert + Assert.NotNull(response.First()); + Assert.Null(response.First().DevicePendingAuthRequest); + Assert.True(response.Count == 2); + } + + [DatabaseTheory] + [DatabaseData] + public async Task GetManyByUserIdWithDeviceAuth_FailsToRespondWithAnyAuthData_ReturnsExpectedResults( + IDeviceRepository deviceRepository, + IUserRepository userRepository, + IAuthRequestRepository authRequestRepository) + { + var casesThatCauseNoAuthDataInResponse = new[] + { + new + { + authRequestType = AuthRequestType.AdminApproval, // Device typing is wrong + authRequestApproved = (bool?)null, + expirey = DateTime.UtcNow.AddMinutes(0), + }, + new + { + authRequestType = AuthRequestType.AuthenticateAndUnlock, + authRequestApproved = (bool?)true, // Auth request is already approved + expirey = DateTime.UtcNow.AddMinutes(0), + }, + new + { + authRequestType = AuthRequestType.AuthenticateAndUnlock, + authRequestApproved = (bool?)null, + expirey = DateTime.UtcNow.AddMinutes(-30), // Past the point of expiring + } + }; + + foreach (var testCase in casesThatCauseNoAuthDataInResponse) + { + // Arrange + const int expirationMinutes = 15; + + var user = await userRepository.CreateAsync(new User + { + Name = "Test User", + Email = $"test+{Guid.NewGuid()}@email.com", + ApiKey = "TEST", + SecurityStamp = "stamp", + }); + + var device = await deviceRepository.CreateAsync(new Device + { + Active = true, + Name = "chrome-test", + UserId = user.Id, + Type = DeviceType.ChromeBrowser, + Identifier = Guid.NewGuid().ToString(), + }); + + var authRequest = await authRequestRepository.CreateAsync(new AuthRequest + { + ResponseDeviceId = null, + Approved = testCase.authRequestApproved, + Type = testCase.authRequestType, + OrganizationId = null, + UserId = user.Id, + RequestIpAddress = ":1", + RequestDeviceIdentifier = device.Identifier, + AccessCode = "AccessCode_1234", + PublicKey = "PublicKey_1234" + }); + + authRequest.CreationDate = DateTime.UtcNow.AddMinutes(-30); + await authRequestRepository.ReplaceAsync(authRequest); + + // Act + var response = await deviceRepository.GetManyByUserIdWithDeviceAuth(user.Id, expirationMinutes); + + // Assert + Assert.Null(response.First().DevicePendingAuthRequest); + } + } +} diff --git a/test/Infrastructure.IntegrationTest/DatabaseDataAttribute.cs b/test/Infrastructure.IntegrationTest/DatabaseDataAttribute.cs index 746ce988a469..2f6d5e157a62 100644 --- a/test/Infrastructure.IntegrationTest/DatabaseDataAttribute.cs +++ b/test/Infrastructure.IntegrationTest/DatabaseDataAttribute.cs @@ -71,7 +71,7 @@ protected virtual IEnumerable GetDatabaseProviders(IConfigurat dapperSqlServerCollection.AddSingleton(globalSettings); dapperSqlServerCollection.AddSingleton(globalSettings); dapperSqlServerCollection.AddSingleton(database); - dapperSqlServerCollection.AddDistributedSqlServerCache((o) => + dapperSqlServerCollection.AddDistributedSqlServerCache(o => { o.ConnectionString = database.ConnectionString; o.SchemaName = "dbo"; @@ -117,7 +117,7 @@ private void AddCommonServices(IServiceCollection services, Action(sp => new SqlMigrationTesterService(connectionString, migrationName)); + services.AddSingleton(_ => new SqlMigrationTesterService(connectionString, migrationName)); } private void AddEfMigrationTester(IServiceCollection services, SupportedDatabaseProviders databaseType, string migrationName) diff --git a/util/Migrator/DbScripts/2024-12-04_00_AddActiveDeviceWithPendighAuth.sql b/util/Migrator/DbScripts/2024-12-04_00_AddActiveDeviceWithPendingAuth.sql similarity index 92% rename from util/Migrator/DbScripts/2024-12-04_00_AddActiveDeviceWithPendighAuth.sql rename to util/Migrator/DbScripts/2024-12-04_00_AddActiveDeviceWithPendingAuth.sql index 8e6ea3cb46a8..80cea91095c1 100644 --- a/util/Migrator/DbScripts/2024-12-04_00_AddActiveDeviceWithPendighAuth.sql +++ b/util/Migrator/DbScripts/2024-12-04_00_AddActiveDeviceWithPendingAuth.sql @@ -1,4 +1,4 @@ -CREATE PROCEDURE [dbo].[Device_ReadActiveWithPendingAuthRequestsByUserId] +CREATE OR ALTER PROCEDURE [dbo].[Device_ReadActiveWithPendingAuthRequestsByUserId] @UserId UNIQUEIDENTIFIER, @ExpirationMinutes INT AS