1
0
mirror of https://github.com/bitwarden/server.git synced 2025-03-02 04:11:04 +01:00

Optimized implementations of sqlite and other db interactions.

This commit is contained in:
Patrick Pimentel 2024-12-09 12:18:56 -05:00
parent 406f941538
commit 23c049a617
No known key found for this signature in database
GPG Key ID: 4B27FC74C6422186
5 changed files with 94 additions and 38 deletions

View File

@ -31,7 +31,7 @@ public class DeviceAuthRequestResponseModel : ResponseModel
}
/**
* Is there a better way to do this in Dapper so that I don't need to explicitly
* 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(
@ -56,7 +56,9 @@ public class DeviceAuthRequestResponseModel : ResponseModel
Type = (DeviceType)type;
Identifier = identifier;
CreationDate = creationDate;
IsTrusted = active;
IsTrusted = !string.IsNullOrEmpty(encryptedUserKey) &&
!string.IsNullOrEmpty(encryptedPublicKey) &&
!string.IsNullOrEmpty(encryptedPrivateKey);
if (authRequestId != Guid.Empty && authRequestCreationDate != DateTime.MinValue)
{
DevicePendingAuthRequest = new PendingAuthRequest()
@ -67,6 +69,7 @@ public class DeviceAuthRequestResponseModel : ResponseModel
}
}
public Guid Id { get; set; }
public string Name { get; set; }
public DeviceType Type { get; set; }

View File

@ -90,6 +90,19 @@ public class DeviceRepository : Repository<Device, Guid>, IDeviceRepository
},
commandType: CommandType.StoredProcedure);
foreach (var result in results)
{
result.IsTrusted = result.IsTrusted;
if (result.DevicePendingAuthRequest != null)
{
result.DevicePendingAuthRequest = new DeviceAuthRequestResponseModel.PendingAuthRequest
{
Id = result.DevicePendingAuthRequest.Id,
CreationDate = result.DevicePendingAuthRequest.CreationDate
};
}
}
return results.ToList();
}
}

View File

@ -1,12 +0,0 @@
using AutoMapper;
using Bit.Core.Auth.Models.Api.Response;
namespace Bit.Infrastructure.EntityFramework.Auth.Models;
public class DeviceAuthRequestResponseModelMapperProfile : Profile
{
public DeviceAuthRequestResponseModelMapperProfile()
{
CreateMap<Core.Entities.Device, DeviceAuthRequestResponseModel>().ReverseMap();
}
}

View File

@ -0,0 +1,73 @@
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;
public class DeviceWithPendingAuthByUserIdQuery
{
public IQueryable<DeviceAuthRequestResponseModel> GetQuery(
DatabaseContext dbContext,
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 query =
(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));
return query;
}
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;
}
}
}

View File

@ -1,7 +1,7 @@
using AutoMapper;
using Bit.Core.Auth.Enums;
using Bit.Core.Auth.Models.Api.Response;
using Bit.Core.Repositories;
using Bit.Infrastructure.EntityFramework.Auth.Repositories.Queries;
using Bit.Infrastructure.EntityFramework.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
@ -77,29 +77,8 @@ public class DeviceRepository : Repository<Core.Entities.Device, Device, Guid>,
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query = 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()
};
var devices =
await query.Select(deviceWithAuthRequest => new DeviceAuthRequestResponseModel(
deviceWithAuthRequest.device,
deviceWithAuthRequest.authRequest != null ? deviceWithAuthRequest.authRequest.Id : Guid.Empty,
deviceWithAuthRequest.authRequest != null ? deviceWithAuthRequest.authRequest.CreationDate : DateTime.MinValue)).ToListAsync();
return devices;
var query = new DeviceWithPendingAuthByUserIdQuery();
return await query.GetQuery(dbContext, userId, expirationMinutes).ToListAsync();
}
}
}