Skip to content

Commit

Permalink
All tests working
Browse files Browse the repository at this point in the history
  • Loading branch information
Patrick-Pimentel-Bitwarden committed Dec 16, 2024
1 parent 04b335e commit 7687a16
Show file tree
Hide file tree
Showing 2 changed files with 219 additions and 64 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using Bit.Core.Auth.Enums;
using Bit.Core.Auth.Models.Api.Response;
using Bit.Infrastructure.EntityFramework.Repositories;
using Microsoft.EntityFrameworkCore;

namespace Bit.Infrastructure.EntityFramework.Auth.Repositories.Queries;

Expand All @@ -12,62 +11,32 @@ public IQueryable<DeviceAuthRequestResponseModel> GetQuery(
Guid userId,
int expirationMinutes)
{
// Handle sqlite differently because it cannot convert some linq queries into sql syntax.
// This error is thrown when trying to run the join else clause below with sqlite:
// 'Translating this query requires the SQL APPLY operation, which is not supported on SQLite.'
if (dbContext.Database.IsSqlite())
{
var devicesWithAuthQuery =
(from device in dbContext.Devices
where device.UserId == userId && device.Active
select new
{
device,
authRequest =
(from authRequest in dbContext.AuthRequests
where authRequest.RequestDeviceIdentifier == device.Identifier
where authRequest.Type == AuthRequestType.AuthenticateAndUnlock ||
authRequest.Type == AuthRequestType.Unlock
where authRequest.Approved == null
where authRequest.UserId == userId
where authRequest.CreationDate.AddMinutes(expirationMinutes) > DateTime.UtcNow
orderby authRequest.CreationDate descending
select authRequest).First()
}
).Select(deviceWithAuthRequest => new DeviceAuthRequestResponseModel(
deviceWithAuthRequest.device,
deviceWithAuthRequest.authRequest != null ? deviceWithAuthRequest.authRequest.Id : Guid.Empty,
deviceWithAuthRequest.authRequest != null
? deviceWithAuthRequest.authRequest.CreationDate
: DateTime.MinValue));
var devicesWithAuthQuery = (
from device in dbContext.Devices
where device.UserId == userId && device.Active
select new
{
device,
authRequest =
(
from authRequest in dbContext.AuthRequests
where authRequest.RequestDeviceIdentifier == device.Identifier
where authRequest.Type == AuthRequestType.AuthenticateAndUnlock || authRequest.Type == AuthRequestType.Unlock
where authRequest.Approved == null
where authRequest.UserId == userId
where authRequest.CreationDate.AddMinutes(expirationMinutes) > DateTime.UtcNow
orderby authRequest.CreationDate descending
select authRequest
).First()
}).Select(deviceWithAuthRequest => new DeviceAuthRequestResponseModel(
deviceWithAuthRequest.device,
deviceWithAuthRequest.authRequest != null
? deviceWithAuthRequest.authRequest.Id
: Guid.Empty,
deviceWithAuthRequest.authRequest != null
? deviceWithAuthRequest.authRequest.CreationDate
: DateTime.MinValue));

Check warning on line 38 in src/Infrastructure.EntityFramework/Auth/Repositories/Queries/DeviceWithPendingAuthByUserIdQuery.cs

View check run for this annotation

Codecov / codecov/patch

src/Infrastructure.EntityFramework/Auth/Repositories/Queries/DeviceWithPendingAuthByUserIdQuery.cs#L13-L38

Added lines #L13 - L38 were not covered by tests

return devicesWithAuthQuery;
}
else
{
var query =
from device in dbContext.Devices
join authRequest in dbContext.AuthRequests
on device.Identifier equals authRequest.RequestDeviceIdentifier
into deviceRequests
from authRequest in deviceRequests
.Where(ar => ar.Type == AuthRequestType.AuthenticateAndUnlock || ar.Type == AuthRequestType.Unlock)
.Where(ar => ar.Approved == null)
.Where(ar => ar.CreationDate.AddMinutes(expirationMinutes) > DateTime.UtcNow)
.OrderByDescending(ar => ar.CreationDate)
.Take(1)
where device.UserId == userId && device.Active == true
select new
{
Device = device,
AuthRequestId = authRequest.Id,
AuthRequestCreationDate = authRequest.CreationDate
};

var devicesWithAuthQuery = query.Select(deviceAndAuthProperty => new DeviceAuthRequestResponseModel(
deviceAndAuthProperty.Device, deviceAndAuthProperty.AuthRequestId, deviceAndAuthProperty.AuthRequestCreationDate));

return devicesWithAuthQuery;
}
return devicesWithAuthQuery;
}

