mirror of
https://github.com/bitwarden/server.git
synced 2025-06-08 20:20:32 -05:00
feat(pm-20348) : Implement stored procedure in AuthRequestRepository for both Dapper and Entity Framework.
This commit is contained in:
parent
9a8317b2a9
commit
f83b0e8978
@ -1,5 +1,6 @@
|
|||||||
using Bit.Api.Auth.Models.Response;
|
using Bit.Api.Auth.Models.Response;
|
||||||
using Bit.Api.Models.Response;
|
using Bit.Api.Models.Response;
|
||||||
|
using Bit.Core;
|
||||||
using Bit.Core.Auth.Enums;
|
using Bit.Core.Auth.Enums;
|
||||||
using Bit.Core.Auth.Models.Api.Request.AuthRequest;
|
using Bit.Core.Auth.Models.Api.Request.AuthRequest;
|
||||||
using Bit.Core.Auth.Services;
|
using Bit.Core.Auth.Services;
|
||||||
@ -7,6 +8,7 @@ using Bit.Core.Exceptions;
|
|||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
using Bit.Core.Settings;
|
using Bit.Core.Settings;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
@ -14,31 +16,25 @@ namespace Bit.Api.Auth.Controllers;
|
|||||||
|
|
||||||
[Route("auth-requests")]
|
[Route("auth-requests")]
|
||||||
[Authorize("Application")]
|
[Authorize("Application")]
|
||||||
public class AuthRequestsController : Controller
|
public class AuthRequestsController(
|
||||||
|
IUserService userService,
|
||||||
|
IAuthRequestRepository authRequestRepository,
|
||||||
|
IGlobalSettings globalSettings,
|
||||||
|
IAuthRequestService authRequestService,
|
||||||
|
IFeatureService featureService) : Controller
|
||||||
{
|
{
|
||||||
private readonly IUserService _userService;
|
private readonly IUserService _userService = userService;
|
||||||
private readonly IAuthRequestRepository _authRequestRepository;
|
private readonly IAuthRequestRepository _authRequestRepository = authRequestRepository;
|
||||||
private readonly IGlobalSettings _globalSettings;
|
private readonly IGlobalSettings _globalSettings = globalSettings;
|
||||||
private readonly IAuthRequestService _authRequestService;
|
private readonly IAuthRequestService _authRequestService = authRequestService;
|
||||||
|
private readonly IFeatureService _featureService = featureService;
|
||||||
public AuthRequestsController(
|
|
||||||
IUserService userService,
|
|
||||||
IAuthRequestRepository authRequestRepository,
|
|
||||||
IGlobalSettings globalSettings,
|
|
||||||
IAuthRequestService authRequestService)
|
|
||||||
{
|
|
||||||
_userService = userService;
|
|
||||||
_authRequestRepository = authRequestRepository;
|
|
||||||
_globalSettings = globalSettings;
|
|
||||||
_authRequestService = authRequestService;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("")]
|
[HttpGet("")]
|
||||||
public async Task<ListResponseModel<AuthRequestResponseModel>> Get()
|
public async Task<ListResponseModel<AuthRequestResponseModel>> Get()
|
||||||
{
|
{
|
||||||
var userId = _userService.GetProperUserId(User).Value;
|
var userId = _userService.GetProperUserId(User).Value;
|
||||||
var authRequests = await _authRequestRepository.GetManyByUserIdAsync(userId);
|
var authRequests = await _authRequestRepository.GetManyByUserIdAsync(userId);
|
||||||
var responses = authRequests.Select(a => new AuthRequestResponseModel(a, _globalSettings.BaseServiceUri.Vault)).ToList();
|
var responses = authRequests.Select(a => new AuthRequestResponseModel(a, _globalSettings.BaseServiceUri.Vault));
|
||||||
return new ListResponseModel<AuthRequestResponseModel>(responses);
|
return new ListResponseModel<AuthRequestResponseModel>(responses);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,6 +52,16 @@ public class AuthRequestsController : Controller
|
|||||||
return new AuthRequestResponseModel(authRequest, _globalSettings.BaseServiceUri.Vault);
|
return new AuthRequestResponseModel(authRequest, _globalSettings.BaseServiceUri.Vault);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet("pending")]
|
||||||
|
[RequireFeature(FeatureFlagKeys.BrowserExtensionLoginApproval)]
|
||||||
|
public async Task<ListResponseModel<AuthRequestResponseModel>> GetPendingAuthRequestsAsync()
|
||||||
|
{
|
||||||
|
var userId = _userService.GetProperUserId(User).Value;
|
||||||
|
var rawResponse = await _authRequestRepository.GetManyPendingAuthRequestByUserId(userId);
|
||||||
|
var responses = rawResponse.Select(a => new AuthRequestResponseModel(a, _globalSettings.BaseServiceUri.Vault));
|
||||||
|
return new ListResponseModel<AuthRequestResponseModel>(responses);
|
||||||
|
}
|
||||||
|
|
||||||
[HttpGet("{id}/response")]
|
[HttpGet("{id}/response")]
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
public async Task<AuthRequestResponseModel> GetResponse(Guid id, [FromQuery] string code)
|
public async Task<AuthRequestResponseModel> GetResponse(Guid id, [FromQuery] string code)
|
||||||
|
@ -9,6 +9,7 @@ public interface IAuthRequestRepository : IRepository<AuthRequest, Guid>
|
|||||||
{
|
{
|
||||||
Task<int> DeleteExpiredAsync(TimeSpan userRequestExpiration, TimeSpan adminRequestExpiration, TimeSpan afterAdminApprovalExpiration);
|
Task<int> DeleteExpiredAsync(TimeSpan userRequestExpiration, TimeSpan adminRequestExpiration, TimeSpan afterAdminApprovalExpiration);
|
||||||
Task<ICollection<AuthRequest>> GetManyByUserIdAsync(Guid userId);
|
Task<ICollection<AuthRequest>> GetManyByUserIdAsync(Guid userId);
|
||||||
|
Task<IEnumerable<AuthRequest>> GetManyPendingAuthRequestByUserId(Guid userId);
|
||||||
Task<ICollection<OrganizationAdminAuthRequest>> GetManyPendingByOrganizationIdAsync(Guid organizationId);
|
Task<ICollection<OrganizationAdminAuthRequest>> GetManyPendingByOrganizationIdAsync(Guid organizationId);
|
||||||
Task<ICollection<OrganizationAdminAuthRequest>> GetManyAdminApprovalRequestsByManyIdsAsync(Guid organizationId, IEnumerable<Guid> ids);
|
Task<ICollection<OrganizationAdminAuthRequest>> GetManyAdminApprovalRequestsByManyIdsAsync(Guid organizationId, IEnumerable<Guid> ids);
|
||||||
Task UpdateManyAsync(IEnumerable<AuthRequest> authRequests);
|
Task UpdateManyAsync(IEnumerable<AuthRequest> authRequests);
|
||||||
|
@ -14,13 +14,12 @@ namespace Bit.Infrastructure.Dapper.Auth.Repositories;
|
|||||||
|
|
||||||
public class AuthRequestRepository : Repository<AuthRequest, Guid>, IAuthRequestRepository
|
public class AuthRequestRepository : Repository<AuthRequest, Guid>, IAuthRequestRepository
|
||||||
{
|
{
|
||||||
|
private readonly GlobalSettings _globalSettings;
|
||||||
public AuthRequestRepository(GlobalSettings globalSettings)
|
public AuthRequestRepository(GlobalSettings globalSettings)
|
||||||
: this(globalSettings.SqlServer.ConnectionString, globalSettings.SqlServer.ReadOnlyConnectionString)
|
: base(globalSettings.SqlServer.ConnectionString, globalSettings.SqlServer.ReadOnlyConnectionString)
|
||||||
{ }
|
{
|
||||||
|
_globalSettings = globalSettings;
|
||||||
public AuthRequestRepository(string connectionString, string readOnlyConnectionString)
|
}
|
||||||
: base(connectionString, readOnlyConnectionString)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
public async Task<int> DeleteExpiredAsync(
|
public async Task<int> DeleteExpiredAsync(
|
||||||
TimeSpan userRequestExpiration, TimeSpan adminRequestExpiration, TimeSpan afterAdminApprovalExpiration)
|
TimeSpan userRequestExpiration, TimeSpan adminRequestExpiration, TimeSpan afterAdminApprovalExpiration)
|
||||||
@ -52,6 +51,18 @@ public class AuthRequestRepository : Repository<AuthRequest, Guid>, IAuthRequest
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<AuthRequest>> GetManyPendingAuthRequestByUserId(Guid userId)
|
||||||
|
{
|
||||||
|
var expirationMinutes = (int)_globalSettings.PasswordlessAuth.UserRequestExpiration.TotalMinutes;
|
||||||
|
using var connection = new SqlConnection(ConnectionString);
|
||||||
|
var results = await connection.QueryAsync<OrganizationAdminAuthRequest>(
|
||||||
|
$"[{Schema}].[AuthRequest_ReadPendingByUserId]",
|
||||||
|
new { UserId = userId, ExpirationMinutes = expirationMinutes },
|
||||||
|
commandType: CommandType.StoredProcedure);
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<ICollection<OrganizationAdminAuthRequest>> GetManyPendingByOrganizationIdAsync(Guid organizationId)
|
public async Task<ICollection<OrganizationAdminAuthRequest>> GetManyPendingByOrganizationIdAsync(Guid organizationId)
|
||||||
{
|
{
|
||||||
using (var connection = new SqlConnection(ConnectionString))
|
using (var connection = new SqlConnection(ConnectionString))
|
||||||
|
@ -3,7 +3,9 @@ using AutoMapper.QueryableExtensions;
|
|||||||
using Bit.Core.Auth.Enums;
|
using Bit.Core.Auth.Enums;
|
||||||
using Bit.Core.Auth.Models.Data;
|
using Bit.Core.Auth.Models.Data;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
|
using Bit.Core.Settings;
|
||||||
using Bit.Infrastructure.EntityFramework.Auth.Models;
|
using Bit.Infrastructure.EntityFramework.Auth.Models;
|
||||||
|
using Bit.Infrastructure.EntityFramework.Auth.Repositories.Queries;
|
||||||
using Bit.Infrastructure.EntityFramework.Repositories;
|
using Bit.Infrastructure.EntityFramework.Repositories;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
@ -14,9 +16,13 @@ namespace Bit.Infrastructure.EntityFramework.Auth.Repositories;
|
|||||||
|
|
||||||
public class AuthRequestRepository : Repository<Core.Auth.Entities.AuthRequest, AuthRequest, Guid>, IAuthRequestRepository
|
public class AuthRequestRepository : Repository<Core.Auth.Entities.AuthRequest, AuthRequest, Guid>, IAuthRequestRepository
|
||||||
{
|
{
|
||||||
public AuthRequestRepository(IServiceScopeFactory serviceScopeFactory, IMapper mapper)
|
private readonly IGlobalSettings _globalSettings;
|
||||||
: base(serviceScopeFactory, mapper, (DatabaseContext context) => context.AuthRequests)
|
public AuthRequestRepository(IServiceScopeFactory serviceScopeFactory, IMapper mapper, IGlobalSettings globalSettings)
|
||||||
{ }
|
: base(serviceScopeFactory, mapper, context => context.AuthRequests)
|
||||||
|
{
|
||||||
|
_globalSettings = globalSettings;
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<int> DeleteExpiredAsync(
|
public async Task<int> DeleteExpiredAsync(
|
||||||
TimeSpan userRequestExpiration, TimeSpan adminRequestExpiration, TimeSpan afterAdminApprovalExpiration)
|
TimeSpan userRequestExpiration, TimeSpan adminRequestExpiration, TimeSpan afterAdminApprovalExpiration)
|
||||||
{
|
{
|
||||||
@ -57,6 +63,17 @@ public class AuthRequestRepository : Repository<Core.Auth.Entities.AuthRequest,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<Core.Auth.Entities.AuthRequest>> GetManyPendingAuthRequestByUserId(Guid userId)
|
||||||
|
{
|
||||||
|
var expirationMinutes = (int)_globalSettings.PasswordlessAuth.UserRequestExpiration.TotalMinutes;
|
||||||
|
using var scope = ServiceScopeFactory.CreateScope();
|
||||||
|
var dbContext = GetDatabaseContext(scope);
|
||||||
|
var pendingAuthRequestQuery = new AuthRequestReadPendingByUserIdQuery()
|
||||||
|
.GetQuery(dbContext, userId, expirationMinutes);
|
||||||
|
|
||||||
|
return await pendingAuthRequestQuery.ToListAsync();
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<ICollection<OrganizationAdminAuthRequest>> GetManyAdminApprovalRequestsByManyIdsAsync(
|
public async Task<ICollection<OrganizationAdminAuthRequest>> GetManyAdminApprovalRequestsByManyIdsAsync(
|
||||||
Guid organizationId,
|
Guid organizationId,
|
||||||
IEnumerable<Guid> ids)
|
IEnumerable<Guid> ids)
|
||||||
|
@ -0,0 +1,28 @@
|
|||||||
|
using Bit.Core.Auth.Enums;
|
||||||
|
using Bit.Infrastructure.EntityFramework.Auth.Models;
|
||||||
|
using Bit.Infrastructure.EntityFramework.Repositories;
|
||||||
|
|
||||||
|
namespace Bit.Infrastructure.EntityFramework.Auth.Repositories.Queries;
|
||||||
|
|
||||||
|
public class AuthRequestReadPendingByUserIdQuery
|
||||||
|
{
|
||||||
|
public IQueryable<AuthRequest> GetQuery(
|
||||||
|
DatabaseContext dbContext,
|
||||||
|
Guid userId,
|
||||||
|
int expirationMinutes)
|
||||||
|
{
|
||||||
|
var pendingAuthRequestQuery =
|
||||||
|
from authRequest in dbContext.AuthRequests
|
||||||
|
group authRequest by authRequest.RequestDeviceIdentifier into groupedRequests
|
||||||
|
select
|
||||||
|
(from pendingRequests in groupedRequests
|
||||||
|
where pendingRequests.UserId == userId
|
||||||
|
where pendingRequests.Type == AuthRequestType.AuthenticateAndUnlock || pendingRequests.Type == AuthRequestType.Unlock
|
||||||
|
where pendingRequests.Approved == null
|
||||||
|
where pendingRequests.CreationDate.AddMinutes(expirationMinutes) > DateTime.UtcNow
|
||||||
|
orderby pendingRequests.CreationDate descending
|
||||||
|
select pendingRequests).First();
|
||||||
|
|
||||||
|
return pendingAuthRequestQuery;
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user