mirror of
https://github.com/bitwarden/server.git
synced 2025-06-30 15:42:48 -05:00
[AC-1192] Create endpoints for new Device Approvals page (#2993)
* [AC-1192] Create new OrganizationAuthRequestsController.cs * [AC-1192] Introduce OrganizationAdminAuthRequest model * [AC-1192] Add GetManyPendingByOrganizationId method to AuthRequest repository * [AC-1192] Add new list pending organization auth requests endpoint * [AC-1192] Add new GetManyAdminApprovalsByManyIdsAsync method to the AuthRequestRepository * [AC-1192] Make the response device identifier optional for admin approval requests * [AC-1192] Add endpoint for bulk denying admin device auth requests * [AC-1192] Add OrganizationUserId to PendingOrganizationAuthRequestResponseModel * [AC-1192] Add UpdateAuthRequest endpoint and logic to OrganizationAuthRequestsController * [AC-1192] Secure new endpoints behind TDE feature flag * [AC-1192] Formatting * [AC-1192] Add sql migration script * [AC-1192] Add optional OrganizationId column to AuthRequest entity - Rename migration script to match existing formatting - Add new column - Add migration scripts - Update new sprocs to filter/join on OrganizationId - Update old sprocs to include OrganizationId * [AC-1192] Format migration scripts * [AC-1192] Fix failing AuthRequest EF unit test * [AC-1192] Make OrganizationId optional in updated AuthRequest sprocs for backwards compatability * [AC-1192] Fix missing comma in migration file * [AC-1192] Rename Key to EncryptedUserKey to be more descriptive * [AC-1192] Move request validation into helper method to reduce repetition * [AC-1192] Return UnauthorizedAccessException instead of NotFound when user is missing permission * [AC-1192] Introduce FeatureUnavailableException * [AC-1192] Introduce RequireFeatureAttribute * [AC-1192] Utilize the new RequireFeatureAttribute in the OrganizationAuthRequestsController * [AC-1192] Attempt to fix out of sync database migration by moving new OrganizationId column * [AC-1192] More attempts to sync database migrations * [AC-1192] Formatting * [AC-1192] Remove unused reference to FeatureService * [AC-1192] Change Id types from String to Guid * [AC-1192] Add EncryptedString attribute * [AC-1192] Remove redundant OrganizationId property * [AC-1192] Switch to projection for OrganizationAdminAuthRequest mapping - Add new OrganizationUser relationship to EF entity - Replace AuthRequest DBContext config with new IEntityTypeConfiguration - Add navigation property to AuthRequest entity configuration for OrganizationUser - Update EF AuthRequestRepository to use new mapping and navigation properties * [AC-1192] Remove OrganizationUser navigation property
This commit is contained in:
@ -0,0 +1,13 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
namespace Bit.Api.Auth.Models.Request;
|
||||
|
||||
public class AdminAuthRequestUpdateRequestModel
|
||||
{
|
||||
[EncryptedString]
|
||||
public string EncryptedUserKey { get; set; }
|
||||
|
||||
[Required]
|
||||
public bool RequestApproved { get; set; }
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
namespace Bit.Api.Auth.Models.Request;
|
||||
|
||||
public class BulkDenyAdminAuthRequestRequestModel
|
||||
{
|
||||
public IEnumerable<Guid> Ids { get; set; }
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Reflection;
|
||||
using Bit.Core.Auth.Models.Data;
|
||||
using Bit.Core.Models.Api;
|
||||
|
||||
namespace Bit.Api.Auth.Models.Response;
|
||||
|
||||
public class PendingOrganizationAuthRequestResponseModel : ResponseModel
|
||||
{
|
||||
public PendingOrganizationAuthRequestResponseModel(OrganizationAdminAuthRequest authRequest, string obj = "pending-org-auth-request") : base(obj)
|
||||
{
|
||||
if (authRequest == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(authRequest));
|
||||
}
|
||||
|
||||
Id = authRequest.Id;
|
||||
UserId = authRequest.UserId;
|
||||
OrganizationUserId = authRequest.OrganizationUserId;
|
||||
Email = authRequest.Email;
|
||||
PublicKey = authRequest.PublicKey;
|
||||
RequestDeviceIdentifier = authRequest.RequestDeviceIdentifier;
|
||||
RequestDeviceType = authRequest.RequestDeviceType.GetType().GetMember(authRequest.RequestDeviceType.ToString())
|
||||
.FirstOrDefault()?.GetCustomAttribute<DisplayAttribute>()?.GetName();
|
||||
RequestIpAddress = authRequest.RequestIpAddress;
|
||||
CreationDate = authRequest.CreationDate;
|
||||
}
|
||||
|
||||
public Guid Id { get; set; }
|
||||
public Guid UserId { get; set; }
|
||||
public Guid OrganizationUserId { get; set; }
|
||||
public string Email { get; set; }
|
||||
public string PublicKey { get; set; }
|
||||
public string RequestDeviceIdentifier { get; set; }
|
||||
public string RequestDeviceType { get; set; }
|
||||
public string RequestIpAddress { get; set; }
|
||||
public DateTime CreationDate { get; set; }
|
||||
}
|
83
src/Api/Controllers/OrganizationAuthRequestsController.cs
Normal file
83
src/Api/Controllers/OrganizationAuthRequestsController.cs
Normal file
@ -0,0 +1,83 @@
|
||||
using Bit.Api.Auth.Models.Request;
|
||||
using Bit.Api.Auth.Models.Response;
|
||||
using Bit.Api.Models.Response;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Auth.Models.Api.Request.AuthRequest;
|
||||
using Bit.Core.Auth.Services;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Utilities;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Bit.Api.Controllers;
|
||||
|
||||
[Route("organizations/{orgId}/auth-requests")]
|
||||
[Authorize("Application")]
|
||||
[RequireFeature(FeatureFlagKeys.TrustedDeviceEncryption)]
|
||||
public class OrganizationAuthRequestsController : Controller
|
||||
{
|
||||
private readonly IAuthRequestRepository _authRequestRepository;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
private readonly IAuthRequestService _authRequestService;
|
||||
|
||||
public OrganizationAuthRequestsController(IAuthRequestRepository authRequestRepository,
|
||||
ICurrentContext currentContext, IAuthRequestService authRequestService)
|
||||
{
|
||||
_authRequestRepository = authRequestRepository;
|
||||
_currentContext = currentContext;
|
||||
_authRequestService = authRequestService;
|
||||
}
|
||||
|
||||
[HttpGet("")]
|
||||
public async Task<ListResponseModel<PendingOrganizationAuthRequestResponseModel>> GetPendingRequests(Guid orgId)
|
||||
{
|
||||
await ValidateAdminRequest(orgId);
|
||||
|
||||
var authRequests = await _authRequestRepository.GetManyPendingByOrganizationIdAsync(orgId);
|
||||
var responses = authRequests
|
||||
.Select(a => new PendingOrganizationAuthRequestResponseModel(a))
|
||||
.ToList();
|
||||
return new ListResponseModel<PendingOrganizationAuthRequestResponseModel>(responses);
|
||||
}
|
||||
|
||||
[HttpPost("{requestId}")]
|
||||
public async Task UpdateAuthRequest(Guid orgId, Guid requestId, [FromBody] AdminAuthRequestUpdateRequestModel model)
|
||||
{
|
||||
await ValidateAdminRequest(orgId);
|
||||
|
||||
var authRequest =
|
||||
(await _authRequestRepository.GetManyAdminApprovalRequestsByManyIdsAsync(orgId, new[] { requestId })).FirstOrDefault();
|
||||
|
||||
if (authRequest == null || authRequest.OrganizationId != orgId)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
await _authRequestService.UpdateAuthRequestAsync(authRequest.Id, authRequest.UserId,
|
||||
new AuthRequestUpdateRequestModel { RequestApproved = model.RequestApproved, Key = model.EncryptedUserKey });
|
||||
}
|
||||
|
||||
[HttpPost("deny")]
|
||||
public async Task BulkDenyRequests(Guid orgId, [FromBody] BulkDenyAdminAuthRequestRequestModel model)
|
||||
{
|
||||
await ValidateAdminRequest(orgId);
|
||||
|
||||
var authRequests = await _authRequestRepository.GetManyAdminApprovalRequestsByManyIdsAsync(orgId, model.Ids);
|
||||
|
||||
foreach (var authRequest in authRequests)
|
||||
{
|
||||
await _authRequestService.UpdateAuthRequestAsync(authRequest.Id, authRequest.UserId,
|
||||
new AuthRequestUpdateRequestModel { RequestApproved = false, });
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ValidateAdminRequest(Guid orgId)
|
||||
{
|
||||
if (!await _currentContext.ManageResetPassword(orgId))
|
||||
{
|
||||
throw new UnauthorizedAccessException();
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user