Check warning on line 41 in src/Infrastructure.EntityFramework/Auth/Repositories/Queries/DeviceWithPendingAuthByUserIdQuery.cs

View check run for this annotation

Codecov / codecov/patch

src/Infrastructure.EntityFramework/Auth/Repositories/Queries/DeviceWithPendingAuthByUserIdQuery.cs#L40-L41

Added lines #L40 - L41 were not covered by tests
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ public async Task CreateAsync_Works_DataMatches(
Assert.False(distinctItems.Skip(1).Any());
}

// Test Cases:
[CiSkippedTheory, EfDeviceAutoData]
public async Task GetManyByUserIdWithDeviceAuth_Works_ReturnsExpectedResults(
Device device,
Expand All @@ -76,8 +75,8 @@ SqlAuthRepo.AuthRequestRepository sqlAuthRequestRepository
device.Active = true;

authRequest.ResponseDeviceId = null;
authRequest.Type = AuthRequestType.Unlock;
authRequest.Approved = null;
authRequest.Type = AuthRequestType.AuthenticateAndUnlock;
authRequest.OrganizationId = null;

// Entity Framework Repo
Expand Down Expand Up @@ -124,9 +123,10 @@ SqlAuthRepo.AuthRequestRepository sqlAuthRequestRepository

// Create auth request
authRequest.UserId = sqlUser.Id;
authRequest.Type = AuthRequestType.Unlock;
authRequest.RequestDeviceIdentifier = sqlDevice.Identifier;

// Old auth reqeust
// Old auth request
authRequest.CreationDate = DateTime.UtcNow.AddMinutes(-10);
await sqlAuthRequestRepository.CreateAsync(authRequest);

Expand All @@ -147,13 +147,12 @@ SqlAuthRepo.AuthRequestRepository sqlAuthRequestRepository
allResponses.Add(await sqlSut.GetManyByUserIdWithDeviceAuth(userIdsToSearchOn.Last(), expirationTime));

// Assert
var totalExpectedSuccessfulQueries = efSuts.Count + 1; // EF repos and the SQL repo.
Assert.True(allResponses.Count == totalExpectedSuccessfulQueries);

// Test all responses to have a device pending auth request
// 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.True(response.First().DevicePendingAuthRequest != null);
Assert.NotNull(response.First().DevicePendingAuthRequest);

// Remove auth request id from the correct auth request pool.
correctAuthRequestsToRetrieve.Remove(response.First().DevicePendingAuthRequest.Id);
Expand All @@ -163,4 +162,191 @@ SqlAuthRepo.AuthRequestRepository sqlAuthRequestRepository
// should be none left.
Assert.True(correctAuthRequestsToRetrieve.Count == 0);
}

[CiSkippedTheory, EfDeviceAutoData]
public async Task GetManyByUserIdWithDeviceAuth_WorksWithNoAuthRequest_ReturnsExpectedResults(
Device device,
User user,
List<EfRepo.DeviceRepository> efSuts,
List<EfRepo.UserRepository> efUserRepos,
SqlRepo.DeviceRepository sqlSut,
SqlRepo.UserRepository sqlUserRepo)
{
// Arrange
var allResponses = new List<ICollection<DeviceAuthRequestResponseModel>>();
var userIdsToSearchOn = new List<Guid>();
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.Name = "test-ef-chrome";

await efSuts[i].CreateAsync(device);
efSut.ClearChangeTracking();
}

// 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";
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);
}
}

[CiSkippedTheory, EfDeviceAutoData]
public async Task GetManyByUserIdWithDeviceAuth_Fails_ReturnsExpectedResults(
Device device,
User user,
AuthRequest authRequest,
List<EfRepo.DeviceRepository> efSuts,
List<EfRepo.UserRepository> efUserRepos,
List<EfAuthRepo.AuthRequestRepository> efAuthRequestRepos,
SqlRepo.DeviceRepository sqlSut,
SqlRepo.UserRepository sqlUserRepo,
SqlAuthRepo.AuthRequestRepository sqlAuthRequestRepository
)
{
var testCases = 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 testCases)
{
// Arrange
var allResponses = new List<ICollection<DeviceAuthRequestResponseModel>>();
var userIdsToSearchOn = new List<Guid>();
var 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);
}
}
}
}

0 comments on commit 7687a16

Please sign in to comment.