mirror of
https://github.com/bitwarden/server
synced 2025-12-13 14:53:34 +00:00
[PM-1188] Server owner auth migration (#2825)
* [PM-1188] add sso project to auth * [PM-1188] move sso api models to auth * [PM-1188] fix sso api model namespace & imports * [PM-1188] move core files to auth * [PM-1188] fix core sso namespace & models * [PM-1188] move sso repository files to auth * [PM-1188] fix sso repo files namespace & imports * [PM-1188] move sso sql files to auth folder * [PM-1188] move sso test files to auth folders * [PM-1188] fix sso tests namespace & imports * [PM-1188] move auth api files to auth folder * [PM-1188] fix auth api files namespace & imports * [PM-1188] move auth core files to auth folder * [PM-1188] fix auth core files namespace & imports * [PM-1188] move auth email templates to auth folder * [PM-1188] move auth email folder back into shared directory * [PM-1188] fix auth email names * [PM-1188] move auth core models to auth folder * [PM-1188] fix auth model namespace & imports * [PM-1188] add entire Identity project to auth codeowners * [PM-1188] fix auth orm files namespace & imports * [PM-1188] move auth orm files to auth folder * [PM-1188] move auth sql files to auth folder * [PM-1188] move auth tests to auth folder * [PM-1188] fix auth test files namespace & imports * [PM-1188] move emergency access api files to auth folder * [PM-1188] fix emergencyaccess api files namespace & imports * [PM-1188] move emergency access core files to auth folder * [PM-1188] fix emergency access core files namespace & imports * [PM-1188] move emergency access orm files to auth folder * [PM-1188] fix emergency access orm files namespace & imports * [PM-1188] move emergency access sql files to auth folder * [PM-1188] move emergencyaccess test files to auth folder * [PM-1188] fix emergency access test files namespace & imports * [PM-1188] move captcha files to auth folder * [PM-1188] fix captcha files namespace & imports * [PM-1188] move auth admin files into auth folder * [PM-1188] fix admin auth files namespace & imports - configure mvc to look in auth folders for views * [PM-1188] remove extra imports and formatting * [PM-1188] fix ef auth model imports * [PM-1188] fix DatabaseContextModelSnapshot paths * [PM-1188] fix grant import in ef * [PM-1188] update sqlproj * [PM-1188] move missed sqlproj files * [PM-1188] move auth ef models out of auth folder * [PM-1188] fix auth ef models namespace * [PM-1188] remove auth ef models unused imports * [PM-1188] fix imports for auth ef models * [PM-1188] fix more ef model imports * [PM-1188] fix file encodings
This commit is contained in:
162
src/Api/Auth/Controllers/AuthRequestsController.cs
Normal file
162
src/Api/Auth/Controllers/AuthRequestsController.cs
Normal file
@@ -0,0 +1,162 @@
|
||||
using Bit.Api.Auth.Models.Request;
|
||||
using Bit.Api.Auth.Models.Response;
|
||||
using Bit.Api.Models.Response;
|
||||
using Bit.Core.Auth.Entities;
|
||||
using Bit.Core.Auth.Exceptions;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Core.Utilities;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Bit.Api.Auth.Controllers;
|
||||
|
||||
[Route("auth-requests")]
|
||||
[Authorize("Application")]
|
||||
public class AuthRequestsController : Controller
|
||||
{
|
||||
private readonly IUserRepository _userRepository;
|
||||
private readonly IDeviceRepository _deviceRepository;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IAuthRequestRepository _authRequestRepository;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
private readonly IPushNotificationService _pushNotificationService;
|
||||
private readonly IGlobalSettings _globalSettings;
|
||||
|
||||
public AuthRequestsController(
|
||||
IUserRepository userRepository,
|
||||
IDeviceRepository deviceRepository,
|
||||
IUserService userService,
|
||||
IAuthRequestRepository authRequestRepository,
|
||||
ICurrentContext currentContext,
|
||||
IPushNotificationService pushNotificationService,
|
||||
IGlobalSettings globalSettings)
|
||||
{
|
||||
_userRepository = userRepository;
|
||||
_deviceRepository = deviceRepository;
|
||||
_userService = userService;
|
||||
_authRequestRepository = authRequestRepository;
|
||||
_currentContext = currentContext;
|
||||
_pushNotificationService = pushNotificationService;
|
||||
_globalSettings = globalSettings;
|
||||
}
|
||||
|
||||
[HttpGet("")]
|
||||
public async Task<ListResponseModel<AuthRequestResponseModel>> Get()
|
||||
{
|
||||
var userId = _userService.GetProperUserId(User).Value;
|
||||
var authRequests = await _authRequestRepository.GetManyByUserIdAsync(userId);
|
||||
var responses = authRequests.Select(a => new AuthRequestResponseModel(a, _globalSettings.BaseServiceUri.Vault)).ToList();
|
||||
return new ListResponseModel<AuthRequestResponseModel>(responses);
|
||||
}
|
||||
|
||||
[HttpGet("{id}")]
|
||||
public async Task<AuthRequestResponseModel> Get(string id)
|
||||
{
|
||||
var userId = _userService.GetProperUserId(User).Value;
|
||||
var authRequest = await _authRequestRepository.GetByIdAsync(new Guid(id));
|
||||
if (authRequest == null || authRequest.UserId != userId)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
return new AuthRequestResponseModel(authRequest, _globalSettings.BaseServiceUri.Vault);
|
||||
}
|
||||
|
||||
[HttpGet("{id}/response")]
|
||||
[AllowAnonymous]
|
||||
public async Task<AuthRequestResponseModel> GetResponse(string id, [FromQuery] string code)
|
||||
{
|
||||
var authRequest = await _authRequestRepository.GetByIdAsync(new Guid(id));
|
||||
if (authRequest == null || !CoreHelpers.FixedTimeEquals(authRequest.AccessCode, code) || authRequest.GetExpirationDate() < DateTime.UtcNow)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
return new AuthRequestResponseModel(authRequest, _globalSettings.BaseServiceUri.Vault);
|
||||
}
|
||||
|
||||
[HttpPost("")]
|
||||
[AllowAnonymous]
|
||||
public async Task<AuthRequestResponseModel> Post([FromBody] AuthRequestCreateRequestModel model)
|
||||
{
|
||||
var user = await _userRepository.GetByEmailAsync(model.Email);
|
||||
if (user == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
if (!_currentContext.DeviceType.HasValue)
|
||||
{
|
||||
throw new BadRequestException("Device type not provided.");
|
||||
}
|
||||
if (_globalSettings.PasswordlessAuth.KnownDevicesOnly)
|
||||
{
|
||||
var devices = await _deviceRepository.GetManyByUserIdAsync(user.Id);
|
||||
if (devices == null || !devices.Any(d => d.Identifier == model.DeviceIdentifier))
|
||||
{
|
||||
throw new BadRequestException("Login with device is only available on devices that have been previously logged in.");
|
||||
}
|
||||
}
|
||||
|
||||
var authRequest = new AuthRequest
|
||||
{
|
||||
RequestDeviceIdentifier = model.DeviceIdentifier,
|
||||
RequestDeviceType = _currentContext.DeviceType.Value,
|
||||
RequestIpAddress = _currentContext.IpAddress,
|
||||
AccessCode = model.AccessCode,
|
||||
PublicKey = model.PublicKey,
|
||||
UserId = user.Id,
|
||||
Type = model.Type.Value
|
||||
};
|
||||
authRequest = await _authRequestRepository.CreateAsync(authRequest);
|
||||
await _pushNotificationService.PushAuthRequestAsync(authRequest);
|
||||
var r = new AuthRequestResponseModel(authRequest, _globalSettings.BaseServiceUri.Vault);
|
||||
return r;
|
||||
}
|
||||
|
||||
[HttpPut("{id}")]
|
||||
public async Task<AuthRequestResponseModel> Put(string id, [FromBody] AuthRequestUpdateRequestModel model)
|
||||
{
|
||||
var userId = _userService.GetProperUserId(User).Value;
|
||||
var authRequest = await _authRequestRepository.GetByIdAsync(new Guid(id));
|
||||
if (authRequest == null || authRequest.UserId != userId || authRequest.GetExpirationDate() < DateTime.UtcNow)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
if (authRequest.Approved is not null)
|
||||
{
|
||||
throw new DuplicateAuthRequestException();
|
||||
}
|
||||
|
||||
var device = await _deviceRepository.GetByIdentifierAsync(model.DeviceIdentifier, userId);
|
||||
if (device == null)
|
||||
{
|
||||
throw new BadRequestException("Invalid device.");
|
||||
}
|
||||
|
||||
authRequest.ResponseDeviceId = device.Id;
|
||||
authRequest.ResponseDate = DateTime.UtcNow;
|
||||
authRequest.Approved = model.RequestApproved;
|
||||
|
||||
if (model.RequestApproved)
|
||||
{
|
||||
authRequest.Key = model.Key;
|
||||
authRequest.MasterPasswordHash = model.MasterPasswordHash;
|
||||
}
|
||||
|
||||
await _authRequestRepository.ReplaceAsync(authRequest);
|
||||
|
||||
// We only want to send an approval notification if the request is approved (or null),
|
||||
// to not leak that it was denied to the originating client if it was originated by a malicious actor.
|
||||
if (authRequest.Approved ?? true)
|
||||
{
|
||||
await _pushNotificationService.PushAuthRequestResponseAsync(authRequest);
|
||||
}
|
||||
|
||||
return new AuthRequestResponseModel(authRequest, _globalSettings.BaseServiceUri.Vault);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user