using AutoMapper; using AutoMapper.QueryableExtensions; using Bit.Core.Auth.Enums; using Bit.Core.Auth.Models.Data; using Bit.Core.Repositories; using Bit.Core.Settings; using Bit.Infrastructure.EntityFramework.Auth.Models; using Bit.Infrastructure.EntityFramework.Repositories; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; #nullable enable namespace Bit.Infrastructure.EntityFramework.Auth.Repositories; public class AuthRequestRepository : Repository, IAuthRequestRepository { private readonly IGlobalSettings _globalSettings; public AuthRequestRepository(IServiceScopeFactory serviceScopeFactory, IMapper mapper, IGlobalSettings globalSettings) : base(serviceScopeFactory, mapper, context => context.AuthRequests) { _globalSettings = globalSettings; } public async Task DeleteExpiredAsync( TimeSpan userRequestExpiration, TimeSpan adminRequestExpiration, TimeSpan afterAdminApprovalExpiration) { using (var scope = ServiceScopeFactory.CreateScope()) { var dbContext = GetDatabaseContext(scope); var expiredRequests = await dbContext.AuthRequests .Where(a => (a.Type != AuthRequestType.AdminApproval && a.CreationDate.AddSeconds(userRequestExpiration.TotalSeconds) < DateTime.UtcNow) || (a.Type == AuthRequestType.AdminApproval && a.Approved != true && a.CreationDate.AddSeconds(adminRequestExpiration.TotalSeconds) < DateTime.UtcNow) || (a.Type == AuthRequestType.AdminApproval && a.Approved == true && a.ResponseDate!.Value.AddSeconds(afterAdminApprovalExpiration.TotalSeconds) < DateTime.UtcNow)) .ToListAsync(); dbContext.AuthRequests.RemoveRange(expiredRequests); return await dbContext.SaveChangesAsync(); } } public async Task> GetManyByUserIdAsync(Guid userId) { using (var scope = ServiceScopeFactory.CreateScope()) { var dbContext = GetDatabaseContext(scope); var userAuthRequests = await dbContext.AuthRequests.Where(a => a.UserId.Equals(userId)).ToListAsync(); return Mapper.Map>(userAuthRequests); } } public async Task> GetManyPendingByOrganizationIdAsync(Guid organizationId) { using (var scope = ServiceScopeFactory.CreateScope()) { var dbContext = GetDatabaseContext(scope); var orgUserAuthRequests = await (from ar in dbContext.AuthRequests where ar.OrganizationId.Equals(organizationId) && ar.ResponseDate == null && ar.Type == AuthRequestType.AdminApproval select ar).ProjectTo(Mapper.ConfigurationProvider).ToListAsync(); return orgUserAuthRequests; } } public async Task> GetManyPendingAuthRequestByUserId(Guid userId) { var expirationMinutes = (int)_globalSettings.PasswordlessAuth.UserRequestExpiration.TotalMinutes; using var scope = ServiceScopeFactory.CreateScope(); var dbContext = GetDatabaseContext(scope); var mostRecentAuthRequests = await (from authRequest in dbContext.AuthRequests where authRequest.Type == AuthRequestType.AuthenticateAndUnlock || authRequest.Type == AuthRequestType.Unlock where authRequest.UserId == userId where authRequest.CreationDate.AddMinutes(expirationMinutes) > DateTime.UtcNow group authRequest by authRequest.RequestDeviceIdentifier into groupedAuthRequests select (from r in groupedAuthRequests join d in dbContext.Devices on r.RequestDeviceIdentifier equals d.Identifier into deviceJoin from dj in deviceJoin.DefaultIfEmpty() // This accomplishes a left join allowing nulld for devices orderby r.CreationDate descending select new PendingAuthRequestDetails(r, dj.Id)).First() ).ToListAsync(); mostRecentAuthRequests.RemoveAll(a => a.Approved != null); return mostRecentAuthRequests; } public async Task> GetManyAdminApprovalRequestsByManyIdsAsync( Guid organizationId, IEnumerable ids) { using (var scope = ServiceScopeFactory.CreateScope()) { var dbContext = GetDatabaseContext(scope); var orgUserAuthRequests = await (from ar in dbContext.AuthRequests where ar.OrganizationId.Equals(organizationId) && ids.Contains(ar.Id) && ar.Type == AuthRequestType.AdminApproval select ar).ProjectTo(Mapper.ConfigurationProvider).ToListAsync(); return orgUserAuthRequests; } } public async Task UpdateManyAsync(IEnumerable authRequests) { if (!authRequests.Any()) { return; } var entities = new List(); foreach (var authRequest in authRequests) { if (!authRequest.Id.Equals(default)) { var entity = Mapper.Map(authRequest); entities.Add(entity); } } using (var scope = ServiceScopeFactory.CreateScope()) { var dbContext = GetDatabaseContext(scope); dbContext.UpdateRange(entities); await dbContext.SaveChangesAsync(); } } }