1
0
mirror of https://github.com/bitwarden/server.git synced 2025-06-30 23:52:50 -05:00

Run formatting (#2230)

This commit is contained in:
Justin Baur
2022-08-29 16:06:55 -04:00
committed by GitHub
parent 9b7aef0763
commit 7f5f010e1e
1205 changed files with 73813 additions and 75022 deletions

View File

@ -4,49 +4,48 @@ using Bit.Core.Utilities;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Bit.Api.Controllers
namespace Bit.Api.Controllers;
[Route("accounts/billing")]
[Authorize("Application")]
public class AccountsBillingController : Controller
{
[Route("accounts/billing")]
[Authorize("Application")]
public class AccountsBillingController : Controller
private readonly IPaymentService _paymentService;
private readonly IUserService _userService;
public AccountsBillingController(
IPaymentService paymentService,
IUserService userService)
{
private readonly IPaymentService _paymentService;
private readonly IUserService _userService;
_paymentService = paymentService;
_userService = userService;
}
public AccountsBillingController(
IPaymentService paymentService,
IUserService userService)
[HttpGet("history")]
[SelfHosted(NotSelfHostedOnly = true)]
public async Task<BillingHistoryResponseModel> GetBillingHistory()
{
var user = await _userService.GetUserByPrincipalAsync(User);
if (user == null)
{
_paymentService = paymentService;
_userService = userService;
throw new UnauthorizedAccessException();
}
[HttpGet("history")]
[SelfHosted(NotSelfHostedOnly = true)]
public async Task<BillingHistoryResponseModel> GetBillingHistory()
{
var user = await _userService.GetUserByPrincipalAsync(User);
if (user == null)
{
throw new UnauthorizedAccessException();
}
var billingInfo = await _paymentService.GetBillingHistoryAsync(user);
return new BillingHistoryResponseModel(billingInfo);
}
var billingInfo = await _paymentService.GetBillingHistoryAsync(user);
return new BillingHistoryResponseModel(billingInfo);
[HttpGet("payment-method")]
[SelfHosted(NotSelfHostedOnly = true)]
public async Task<BillingPaymentResponseModel> GetPaymentMethod()
{
var user = await _userService.GetUserByPrincipalAsync(User);
if (user == null)
{
throw new UnauthorizedAccessException();
}
[HttpGet("payment-method")]
[SelfHosted(NotSelfHostedOnly = true)]
public async Task<BillingPaymentResponseModel> GetPaymentMethod()
{
var user = await _userService.GetUserByPrincipalAsync(User);
if (user == null)
{
throw new UnauthorizedAccessException();
}
var billingInfo = await _paymentService.GetBillingBalanceAndSourceAsync(user);
return new BillingPaymentResponseModel(billingInfo);
}
var billingInfo = await _paymentService.GetBillingBalanceAndSourceAsync(user);
return new BillingPaymentResponseModel(billingInfo);
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -8,261 +8,260 @@ using Bit.Core.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Bit.Api.Controllers
namespace Bit.Api.Controllers;
[Route("organizations/{orgId}/collections")]
[Authorize("Application")]
public class CollectionsController : Controller
{
[Route("organizations/{orgId}/collections")]
[Authorize("Application")]
public class CollectionsController : Controller
private readonly ICollectionRepository _collectionRepository;
private readonly ICollectionService _collectionService;
private readonly IUserService _userService;
private readonly ICurrentContext _currentContext;
public CollectionsController(
ICollectionRepository collectionRepository,
ICollectionService collectionService,
IUserService userService,
ICurrentContext currentContext)
{
private readonly ICollectionRepository _collectionRepository;
private readonly ICollectionService _collectionService;
private readonly IUserService _userService;
private readonly ICurrentContext _currentContext;
_collectionRepository = collectionRepository;
_collectionService = collectionService;
_userService = userService;
_currentContext = currentContext;
}
public CollectionsController(
ICollectionRepository collectionRepository,
ICollectionService collectionService,
IUserService userService,
ICurrentContext currentContext)
[HttpGet("{id}")]
public async Task<CollectionResponseModel> Get(Guid orgId, Guid id)
{
if (!await CanViewCollectionAsync(orgId, id))
{
_collectionRepository = collectionRepository;
_collectionService = collectionService;
_userService = userService;
_currentContext = currentContext;
throw new NotFoundException();
}
[HttpGet("{id}")]
public async Task<CollectionResponseModel> Get(Guid orgId, Guid id)
var collection = await GetCollectionAsync(id, orgId);
return new CollectionResponseModel(collection);
}
[HttpGet("{id}/details")]
public async Task<CollectionGroupDetailsResponseModel> GetDetails(Guid orgId, Guid id)
{
if (!await ViewAtLeastOneCollectionAsync(orgId) && !await _currentContext.ManageUsers(orgId))
{
if (!await CanViewCollectionAsync(orgId, id))
throw new NotFoundException();
}
if (await _currentContext.ViewAllCollections(orgId))
{
var collectionDetails = await _collectionRepository.GetByIdWithGroupsAsync(id);
if (collectionDetails?.Item1 == null || collectionDetails.Item1.OrganizationId != orgId)
{
throw new NotFoundException();
}
var collection = await GetCollectionAsync(id, orgId);
return new CollectionResponseModel(collection);
return new CollectionGroupDetailsResponseModel(collectionDetails.Item1, collectionDetails.Item2);
}
[HttpGet("{id}/details")]
public async Task<CollectionGroupDetailsResponseModel> GetDetails(Guid orgId, Guid id)
else
{
if (!await ViewAtLeastOneCollectionAsync(orgId) && !await _currentContext.ManageUsers(orgId))
var collectionDetails = await _collectionRepository.GetByIdWithGroupsAsync(id,
_currentContext.UserId.Value);
if (collectionDetails?.Item1 == null || collectionDetails.Item1.OrganizationId != orgId)
{
throw new NotFoundException();
}
if (await _currentContext.ViewAllCollections(orgId))
{
var collectionDetails = await _collectionRepository.GetByIdWithGroupsAsync(id);
if (collectionDetails?.Item1 == null || collectionDetails.Item1.OrganizationId != orgId)
{
throw new NotFoundException();
}
return new CollectionGroupDetailsResponseModel(collectionDetails.Item1, collectionDetails.Item2);
}
else
{
var collectionDetails = await _collectionRepository.GetByIdWithGroupsAsync(id,
_currentContext.UserId.Value);
if (collectionDetails?.Item1 == null || collectionDetails.Item1.OrganizationId != orgId)
{
throw new NotFoundException();
}
return new CollectionGroupDetailsResponseModel(collectionDetails.Item1, collectionDetails.Item2);
}
}
[HttpGet("")]
public async Task<ListResponseModel<CollectionResponseModel>> Get(Guid orgId)
{
IEnumerable<Collection> orgCollections = await _collectionService.GetOrganizationCollections(orgId);
var responses = orgCollections.Select(c => new CollectionResponseModel(c));
return new ListResponseModel<CollectionResponseModel>(responses);
}
[HttpGet("~/collections")]
public async Task<ListResponseModel<CollectionDetailsResponseModel>> GetUser()
{
var collections = await _collectionRepository.GetManyByUserIdAsync(
_userService.GetProperUserId(User).Value);
var responses = collections.Select(c => new CollectionDetailsResponseModel(c));
return new ListResponseModel<CollectionDetailsResponseModel>(responses);
}
[HttpGet("{id}/users")]
public async Task<IEnumerable<SelectionReadOnlyResponseModel>> GetUsers(Guid orgId, Guid id)
{
var collection = await GetCollectionAsync(id, orgId);
var collectionUsers = await _collectionRepository.GetManyUsersByIdAsync(collection.Id);
var responses = collectionUsers.Select(cu => new SelectionReadOnlyResponseModel(cu));
return responses;
}
[HttpPost("")]
public async Task<CollectionResponseModel> Post(Guid orgId, [FromBody] CollectionRequestModel model)
{
var collection = model.ToCollection(orgId);
if (!await CanCreateCollection(orgId, collection.Id) &&
!await CanEditCollectionAsync(orgId, collection.Id))
{
throw new NotFoundException();
}
var assignUserToCollection = !(await _currentContext.EditAnyCollection(orgId)) &&
await _currentContext.EditAssignedCollections(orgId);
await _collectionService.SaveAsync(collection, model.Groups?.Select(g => g.ToSelectionReadOnly()),
assignUserToCollection ? _currentContext.UserId : null);
return new CollectionResponseModel(collection);
}
[HttpPut("{id}")]
[HttpPost("{id}")]
public async Task<CollectionResponseModel> Put(Guid orgId, Guid id, [FromBody] CollectionRequestModel model)
{
if (!await CanEditCollectionAsync(orgId, id))
{
throw new NotFoundException();
}
var collection = await GetCollectionAsync(id, orgId);
await _collectionService.SaveAsync(model.ToCollection(collection),
model.Groups?.Select(g => g.ToSelectionReadOnly()));
return new CollectionResponseModel(collection);
}
[HttpPut("{id}/users")]
public async Task PutUsers(Guid orgId, Guid id, [FromBody] IEnumerable<SelectionReadOnlyRequestModel> model)
{
if (!await CanEditCollectionAsync(orgId, id))
{
throw new NotFoundException();
}
var collection = await GetCollectionAsync(id, orgId);
await _collectionRepository.UpdateUsersAsync(collection.Id, model?.Select(g => g.ToSelectionReadOnly()));
}
[HttpDelete("{id}")]
[HttpPost("{id}/delete")]
public async Task Delete(Guid orgId, Guid id)
{
if (!await CanDeleteCollectionAsync(orgId, id))
{
throw new NotFoundException();
}
var collection = await GetCollectionAsync(id, orgId);
await _collectionService.DeleteAsync(collection);
}
[HttpDelete("{id}/user/{orgUserId}")]
[HttpPost("{id}/delete-user/{orgUserId}")]
public async Task Delete(string orgId, string id, string orgUserId)
{
var collection = await GetCollectionAsync(new Guid(id), new Guid(orgId));
await _collectionService.DeleteUserAsync(collection, new Guid(orgUserId));
}
private async Task<Collection> GetCollectionAsync(Guid id, Guid orgId)
{
Collection collection = default;
if (await _currentContext.ViewAllCollections(orgId))
{
collection = await _collectionRepository.GetByIdAsync(id);
}
else if (await _currentContext.ViewAssignedCollections(orgId))
{
collection = await _collectionRepository.GetByIdAsync(id, _currentContext.UserId.Value);
}
if (collection == null || collection.OrganizationId != orgId)
{
throw new NotFoundException();
}
return collection;
}
private async Task<bool> CanCreateCollection(Guid orgId, Guid collectionId)
{
if (collectionId != default)
{
return false;
}
return await _currentContext.CreateNewCollections(orgId);
}
private async Task<bool> CanEditCollectionAsync(Guid orgId, Guid collectionId)
{
if (collectionId == default)
{
return false;
}
if (await _currentContext.EditAnyCollection(orgId))
{
return true;
}
if (await _currentContext.EditAssignedCollections(orgId))
{
var collectionDetails = await _collectionRepository.GetByIdAsync(collectionId, _currentContext.UserId.Value);
return collectionDetails != null;
}
return false;
}
private async Task<bool> CanDeleteCollectionAsync(Guid orgId, Guid collectionId)
{
if (collectionId == default)
{
return false;
}
if (await _currentContext.DeleteAnyCollection(orgId))
{
return true;
}
if (await _currentContext.DeleteAssignedCollections(orgId))
{
var collectionDetails = await _collectionRepository.GetByIdAsync(collectionId, _currentContext.UserId.Value);
return collectionDetails != null;
}
return false;
}
private async Task<bool> CanViewCollectionAsync(Guid orgId, Guid collectionId)
{
if (collectionId == default)
{
return false;
}
if (await _currentContext.ViewAllCollections(orgId))
{
return true;
}
if (await _currentContext.ViewAssignedCollections(orgId))
{
var collectionDetails = await _collectionRepository.GetByIdAsync(collectionId, _currentContext.UserId.Value);
return collectionDetails != null;
}
return false;
}
private async Task<bool> ViewAtLeastOneCollectionAsync(Guid orgId)
{
return await _currentContext.ViewAllCollections(orgId) || await _currentContext.ViewAssignedCollections(orgId);
return new CollectionGroupDetailsResponseModel(collectionDetails.Item1, collectionDetails.Item2);
}
}
[HttpGet("")]
public async Task<ListResponseModel<CollectionResponseModel>> Get(Guid orgId)
{
IEnumerable<Collection> orgCollections = await _collectionService.GetOrganizationCollections(orgId);
var responses = orgCollections.Select(c => new CollectionResponseModel(c));
return new ListResponseModel<CollectionResponseModel>(responses);
}
[HttpGet("~/collections")]
public async Task<ListResponseModel<CollectionDetailsResponseModel>> GetUser()
{
var collections = await _collectionRepository.GetManyByUserIdAsync(
_userService.GetProperUserId(User).Value);
var responses = collections.Select(c => new CollectionDetailsResponseModel(c));
return new ListResponseModel<CollectionDetailsResponseModel>(responses);
}
[HttpGet("{id}/users")]
public async Task<IEnumerable<SelectionReadOnlyResponseModel>> GetUsers(Guid orgId, Guid id)
{
var collection = await GetCollectionAsync(id, orgId);
var collectionUsers = await _collectionRepository.GetManyUsersByIdAsync(collection.Id);
var responses = collectionUsers.Select(cu => new SelectionReadOnlyResponseModel(cu));
return responses;
}
[HttpPost("")]
public async Task<CollectionResponseModel> Post(Guid orgId, [FromBody] CollectionRequestModel model)
{
var collection = model.ToCollection(orgId);
if (!await CanCreateCollection(orgId, collection.Id) &&
!await CanEditCollectionAsync(orgId, collection.Id))
{
throw new NotFoundException();
}
var assignUserToCollection = !(await _currentContext.EditAnyCollection(orgId)) &&
await _currentContext.EditAssignedCollections(orgId);
await _collectionService.SaveAsync(collection, model.Groups?.Select(g => g.ToSelectionReadOnly()),
assignUserToCollection ? _currentContext.UserId : null);
return new CollectionResponseModel(collection);
}
[HttpPut("{id}")]
[HttpPost("{id}")]
public async Task<CollectionResponseModel> Put(Guid orgId, Guid id, [FromBody] CollectionRequestModel model)
{
if (!await CanEditCollectionAsync(orgId, id))
{
throw new NotFoundException();
}
var collection = await GetCollectionAsync(id, orgId);
await _collectionService.SaveAsync(model.ToCollection(collection),
model.Groups?.Select(g => g.ToSelectionReadOnly()));
return new CollectionResponseModel(collection);
}
[HttpPut("{id}/users")]
public async Task PutUsers(Guid orgId, Guid id, [FromBody] IEnumerable<SelectionReadOnlyRequestModel> model)
{
if (!await CanEditCollectionAsync(orgId, id))
{
throw new NotFoundException();
}
var collection = await GetCollectionAsync(id, orgId);
await _collectionRepository.UpdateUsersAsync(collection.Id, model?.Select(g => g.ToSelectionReadOnly()));
}
[HttpDelete("{id}")]
[HttpPost("{id}/delete")]
public async Task Delete(Guid orgId, Guid id)
{
if (!await CanDeleteCollectionAsync(orgId, id))
{
throw new NotFoundException();
}
var collection = await GetCollectionAsync(id, orgId);
await _collectionService.DeleteAsync(collection);
}
[HttpDelete("{id}/user/{orgUserId}")]
[HttpPost("{id}/delete-user/{orgUserId}")]
public async Task Delete(string orgId, string id, string orgUserId)
{
var collection = await GetCollectionAsync(new Guid(id), new Guid(orgId));
await _collectionService.DeleteUserAsync(collection, new Guid(orgUserId));
}
private async Task<Collection> GetCollectionAsync(Guid id, Guid orgId)
{
Collection collection = default;
if (await _currentContext.ViewAllCollections(orgId))
{
collection = await _collectionRepository.GetByIdAsync(id);
}
else if (await _currentContext.ViewAssignedCollections(orgId))
{
collection = await _collectionRepository.GetByIdAsync(id, _currentContext.UserId.Value);
}
if (collection == null || collection.OrganizationId != orgId)
{
throw new NotFoundException();
}
return collection;
}
private async Task<bool> CanCreateCollection(Guid orgId, Guid collectionId)
{
if (collectionId != default)
{
return false;
}
return await _currentContext.CreateNewCollections(orgId);
}
private async Task<bool> CanEditCollectionAsync(Guid orgId, Guid collectionId)
{
if (collectionId == default)
{
return false;
}
if (await _currentContext.EditAnyCollection(orgId))
{
return true;
}
if (await _currentContext.EditAssignedCollections(orgId))
{
var collectionDetails = await _collectionRepository.GetByIdAsync(collectionId, _currentContext.UserId.Value);
return collectionDetails != null;
}
return false;
}
private async Task<bool> CanDeleteCollectionAsync(Guid orgId, Guid collectionId)
{
if (collectionId == default)
{
return false;
}
if (await _currentContext.DeleteAnyCollection(orgId))
{
return true;
}
if (await _currentContext.DeleteAssignedCollections(orgId))
{
var collectionDetails = await _collectionRepository.GetByIdAsync(collectionId, _currentContext.UserId.Value);
return collectionDetails != null;
}
return false;
}
private async Task<bool> CanViewCollectionAsync(Guid orgId, Guid collectionId)
{
if (collectionId == default)
{
return false;
}
if (await _currentContext.ViewAllCollections(orgId))
{
return true;
}
if (await _currentContext.ViewAssignedCollections(orgId))
{
var collectionDetails = await _collectionRepository.GetByIdAsync(collectionId, _currentContext.UserId.Value);
return collectionDetails != null;
}
return false;
}
private async Task<bool> ViewAtLeastOneCollectionAsync(Guid orgId)
{
return await _currentContext.ViewAllCollections(orgId) || await _currentContext.ViewAssignedCollections(orgId);
}
}

View File

@ -7,124 +7,123 @@ using Bit.Core.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Bit.Api.Controllers
namespace Bit.Api.Controllers;
[Route("devices")]
[Authorize("Application")]
public class DevicesController : Controller
{
[Route("devices")]
[Authorize("Application")]
public class DevicesController : Controller
private readonly IDeviceRepository _deviceRepository;
private readonly IDeviceService _deviceService;
private readonly IUserService _userService;
public DevicesController(
IDeviceRepository deviceRepository,
IDeviceService deviceService,
IUserService userService)
{
private readonly IDeviceRepository _deviceRepository;
private readonly IDeviceService _deviceService;
private readonly IUserService _userService;
_deviceRepository = deviceRepository;
_deviceService = deviceService;
_userService = userService;
}
public DevicesController(
IDeviceRepository deviceRepository,
IDeviceService deviceService,
IUserService userService)
[HttpGet("{id}")]
public async Task<DeviceResponseModel> Get(string id)
{
var device = await _deviceRepository.GetByIdAsync(new Guid(id), _userService.GetProperUserId(User).Value);
if (device == null)
{
_deviceRepository = deviceRepository;
_deviceService = deviceService;
_userService = userService;
throw new NotFoundException();
}
[HttpGet("{id}")]
public async Task<DeviceResponseModel> Get(string id)
{
var device = await _deviceRepository.GetByIdAsync(new Guid(id), _userService.GetProperUserId(User).Value);
if (device == null)
{
throw new NotFoundException();
}
var response = new DeviceResponseModel(device);
return response;
}
var response = new DeviceResponseModel(device);
return response;
[HttpGet("identifier/{identifier}")]
public async Task<DeviceResponseModel> GetByIdentifier(string identifier)
{
var device = await _deviceRepository.GetByIdentifierAsync(identifier, _userService.GetProperUserId(User).Value);
if (device == null)
{
throw new NotFoundException();
}
[HttpGet("identifier/{identifier}")]
public async Task<DeviceResponseModel> GetByIdentifier(string identifier)
{
var device = await _deviceRepository.GetByIdentifierAsync(identifier, _userService.GetProperUserId(User).Value);
if (device == null)
{
throw new NotFoundException();
}
var response = new DeviceResponseModel(device);
return response;
}
var response = new DeviceResponseModel(device);
return response;
[HttpGet("")]
public async Task<ListResponseModel<DeviceResponseModel>> Get()
{
ICollection<Device> devices = await _deviceRepository.GetManyByUserIdAsync(_userService.GetProperUserId(User).Value);
var responses = devices.Select(d => new DeviceResponseModel(d));
return new ListResponseModel<DeviceResponseModel>(responses);
}
[HttpPost("")]
public async Task<DeviceResponseModel> Post([FromBody] DeviceRequestModel model)
{
var device = model.ToDevice(_userService.GetProperUserId(User));
await _deviceService.SaveAsync(device);
var response = new DeviceResponseModel(device);
return response;
}
[HttpPut("{id}")]
[HttpPost("{id}")]
public async Task<DeviceResponseModel> Put(string id, [FromBody] DeviceRequestModel model)
{
var device = await _deviceRepository.GetByIdAsync(new Guid(id), _userService.GetProperUserId(User).Value);
if (device == null)
{
throw new NotFoundException();
}
[HttpGet("")]
public async Task<ListResponseModel<DeviceResponseModel>> Get()
await _deviceService.SaveAsync(model.ToDevice(device));
var response = new DeviceResponseModel(device);
return response;
}
[HttpPut("identifier/{identifier}/token")]
[HttpPost("identifier/{identifier}/token")]
public async Task PutToken(string identifier, [FromBody] DeviceTokenRequestModel model)
{
var device = await _deviceRepository.GetByIdentifierAsync(identifier, _userService.GetProperUserId(User).Value);
if (device == null)
{
ICollection<Device> devices = await _deviceRepository.GetManyByUserIdAsync(_userService.GetProperUserId(User).Value);
var responses = devices.Select(d => new DeviceResponseModel(d));
return new ListResponseModel<DeviceResponseModel>(responses);
throw new NotFoundException();
}
[HttpPost("")]
public async Task<DeviceResponseModel> Post([FromBody] DeviceRequestModel model)
{
var device = model.ToDevice(_userService.GetProperUserId(User));
await _deviceService.SaveAsync(device);
await _deviceService.SaveAsync(model.ToDevice(device));
}
var response = new DeviceResponseModel(device);
return response;
[AllowAnonymous]
[HttpPut("identifier/{identifier}/clear-token")]
[HttpPost("identifier/{identifier}/clear-token")]
public async Task PutClearToken(string identifier)
{
var device = await _deviceRepository.GetByIdentifierAsync(identifier);
if (device == null)
{
throw new NotFoundException();
}
[HttpPut("{id}")]
[HttpPost("{id}")]
public async Task<DeviceResponseModel> Put(string id, [FromBody] DeviceRequestModel model)
await _deviceService.ClearTokenAsync(device);
}
[HttpDelete("{id}")]
[HttpPost("{id}/delete")]
public async Task Delete(string id)
{
var device = await _deviceRepository.GetByIdAsync(new Guid(id), _userService.GetProperUserId(User).Value);
if (device == null)
{
var device = await _deviceRepository.GetByIdAsync(new Guid(id), _userService.GetProperUserId(User).Value);
if (device == null)
{
throw new NotFoundException();
}
await _deviceService.SaveAsync(model.ToDevice(device));
var response = new DeviceResponseModel(device);
return response;
throw new NotFoundException();
}
[HttpPut("identifier/{identifier}/token")]
[HttpPost("identifier/{identifier}/token")]
public async Task PutToken(string identifier, [FromBody] DeviceTokenRequestModel model)
{
var device = await _deviceRepository.GetByIdentifierAsync(identifier, _userService.GetProperUserId(User).Value);
if (device == null)
{
throw new NotFoundException();
}
await _deviceService.SaveAsync(model.ToDevice(device));
}
[AllowAnonymous]
[HttpPut("identifier/{identifier}/clear-token")]
[HttpPost("identifier/{identifier}/clear-token")]
public async Task PutClearToken(string identifier)
{
var device = await _deviceRepository.GetByIdentifierAsync(identifier);
if (device == null)
{
throw new NotFoundException();
}
await _deviceService.ClearTokenAsync(device);
}
[HttpDelete("{id}")]
[HttpPost("{id}/delete")]
public async Task Delete(string id)
{
var device = await _deviceRepository.GetByIdAsync(new Guid(id), _userService.GetProperUserId(User).Value);
if (device == null)
{
throw new NotFoundException();
}
await _deviceService.DeleteAsync(device);
}
await _deviceService.DeleteAsync(device);
}
}

View File

@ -9,170 +9,169 @@ using Bit.Core.Settings;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Bit.Api.Controllers
namespace Bit.Api.Controllers;
[Route("emergency-access")]
[Authorize("Application")]
public class EmergencyAccessController : Controller
{
[Route("emergency-access")]
[Authorize("Application")]
public class EmergencyAccessController : Controller
private readonly IUserService _userService;
private readonly IEmergencyAccessRepository _emergencyAccessRepository;
private readonly IEmergencyAccessService _emergencyAccessService;
private readonly IGlobalSettings _globalSettings;
public EmergencyAccessController(
IUserService userService,
IEmergencyAccessRepository emergencyAccessRepository,
IEmergencyAccessService emergencyAccessService,
IGlobalSettings globalSettings)
{
private readonly IUserService _userService;
private readonly IEmergencyAccessRepository _emergencyAccessRepository;
private readonly IEmergencyAccessService _emergencyAccessService;
private readonly IGlobalSettings _globalSettings;
_userService = userService;
_emergencyAccessRepository = emergencyAccessRepository;
_emergencyAccessService = emergencyAccessService;
_globalSettings = globalSettings;
}
public EmergencyAccessController(
IUserService userService,
IEmergencyAccessRepository emergencyAccessRepository,
IEmergencyAccessService emergencyAccessService,
IGlobalSettings globalSettings)
[HttpGet("trusted")]
public async Task<ListResponseModel<EmergencyAccessGranteeDetailsResponseModel>> GetContacts()
{
var userId = _userService.GetProperUserId(User);
var granteeDetails = await _emergencyAccessRepository.GetManyDetailsByGrantorIdAsync(userId.Value);
var responses = granteeDetails.Select(d =>
new EmergencyAccessGranteeDetailsResponseModel(d));
return new ListResponseModel<EmergencyAccessGranteeDetailsResponseModel>(responses);
}
[HttpGet("granted")]
public async Task<ListResponseModel<EmergencyAccessGrantorDetailsResponseModel>> GetGrantees()
{
var userId = _userService.GetProperUserId(User);
var granteeDetails = await _emergencyAccessRepository.GetManyDetailsByGranteeIdAsync(userId.Value);
var responses = granteeDetails.Select(d => new EmergencyAccessGrantorDetailsResponseModel(d));
return new ListResponseModel<EmergencyAccessGrantorDetailsResponseModel>(responses);
}
[HttpGet("{id}")]
public async Task<EmergencyAccessGranteeDetailsResponseModel> Get(Guid id)
{
var userId = _userService.GetProperUserId(User);
var result = await _emergencyAccessService.GetAsync(id, userId.Value);
return new EmergencyAccessGranteeDetailsResponseModel(result);
}
[HttpGet("{id}/policies")]
public async Task<ListResponseModel<PolicyResponseModel>> Policies(Guid id)
{
var user = await _userService.GetUserByPrincipalAsync(User);
var policies = await _emergencyAccessService.GetPoliciesAsync(id, user);
var responses = policies.Select<Policy, PolicyResponseModel>(policy => new PolicyResponseModel(policy));
return new ListResponseModel<PolicyResponseModel>(responses);
}
[HttpPut("{id}")]
[HttpPost("{id}")]
public async Task Put(Guid id, [FromBody] EmergencyAccessUpdateRequestModel model)
{
var emergencyAccess = await _emergencyAccessRepository.GetByIdAsync(id);
if (emergencyAccess == null)
{
_userService = userService;
_emergencyAccessRepository = emergencyAccessRepository;
_emergencyAccessService = emergencyAccessService;
_globalSettings = globalSettings;
throw new NotFoundException();
}
[HttpGet("trusted")]
public async Task<ListResponseModel<EmergencyAccessGranteeDetailsResponseModel>> GetContacts()
{
var userId = _userService.GetProperUserId(User);
var granteeDetails = await _emergencyAccessRepository.GetManyDetailsByGrantorIdAsync(userId.Value);
var user = await _userService.GetUserByPrincipalAsync(User);
await _emergencyAccessService.SaveAsync(model.ToEmergencyAccess(emergencyAccess), user);
}
var responses = granteeDetails.Select(d =>
new EmergencyAccessGranteeDetailsResponseModel(d));
[HttpDelete("{id}")]
[HttpPost("{id}/delete")]
public async Task Delete(Guid id)
{
var userId = _userService.GetProperUserId(User);
await _emergencyAccessService.DeleteAsync(id, userId.Value);
}
return new ListResponseModel<EmergencyAccessGranteeDetailsResponseModel>(responses);
}
[HttpPost("invite")]
public async Task Invite([FromBody] EmergencyAccessInviteRequestModel model)
{
var user = await _userService.GetUserByPrincipalAsync(User);
await _emergencyAccessService.InviteAsync(user, model.Email, model.Type.Value, model.WaitTimeDays);
}
[HttpGet("granted")]
public async Task<ListResponseModel<EmergencyAccessGrantorDetailsResponseModel>> GetGrantees()
{
var userId = _userService.GetProperUserId(User);
var granteeDetails = await _emergencyAccessRepository.GetManyDetailsByGranteeIdAsync(userId.Value);
[HttpPost("{id}/reinvite")]
public async Task Reinvite(Guid id)
{
var user = await _userService.GetUserByPrincipalAsync(User);
await _emergencyAccessService.ResendInviteAsync(user, id);
}
var responses = granteeDetails.Select(d => new EmergencyAccessGrantorDetailsResponseModel(d));
[HttpPost("{id}/accept")]
public async Task Accept(Guid id, [FromBody] OrganizationUserAcceptRequestModel model)
{
var user = await _userService.GetUserByPrincipalAsync(User);
await _emergencyAccessService.AcceptUserAsync(id, user, model.Token, _userService);
}
return new ListResponseModel<EmergencyAccessGrantorDetailsResponseModel>(responses);
}
[HttpPost("{id}/confirm")]
public async Task Confirm(Guid id, [FromBody] OrganizationUserConfirmRequestModel model)
{
var userId = _userService.GetProperUserId(User);
await _emergencyAccessService.ConfirmUserAsync(id, model.Key, userId.Value);
}
[HttpGet("{id}")]
public async Task<EmergencyAccessGranteeDetailsResponseModel> Get(Guid id)
{
var userId = _userService.GetProperUserId(User);
var result = await _emergencyAccessService.GetAsync(id, userId.Value);
return new EmergencyAccessGranteeDetailsResponseModel(result);
}
[HttpPost("{id}/initiate")]
public async Task Initiate(Guid id)
{
var user = await _userService.GetUserByPrincipalAsync(User);
await _emergencyAccessService.InitiateAsync(id, user);
}
[HttpGet("{id}/policies")]
public async Task<ListResponseModel<PolicyResponseModel>> Policies(Guid id)
{
var user = await _userService.GetUserByPrincipalAsync(User);
var policies = await _emergencyAccessService.GetPoliciesAsync(id, user);
var responses = policies.Select<Policy, PolicyResponseModel>(policy => new PolicyResponseModel(policy));
return new ListResponseModel<PolicyResponseModel>(responses);
}
[HttpPost("{id}/approve")]
public async Task Accept(Guid id)
{
var user = await _userService.GetUserByPrincipalAsync(User);
await _emergencyAccessService.ApproveAsync(id, user);
}
[HttpPut("{id}")]
[HttpPost("{id}")]
public async Task Put(Guid id, [FromBody] EmergencyAccessUpdateRequestModel model)
{
var emergencyAccess = await _emergencyAccessRepository.GetByIdAsync(id);
if (emergencyAccess == null)
{
throw new NotFoundException();
}
[HttpPost("{id}/reject")]
public async Task Reject(Guid id)
{
var user = await _userService.GetUserByPrincipalAsync(User);
await _emergencyAccessService.RejectAsync(id, user);
}
var user = await _userService.GetUserByPrincipalAsync(User);
await _emergencyAccessService.SaveAsync(model.ToEmergencyAccess(emergencyAccess), user);
}
[HttpPost("{id}/takeover")]
public async Task<EmergencyAccessTakeoverResponseModel> Takeover(Guid id)
{
var user = await _userService.GetUserByPrincipalAsync(User);
var (result, grantor) = await _emergencyAccessService.TakeoverAsync(id, user);
return new EmergencyAccessTakeoverResponseModel(result, grantor);
}
[HttpDelete("{id}")]
[HttpPost("{id}/delete")]
public async Task Delete(Guid id)
{
var userId = _userService.GetProperUserId(User);
await _emergencyAccessService.DeleteAsync(id, userId.Value);
}
[HttpPost("{id}/password")]
public async Task Password(Guid id, [FromBody] EmergencyAccessPasswordRequestModel model)
{
var user = await _userService.GetUserByPrincipalAsync(User);
await _emergencyAccessService.PasswordAsync(id, user, model.NewMasterPasswordHash, model.Key);
}
[HttpPost("invite")]
public async Task Invite([FromBody] EmergencyAccessInviteRequestModel model)
{
var user = await _userService.GetUserByPrincipalAsync(User);
await _emergencyAccessService.InviteAsync(user, model.Email, model.Type.Value, model.WaitTimeDays);
}
[HttpPost("{id}/view")]
public async Task<EmergencyAccessViewResponseModel> ViewCiphers(Guid id)
{
var user = await _userService.GetUserByPrincipalAsync(User);
var viewResult = await _emergencyAccessService.ViewAsync(id, user);
return new EmergencyAccessViewResponseModel(_globalSettings, viewResult.EmergencyAccess, viewResult.Ciphers);
}
[HttpPost("{id}/reinvite")]
public async Task Reinvite(Guid id)
{
var user = await _userService.GetUserByPrincipalAsync(User);
await _emergencyAccessService.ResendInviteAsync(user, id);
}
[HttpPost("{id}/accept")]
public async Task Accept(Guid id, [FromBody] OrganizationUserAcceptRequestModel model)
{
var user = await _userService.GetUserByPrincipalAsync(User);
await _emergencyAccessService.AcceptUserAsync(id, user, model.Token, _userService);
}
[HttpPost("{id}/confirm")]
public async Task Confirm(Guid id, [FromBody] OrganizationUserConfirmRequestModel model)
{
var userId = _userService.GetProperUserId(User);
await _emergencyAccessService.ConfirmUserAsync(id, model.Key, userId.Value);
}
[HttpPost("{id}/initiate")]
public async Task Initiate(Guid id)
{
var user = await _userService.GetUserByPrincipalAsync(User);
await _emergencyAccessService.InitiateAsync(id, user);
}
[HttpPost("{id}/approve")]
public async Task Accept(Guid id)
{
var user = await _userService.GetUserByPrincipalAsync(User);
await _emergencyAccessService.ApproveAsync(id, user);
}
[HttpPost("{id}/reject")]
public async Task Reject(Guid id)
{
var user = await _userService.GetUserByPrincipalAsync(User);
await _emergencyAccessService.RejectAsync(id, user);
}
[HttpPost("{id}/takeover")]
public async Task<EmergencyAccessTakeoverResponseModel> Takeover(Guid id)
{
var user = await _userService.GetUserByPrincipalAsync(User);
var (result, grantor) = await _emergencyAccessService.TakeoverAsync(id, user);
return new EmergencyAccessTakeoverResponseModel(result, grantor);
}
[HttpPost("{id}/password")]
public async Task Password(Guid id, [FromBody] EmergencyAccessPasswordRequestModel model)
{
var user = await _userService.GetUserByPrincipalAsync(User);
await _emergencyAccessService.PasswordAsync(id, user, model.NewMasterPasswordHash, model.Key);
}
[HttpPost("{id}/view")]
public async Task<EmergencyAccessViewResponseModel> ViewCiphers(Guid id)
{
var user = await _userService.GetUserByPrincipalAsync(User);
var viewResult = await _emergencyAccessService.ViewAsync(id, user);
return new EmergencyAccessViewResponseModel(_globalSettings, viewResult.EmergencyAccess, viewResult.Ciphers);
}
[HttpGet("{id}/{cipherId}/attachment/{attachmentId}")]
public async Task<AttachmentResponseModel> GetAttachmentData(Guid id, Guid cipherId, string attachmentId)
{
var user = await _userService.GetUserByPrincipalAsync(User);
var result =
await _emergencyAccessService.GetAttachmentDownloadAsync(id, cipherId, attachmentId, user);
return new AttachmentResponseModel(result);
}
[HttpGet("{id}/{cipherId}/attachment/{attachmentId}")]
public async Task<AttachmentResponseModel> GetAttachmentData(Guid id, Guid cipherId, string attachmentId)
{
var user = await _userService.GetUserByPrincipalAsync(User);
var result =
await _emergencyAccessService.GetAttachmentDownloadAsync(id, cipherId, attachmentId, user);
return new AttachmentResponseModel(result);
}
}

View File

@ -7,171 +7,170 @@ using Bit.Core.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Bit.Api.Controllers
namespace Bit.Api.Controllers;
[Route("events")]
[Authorize("Application")]
public class EventsController : Controller
{
[Route("events")]
[Authorize("Application")]
public class EventsController : Controller
private readonly IUserService _userService;
private readonly ICipherRepository _cipherRepository;
private readonly IOrganizationUserRepository _organizationUserRepository;
private readonly IProviderUserRepository _providerUserRepository;
private readonly IEventRepository _eventRepository;
private readonly ICurrentContext _currentContext;
public EventsController(
IUserService userService,
ICipherRepository cipherRepository,
IOrganizationUserRepository organizationUserRepository,
IProviderUserRepository providerUserRepository,
IEventRepository eventRepository,
ICurrentContext currentContext)
{
private readonly IUserService _userService;
private readonly ICipherRepository _cipherRepository;
private readonly IOrganizationUserRepository _organizationUserRepository;
private readonly IProviderUserRepository _providerUserRepository;
private readonly IEventRepository _eventRepository;
private readonly ICurrentContext _currentContext;
_userService = userService;
_cipherRepository = cipherRepository;
_organizationUserRepository = organizationUserRepository;
_providerUserRepository = providerUserRepository;
_eventRepository = eventRepository;
_currentContext = currentContext;
}
public EventsController(
IUserService userService,
ICipherRepository cipherRepository,
IOrganizationUserRepository organizationUserRepository,
IProviderUserRepository providerUserRepository,
IEventRepository eventRepository,
ICurrentContext currentContext)
[HttpGet("")]
public async Task<ListResponseModel<EventResponseModel>> GetUser(
[FromQuery] DateTime? start = null, [FromQuery] DateTime? end = null, [FromQuery] string continuationToken = null)
{
var dateRange = GetDateRange(start, end);
var userId = _userService.GetProperUserId(User).Value;
var result = await _eventRepository.GetManyByUserAsync(userId, dateRange.Item1, dateRange.Item2,
new PageOptions { ContinuationToken = continuationToken });
var responses = result.Data.Select(e => new EventResponseModel(e));
return new ListResponseModel<EventResponseModel>(responses, result.ContinuationToken);
}
[HttpGet("~/ciphers/{id}/events")]
public async Task<ListResponseModel<EventResponseModel>> GetCipher(string id,
[FromQuery] DateTime? start = null, [FromQuery] DateTime? end = null, [FromQuery] string continuationToken = null)
{
var cipher = await _cipherRepository.GetByIdAsync(new Guid(id));
if (cipher == null)
{
_userService = userService;
_cipherRepository = cipherRepository;
_organizationUserRepository = organizationUserRepository;
_providerUserRepository = providerUserRepository;
_eventRepository = eventRepository;
_currentContext = currentContext;
throw new NotFoundException();
}
[HttpGet("")]
public async Task<ListResponseModel<EventResponseModel>> GetUser(
[FromQuery] DateTime? start = null, [FromQuery] DateTime? end = null, [FromQuery] string continuationToken = null)
var canView = false;
if (cipher.OrganizationId.HasValue)
{
canView = await _currentContext.AccessEventLogs(cipher.OrganizationId.Value);
}
else if (cipher.UserId.HasValue)
{
var dateRange = GetDateRange(start, end);
var userId = _userService.GetProperUserId(User).Value;
var result = await _eventRepository.GetManyByUserAsync(userId, dateRange.Item1, dateRange.Item2,
new PageOptions { ContinuationToken = continuationToken });
var responses = result.Data.Select(e => new EventResponseModel(e));
return new ListResponseModel<EventResponseModel>(responses, result.ContinuationToken);
canView = userId == cipher.UserId.Value;
}
[HttpGet("~/ciphers/{id}/events")]
public async Task<ListResponseModel<EventResponseModel>> GetCipher(string id,
[FromQuery] DateTime? start = null, [FromQuery] DateTime? end = null, [FromQuery] string continuationToken = null)
if (!canView)
{
var cipher = await _cipherRepository.GetByIdAsync(new Guid(id));
if (cipher == null)
{
throw new NotFoundException();
}
var canView = false;
if (cipher.OrganizationId.HasValue)
{
canView = await _currentContext.AccessEventLogs(cipher.OrganizationId.Value);
}
else if (cipher.UserId.HasValue)
{
var userId = _userService.GetProperUserId(User).Value;
canView = userId == cipher.UserId.Value;
}
if (!canView)
{
throw new NotFoundException();
}
var dateRange = GetDateRange(start, end);
var result = await _eventRepository.GetManyByCipherAsync(cipher, dateRange.Item1, dateRange.Item2,
new PageOptions { ContinuationToken = continuationToken });
var responses = result.Data.Select(e => new EventResponseModel(e));
return new ListResponseModel<EventResponseModel>(responses, result.ContinuationToken);
throw new NotFoundException();
}
[HttpGet("~/organizations/{id}/events")]
public async Task<ListResponseModel<EventResponseModel>> GetOrganization(string id,
[FromQuery] DateTime? start = null, [FromQuery] DateTime? end = null, [FromQuery] string continuationToken = null)
var dateRange = GetDateRange(start, end);
var result = await _eventRepository.GetManyByCipherAsync(cipher, dateRange.Item1, dateRange.Item2,
new PageOptions { ContinuationToken = continuationToken });
var responses = result.Data.Select(e => new EventResponseModel(e));
return new ListResponseModel<EventResponseModel>(responses, result.ContinuationToken);
}
[HttpGet("~/organizations/{id}/events")]
public async Task<ListResponseModel<EventResponseModel>> GetOrganization(string id,
[FromQuery] DateTime? start = null, [FromQuery] DateTime? end = null, [FromQuery] string continuationToken = null)
{
var orgId = new Guid(id);
if (!await _currentContext.AccessEventLogs(orgId))
{
var orgId = new Guid(id);
if (!await _currentContext.AccessEventLogs(orgId))
{
throw new NotFoundException();
}
var dateRange = GetDateRange(start, end);
var result = await _eventRepository.GetManyByOrganizationAsync(orgId, dateRange.Item1, dateRange.Item2,
new PageOptions { ContinuationToken = continuationToken });
var responses = result.Data.Select(e => new EventResponseModel(e));
return new ListResponseModel<EventResponseModel>(responses, result.ContinuationToken);
throw new NotFoundException();
}
[HttpGet("~/organizations/{orgId}/users/{id}/events")]
public async Task<ListResponseModel<EventResponseModel>> GetOrganizationUser(string orgId, string id,
[FromQuery] DateTime? start = null, [FromQuery] DateTime? end = null, [FromQuery] string continuationToken = null)
var dateRange = GetDateRange(start, end);
var result = await _eventRepository.GetManyByOrganizationAsync(orgId, dateRange.Item1, dateRange.Item2,
new PageOptions { ContinuationToken = continuationToken });
var responses = result.Data.Select(e => new EventResponseModel(e));
return new ListResponseModel<EventResponseModel>(responses, result.ContinuationToken);
}
[HttpGet("~/organizations/{orgId}/users/{id}/events")]
public async Task<ListResponseModel<EventResponseModel>> GetOrganizationUser(string orgId, string id,
[FromQuery] DateTime? start = null, [FromQuery] DateTime? end = null, [FromQuery] string continuationToken = null)
{
var organizationUser = await _organizationUserRepository.GetByIdAsync(new Guid(id));
if (organizationUser == null || !organizationUser.UserId.HasValue ||
!await _currentContext.AccessEventLogs(organizationUser.OrganizationId))
{
var organizationUser = await _organizationUserRepository.GetByIdAsync(new Guid(id));
if (organizationUser == null || !organizationUser.UserId.HasValue ||
!await _currentContext.AccessEventLogs(organizationUser.OrganizationId))
{
throw new NotFoundException();
}
var dateRange = GetDateRange(start, end);
var result = await _eventRepository.GetManyByOrganizationActingUserAsync(organizationUser.OrganizationId,
organizationUser.UserId.Value, dateRange.Item1, dateRange.Item2,
new PageOptions { ContinuationToken = continuationToken });
var responses = result.Data.Select(e => new EventResponseModel(e));
return new ListResponseModel<EventResponseModel>(responses, result.ContinuationToken);
throw new NotFoundException();
}
[HttpGet("~/providers/{providerId:guid}/events")]
public async Task<ListResponseModel<EventResponseModel>> GetProvider(Guid providerId,
[FromQuery] DateTime? start = null, [FromQuery] DateTime? end = null, [FromQuery] string continuationToken = null)
var dateRange = GetDateRange(start, end);
var result = await _eventRepository.GetManyByOrganizationActingUserAsync(organizationUser.OrganizationId,
organizationUser.UserId.Value, dateRange.Item1, dateRange.Item2,
new PageOptions { ContinuationToken = continuationToken });
var responses = result.Data.Select(e => new EventResponseModel(e));
return new ListResponseModel<EventResponseModel>(responses, result.ContinuationToken);
}
[HttpGet("~/providers/{providerId:guid}/events")]
public async Task<ListResponseModel<EventResponseModel>> GetProvider(Guid providerId,
[FromQuery] DateTime? start = null, [FromQuery] DateTime? end = null, [FromQuery] string continuationToken = null)
{
if (!_currentContext.ProviderAccessEventLogs(providerId))
{
if (!_currentContext.ProviderAccessEventLogs(providerId))
{
throw new NotFoundException();
}
var dateRange = GetDateRange(start, end);
var result = await _eventRepository.GetManyByProviderAsync(providerId, dateRange.Item1, dateRange.Item2,
new PageOptions { ContinuationToken = continuationToken });
var responses = result.Data.Select(e => new EventResponseModel(e));
return new ListResponseModel<EventResponseModel>(responses, result.ContinuationToken);
throw new NotFoundException();
}
[HttpGet("~/providers/{providerId:guid}/users/{id:guid}/events")]
public async Task<ListResponseModel<EventResponseModel>> GetProviderUser(Guid providerId, Guid id,
[FromQuery] DateTime? start = null, [FromQuery] DateTime? end = null, [FromQuery] string continuationToken = null)
var dateRange = GetDateRange(start, end);
var result = await _eventRepository.GetManyByProviderAsync(providerId, dateRange.Item1, dateRange.Item2,
new PageOptions { ContinuationToken = continuationToken });
var responses = result.Data.Select(e => new EventResponseModel(e));
return new ListResponseModel<EventResponseModel>(responses, result.ContinuationToken);
}
[HttpGet("~/providers/{providerId:guid}/users/{id:guid}/events")]
public async Task<ListResponseModel<EventResponseModel>> GetProviderUser(Guid providerId, Guid id,
[FromQuery] DateTime? start = null, [FromQuery] DateTime? end = null, [FromQuery] string continuationToken = null)
{
var providerUser = await _providerUserRepository.GetByIdAsync(id);
if (providerUser == null || !providerUser.UserId.HasValue ||
!_currentContext.ProviderAccessEventLogs(providerUser.ProviderId))
{
var providerUser = await _providerUserRepository.GetByIdAsync(id);
if (providerUser == null || !providerUser.UserId.HasValue ||
!_currentContext.ProviderAccessEventLogs(providerUser.ProviderId))
{
throw new NotFoundException();
}
var dateRange = GetDateRange(start, end);
var result = await _eventRepository.GetManyByProviderActingUserAsync(providerUser.ProviderId,
providerUser.UserId.Value, dateRange.Item1, dateRange.Item2,
new PageOptions { ContinuationToken = continuationToken });
var responses = result.Data.Select(e => new EventResponseModel(e));
return new ListResponseModel<EventResponseModel>(responses, result.ContinuationToken);
throw new NotFoundException();
}
private Tuple<DateTime, DateTime> GetDateRange(DateTime? start, DateTime? end)
var dateRange = GetDateRange(start, end);
var result = await _eventRepository.GetManyByProviderActingUserAsync(providerUser.ProviderId,
providerUser.UserId.Value, dateRange.Item1, dateRange.Item2,
new PageOptions { ContinuationToken = continuationToken });
var responses = result.Data.Select(e => new EventResponseModel(e));
return new ListResponseModel<EventResponseModel>(responses, result.ContinuationToken);
}
private Tuple<DateTime, DateTime> GetDateRange(DateTime? start, DateTime? end)
{
if (!end.HasValue || !start.HasValue)
{
if (!end.HasValue || !start.HasValue)
{
end = DateTime.UtcNow.Date.AddDays(1).AddMilliseconds(-1);
start = DateTime.UtcNow.Date.AddDays(-30);
}
else if (start.Value > end.Value)
{
var newEnd = start;
start = end;
end = newEnd;
}
if ((end.Value - start.Value) > TimeSpan.FromDays(367))
{
throw new BadRequestException("Range too large.");
}
return new Tuple<DateTime, DateTime>(start.Value, end.Value);
end = DateTime.UtcNow.Date.AddDays(1).AddMilliseconds(-1);
start = DateTime.UtcNow.Date.AddDays(-30);
}
else if (start.Value > end.Value)
{
var newEnd = start;
start = end;
end = newEnd;
}
if ((end.Value - start.Value) > TimeSpan.FromDays(367))
{
throw new BadRequestException("Range too large.");
}
return new Tuple<DateTime, DateTime>(start.Value, end.Value);
}
}

View File

@ -6,84 +6,83 @@ using Bit.Core.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Bit.Api.Controllers
namespace Bit.Api.Controllers;
[Route("folders")]
[Authorize("Application")]
public class FoldersController : Controller
{
[Route("folders")]
[Authorize("Application")]
public class FoldersController : Controller
private readonly IFolderRepository _folderRepository;
private readonly ICipherService _cipherService;
private readonly IUserService _userService;
public FoldersController(
IFolderRepository folderRepository,
ICipherService cipherService,
IUserService userService)
{
private readonly IFolderRepository _folderRepository;
private readonly ICipherService _cipherService;
private readonly IUserService _userService;
_folderRepository = folderRepository;
_cipherService = cipherService;
_userService = userService;
}
public FoldersController(
IFolderRepository folderRepository,
ICipherService cipherService,
IUserService userService)
[HttpGet("{id}")]
public async Task<FolderResponseModel> Get(string id)
{
var userId = _userService.GetProperUserId(User).Value;
var folder = await _folderRepository.GetByIdAsync(new Guid(id), userId);
if (folder == null)
{
_folderRepository = folderRepository;
_cipherService = cipherService;
_userService = userService;
throw new NotFoundException();
}
[HttpGet("{id}")]
public async Task<FolderResponseModel> Get(string id)
{
var userId = _userService.GetProperUserId(User).Value;
var folder = await _folderRepository.GetByIdAsync(new Guid(id), userId);
if (folder == null)
{
throw new NotFoundException();
}
return new FolderResponseModel(folder);
}
return new FolderResponseModel(folder);
[HttpGet("")]
public async Task<ListResponseModel<FolderResponseModel>> Get()
{
var userId = _userService.GetProperUserId(User).Value;
var folders = await _folderRepository.GetManyByUserIdAsync(userId);
var responses = folders.Select(f => new FolderResponseModel(f));
return new ListResponseModel<FolderResponseModel>(responses);
}
[HttpPost("")]
public async Task<FolderResponseModel> Post([FromBody] FolderRequestModel model)
{
var userId = _userService.GetProperUserId(User).Value;
var folder = model.ToFolder(_userService.GetProperUserId(User).Value);
await _cipherService.SaveFolderAsync(folder);
return new FolderResponseModel(folder);
}
[HttpPut("{id}")]
[HttpPost("{id}")]
public async Task<FolderResponseModel> Put(string id, [FromBody] FolderRequestModel model)
{
var userId = _userService.GetProperUserId(User).Value;
var folder = await _folderRepository.GetByIdAsync(new Guid(id), userId);
if (folder == null)
{
throw new NotFoundException();
}
[HttpGet("")]
public async Task<ListResponseModel<FolderResponseModel>> Get()
await _cipherService.SaveFolderAsync(model.ToFolder(folder));
return new FolderResponseModel(folder);
}
[HttpDelete("{id}")]
[HttpPost("{id}/delete")]
public async Task Delete(string id)
{
var userId = _userService.GetProperUserId(User).Value;
var folder = await _folderRepository.GetByIdAsync(new Guid(id), userId);
if (folder == null)
{
var userId = _userService.GetProperUserId(User).Value;
var folders = await _folderRepository.GetManyByUserIdAsync(userId);
var responses = folders.Select(f => new FolderResponseModel(f));
return new ListResponseModel<FolderResponseModel>(responses);
throw new NotFoundException();
}
[HttpPost("")]
public async Task<FolderResponseModel> Post([FromBody] FolderRequestModel model)
{
var userId = _userService.GetProperUserId(User).Value;
var folder = model.ToFolder(_userService.GetProperUserId(User).Value);
await _cipherService.SaveFolderAsync(folder);
return new FolderResponseModel(folder);
}
[HttpPut("{id}")]
[HttpPost("{id}")]
public async Task<FolderResponseModel> Put(string id, [FromBody] FolderRequestModel model)
{
var userId = _userService.GetProperUserId(User).Value;
var folder = await _folderRepository.GetByIdAsync(new Guid(id), userId);
if (folder == null)
{
throw new NotFoundException();
}
await _cipherService.SaveFolderAsync(model.ToFolder(folder));
return new FolderResponseModel(folder);
}
[HttpDelete("{id}")]
[HttpPost("{id}/delete")]
public async Task Delete(string id)
{
var userId = _userService.GetProperUserId(User).Value;
var folder = await _folderRepository.GetByIdAsync(new Guid(id), userId);
if (folder == null)
{
throw new NotFoundException();
}
await _cipherService.DeleteFolderAsync(folder);
}
await _cipherService.DeleteFolderAsync(folder);
}
}

View File

@ -7,146 +7,145 @@ using Bit.Core.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Bit.Api.Controllers
namespace Bit.Api.Controllers;
[Route("organizations/{orgId}/groups")]
[Authorize("Application")]
public class GroupsController : Controller
{
[Route("organizations/{orgId}/groups")]
[Authorize("Application")]
public class GroupsController : Controller
private readonly IGroupRepository _groupRepository;
private readonly IGroupService _groupService;
private readonly ICurrentContext _currentContext;
public GroupsController(
IGroupRepository groupRepository,
IGroupService groupService,
ICurrentContext currentContext)
{
private readonly IGroupRepository _groupRepository;
private readonly IGroupService _groupService;
private readonly ICurrentContext _currentContext;
_groupRepository = groupRepository;
_groupService = groupService;
_currentContext = currentContext;
}
public GroupsController(
IGroupRepository groupRepository,
IGroupService groupService,
ICurrentContext currentContext)
[HttpGet("{id}")]
public async Task<GroupResponseModel> Get(string orgId, string id)
{
var group = await _groupRepository.GetByIdAsync(new Guid(id));
if (group == null || !await _currentContext.ManageGroups(group.OrganizationId))
{
_groupRepository = groupRepository;
_groupService = groupService;
_currentContext = currentContext;
throw new NotFoundException();
}
[HttpGet("{id}")]
public async Task<GroupResponseModel> Get(string orgId, string id)
{
var group = await _groupRepository.GetByIdAsync(new Guid(id));
if (group == null || !await _currentContext.ManageGroups(group.OrganizationId))
{
throw new NotFoundException();
}
return new GroupResponseModel(group);
}
return new GroupResponseModel(group);
[HttpGet("{id}/details")]
public async Task<GroupDetailsResponseModel> GetDetails(string orgId, string id)
{
var groupDetails = await _groupRepository.GetByIdWithCollectionsAsync(new Guid(id));
if (groupDetails?.Item1 == null || !await _currentContext.ManageGroups(groupDetails.Item1.OrganizationId))
{
throw new NotFoundException();
}
[HttpGet("{id}/details")]
public async Task<GroupDetailsResponseModel> GetDetails(string orgId, string id)
{
var groupDetails = await _groupRepository.GetByIdWithCollectionsAsync(new Guid(id));
if (groupDetails?.Item1 == null || !await _currentContext.ManageGroups(groupDetails.Item1.OrganizationId))
{
throw new NotFoundException();
}
return new GroupDetailsResponseModel(groupDetails.Item1, groupDetails.Item2);
}
return new GroupDetailsResponseModel(groupDetails.Item1, groupDetails.Item2);
[HttpGet("")]
public async Task<ListResponseModel<GroupResponseModel>> Get(string orgId)
{
var orgIdGuid = new Guid(orgId);
var canAccess = await _currentContext.ManageGroups(orgIdGuid) ||
await _currentContext.ViewAssignedCollections(orgIdGuid) ||
await _currentContext.ViewAllCollections(orgIdGuid) ||
await _currentContext.ManageUsers(orgIdGuid);
if (!canAccess)
{
throw new NotFoundException();
}
[HttpGet("")]
public async Task<ListResponseModel<GroupResponseModel>> Get(string orgId)
var groups = await _groupRepository.GetManyByOrganizationIdAsync(orgIdGuid);
var responses = groups.Select(g => new GroupResponseModel(g));
return new ListResponseModel<GroupResponseModel>(responses);
}
[HttpGet("{id}/users")]
public async Task<IEnumerable<Guid>> GetUsers(string orgId, string id)
{
var idGuid = new Guid(id);
var group = await _groupRepository.GetByIdAsync(idGuid);
if (group == null || !await _currentContext.ManageGroups(group.OrganizationId))
{
var orgIdGuid = new Guid(orgId);
var canAccess = await _currentContext.ManageGroups(orgIdGuid) ||
await _currentContext.ViewAssignedCollections(orgIdGuid) ||
await _currentContext.ViewAllCollections(orgIdGuid) ||
await _currentContext.ManageUsers(orgIdGuid);
if (!canAccess)
{
throw new NotFoundException();
}
var groups = await _groupRepository.GetManyByOrganizationIdAsync(orgIdGuid);
var responses = groups.Select(g => new GroupResponseModel(g));
return new ListResponseModel<GroupResponseModel>(responses);
throw new NotFoundException();
}
[HttpGet("{id}/users")]
public async Task<IEnumerable<Guid>> GetUsers(string orgId, string id)
{
var idGuid = new Guid(id);
var group = await _groupRepository.GetByIdAsync(idGuid);
if (group == null || !await _currentContext.ManageGroups(group.OrganizationId))
{
throw new NotFoundException();
}
var groupIds = await _groupRepository.GetManyUserIdsByIdAsync(idGuid);
return groupIds;
}
var groupIds = await _groupRepository.GetManyUserIdsByIdAsync(idGuid);
return groupIds;
[HttpPost("")]
public async Task<GroupResponseModel> Post(string orgId, [FromBody] GroupRequestModel model)
{
var orgIdGuid = new Guid(orgId);
if (!await _currentContext.ManageGroups(orgIdGuid))
{
throw new NotFoundException();
}
[HttpPost("")]
public async Task<GroupResponseModel> Post(string orgId, [FromBody] GroupRequestModel model)
{
var orgIdGuid = new Guid(orgId);
if (!await _currentContext.ManageGroups(orgIdGuid))
{
throw new NotFoundException();
}
var group = model.ToGroup(orgIdGuid);
await _groupService.SaveAsync(group, model.Collections?.Select(c => c.ToSelectionReadOnly()));
return new GroupResponseModel(group);
}
var group = model.ToGroup(orgIdGuid);
await _groupService.SaveAsync(group, model.Collections?.Select(c => c.ToSelectionReadOnly()));
return new GroupResponseModel(group);
[HttpPut("{id}")]
[HttpPost("{id}")]
public async Task<GroupResponseModel> Put(string orgId, string id, [FromBody] GroupRequestModel model)
{
var group = await _groupRepository.GetByIdAsync(new Guid(id));
if (group == null || !await _currentContext.ManageGroups(group.OrganizationId))
{
throw new NotFoundException();
}
[HttpPut("{id}")]
[HttpPost("{id}")]
public async Task<GroupResponseModel> Put(string orgId, string id, [FromBody] GroupRequestModel model)
{
var group = await _groupRepository.GetByIdAsync(new Guid(id));
if (group == null || !await _currentContext.ManageGroups(group.OrganizationId))
{
throw new NotFoundException();
}
await _groupService.SaveAsync(model.ToGroup(group), model.Collections?.Select(c => c.ToSelectionReadOnly()));
return new GroupResponseModel(group);
}
await _groupService.SaveAsync(model.ToGroup(group), model.Collections?.Select(c => c.ToSelectionReadOnly()));
return new GroupResponseModel(group);
[HttpPut("{id}/users")]
public async Task PutUsers(string orgId, string id, [FromBody] IEnumerable<Guid> model)
{
var group = await _groupRepository.GetByIdAsync(new Guid(id));
if (group == null || !await _currentContext.ManageGroups(group.OrganizationId))
{
throw new NotFoundException();
}
await _groupRepository.UpdateUsersAsync(group.Id, model);
}
[HttpDelete("{id}")]
[HttpPost("{id}/delete")]
public async Task Delete(string orgId, string id)
{
var group = await _groupRepository.GetByIdAsync(new Guid(id));
if (group == null || !await _currentContext.ManageGroups(group.OrganizationId))
{
throw new NotFoundException();
}
[HttpPut("{id}/users")]
public async Task PutUsers(string orgId, string id, [FromBody] IEnumerable<Guid> model)
await _groupService.DeleteAsync(group);
}
[HttpDelete("{id}/user/{orgUserId}")]
[HttpPost("{id}/delete-user/{orgUserId}")]
public async Task Delete(string orgId, string id, string orgUserId)
{
var group = await _groupRepository.GetByIdAsync(new Guid(id));
if (group == null || !await _currentContext.ManageGroups(group.OrganizationId))
{
var group = await _groupRepository.GetByIdAsync(new Guid(id));
if (group == null || !await _currentContext.ManageGroups(group.OrganizationId))
{
throw new NotFoundException();
}
await _groupRepository.UpdateUsersAsync(group.Id, model);
throw new NotFoundException();
}
[HttpDelete("{id}")]
[HttpPost("{id}/delete")]
public async Task Delete(string orgId, string id)
{
var group = await _groupRepository.GetByIdAsync(new Guid(id));
if (group == null || !await _currentContext.ManageGroups(group.OrganizationId))
{
throw new NotFoundException();
}
await _groupService.DeleteAsync(group);
}
[HttpDelete("{id}/user/{orgUserId}")]
[HttpPost("{id}/delete-user/{orgUserId}")]
public async Task Delete(string orgId, string id, string orgUserId)
{
var group = await _groupRepository.GetByIdAsync(new Guid(id));
if (group == null || !await _currentContext.ManageGroups(group.OrganizationId))
{
throw new NotFoundException();
}
await _groupService.DeleteUserAsync(group, new Guid(orgUserId));
}
await _groupService.DeleteUserAsync(group, new Guid(orgUserId));
}
}

View File

@ -8,91 +8,90 @@ using Bit.Core.Utilities;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Bit.Api.Controllers
namespace Bit.Api.Controllers;
[Route("hibp")]
[Authorize("Application")]
public class HibpController : Controller
{
[Route("hibp")]
[Authorize("Application")]
public class HibpController : Controller
private const string HibpBreachApi = "https://haveibeenpwned.com/api/v3/breachedaccount/{0}" +
"?truncateResponse=false&includeUnverified=false";
private static HttpClient _httpClient;
private readonly IUserService _userService;
private readonly ICurrentContext _currentContext;
private readonly GlobalSettings _globalSettings;
private readonly string _userAgent;
static HibpController()
{
private const string HibpBreachApi = "https://haveibeenpwned.com/api/v3/breachedaccount/{0}" +
"?truncateResponse=false&includeUnverified=false";
private static HttpClient _httpClient;
_httpClient = new HttpClient();
}
private readonly IUserService _userService;
private readonly ICurrentContext _currentContext;
private readonly GlobalSettings _globalSettings;
private readonly string _userAgent;
public HibpController(
IUserService userService,
ICurrentContext currentContext,
GlobalSettings globalSettings)
{
_userService = userService;
_currentContext = currentContext;
_globalSettings = globalSettings;
_userAgent = _globalSettings.SelfHosted ? "Bitwarden Self-Hosted" : "Bitwarden";
}
static HibpController()
[HttpGet("breach")]
public async Task<IActionResult> Get(string username)
{
return await SendAsync(WebUtility.UrlEncode(username), true);
}
private async Task<IActionResult> SendAsync(string username, bool retry)
{
if (!CoreHelpers.SettingHasValue(_globalSettings.HibpApiKey))
{
_httpClient = new HttpClient();
throw new BadRequestException("HaveIBeenPwned API key not set.");
}
public HibpController(
IUserService userService,
ICurrentContext currentContext,
GlobalSettings globalSettings)
var request = new HttpRequestMessage(HttpMethod.Get, string.Format(HibpBreachApi, username));
request.Headers.Add("hibp-api-key", _globalSettings.HibpApiKey);
request.Headers.Add("hibp-client-id", GetClientId());
request.Headers.Add("User-Agent", _userAgent);
var response = await _httpClient.SendAsync(request);
if (response.IsSuccessStatusCode)
{
_userService = userService;
_currentContext = currentContext;
_globalSettings = globalSettings;
_userAgent = _globalSettings.SelfHosted ? "Bitwarden Self-Hosted" : "Bitwarden";
var data = await response.Content.ReadAsStringAsync();
return Content(data, "application/json");
}
[HttpGet("breach")]
public async Task<IActionResult> Get(string username)
else if (response.StatusCode == HttpStatusCode.NotFound)
{
return await SendAsync(WebUtility.UrlEncode(username), true);
return new NotFoundResult();
}
private async Task<IActionResult> SendAsync(string username, bool retry)
else if (response.StatusCode == HttpStatusCode.TooManyRequests && retry)
{
if (!CoreHelpers.SettingHasValue(_globalSettings.HibpApiKey))
var delay = 2000;
if (response.Headers.Contains("retry-after"))
{
throw new BadRequestException("HaveIBeenPwned API key not set.");
}
var request = new HttpRequestMessage(HttpMethod.Get, string.Format(HibpBreachApi, username));
request.Headers.Add("hibp-api-key", _globalSettings.HibpApiKey);
request.Headers.Add("hibp-client-id", GetClientId());
request.Headers.Add("User-Agent", _userAgent);
var response = await _httpClient.SendAsync(request);
if (response.IsSuccessStatusCode)
{
var data = await response.Content.ReadAsStringAsync();
return Content(data, "application/json");
}
else if (response.StatusCode == HttpStatusCode.NotFound)
{
return new NotFoundResult();
}
else if (response.StatusCode == HttpStatusCode.TooManyRequests && retry)
{
var delay = 2000;
if (response.Headers.Contains("retry-after"))
var vals = response.Headers.GetValues("retry-after");
if (vals.Any() && int.TryParse(vals.FirstOrDefault(), out var secDelay))
{
var vals = response.Headers.GetValues("retry-after");
if (vals.Any() && int.TryParse(vals.FirstOrDefault(), out var secDelay))
{
delay = (secDelay * 1000) + 200;
}
delay = (secDelay * 1000) + 200;
}
await Task.Delay(delay);
return await SendAsync(username, false);
}
else
{
throw new BadRequestException("Request failed. Status code: " + response.StatusCode);
}
await Task.Delay(delay);
return await SendAsync(username, false);
}
private string GetClientId()
else
{
var userId = _userService.GetProperUserId(User).Value;
using (var sha256 = SHA256.Create())
{
var hash = sha256.ComputeHash(userId.ToByteArray());
return Convert.ToBase64String(hash);
}
throw new BadRequestException("Request failed. Status code: " + response.StatusCode);
}
}
private string GetClientId()
{
var userId = _userService.GetProperUserId(User).Value;
using (var sha256 = SHA256.Create())
{
var hash = sha256.ComputeHash(userId.ToByteArray());
return Convert.ToBase64String(hash);
}
}
}

View File

@ -1,35 +1,34 @@
using Bit.Core.Utilities;
using Microsoft.AspNetCore.Mvc;
namespace Bit.Api.Controllers
namespace Bit.Api.Controllers;
public class InfoController : Controller
{
public class InfoController : Controller
[HttpGet("~/alive")]
[HttpGet("~/now")]
public DateTime GetAlive()
{
[HttpGet("~/alive")]
[HttpGet("~/now")]
public DateTime GetAlive()
{
return DateTime.UtcNow;
}
return DateTime.UtcNow;
}
[HttpGet("~/version")]
public JsonResult GetVersion()
{
return Json(CoreHelpers.GetVersion());
}
[HttpGet("~/version")]
public JsonResult GetVersion()
{
return Json(CoreHelpers.GetVersion());
}
[HttpGet("~/ip")]
public JsonResult Ip()
[HttpGet("~/ip")]
public JsonResult Ip()
{
var headerSet = new HashSet<string> { "x-forwarded-for", "cf-connecting-ip", "client-ip" };
var headers = HttpContext.Request?.Headers
.Where(h => headerSet.Contains(h.Key.ToLower()))
.ToDictionary(h => h.Key);
return new JsonResult(new
{
var headerSet = new HashSet<string> { "x-forwarded-for", "cf-connecting-ip", "client-ip" };
var headers = HttpContext.Request?.Headers
.Where(h => headerSet.Contains(h.Key.ToLower()))
.ToDictionary(h => h.Key);
return new JsonResult(new
{
Ip = HttpContext.Connection?.RemoteIpAddress?.ToString(),
Headers = headers,
});
}
Ip = HttpContext.Connection?.RemoteIpAddress?.ToString(),
Headers = headers,
});
}
}

View File

@ -6,40 +6,39 @@ using Bit.Core.Utilities;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Bit.Api.Controllers
namespace Bit.Api.Controllers;
[Route("installations")]
[SelfHosted(NotSelfHostedOnly = true)]
public class InstallationsController : Controller
{
[Route("installations")]
[SelfHosted(NotSelfHostedOnly = true)]
public class InstallationsController : Controller
private readonly IInstallationRepository _installationRepository;
public InstallationsController(
IInstallationRepository installationRepository)
{
private readonly IInstallationRepository _installationRepository;
_installationRepository = installationRepository;
}
public InstallationsController(
IInstallationRepository installationRepository)
[HttpGet("{id}")]
[AllowAnonymous]
public async Task<InstallationResponseModel> Get(Guid id)
{
var installation = await _installationRepository.GetByIdAsync(id);
if (installation == null)
{
_installationRepository = installationRepository;
throw new NotFoundException();
}
[HttpGet("{id}")]
[AllowAnonymous]
public async Task<InstallationResponseModel> Get(Guid id)
{
var installation = await _installationRepository.GetByIdAsync(id);
if (installation == null)
{
throw new NotFoundException();
}
return new InstallationResponseModel(installation, false);
}
return new InstallationResponseModel(installation, false);
}
[HttpPost("")]
[AllowAnonymous]
public async Task<InstallationResponseModel> Post([FromBody] InstallationRequestModel model)
{
var installation = model.ToInstallation();
await _installationRepository.CreateAsync(installation);
return new InstallationResponseModel(installation, true);
}
[HttpPost("")]
[AllowAnonymous]
public async Task<InstallationResponseModel> Post([FromBody] InstallationRequestModel model)
{
var installation = model.ToInstallation();
await _installationRepository.CreateAsync(installation);
return new InstallationResponseModel(installation, true);
}
}

View File

@ -7,70 +7,69 @@ using Bit.Core.Utilities;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Bit.Api.Controllers
namespace Bit.Api.Controllers;
[Route("licenses")]
[Authorize("Licensing")]
[SelfHosted(NotSelfHostedOnly = true)]
public class LicensesController : Controller
{
[Route("licenses")]
[Authorize("Licensing")]
[SelfHosted(NotSelfHostedOnly = true)]
public class LicensesController : Controller
private readonly ILicensingService _licensingService;
private readonly IUserRepository _userRepository;
private readonly IUserService _userService;
private readonly IOrganizationRepository _organizationRepository;
private readonly IOrganizationService _organizationService;
private readonly ICurrentContext _currentContext;
public LicensesController(
ILicensingService licensingService,
IUserRepository userRepository,
IUserService userService,
IOrganizationRepository organizationRepository,
IOrganizationService organizationService,
ICurrentContext currentContext)
{
private readonly ILicensingService _licensingService;
private readonly IUserRepository _userRepository;
private readonly IUserService _userService;
private readonly IOrganizationRepository _organizationRepository;
private readonly IOrganizationService _organizationService;
private readonly ICurrentContext _currentContext;
_licensingService = licensingService;
_userRepository = userRepository;
_userService = userService;
_organizationRepository = organizationRepository;
_organizationService = organizationService;
_currentContext = currentContext;
}
public LicensesController(
ILicensingService licensingService,
IUserRepository userRepository,
IUserService userService,
IOrganizationRepository organizationRepository,
IOrganizationService organizationService,
ICurrentContext currentContext)
[HttpGet("user/{id}")]
public async Task<UserLicense> GetUser(string id, [FromQuery] string key)
{
var user = await _userRepository.GetByIdAsync(new Guid(id));
if (user == null)
{
_licensingService = licensingService;
_userRepository = userRepository;
_userService = userService;
_organizationRepository = organizationRepository;
_organizationService = organizationService;
_currentContext = currentContext;
return null;
}
else if (!user.LicenseKey.Equals(key))
{
await Task.Delay(2000);
throw new BadRequestException("Invalid license key.");
}
[HttpGet("user/{id}")]
public async Task<UserLicense> GetUser(string id, [FromQuery] string key)
{
var user = await _userRepository.GetByIdAsync(new Guid(id));
if (user == null)
{
return null;
}
else if (!user.LicenseKey.Equals(key))
{
await Task.Delay(2000);
throw new BadRequestException("Invalid license key.");
}
var license = await _userService.GenerateLicenseAsync(user, null);
return license;
}
var license = await _userService.GenerateLicenseAsync(user, null);
return license;
[HttpGet("organization/{id}")]
public async Task<OrganizationLicense> GetOrganization(string id, [FromQuery] string key)
{
var org = await _organizationRepository.GetByIdAsync(new Guid(id));
if (org == null)
{
return null;
}
else if (!org.LicenseKey.Equals(key))
{
await Task.Delay(2000);
throw new BadRequestException("Invalid license key.");
}
[HttpGet("organization/{id}")]
public async Task<OrganizationLicense> GetOrganization(string id, [FromQuery] string key)
{
var org = await _organizationRepository.GetByIdAsync(new Guid(id));
if (org == null)
{
return null;
}
else if (!org.LicenseKey.Equals(key))
{
await Task.Delay(2000);
throw new BadRequestException("Invalid license key.");
}
var license = await _organizationService.GenerateLicenseAsync(org, _currentContext.InstallationId.Value);
return license;
}
var license = await _organizationService.GenerateLicenseAsync(org, _currentContext.InstallationId.Value);
return license;
}
}

View File

@ -5,42 +5,41 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Stripe;
namespace Bit.Api.Controllers
namespace Bit.Api.Controllers;
public class MiscController : Controller
{
public class MiscController : Controller
private readonly BitPayClient _bitPayClient;
private readonly GlobalSettings _globalSettings;
public MiscController(
BitPayClient bitPayClient,
GlobalSettings globalSettings)
{
private readonly BitPayClient _bitPayClient;
private readonly GlobalSettings _globalSettings;
_bitPayClient = bitPayClient;
_globalSettings = globalSettings;
}
public MiscController(
BitPayClient bitPayClient,
GlobalSettings globalSettings)
{
_bitPayClient = bitPayClient;
_globalSettings = globalSettings;
}
[Authorize("Application")]
[HttpPost("~/bitpay-invoice")]
[SelfHosted(NotSelfHostedOnly = true)]
public async Task<string> PostBitPayInvoice([FromBody] BitPayInvoiceRequestModel model)
{
var invoice = await _bitPayClient.CreateInvoiceAsync(model.ToBitpayInvoice(_globalSettings));
return invoice.Url;
}
[Authorize("Application")]
[HttpPost("~/bitpay-invoice")]
[SelfHosted(NotSelfHostedOnly = true)]
public async Task<string> PostBitPayInvoice([FromBody] BitPayInvoiceRequestModel model)
[Authorize("Application")]
[HttpPost("~/setup-payment")]
[SelfHosted(NotSelfHostedOnly = true)]
public async Task<string> PostSetupPayment()
{
var options = new SetupIntentCreateOptions
{
var invoice = await _bitPayClient.CreateInvoiceAsync(model.ToBitpayInvoice(_globalSettings));
return invoice.Url;
}
[Authorize("Application")]
[HttpPost("~/setup-payment")]
[SelfHosted(NotSelfHostedOnly = true)]
public async Task<string> PostSetupPayment()
{
var options = new SetupIntentCreateOptions
{
Usage = "off_session"
};
var service = new SetupIntentService();
var setupIntent = await service.CreateAsync(options);
return setupIntent.ClientSecret;
}
Usage = "off_session"
};
var service = new SetupIntentService();
var setupIntent = await service.CreateAsync(options);
return setupIntent.ClientSecret;
}
}

View File

@ -12,199 +12,198 @@ using Bit.Core.Settings;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Bit.Api.Controllers
namespace Bit.Api.Controllers;
[Authorize("Application")]
[Route("organizations/connections")]
public class OrganizationConnectionsController : Controller
{
[Authorize("Application")]
[Route("organizations/connections")]
public class OrganizationConnectionsController : Controller
private readonly ICreateOrganizationConnectionCommand _createOrganizationConnectionCommand;
private readonly IUpdateOrganizationConnectionCommand _updateOrganizationConnectionCommand;
private readonly IDeleteOrganizationConnectionCommand _deleteOrganizationConnectionCommand;
private readonly IOrganizationConnectionRepository _organizationConnectionRepository;
private readonly ICurrentContext _currentContext;
private readonly IGlobalSettings _globalSettings;
private readonly ILicensingService _licensingService;
public OrganizationConnectionsController(
ICreateOrganizationConnectionCommand createOrganizationConnectionCommand,
IUpdateOrganizationConnectionCommand updateOrganizationConnectionCommand,
IDeleteOrganizationConnectionCommand deleteOrganizationConnectionCommand,
IOrganizationConnectionRepository organizationConnectionRepository,
ICurrentContext currentContext,
IGlobalSettings globalSettings,
ILicensingService licensingService)
{
private readonly ICreateOrganizationConnectionCommand _createOrganizationConnectionCommand;
private readonly IUpdateOrganizationConnectionCommand _updateOrganizationConnectionCommand;
private readonly IDeleteOrganizationConnectionCommand _deleteOrganizationConnectionCommand;
private readonly IOrganizationConnectionRepository _organizationConnectionRepository;
private readonly ICurrentContext _currentContext;
private readonly IGlobalSettings _globalSettings;
private readonly ILicensingService _licensingService;
_createOrganizationConnectionCommand = createOrganizationConnectionCommand;
_updateOrganizationConnectionCommand = updateOrganizationConnectionCommand;
_deleteOrganizationConnectionCommand = deleteOrganizationConnectionCommand;
_organizationConnectionRepository = organizationConnectionRepository;
_currentContext = currentContext;
_globalSettings = globalSettings;
_licensingService = licensingService;
}
public OrganizationConnectionsController(
ICreateOrganizationConnectionCommand createOrganizationConnectionCommand,
IUpdateOrganizationConnectionCommand updateOrganizationConnectionCommand,
IDeleteOrganizationConnectionCommand deleteOrganizationConnectionCommand,
IOrganizationConnectionRepository organizationConnectionRepository,
ICurrentContext currentContext,
IGlobalSettings globalSettings,
ILicensingService licensingService)
[HttpGet("enabled")]
public bool ConnectionsEnabled()
{
return _globalSettings.SelfHosted && _globalSettings.EnableCloudCommunication;
}
[HttpPost]
public async Task<OrganizationConnectionResponseModel> CreateConnection([FromBody] OrganizationConnectionRequestModel model)
{
if (!await HasPermissionAsync(model?.OrganizationId))
{
_createOrganizationConnectionCommand = createOrganizationConnectionCommand;
_updateOrganizationConnectionCommand = updateOrganizationConnectionCommand;
_deleteOrganizationConnectionCommand = deleteOrganizationConnectionCommand;
_organizationConnectionRepository = organizationConnectionRepository;
_currentContext = currentContext;
_globalSettings = globalSettings;
_licensingService = licensingService;
throw new BadRequestException($"You do not have permission to create a connection of type {model.Type}.");
}
[HttpGet("enabled")]
public bool ConnectionsEnabled()
if (await HasConnectionTypeAsync(model, null, model.Type))
{
return _globalSettings.SelfHosted && _globalSettings.EnableCloudCommunication;
throw new BadRequestException($"The requested organization already has a connection of type {model.Type}. Only one of each connection type may exist per organization.");
}
[HttpPost]
public async Task<OrganizationConnectionResponseModel> CreateConnection([FromBody] OrganizationConnectionRequestModel model)
switch (model.Type)
{
if (!await HasPermissionAsync(model?.OrganizationId))
{
throw new BadRequestException($"You do not have permission to create a connection of type {model.Type}.");
}
if (await HasConnectionTypeAsync(model, null, model.Type))
{
throw new BadRequestException($"The requested organization already has a connection of type {model.Type}. Only one of each connection type may exist per organization.");
}
switch (model.Type)
{
case OrganizationConnectionType.CloudBillingSync:
return await CreateOrUpdateOrganizationConnectionAsync<BillingSyncConfig>(null, model, ValidateBillingSyncConfig);
case OrganizationConnectionType.Scim:
return await CreateOrUpdateOrganizationConnectionAsync<ScimConfig>(null, model);
default:
throw new BadRequestException($"Unknown Organization connection Type: {model.Type}");
}
}
[HttpPut("{organizationConnectionId}")]
public async Task<OrganizationConnectionResponseModel> UpdateConnection(Guid organizationConnectionId, [FromBody] OrganizationConnectionRequestModel model)
{
var existingOrganizationConnection = await _organizationConnectionRepository.GetByIdAsync(organizationConnectionId);
if (existingOrganizationConnection == null)
{
throw new NotFoundException();
}
if (!await HasPermissionAsync(model?.OrganizationId, model?.Type))
{
throw new BadRequestException("You do not have permission to update this connection.");
}
if (await HasConnectionTypeAsync(model, organizationConnectionId, model.Type))
{
throw new BadRequestException($"The requested organization already has a connection of type {model.Type}. Only one of each connection type may exist per organization.");
}
switch (model.Type)
{
case OrganizationConnectionType.CloudBillingSync:
return await CreateOrUpdateOrganizationConnectionAsync<BillingSyncConfig>(organizationConnectionId, model);
case OrganizationConnectionType.Scim:
return await CreateOrUpdateOrganizationConnectionAsync<ScimConfig>(organizationConnectionId, model);
default:
throw new BadRequestException($"Unkown Organization connection Type: {model.Type}");
}
}
[HttpGet("{organizationId}/{type}")]
public async Task<OrganizationConnectionResponseModel> GetConnection(Guid organizationId, OrganizationConnectionType type)
{
if (!await HasPermissionAsync(organizationId, type))
{
throw new BadRequestException($"You do not have permission to retrieve a connection of type {type}.");
}
var connections = await GetConnectionsAsync(organizationId, type);
var connection = connections.FirstOrDefault(c => c.Type == type);
switch (type)
{
case OrganizationConnectionType.CloudBillingSync:
if (!_globalSettings.SelfHosted)
{
throw new BadRequestException($"Cannot get a {type} connection outside of a self-hosted instance.");
}
return new OrganizationConnectionResponseModel(connection, typeof(BillingSyncConfig));
case OrganizationConnectionType.Scim:
return new OrganizationConnectionResponseModel(connection, typeof(ScimConfig));
default:
throw new BadRequestException($"Unkown Organization connection Type: {type}");
}
}
[HttpDelete("{organizationConnectionId}")]
[HttpPost("{organizationConnectionId}/delete")]
public async Task DeleteConnection(Guid organizationConnectionId)
{
var connection = await _organizationConnectionRepository.GetByIdAsync(organizationConnectionId);
if (connection == null)
{
throw new NotFoundException();
}
if (!await HasPermissionAsync(connection.OrganizationId, connection.Type))
{
throw new BadRequestException($"You do not have permission to remove this connection of type {connection.Type}.");
}
await _deleteOrganizationConnectionCommand.DeleteAsync(connection);
}
private async Task<ICollection<OrganizationConnection>> GetConnectionsAsync(Guid organizationId, OrganizationConnectionType type) =>
await _organizationConnectionRepository.GetByOrganizationIdTypeAsync(organizationId, type);
private async Task<bool> HasConnectionTypeAsync(OrganizationConnectionRequestModel model, Guid? connectionId,
OrganizationConnectionType type)
{
var existingConnections = await GetConnectionsAsync(model.OrganizationId, type);
return existingConnections.Any(c => c.Type == model.Type && (!connectionId.HasValue || c.Id != connectionId.Value));
}
private async Task<bool> HasPermissionAsync(Guid? organizationId, OrganizationConnectionType? type = null)
{
if (!organizationId.HasValue)
{
return false;
}
return type switch
{
OrganizationConnectionType.Scim => await _currentContext.ManageScim(organizationId.Value),
_ => await _currentContext.OrganizationOwner(organizationId.Value),
};
}
private async Task ValidateBillingSyncConfig(OrganizationConnectionRequestModel<BillingSyncConfig> typedModel)
{
if (!_globalSettings.SelfHosted)
{
throw new BadRequestException($"Cannot create a {typedModel.Type} connection outside of a self-hosted instance.");
}
var license = await _licensingService.ReadOrganizationLicenseAsync(typedModel.OrganizationId);
if (!_licensingService.VerifyLicense(license))
{
throw new BadRequestException("Cannot verify license file.");
}
typedModel.ParsedConfig.CloudOrganizationId = license.Id;
}
private async Task<OrganizationConnectionResponseModel> CreateOrUpdateOrganizationConnectionAsync<T>(
Guid? organizationConnectionId,
OrganizationConnectionRequestModel model,
Func<OrganizationConnectionRequestModel<T>, Task> validateAction = null)
where T : new()
{
var typedModel = new OrganizationConnectionRequestModel<T>(model);
if (validateAction != null)
{
await validateAction(typedModel);
}
var data = typedModel.ToData(organizationConnectionId);
var connection = organizationConnectionId.HasValue
? await _updateOrganizationConnectionCommand.UpdateAsync(data)
: await _createOrganizationConnectionCommand.CreateAsync(data);
return new OrganizationConnectionResponseModel(connection, typeof(T));
case OrganizationConnectionType.CloudBillingSync:
return await CreateOrUpdateOrganizationConnectionAsync<BillingSyncConfig>(null, model, ValidateBillingSyncConfig);
case OrganizationConnectionType.Scim:
return await CreateOrUpdateOrganizationConnectionAsync<ScimConfig>(null, model);
default:
throw new BadRequestException($"Unknown Organization connection Type: {model.Type}");
}
}
[HttpPut("{organizationConnectionId}")]
public async Task<OrganizationConnectionResponseModel> UpdateConnection(Guid organizationConnectionId, [FromBody] OrganizationConnectionRequestModel model)
{
var existingOrganizationConnection = await _organizationConnectionRepository.GetByIdAsync(organizationConnectionId);
if (existingOrganizationConnection == null)
{
throw new NotFoundException();
}
if (!await HasPermissionAsync(model?.OrganizationId, model?.Type))
{
throw new BadRequestException("You do not have permission to update this connection.");
}
if (await HasConnectionTypeAsync(model, organizationConnectionId, model.Type))
{
throw new BadRequestException($"The requested organization already has a connection of type {model.Type}. Only one of each connection type may exist per organization.");
}
switch (model.Type)
{
case OrganizationConnectionType.CloudBillingSync:
return await CreateOrUpdateOrganizationConnectionAsync<BillingSyncConfig>(organizationConnectionId, model);
case OrganizationConnectionType.Scim:
return await CreateOrUpdateOrganizationConnectionAsync<ScimConfig>(organizationConnectionId, model);
default:
throw new BadRequestException($"Unkown Organization connection Type: {model.Type}");
}
}
[HttpGet("{organizationId}/{type}")]
public async Task<OrganizationConnectionResponseModel> GetConnection(Guid organizationId, OrganizationConnectionType type)
{
if (!await HasPermissionAsync(organizationId, type))
{
throw new BadRequestException($"You do not have permission to retrieve a connection of type {type}.");
}
var connections = await GetConnectionsAsync(organizationId, type);
var connection = connections.FirstOrDefault(c => c.Type == type);
switch (type)
{
case OrganizationConnectionType.CloudBillingSync:
if (!_globalSettings.SelfHosted)
{
throw new BadRequestException($"Cannot get a {type} connection outside of a self-hosted instance.");
}
return new OrganizationConnectionResponseModel(connection, typeof(BillingSyncConfig));
case OrganizationConnectionType.Scim:
return new OrganizationConnectionResponseModel(connection, typeof(ScimConfig));
default:
throw new BadRequestException($"Unkown Organization connection Type: {type}");
}
}
[HttpDelete("{organizationConnectionId}")]
[HttpPost("{organizationConnectionId}/delete")]
public async Task DeleteConnection(Guid organizationConnectionId)
{
var connection = await _organizationConnectionRepository.GetByIdAsync(organizationConnectionId);
if (connection == null)
{
throw new NotFoundException();
}
if (!await HasPermissionAsync(connection.OrganizationId, connection.Type))
{
throw new BadRequestException($"You do not have permission to remove this connection of type {connection.Type}.");
}
await _deleteOrganizationConnectionCommand.DeleteAsync(connection);
}
private async Task<ICollection<OrganizationConnection>> GetConnectionsAsync(Guid organizationId, OrganizationConnectionType type) =>
await _organizationConnectionRepository.GetByOrganizationIdTypeAsync(organizationId, type);
private async Task<bool> HasConnectionTypeAsync(OrganizationConnectionRequestModel model, Guid? connectionId,
OrganizationConnectionType type)
{
var existingConnections = await GetConnectionsAsync(model.OrganizationId, type);
return existingConnections.Any(c => c.Type == model.Type && (!connectionId.HasValue || c.Id != connectionId.Value));
}
private async Task<bool> HasPermissionAsync(Guid? organizationId, OrganizationConnectionType? type = null)
{
if (!organizationId.HasValue)
{
return false;
}
return type switch
{
OrganizationConnectionType.Scim => await _currentContext.ManageScim(organizationId.Value),
_ => await _currentContext.OrganizationOwner(organizationId.Value),
};
}
private async Task ValidateBillingSyncConfig(OrganizationConnectionRequestModel<BillingSyncConfig> typedModel)
{
if (!_globalSettings.SelfHosted)
{
throw new BadRequestException($"Cannot create a {typedModel.Type} connection outside of a self-hosted instance.");
}
var license = await _licensingService.ReadOrganizationLicenseAsync(typedModel.OrganizationId);
if (!_licensingService.VerifyLicense(license))
{
throw new BadRequestException("Cannot verify license file.");
}
typedModel.ParsedConfig.CloudOrganizationId = license.Id;
}
private async Task<OrganizationConnectionResponseModel> CreateOrUpdateOrganizationConnectionAsync<T>(
Guid? organizationConnectionId,
OrganizationConnectionRequestModel model,
Func<OrganizationConnectionRequestModel<T>, Task> validateAction = null)
where T : new()
{
var typedModel = new OrganizationConnectionRequestModel<T>(model);
if (validateAction != null)
{
await validateAction(typedModel);
}
var data = typedModel.ToData(organizationConnectionId);
var connection = organizationConnectionId.HasValue
? await _updateOrganizationConnectionCommand.UpdateAsync(data)
: await _createOrganizationConnectionCommand.CreateAsync(data);
return new OrganizationConnectionResponseModel(connection, typeof(T));
}
}

View File

@ -6,59 +6,58 @@ using Core.Models.Data;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Bit.Api.Controllers
namespace Bit.Api.Controllers;
[Route("organizations/{organizationId}")]
[Authorize("Application")]
public class OrganizationExportController : Controller
{
[Route("organizations/{organizationId}")]
[Authorize("Application")]
public class OrganizationExportController : Controller
private readonly IUserService _userService;
private readonly ICollectionService _collectionService;
private readonly ICipherService _cipherService;
private readonly GlobalSettings _globalSettings;
public OrganizationExportController(
ICipherService cipherService,
ICollectionService collectionService,
IUserService userService,
GlobalSettings globalSettings)
{
private readonly IUserService _userService;
private readonly ICollectionService _collectionService;
private readonly ICipherService _cipherService;
private readonly GlobalSettings _globalSettings;
_cipherService = cipherService;
_collectionService = collectionService;
_userService = userService;
_globalSettings = globalSettings;
}
public OrganizationExportController(
ICipherService cipherService,
ICollectionService collectionService,
IUserService userService,
GlobalSettings globalSettings)
[HttpGet("export")]
public async Task<OrganizationExportResponseModel> Export(Guid organizationId)
{
var userId = _userService.GetProperUserId(User).Value;
IEnumerable<Collection> orgCollections = await _collectionService.GetOrganizationCollections(organizationId);
(IEnumerable<CipherOrganizationDetails> orgCiphers, Dictionary<Guid, IGrouping<Guid, CollectionCipher>> collectionCiphersGroupDict) = await _cipherService.GetOrganizationCiphers(userId, organizationId);
var result = new OrganizationExportResponseModel
{
_cipherService = cipherService;
_collectionService = collectionService;
_userService = userService;
_globalSettings = globalSettings;
}
Collections = GetOrganizationCollectionsResponse(orgCollections),
Ciphers = GetOrganizationCiphersResponse(orgCiphers, collectionCiphersGroupDict)
};
[HttpGet("export")]
public async Task<OrganizationExportResponseModel> Export(Guid organizationId)
{
var userId = _userService.GetProperUserId(User).Value;
return result;
}
IEnumerable<Collection> orgCollections = await _collectionService.GetOrganizationCollections(organizationId);
(IEnumerable<CipherOrganizationDetails> orgCiphers, Dictionary<Guid, IGrouping<Guid, CollectionCipher>> collectionCiphersGroupDict) = await _cipherService.GetOrganizationCiphers(userId, organizationId);
private ListResponseModel<CollectionResponseModel> GetOrganizationCollectionsResponse(IEnumerable<Collection> orgCollections)
{
var collections = orgCollections.Select(c => new CollectionResponseModel(c));
return new ListResponseModel<CollectionResponseModel>(collections);
}
var result = new OrganizationExportResponseModel
{
Collections = GetOrganizationCollectionsResponse(orgCollections),
Ciphers = GetOrganizationCiphersResponse(orgCiphers, collectionCiphersGroupDict)
};
private ListResponseModel<CipherMiniDetailsResponseModel> GetOrganizationCiphersResponse(IEnumerable<CipherOrganizationDetails> orgCiphers,
Dictionary<Guid, IGrouping<Guid, CollectionCipher>> collectionCiphersGroupDict)
{
var responses = orgCiphers.Select(c => new CipherMiniDetailsResponseModel(c, _globalSettings,
collectionCiphersGroupDict, c.OrganizationUseTotp));
return result;
}
private ListResponseModel<CollectionResponseModel> GetOrganizationCollectionsResponse(IEnumerable<Collection> orgCollections)
{
var collections = orgCollections.Select(c => new CollectionResponseModel(c));
return new ListResponseModel<CollectionResponseModel>(collections);
}
private ListResponseModel<CipherMiniDetailsResponseModel> GetOrganizationCiphersResponse(IEnumerable<CipherOrganizationDetails> orgCiphers,
Dictionary<Guid, IGrouping<Guid, CollectionCipher>> collectionCiphersGroupDict)
{
var responses = orgCiphers.Select(c => new CipherMiniDetailsResponseModel(c, _globalSettings,
collectionCiphersGroupDict, c.OrganizationUseTotp));
return new ListResponseModel<CipherMiniDetailsResponseModel>(responses);
}
return new ListResponseModel<CipherMiniDetailsResponseModel>(responses);
}
}

View File

@ -12,180 +12,179 @@ using Bit.Core.Utilities;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Bit.Api.Controllers
namespace Bit.Api.Controllers;
[Route("organization/sponsorship")]
public class OrganizationSponsorshipsController : Controller
{
[Route("organization/sponsorship")]
public class OrganizationSponsorshipsController : Controller
private readonly IOrganizationSponsorshipRepository _organizationSponsorshipRepository;
private readonly IOrganizationRepository _organizationRepository;
private readonly IOrganizationUserRepository _organizationUserRepository;
private readonly IValidateRedemptionTokenCommand _validateRedemptionTokenCommand;
private readonly IValidateBillingSyncKeyCommand _validateBillingSyncKeyCommand;
private readonly ICreateSponsorshipCommand _createSponsorshipCommand;
private readonly ISendSponsorshipOfferCommand _sendSponsorshipOfferCommand;
private readonly ISetUpSponsorshipCommand _setUpSponsorshipCommand;
private readonly IRevokeSponsorshipCommand _revokeSponsorshipCommand;
private readonly IRemoveSponsorshipCommand _removeSponsorshipCommand;
private readonly ICloudSyncSponsorshipsCommand _syncSponsorshipsCommand;
private readonly ICurrentContext _currentContext;
private readonly IUserService _userService;
public OrganizationSponsorshipsController(
IOrganizationSponsorshipRepository organizationSponsorshipRepository,
IOrganizationRepository organizationRepository,
IOrganizationUserRepository organizationUserRepository,
IValidateRedemptionTokenCommand validateRedemptionTokenCommand,
IValidateBillingSyncKeyCommand validateBillingSyncKeyCommand,
ICreateSponsorshipCommand createSponsorshipCommand,
ISendSponsorshipOfferCommand sendSponsorshipOfferCommand,
ISetUpSponsorshipCommand setUpSponsorshipCommand,
IRevokeSponsorshipCommand revokeSponsorshipCommand,
IRemoveSponsorshipCommand removeSponsorshipCommand,
ICloudSyncSponsorshipsCommand syncSponsorshipsCommand,
IUserService userService,
ICurrentContext currentContext)
{
private readonly IOrganizationSponsorshipRepository _organizationSponsorshipRepository;
private readonly IOrganizationRepository _organizationRepository;
private readonly IOrganizationUserRepository _organizationUserRepository;
private readonly IValidateRedemptionTokenCommand _validateRedemptionTokenCommand;
private readonly IValidateBillingSyncKeyCommand _validateBillingSyncKeyCommand;
private readonly ICreateSponsorshipCommand _createSponsorshipCommand;
private readonly ISendSponsorshipOfferCommand _sendSponsorshipOfferCommand;
private readonly ISetUpSponsorshipCommand _setUpSponsorshipCommand;
private readonly IRevokeSponsorshipCommand _revokeSponsorshipCommand;
private readonly IRemoveSponsorshipCommand _removeSponsorshipCommand;
private readonly ICloudSyncSponsorshipsCommand _syncSponsorshipsCommand;
private readonly ICurrentContext _currentContext;
private readonly IUserService _userService;
public OrganizationSponsorshipsController(
IOrganizationSponsorshipRepository organizationSponsorshipRepository,
IOrganizationRepository organizationRepository,
IOrganizationUserRepository organizationUserRepository,
IValidateRedemptionTokenCommand validateRedemptionTokenCommand,
IValidateBillingSyncKeyCommand validateBillingSyncKeyCommand,
ICreateSponsorshipCommand createSponsorshipCommand,
ISendSponsorshipOfferCommand sendSponsorshipOfferCommand,
ISetUpSponsorshipCommand setUpSponsorshipCommand,
IRevokeSponsorshipCommand revokeSponsorshipCommand,
IRemoveSponsorshipCommand removeSponsorshipCommand,
ICloudSyncSponsorshipsCommand syncSponsorshipsCommand,
IUserService userService,
ICurrentContext currentContext)
{
_organizationSponsorshipRepository = organizationSponsorshipRepository;
_organizationRepository = organizationRepository;
_organizationUserRepository = organizationUserRepository;
_validateRedemptionTokenCommand = validateRedemptionTokenCommand;
_validateBillingSyncKeyCommand = validateBillingSyncKeyCommand;
_createSponsorshipCommand = createSponsorshipCommand;
_sendSponsorshipOfferCommand = sendSponsorshipOfferCommand;
_setUpSponsorshipCommand = setUpSponsorshipCommand;
_revokeSponsorshipCommand = revokeSponsorshipCommand;
_removeSponsorshipCommand = removeSponsorshipCommand;
_syncSponsorshipsCommand = syncSponsorshipsCommand;
_userService = userService;
_currentContext = currentContext;
}
[Authorize("Application")]
[HttpPost("{sponsoringOrgId}/families-for-enterprise")]
[SelfHosted(NotSelfHostedOnly = true)]
public async Task CreateSponsorship(Guid sponsoringOrgId, [FromBody] OrganizationSponsorshipCreateRequestModel model)
{
var sponsoringOrg = await _organizationRepository.GetByIdAsync(sponsoringOrgId);
var sponsorship = await _createSponsorshipCommand.CreateSponsorshipAsync(
sponsoringOrg,
await _organizationUserRepository.GetByOrganizationAsync(sponsoringOrgId, _currentContext.UserId ?? default),
model.PlanSponsorshipType, model.SponsoredEmail, model.FriendlyName);
await _sendSponsorshipOfferCommand.SendSponsorshipOfferAsync(sponsorship, sponsoringOrg.Name);
}
[Authorize("Application")]
[HttpPost("{sponsoringOrgId}/families-for-enterprise/resend")]
[SelfHosted(NotSelfHostedOnly = true)]
public async Task ResendSponsorshipOffer(Guid sponsoringOrgId)
{
var sponsoringOrgUser = await _organizationUserRepository
.GetByOrganizationAsync(sponsoringOrgId, _currentContext.UserId ?? default);
await _sendSponsorshipOfferCommand.SendSponsorshipOfferAsync(
await _organizationRepository.GetByIdAsync(sponsoringOrgId),
sponsoringOrgUser,
await _organizationSponsorshipRepository
.GetBySponsoringOrganizationUserIdAsync(sponsoringOrgUser.Id));
}
[Authorize("Application")]
[HttpPost("validate-token")]
[SelfHosted(NotSelfHostedOnly = true)]
public async Task<bool> PreValidateSponsorshipToken([FromQuery] string sponsorshipToken)
{
return (await _validateRedemptionTokenCommand.ValidateRedemptionTokenAsync(sponsorshipToken, (await CurrentUser).Email)).valid;
}
[Authorize("Application")]
[HttpPost("redeem")]
[SelfHosted(NotSelfHostedOnly = true)]
public async Task RedeemSponsorship([FromQuery] string sponsorshipToken, [FromBody] OrganizationSponsorshipRedeemRequestModel model)
{
var (valid, sponsorship) = await _validateRedemptionTokenCommand.ValidateRedemptionTokenAsync(sponsorshipToken, (await CurrentUser).Email);
if (!valid)
{
throw new BadRequestException("Failed to parse sponsorship token.");
}
if (!await _currentContext.OrganizationOwner(model.SponsoredOrganizationId))
{
throw new BadRequestException("Can only redeem sponsorship for an organization you own.");
}
await _setUpSponsorshipCommand.SetUpSponsorshipAsync(
sponsorship,
await _organizationRepository.GetByIdAsync(model.SponsoredOrganizationId));
}
[Authorize("Installation")]
[HttpPost("sync")]
public async Task<OrganizationSponsorshipSyncResponseModel> Sync([FromBody] OrganizationSponsorshipSyncRequestModel model)
{
var sponsoringOrg = await _organizationRepository.GetByIdAsync(model.SponsoringOrganizationCloudId);
if (!await _validateBillingSyncKeyCommand.ValidateBillingSyncKeyAsync(sponsoringOrg, model.BillingSyncKey))
{
throw new BadRequestException("Invalid Billing Sync Key");
}
var (syncResponseData, offersToSend) = await _syncSponsorshipsCommand.SyncOrganization(sponsoringOrg, model.ToOrganizationSponsorshipSync().SponsorshipsBatch);
await _sendSponsorshipOfferCommand.BulkSendSponsorshipOfferAsync(sponsoringOrg.Name, offersToSend);
return new OrganizationSponsorshipSyncResponseModel(syncResponseData);
}
[Authorize("Application")]
[HttpDelete("{sponsoringOrganizationId}")]
[HttpPost("{sponsoringOrganizationId}/delete")]
[SelfHosted(NotSelfHostedOnly = true)]
public async Task RevokeSponsorship(Guid sponsoringOrganizationId)
{
var orgUser = await _organizationUserRepository.GetByOrganizationAsync(sponsoringOrganizationId, _currentContext.UserId ?? default);
if (_currentContext.UserId != orgUser?.UserId)
{
throw new BadRequestException("Can only revoke a sponsorship you granted.");
}
var existingOrgSponsorship = await _organizationSponsorshipRepository
.GetBySponsoringOrganizationUserIdAsync(orgUser.Id);
await _revokeSponsorshipCommand.RevokeSponsorshipAsync(existingOrgSponsorship);
}
[Authorize("Application")]
[HttpDelete("sponsored/{sponsoredOrgId}")]
[HttpPost("sponsored/{sponsoredOrgId}/remove")]
[SelfHosted(NotSelfHostedOnly = true)]
public async Task RemoveSponsorship(Guid sponsoredOrgId)
{
if (!await _currentContext.OrganizationOwner(sponsoredOrgId))
{
throw new BadRequestException("Only the owner of an organization can remove sponsorship.");
}
var existingOrgSponsorship = await _organizationSponsorshipRepository
.GetBySponsoredOrganizationIdAsync(sponsoredOrgId);
await _removeSponsorshipCommand.RemoveSponsorshipAsync(existingOrgSponsorship);
}
[HttpGet("{sponsoringOrgId}/sync-status")]
public async Task<object> GetSyncStatus(Guid sponsoringOrgId)
{
var sponsoringOrg = await _organizationRepository.GetByIdAsync(sponsoringOrgId);
if (!await _currentContext.OrganizationOwner(sponsoringOrg.Id))
{
throw new NotFoundException();
}
var lastSyncDate = await _organizationSponsorshipRepository.GetLatestSyncDateBySponsoringOrganizationIdAsync(sponsoringOrg.Id);
return new OrganizationSponsorshipSyncStatusResponseModel(lastSyncDate);
}
private Task<User> CurrentUser => _userService.GetUserByIdAsync(_currentContext.UserId.Value);
_organizationSponsorshipRepository = organizationSponsorshipRepository;
_organizationRepository = organizationRepository;
_organizationUserRepository = organizationUserRepository;
_validateRedemptionTokenCommand = validateRedemptionTokenCommand;
_validateBillingSyncKeyCommand = validateBillingSyncKeyCommand;
_createSponsorshipCommand = createSponsorshipCommand;
_sendSponsorshipOfferCommand = sendSponsorshipOfferCommand;
_setUpSponsorshipCommand = setUpSponsorshipCommand;
_revokeSponsorshipCommand = revokeSponsorshipCommand;
_removeSponsorshipCommand = removeSponsorshipCommand;
_syncSponsorshipsCommand = syncSponsorshipsCommand;
_userService = userService;
_currentContext = currentContext;
}
[Authorize("Application")]
[HttpPost("{sponsoringOrgId}/families-for-enterprise")]
[SelfHosted(NotSelfHostedOnly = true)]
public async Task CreateSponsorship(Guid sponsoringOrgId, [FromBody] OrganizationSponsorshipCreateRequestModel model)
{
var sponsoringOrg = await _organizationRepository.GetByIdAsync(sponsoringOrgId);
var sponsorship = await _createSponsorshipCommand.CreateSponsorshipAsync(
sponsoringOrg,
await _organizationUserRepository.GetByOrganizationAsync(sponsoringOrgId, _currentContext.UserId ?? default),
model.PlanSponsorshipType, model.SponsoredEmail, model.FriendlyName);
await _sendSponsorshipOfferCommand.SendSponsorshipOfferAsync(sponsorship, sponsoringOrg.Name);
}
[Authorize("Application")]
[HttpPost("{sponsoringOrgId}/families-for-enterprise/resend")]
[SelfHosted(NotSelfHostedOnly = true)]
public async Task ResendSponsorshipOffer(Guid sponsoringOrgId)
{
var sponsoringOrgUser = await _organizationUserRepository
.GetByOrganizationAsync(sponsoringOrgId, _currentContext.UserId ?? default);
await _sendSponsorshipOfferCommand.SendSponsorshipOfferAsync(
await _organizationRepository.GetByIdAsync(sponsoringOrgId),
sponsoringOrgUser,
await _organizationSponsorshipRepository
.GetBySponsoringOrganizationUserIdAsync(sponsoringOrgUser.Id));
}
[Authorize("Application")]
[HttpPost("validate-token")]
[SelfHosted(NotSelfHostedOnly = true)]
public async Task<bool> PreValidateSponsorshipToken([FromQuery] string sponsorshipToken)
{
return (await _validateRedemptionTokenCommand.ValidateRedemptionTokenAsync(sponsorshipToken, (await CurrentUser).Email)).valid;
}
[Authorize("Application")]
[HttpPost("redeem")]
[SelfHosted(NotSelfHostedOnly = true)]
public async Task RedeemSponsorship([FromQuery] string sponsorshipToken, [FromBody] OrganizationSponsorshipRedeemRequestModel model)
{
var (valid, sponsorship) = await _validateRedemptionTokenCommand.ValidateRedemptionTokenAsync(sponsorshipToken, (await CurrentUser).Email);
if (!valid)
{
throw new BadRequestException("Failed to parse sponsorship token.");
}
if (!await _currentContext.OrganizationOwner(model.SponsoredOrganizationId))
{
throw new BadRequestException("Can only redeem sponsorship for an organization you own.");
}
await _setUpSponsorshipCommand.SetUpSponsorshipAsync(
sponsorship,
await _organizationRepository.GetByIdAsync(model.SponsoredOrganizationId));
}
[Authorize("Installation")]
[HttpPost("sync")]
public async Task<OrganizationSponsorshipSyncResponseModel> Sync([FromBody] OrganizationSponsorshipSyncRequestModel model)
{
var sponsoringOrg = await _organizationRepository.GetByIdAsync(model.SponsoringOrganizationCloudId);
if (!await _validateBillingSyncKeyCommand.ValidateBillingSyncKeyAsync(sponsoringOrg, model.BillingSyncKey))
{
throw new BadRequestException("Invalid Billing Sync Key");
}
var (syncResponseData, offersToSend) = await _syncSponsorshipsCommand.SyncOrganization(sponsoringOrg, model.ToOrganizationSponsorshipSync().SponsorshipsBatch);
await _sendSponsorshipOfferCommand.BulkSendSponsorshipOfferAsync(sponsoringOrg.Name, offersToSend);
return new OrganizationSponsorshipSyncResponseModel(syncResponseData);
}
[Authorize("Application")]
[HttpDelete("{sponsoringOrganizationId}")]
[HttpPost("{sponsoringOrganizationId}/delete")]
[SelfHosted(NotSelfHostedOnly = true)]
public async Task RevokeSponsorship(Guid sponsoringOrganizationId)
{
var orgUser = await _organizationUserRepository.GetByOrganizationAsync(sponsoringOrganizationId, _currentContext.UserId ?? default);
if (_currentContext.UserId != orgUser?.UserId)
{
throw new BadRequestException("Can only revoke a sponsorship you granted.");
}
var existingOrgSponsorship = await _organizationSponsorshipRepository
.GetBySponsoringOrganizationUserIdAsync(orgUser.Id);
await _revokeSponsorshipCommand.RevokeSponsorshipAsync(existingOrgSponsorship);
}
[Authorize("Application")]
[HttpDelete("sponsored/{sponsoredOrgId}")]
[HttpPost("sponsored/{sponsoredOrgId}/remove")]
[SelfHosted(NotSelfHostedOnly = true)]
public async Task RemoveSponsorship(Guid sponsoredOrgId)
{
if (!await _currentContext.OrganizationOwner(sponsoredOrgId))
{
throw new BadRequestException("Only the owner of an organization can remove sponsorship.");
}
var existingOrgSponsorship = await _organizationSponsorshipRepository
.GetBySponsoredOrganizationIdAsync(sponsoredOrgId);
await _removeSponsorshipCommand.RemoveSponsorshipAsync(existingOrgSponsorship);
}
[HttpGet("{sponsoringOrgId}/sync-status")]
public async Task<object> GetSyncStatus(Guid sponsoringOrgId)
{
var sponsoringOrg = await _organizationRepository.GetByIdAsync(sponsoringOrgId);
if (!await _currentContext.OrganizationOwner(sponsoringOrg.Id))
{
throw new NotFoundException();
}
var lastSyncDate = await _organizationSponsorshipRepository.GetLatestSyncDateBySponsoringOrganizationIdAsync(sponsoringOrg.Id);
return new OrganizationSponsorshipSyncStatusResponseModel(lastSyncDate);
}
private Task<User> CurrentUser => _userService.GetUserByIdAsync(_currentContext.UserId.Value);
}

View File

@ -13,460 +13,459 @@ using Bit.Core.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Bit.Api.Controllers
namespace Bit.Api.Controllers;
[Route("organizations/{orgId}/users")]
[Authorize("Application")]
public class OrganizationUsersController : Controller
{
[Route("organizations/{orgId}/users")]
[Authorize("Application")]
public class OrganizationUsersController : Controller
private readonly IOrganizationRepository _organizationRepository;
private readonly IOrganizationUserRepository _organizationUserRepository;
private readonly IOrganizationService _organizationService;
private readonly ICollectionRepository _collectionRepository;
private readonly IGroupRepository _groupRepository;
private readonly IUserService _userService;
private readonly IPolicyRepository _policyRepository;
private readonly ICurrentContext _currentContext;
public OrganizationUsersController(
IOrganizationRepository organizationRepository,
IOrganizationUserRepository organizationUserRepository,
IOrganizationService organizationService,
ICollectionRepository collectionRepository,
IGroupRepository groupRepository,
IUserService userService,
IPolicyRepository policyRepository,
ICurrentContext currentContext)
{
private readonly IOrganizationRepository _organizationRepository;
private readonly IOrganizationUserRepository _organizationUserRepository;
private readonly IOrganizationService _organizationService;
private readonly ICollectionRepository _collectionRepository;
private readonly IGroupRepository _groupRepository;
private readonly IUserService _userService;
private readonly IPolicyRepository _policyRepository;
private readonly ICurrentContext _currentContext;
_organizationRepository = organizationRepository;
_organizationUserRepository = organizationUserRepository;
_organizationService = organizationService;
_collectionRepository = collectionRepository;
_groupRepository = groupRepository;
_userService = userService;
_policyRepository = policyRepository;
_currentContext = currentContext;
}
public OrganizationUsersController(
IOrganizationRepository organizationRepository,
IOrganizationUserRepository organizationUserRepository,
IOrganizationService organizationService,
ICollectionRepository collectionRepository,
IGroupRepository groupRepository,
IUserService userService,
IPolicyRepository policyRepository,
ICurrentContext currentContext)
[HttpGet("{id}")]
public async Task<OrganizationUserDetailsResponseModel> Get(string orgId, string id)
{
var organizationUser = await _organizationUserRepository.GetByIdWithCollectionsAsync(new Guid(id));
if (organizationUser == null || !await _currentContext.ManageUsers(organizationUser.Item1.OrganizationId))
{
_organizationRepository = organizationRepository;
_organizationUserRepository = organizationUserRepository;
_organizationService = organizationService;
_collectionRepository = collectionRepository;
_groupRepository = groupRepository;
_userService = userService;
_policyRepository = policyRepository;
_currentContext = currentContext;
throw new NotFoundException();
}
[HttpGet("{id}")]
public async Task<OrganizationUserDetailsResponseModel> Get(string orgId, string id)
{
var organizationUser = await _organizationUserRepository.GetByIdWithCollectionsAsync(new Guid(id));
if (organizationUser == null || !await _currentContext.ManageUsers(organizationUser.Item1.OrganizationId))
{
throw new NotFoundException();
}
return new OrganizationUserDetailsResponseModel(organizationUser.Item1, organizationUser.Item2);
}
return new OrganizationUserDetailsResponseModel(organizationUser.Item1, organizationUser.Item2);
[HttpGet("")]
public async Task<ListResponseModel<OrganizationUserUserDetailsResponseModel>> Get(string orgId)
{
var orgGuidId = new Guid(orgId);
if (!await _currentContext.ViewAllCollections(orgGuidId) &&
!await _currentContext.ViewAssignedCollections(orgGuidId) &&
!await _currentContext.ManageGroups(orgGuidId) &&
!await _currentContext.ManageUsers(orgGuidId))
{
throw new NotFoundException();
}
[HttpGet("")]
public async Task<ListResponseModel<OrganizationUserUserDetailsResponseModel>> Get(string orgId)
{
var orgGuidId = new Guid(orgId);
if (!await _currentContext.ViewAllCollections(orgGuidId) &&
!await _currentContext.ViewAssignedCollections(orgGuidId) &&
!await _currentContext.ManageGroups(orgGuidId) &&
!await _currentContext.ManageUsers(orgGuidId))
{
throw new NotFoundException();
}
var organizationUsers = await _organizationUserRepository.GetManyDetailsByOrganizationAsync(orgGuidId);
var responseTasks = organizationUsers.Select(async o => new OrganizationUserUserDetailsResponseModel(o,
await _userService.TwoFactorIsEnabledAsync(o)));
var responses = await Task.WhenAll(responseTasks);
return new ListResponseModel<OrganizationUserUserDetailsResponseModel>(responses);
}
var organizationUsers = await _organizationUserRepository.GetManyDetailsByOrganizationAsync(orgGuidId);
var responseTasks = organizationUsers.Select(async o => new OrganizationUserUserDetailsResponseModel(o,
await _userService.TwoFactorIsEnabledAsync(o)));
var responses = await Task.WhenAll(responseTasks);
return new ListResponseModel<OrganizationUserUserDetailsResponseModel>(responses);
[HttpGet("{id}/groups")]
public async Task<IEnumerable<string>> GetGroups(string orgId, string id)
{
var organizationUser = await _organizationUserRepository.GetByIdAsync(new Guid(id));
if (organizationUser == null || (!await _currentContext.ManageGroups(organizationUser.OrganizationId) &&
!await _currentContext.ManageUsers(organizationUser.OrganizationId)))
{
throw new NotFoundException();
}
[HttpGet("{id}/groups")]
public async Task<IEnumerable<string>> GetGroups(string orgId, string id)
{
var organizationUser = await _organizationUserRepository.GetByIdAsync(new Guid(id));
if (organizationUser == null || (!await _currentContext.ManageGroups(organizationUser.OrganizationId) &&
!await _currentContext.ManageUsers(organizationUser.OrganizationId)))
{
throw new NotFoundException();
}
var groupIds = await _groupRepository.GetManyIdsByUserIdAsync(organizationUser.Id);
var responses = groupIds.Select(g => g.ToString());
return responses;
}
var groupIds = await _groupRepository.GetManyIdsByUserIdAsync(organizationUser.Id);
var responses = groupIds.Select(g => g.ToString());
return responses;
[HttpGet("{id}/reset-password-details")]
public async Task<OrganizationUserResetPasswordDetailsResponseModel> GetResetPasswordDetails(string orgId, string id)
{
// Make sure the calling user can reset passwords for this org
var orgGuidId = new Guid(orgId);
if (!await _currentContext.ManageResetPassword(orgGuidId))
{
throw new NotFoundException();
}
[HttpGet("{id}/reset-password-details")]
public async Task<OrganizationUserResetPasswordDetailsResponseModel> GetResetPasswordDetails(string orgId, string id)
var organizationUser = await _organizationUserRepository.GetByIdAsync(new Guid(id));
if (organizationUser == null || !organizationUser.UserId.HasValue)
{
// Make sure the calling user can reset passwords for this org
var orgGuidId = new Guid(orgId);
if (!await _currentContext.ManageResetPassword(orgGuidId))
{
throw new NotFoundException();
}
var organizationUser = await _organizationUserRepository.GetByIdAsync(new Guid(id));
if (organizationUser == null || !organizationUser.UserId.HasValue)
{
throw new NotFoundException();
}
// Retrieve data necessary for response (KDF, KDF Iterations, ResetPasswordKey)
// TODO Reset Password - Revisit this and create SPROC to reduce DB calls
var user = await _userService.GetUserByIdAsync(organizationUser.UserId.Value);
if (user == null)
{
throw new NotFoundException();
}
// Retrieve Encrypted Private Key from organization
var org = await _organizationRepository.GetByIdAsync(orgGuidId);
if (org == null)
{
throw new NotFoundException();
}
return new OrganizationUserResetPasswordDetailsResponseModel(new OrganizationUserResetPasswordDetails(organizationUser, user, org));
throw new NotFoundException();
}
[HttpPost("invite")]
public async Task Invite(string orgId, [FromBody] OrganizationUserInviteRequestModel model)
// Retrieve data necessary for response (KDF, KDF Iterations, ResetPasswordKey)
// TODO Reset Password - Revisit this and create SPROC to reduce DB calls
var user = await _userService.GetUserByIdAsync(organizationUser.UserId.Value);
if (user == null)
{
var orgGuidId = new Guid(orgId);
if (!await _currentContext.ManageUsers(orgGuidId))
{
throw new NotFoundException();
}
var userId = _userService.GetProperUserId(User);
var result = await _organizationService.InviteUsersAsync(orgGuidId, userId.Value,
new (OrganizationUserInvite, string)[] { (new OrganizationUserInvite(model.ToData()), null) });
throw new NotFoundException();
}
[HttpPost("reinvite")]
public async Task<ListResponseModel<OrganizationUserBulkResponseModel>> BulkReinvite(string orgId, [FromBody] OrganizationUserBulkRequestModel model)
// Retrieve Encrypted Private Key from organization
var org = await _organizationRepository.GetByIdAsync(orgGuidId);
if (org == null)
{
var orgGuidId = new Guid(orgId);
if (!await _currentContext.ManageUsers(orgGuidId))
{
throw new NotFoundException();
}
var userId = _userService.GetProperUserId(User);
var result = await _organizationService.ResendInvitesAsync(orgGuidId, userId.Value, model.Ids);
return new ListResponseModel<OrganizationUserBulkResponseModel>(
result.Select(t => new OrganizationUserBulkResponseModel(t.Item1.Id, t.Item2)));
throw new NotFoundException();
}
[HttpPost("{id}/reinvite")]
public async Task Reinvite(string orgId, string id)
{
var orgGuidId = new Guid(orgId);
if (!await _currentContext.ManageUsers(orgGuidId))
{
throw new NotFoundException();
}
return new OrganizationUserResetPasswordDetailsResponseModel(new OrganizationUserResetPasswordDetails(organizationUser, user, org));
}
var userId = _userService.GetProperUserId(User);
await _organizationService.ResendInviteAsync(orgGuidId, userId.Value, new Guid(id));
[HttpPost("invite")]
public async Task Invite(string orgId, [FromBody] OrganizationUserInviteRequestModel model)
{
var orgGuidId = new Guid(orgId);
if (!await _currentContext.ManageUsers(orgGuidId))
{
throw new NotFoundException();
}
[HttpPost("{organizationUserId}/accept")]
public async Task Accept(Guid orgId, Guid organizationUserId, [FromBody] OrganizationUserAcceptRequestModel model)
var userId = _userService.GetProperUserId(User);
var result = await _organizationService.InviteUsersAsync(orgGuidId, userId.Value,
new (OrganizationUserInvite, string)[] { (new OrganizationUserInvite(model.ToData()), null) });
}
[HttpPost("reinvite")]
public async Task<ListResponseModel<OrganizationUserBulkResponseModel>> BulkReinvite(string orgId, [FromBody] OrganizationUserBulkRequestModel model)
{
var orgGuidId = new Guid(orgId);
if (!await _currentContext.ManageUsers(orgGuidId))
{
var user = await _userService.GetUserByPrincipalAsync(User);
if (user == null)
{
throw new UnauthorizedAccessException();
}
var masterPasswordPolicy = await _policyRepository.GetByOrganizationIdTypeAsync(orgId, PolicyType.ResetPassword);
var useMasterPasswordPolicy = masterPasswordPolicy != null &&
masterPasswordPolicy.Enabled &&
masterPasswordPolicy.GetDataModel<ResetPasswordDataModel>().AutoEnrollEnabled;
if (useMasterPasswordPolicy &&
string.IsNullOrWhiteSpace(model.ResetPasswordKey))
{
throw new BadRequestException(string.Empty, "Master Password reset is required, but not provided.");
}
await _organizationService.AcceptUserAsync(organizationUserId, user, model.Token, _userService);
if (useMasterPasswordPolicy)
{
await _organizationService.UpdateUserResetPasswordEnrollmentAsync(orgId, user.Id, model.ResetPasswordKey, user.Id);
}
throw new NotFoundException();
}
[HttpPost("{id}/confirm")]
public async Task Confirm(string orgId, string id, [FromBody] OrganizationUserConfirmRequestModel model)
{
var orgGuidId = new Guid(orgId);
if (!await _currentContext.ManageUsers(orgGuidId))
{
throw new NotFoundException();
}
var userId = _userService.GetProperUserId(User);
var result = await _organizationService.ResendInvitesAsync(orgGuidId, userId.Value, model.Ids);
return new ListResponseModel<OrganizationUserBulkResponseModel>(
result.Select(t => new OrganizationUserBulkResponseModel(t.Item1.Id, t.Item2)));
}
var userId = _userService.GetProperUserId(User);
var result = await _organizationService.ConfirmUserAsync(orgGuidId, new Guid(id), model.Key, userId.Value,
_userService);
[HttpPost("{id}/reinvite")]
public async Task Reinvite(string orgId, string id)
{
var orgGuidId = new Guid(orgId);
if (!await _currentContext.ManageUsers(orgGuidId))
{
throw new NotFoundException();
}
[HttpPost("confirm")]
public async Task<ListResponseModel<OrganizationUserBulkResponseModel>> BulkConfirm(string orgId,
[FromBody] OrganizationUserBulkConfirmRequestModel model)
var userId = _userService.GetProperUserId(User);
await _organizationService.ResendInviteAsync(orgGuidId, userId.Value, new Guid(id));
}
[HttpPost("{organizationUserId}/accept")]
public async Task Accept(Guid orgId, Guid organizationUserId, [FromBody] OrganizationUserAcceptRequestModel model)
{
var user = await _userService.GetUserByPrincipalAsync(User);
if (user == null)
{
var orgGuidId = new Guid(orgId);
if (!await _currentContext.ManageUsers(orgGuidId))
{
throw new NotFoundException();
}
var userId = _userService.GetProperUserId(User);
var results = await _organizationService.ConfirmUsersAsync(orgGuidId, model.ToDictionary(), userId.Value,
_userService);
return new ListResponseModel<OrganizationUserBulkResponseModel>(results.Select(r =>
new OrganizationUserBulkResponseModel(r.Item1.Id, r.Item2)));
throw new UnauthorizedAccessException();
}
[HttpPost("public-keys")]
public async Task<ListResponseModel<OrganizationUserPublicKeyResponseModel>> UserPublicKeys(string orgId, [FromBody] OrganizationUserBulkRequestModel model)
{
var orgGuidId = new Guid(orgId);
if (!await _currentContext.ManageUsers(orgGuidId))
{
throw new NotFoundException();
}
var masterPasswordPolicy = await _policyRepository.GetByOrganizationIdTypeAsync(orgId, PolicyType.ResetPassword);
var useMasterPasswordPolicy = masterPasswordPolicy != null &&
masterPasswordPolicy.Enabled &&
masterPasswordPolicy.GetDataModel<ResetPasswordDataModel>().AutoEnrollEnabled;
var result = await _organizationUserRepository.GetManyPublicKeysByOrganizationUserAsync(orgGuidId, model.Ids);
var responses = result.Select(r => new OrganizationUserPublicKeyResponseModel(r.Id, r.UserId, r.PublicKey)).ToList();
return new ListResponseModel<OrganizationUserPublicKeyResponseModel>(responses);
if (useMasterPasswordPolicy &&
string.IsNullOrWhiteSpace(model.ResetPasswordKey))
{
throw new BadRequestException(string.Empty, "Master Password reset is required, but not provided.");
}
[HttpPut("{id}")]
[HttpPost("{id}")]
public async Task Put(string orgId, string id, [FromBody] OrganizationUserUpdateRequestModel model)
await _organizationService.AcceptUserAsync(organizationUserId, user, model.Token, _userService);
if (useMasterPasswordPolicy)
{
var orgGuidId = new Guid(orgId);
if (!await _currentContext.ManageUsers(orgGuidId))
{
throw new NotFoundException();
}
var organizationUser = await _organizationUserRepository.GetByIdAsync(new Guid(id));
if (organizationUser == null || organizationUser.OrganizationId != orgGuidId)
{
throw new NotFoundException();
}
var userId = _userService.GetProperUserId(User);
await _organizationService.SaveUserAsync(model.ToOrganizationUser(organizationUser), userId.Value,
model.Collections?.Select(c => c.ToSelectionReadOnly()));
}
[HttpPut("{id}/groups")]
[HttpPost("{id}/groups")]
public async Task PutGroups(string orgId, string id, [FromBody] OrganizationUserUpdateGroupsRequestModel model)
{
var orgGuidId = new Guid(orgId);
if (!await _currentContext.ManageUsers(orgGuidId))
{
throw new NotFoundException();
}
var organizationUser = await _organizationUserRepository.GetByIdAsync(new Guid(id));
if (organizationUser == null || organizationUser.OrganizationId != orgGuidId)
{
throw new NotFoundException();
}
var loggedInUserId = _userService.GetProperUserId(User);
await _organizationService.UpdateUserGroupsAsync(organizationUser, model.GroupIds.Select(g => new Guid(g)), loggedInUserId);
}
[HttpPut("{userId}/reset-password-enrollment")]
public async Task PutResetPasswordEnrollment(string orgId, string userId, [FromBody] OrganizationUserResetPasswordEnrollmentRequestModel model)
{
var user = await _userService.GetUserByPrincipalAsync(User);
if (user == null)
{
throw new UnauthorizedAccessException();
}
if (model.ResetPasswordKey != null && !await _userService.VerifySecretAsync(user, model.Secret))
{
await Task.Delay(2000);
throw new BadRequestException("MasterPasswordHash", "Invalid password.");
}
else
{
var callingUserId = user.Id;
await _organizationService.UpdateUserResetPasswordEnrollmentAsync(
new Guid(orgId), new Guid(userId), model.ResetPasswordKey, callingUserId);
}
}
[HttpPut("{id}/reset-password")]
public async Task PutResetPassword(string orgId, string id, [FromBody] OrganizationUserResetPasswordRequestModel model)
{
var orgGuidId = new Guid(orgId);
// Calling user must have Manage Reset Password permission
if (!await _currentContext.ManageResetPassword(orgGuidId))
{
throw new NotFoundException();
}
// Get the users role, since provider users aren't a member of the organization we use the owner check
var orgUserType = await _currentContext.OrganizationOwner(orgGuidId)
? OrganizationUserType.Owner
: _currentContext.Organizations?.FirstOrDefault(o => o.Id == orgGuidId)?.Type;
if (orgUserType == null)
{
throw new NotFoundException();
}
var result = await _userService.AdminResetPasswordAsync(orgUserType.Value, orgGuidId, new Guid(id), model.NewMasterPasswordHash, model.Key);
if (result.Succeeded)
{
return;
}
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
await Task.Delay(2000);
throw new BadRequestException(ModelState);
}
[HttpDelete("{id}")]
[HttpPost("{id}/delete")]
public async Task Delete(string orgId, string id)
{
var orgGuidId = new Guid(orgId);
if (!await _currentContext.ManageUsers(orgGuidId))
{
throw new NotFoundException();
}
var userId = _userService.GetProperUserId(User);
await _organizationService.DeleteUserAsync(orgGuidId, new Guid(id), userId.Value);
}
[HttpDelete("")]
[HttpPost("delete")]
public async Task<ListResponseModel<OrganizationUserBulkResponseModel>> BulkDelete(string orgId, [FromBody] OrganizationUserBulkRequestModel model)
{
var orgGuidId = new Guid(orgId);
if (!await _currentContext.ManageUsers(orgGuidId))
{
throw new NotFoundException();
}
var userId = _userService.GetProperUserId(User);
var result = await _organizationService.DeleteUsersAsync(orgGuidId, model.Ids, userId.Value);
return new ListResponseModel<OrganizationUserBulkResponseModel>(result.Select(r =>
new OrganizationUserBulkResponseModel(r.Item1.Id, r.Item2)));
}
[Obsolete("2022-07-22 Moved to {id}/revoke endpoint")]
[HttpPatch("{id}/deactivate")]
[HttpPut("{id}/deactivate")]
public async Task Deactivate(Guid orgId, Guid id)
{
await RevokeAsync(orgId, id);
}
[Obsolete("2022-07-22 Moved to /revoke endpoint")]
[HttpPatch("deactivate")]
[HttpPut("deactivate")]
public async Task<ListResponseModel<OrganizationUserBulkResponseModel>> BulkDeactivate(Guid orgId, [FromBody] OrganizationUserBulkRequestModel model)
{
return await BulkRevokeAsync(orgId, model);
}
[Obsolete("2022-07-22 Moved to {id}/restore endpoint")]
[HttpPatch("{id}/activate")]
[HttpPut("{id}/activate")]
public async Task Activate(Guid orgId, Guid id)
{
await RestoreAsync(orgId, id);
}
[Obsolete("2022-07-22 Moved to /restore endpoint")]
[HttpPatch("activate")]
[HttpPut("activate")]
public async Task<ListResponseModel<OrganizationUserBulkResponseModel>> BulkActivate(Guid orgId, [FromBody] OrganizationUserBulkRequestModel model)
{
return await BulkRestoreAsync(orgId, model);
}
[HttpPatch("{id}/revoke")]
[HttpPut("{id}/revoke")]
public async Task RevokeAsync(Guid orgId, Guid id)
{
await RestoreOrRevokeUserAsync(orgId, id, _organizationService.RevokeUserAsync);
}
[HttpPatch("revoke")]
[HttpPut("revoke")]
public async Task<ListResponseModel<OrganizationUserBulkResponseModel>> BulkRevokeAsync(Guid orgId, [FromBody] OrganizationUserBulkRequestModel model)
{
return await RestoreOrRevokeUsersAsync(orgId, model, _organizationService.RevokeUsersAsync);
}
[HttpPatch("{id}/restore")]
[HttpPut("{id}/restore")]
public async Task RestoreAsync(Guid orgId, Guid id)
{
await RestoreOrRevokeUserAsync(orgId, id, (orgUser, userId) => _organizationService.RestoreUserAsync(orgUser, userId, _userService));
}
[HttpPatch("restore")]
[HttpPut("restore")]
public async Task<ListResponseModel<OrganizationUserBulkResponseModel>> BulkRestoreAsync(Guid orgId, [FromBody] OrganizationUserBulkRequestModel model)
{
return await RestoreOrRevokeUsersAsync(orgId, model, (orgId, orgUserIds, restoringUserId) => _organizationService.RestoreUsersAsync(orgId, orgUserIds, restoringUserId, _userService));
}
private async Task RestoreOrRevokeUserAsync(
Guid orgId,
Guid id,
Func<OrganizationUser, Guid?, Task> statusAction)
{
if (!await _currentContext.ManageUsers(orgId))
{
throw new NotFoundException();
}
var userId = _userService.GetProperUserId(User);
var orgUser = await _organizationUserRepository.GetByIdAsync(id);
if (orgUser == null || orgUser.OrganizationId != orgId)
{
throw new NotFoundException();
}
await statusAction(orgUser, userId);
}
private async Task<ListResponseModel<OrganizationUserBulkResponseModel>> RestoreOrRevokeUsersAsync(
Guid orgId,
OrganizationUserBulkRequestModel model,
Func<Guid, IEnumerable<Guid>, Guid?, Task<List<Tuple<OrganizationUser, string>>>> statusAction)
{
if (!await _currentContext.ManageUsers(orgId))
{
throw new NotFoundException();
}
var userId = _userService.GetProperUserId(User);
var result = await statusAction(orgId, model.Ids, userId.Value);
return new ListResponseModel<OrganizationUserBulkResponseModel>(result.Select(r =>
new OrganizationUserBulkResponseModel(r.Item1.Id, r.Item2)));
await _organizationService.UpdateUserResetPasswordEnrollmentAsync(orgId, user.Id, model.ResetPasswordKey, user.Id);
}
}
[HttpPost("{id}/confirm")]
public async Task Confirm(string orgId, string id, [FromBody] OrganizationUserConfirmRequestModel model)
{
var orgGuidId = new Guid(orgId);
if (!await _currentContext.ManageUsers(orgGuidId))
{
throw new NotFoundException();
}
var userId = _userService.GetProperUserId(User);
var result = await _organizationService.ConfirmUserAsync(orgGuidId, new Guid(id), model.Key, userId.Value,
_userService);
}
[HttpPost("confirm")]
public async Task<ListResponseModel<OrganizationUserBulkResponseModel>> BulkConfirm(string orgId,
[FromBody] OrganizationUserBulkConfirmRequestModel model)
{
var orgGuidId = new Guid(orgId);
if (!await _currentContext.ManageUsers(orgGuidId))
{
throw new NotFoundException();
}
var userId = _userService.GetProperUserId(User);
var results = await _organizationService.ConfirmUsersAsync(orgGuidId, model.ToDictionary(), userId.Value,
_userService);
return new ListResponseModel<OrganizationUserBulkResponseModel>(results.Select(r =>
new OrganizationUserBulkResponseModel(r.Item1.Id, r.Item2)));
}
[HttpPost("public-keys")]
public async Task<ListResponseModel<OrganizationUserPublicKeyResponseModel>> UserPublicKeys(string orgId, [FromBody] OrganizationUserBulkRequestModel model)
{
var orgGuidId = new Guid(orgId);
if (!await _currentContext.ManageUsers(orgGuidId))
{
throw new NotFoundException();
}
var result = await _organizationUserRepository.GetManyPublicKeysByOrganizationUserAsync(orgGuidId, model.Ids);
var responses = result.Select(r => new OrganizationUserPublicKeyResponseModel(r.Id, r.UserId, r.PublicKey)).ToList();
return new ListResponseModel<OrganizationUserPublicKeyResponseModel>(responses);
}
[HttpPut("{id}")]
[HttpPost("{id}")]
public async Task Put(string orgId, string id, [FromBody] OrganizationUserUpdateRequestModel model)
{
var orgGuidId = new Guid(orgId);
if (!await _currentContext.ManageUsers(orgGuidId))
{
throw new NotFoundException();
}
var organizationUser = await _organizationUserRepository.GetByIdAsync(new Guid(id));
if (organizationUser == null || organizationUser.OrganizationId != orgGuidId)
{
throw new NotFoundException();
}
var userId = _userService.GetProperUserId(User);
await _organizationService.SaveUserAsync(model.ToOrganizationUser(organizationUser), userId.Value,
model.Collections?.Select(c => c.ToSelectionReadOnly()));
}
[HttpPut("{id}/groups")]
[HttpPost("{id}/groups")]
public async Task PutGroups(string orgId, string id, [FromBody] OrganizationUserUpdateGroupsRequestModel model)
{
var orgGuidId = new Guid(orgId);
if (!await _currentContext.ManageUsers(orgGuidId))
{
throw new NotFoundException();
}
var organizationUser = await _organizationUserRepository.GetByIdAsync(new Guid(id));
if (organizationUser == null || organizationUser.OrganizationId != orgGuidId)
{
throw new NotFoundException();
}
var loggedInUserId = _userService.GetProperUserId(User);
await _organizationService.UpdateUserGroupsAsync(organizationUser, model.GroupIds.Select(g => new Guid(g)), loggedInUserId);
}
[HttpPut("{userId}/reset-password-enrollment")]
public async Task PutResetPasswordEnrollment(string orgId, string userId, [FromBody] OrganizationUserResetPasswordEnrollmentRequestModel model)
{
var user = await _userService.GetUserByPrincipalAsync(User);
if (user == null)
{
throw new UnauthorizedAccessException();
}
if (model.ResetPasswordKey != null && !await _userService.VerifySecretAsync(user, model.Secret))
{
await Task.Delay(2000);
throw new BadRequestException("MasterPasswordHash", "Invalid password.");
}
else
{
var callingUserId = user.Id;
await _organizationService.UpdateUserResetPasswordEnrollmentAsync(
new Guid(orgId), new Guid(userId), model.ResetPasswordKey, callingUserId);
}
}
[HttpPut("{id}/reset-password")]
public async Task PutResetPassword(string orgId, string id, [FromBody] OrganizationUserResetPasswordRequestModel model)
{
var orgGuidId = new Guid(orgId);
// Calling user must have Manage Reset Password permission
if (!await _currentContext.ManageResetPassword(orgGuidId))
{
throw new NotFoundException();
}
// Get the users role, since provider users aren't a member of the organization we use the owner check
var orgUserType = await _currentContext.OrganizationOwner(orgGuidId)
? OrganizationUserType.Owner
: _currentContext.Organizations?.FirstOrDefault(o => o.Id == orgGuidId)?.Type;
if (orgUserType == null)
{
throw new NotFoundException();
}
var result = await _userService.AdminResetPasswordAsync(orgUserType.Value, orgGuidId, new Guid(id), model.NewMasterPasswordHash, model.Key);
if (result.Succeeded)
{
return;
}
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
await Task.Delay(2000);
throw new BadRequestException(ModelState);
}
[HttpDelete("{id}")]
[HttpPost("{id}/delete")]
public async Task Delete(string orgId, string id)
{
var orgGuidId = new Guid(orgId);
if (!await _currentContext.ManageUsers(orgGuidId))
{
throw new NotFoundException();
}
var userId = _userService.GetProperUserId(User);
await _organizationService.DeleteUserAsync(orgGuidId, new Guid(id), userId.Value);
}
[HttpDelete("")]
[HttpPost("delete")]
public async Task<ListResponseModel<OrganizationUserBulkResponseModel>> BulkDelete(string orgId, [FromBody] OrganizationUserBulkRequestModel model)
{
var orgGuidId = new Guid(orgId);
if (!await _currentContext.ManageUsers(orgGuidId))
{
throw new NotFoundException();
}
var userId = _userService.GetProperUserId(User);
var result = await _organizationService.DeleteUsersAsync(orgGuidId, model.Ids, userId.Value);
return new ListResponseModel<OrganizationUserBulkResponseModel>(result.Select(r =>
new OrganizationUserBulkResponseModel(r.Item1.Id, r.Item2)));
}
[Obsolete("2022-07-22 Moved to {id}/revoke endpoint")]
[HttpPatch("{id}/deactivate")]
[HttpPut("{id}/deactivate")]
public async Task Deactivate(Guid orgId, Guid id)
{
await RevokeAsync(orgId, id);
}
[Obsolete("2022-07-22 Moved to /revoke endpoint")]
[HttpPatch("deactivate")]
[HttpPut("deactivate")]
public async Task<ListResponseModel<OrganizationUserBulkResponseModel>> BulkDeactivate(Guid orgId, [FromBody] OrganizationUserBulkRequestModel model)
{
return await BulkRevokeAsync(orgId, model);
}
[Obsolete("2022-07-22 Moved to {id}/restore endpoint")]
[HttpPatch("{id}/activate")]
[HttpPut("{id}/activate")]
public async Task Activate(Guid orgId, Guid id)
{
await RestoreAsync(orgId, id);
}
[Obsolete("2022-07-22 Moved to /restore endpoint")]
[HttpPatch("activate")]
[HttpPut("activate")]
public async Task<ListResponseModel<OrganizationUserBulkResponseModel>> BulkActivate(Guid orgId, [FromBody] OrganizationUserBulkRequestModel model)
{
return await BulkRestoreAsync(orgId, model);
}
[HttpPatch("{id}/revoke")]
[HttpPut("{id}/revoke")]
public async Task RevokeAsync(Guid orgId, Guid id)
{
await RestoreOrRevokeUserAsync(orgId, id, _organizationService.RevokeUserAsync);
}
[HttpPatch("revoke")]
[HttpPut("revoke")]
public async Task<ListResponseModel<OrganizationUserBulkResponseModel>> BulkRevokeAsync(Guid orgId, [FromBody] OrganizationUserBulkRequestModel model)
{
return await RestoreOrRevokeUsersAsync(orgId, model, _organizationService.RevokeUsersAsync);
}
[HttpPatch("{id}/restore")]
[HttpPut("{id}/restore")]
public async Task RestoreAsync(Guid orgId, Guid id)
{
await RestoreOrRevokeUserAsync(orgId, id, (orgUser, userId) => _organizationService.RestoreUserAsync(orgUser, userId, _userService));
}
[HttpPatch("restore")]
[HttpPut("restore")]
public async Task<ListResponseModel<OrganizationUserBulkResponseModel>> BulkRestoreAsync(Guid orgId, [FromBody] OrganizationUserBulkRequestModel model)
{
return await RestoreOrRevokeUsersAsync(orgId, model, (orgId, orgUserIds, restoringUserId) => _organizationService.RestoreUsersAsync(orgId, orgUserIds, restoringUserId, _userService));
}
private async Task RestoreOrRevokeUserAsync(
Guid orgId,
Guid id,
Func<OrganizationUser, Guid?, Task> statusAction)
{
if (!await _currentContext.ManageUsers(orgId))
{
throw new NotFoundException();
}
var userId = _userService.GetProperUserId(User);
var orgUser = await _organizationUserRepository.GetByIdAsync(id);
if (orgUser == null || orgUser.OrganizationId != orgId)
{
throw new NotFoundException();
}
await statusAction(orgUser, userId);
}
private async Task<ListResponseModel<OrganizationUserBulkResponseModel>> RestoreOrRevokeUsersAsync(
Guid orgId,
OrganizationUserBulkRequestModel model,
Func<Guid, IEnumerable<Guid>, Guid?, Task<List<Tuple<OrganizationUser, string>>>> statusAction)
{
if (!await _currentContext.ManageUsers(orgId))
{
throw new NotFoundException();
}
var userId = _userService.GetProperUserId(User);
var result = await statusAction(orgId, model.Ids, userId.Value);
return new ListResponseModel<OrganizationUserBulkResponseModel>(result.Select(r =>
new OrganizationUserBulkResponseModel(r.Item1.Id, r.Item2)));
}
}

File diff suppressed because it is too large Load Diff

View File

@ -4,33 +4,32 @@ using Bit.Core.Utilities;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Bit.Api.Controllers
namespace Bit.Api.Controllers;
[Route("plans")]
[Authorize("Web")]
public class PlansController : Controller
{
[Route("plans")]
[Authorize("Web")]
public class PlansController : Controller
private readonly ITaxRateRepository _taxRateRepository;
public PlansController(ITaxRateRepository taxRateRepository)
{
private readonly ITaxRateRepository _taxRateRepository;
public PlansController(ITaxRateRepository taxRateRepository)
{
_taxRateRepository = taxRateRepository;
}
_taxRateRepository = taxRateRepository;
}
[HttpGet("")]
[AllowAnonymous]
public ListResponseModel<PlanResponseModel> Get()
{
var data = StaticStore.Plans;
var responses = data.Select(plan => new PlanResponseModel(plan));
return new ListResponseModel<PlanResponseModel>(responses);
}
[HttpGet("")]
[AllowAnonymous]
public ListResponseModel<PlanResponseModel> Get()
{
var data = StaticStore.Plans;
var responses = data.Select(plan => new PlanResponseModel(plan));
return new ListResponseModel<PlanResponseModel>(responses);
}
[HttpGet("sales-tax-rates")]
public async Task<ListResponseModel<TaxRateResponseModel>> GetTaxRates()
{
var data = await _taxRateRepository.GetAllActiveAsync();
var responses = data.Select(x => new TaxRateResponseModel(x));
return new ListResponseModel<TaxRateResponseModel>(responses);
}
[HttpGet("sales-tax-rates")]
public async Task<ListResponseModel<TaxRateResponseModel>> GetTaxRates()
{
var data = await _taxRateRepository.GetAllActiveAsync();
var responses = data.Select(x => new TaxRateResponseModel(x));
return new ListResponseModel<TaxRateResponseModel>(responses);
}
}

View File

@ -11,145 +11,144 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Mvc;
namespace Bit.Api.Controllers
namespace Bit.Api.Controllers;
[Route("organizations/{orgId}/policies")]
[Authorize("Application")]
public class PoliciesController : Controller
{
[Route("organizations/{orgId}/policies")]
[Authorize("Application")]
public class PoliciesController : Controller
private readonly IPolicyRepository _policyRepository;
private readonly IPolicyService _policyService;
private readonly IOrganizationService _organizationService;
private readonly IOrganizationUserRepository _organizationUserRepository;
private readonly IUserService _userService;
private readonly ICurrentContext _currentContext;
private readonly GlobalSettings _globalSettings;
private readonly IDataProtector _organizationServiceDataProtector;
public PoliciesController(
IPolicyRepository policyRepository,
IPolicyService policyService,
IOrganizationService organizationService,
IOrganizationUserRepository organizationUserRepository,
IUserService userService,
ICurrentContext currentContext,
GlobalSettings globalSettings,
IDataProtectionProvider dataProtectionProvider)
{
private readonly IPolicyRepository _policyRepository;
private readonly IPolicyService _policyService;
private readonly IOrganizationService _organizationService;
private readonly IOrganizationUserRepository _organizationUserRepository;
private readonly IUserService _userService;
private readonly ICurrentContext _currentContext;
private readonly GlobalSettings _globalSettings;
private readonly IDataProtector _organizationServiceDataProtector;
_policyRepository = policyRepository;
_policyService = policyService;
_organizationService = organizationService;
_organizationUserRepository = organizationUserRepository;
_userService = userService;
_currentContext = currentContext;
_globalSettings = globalSettings;
_organizationServiceDataProtector = dataProtectionProvider.CreateProtector(
"OrganizationServiceDataProtector");
}
public PoliciesController(
IPolicyRepository policyRepository,
IPolicyService policyService,
IOrganizationService organizationService,
IOrganizationUserRepository organizationUserRepository,
IUserService userService,
ICurrentContext currentContext,
GlobalSettings globalSettings,
IDataProtectionProvider dataProtectionProvider)
[HttpGet("{type}")]
public async Task<PolicyResponseModel> Get(string orgId, int type)
{
var orgIdGuid = new Guid(orgId);
if (!await _currentContext.ManagePolicies(orgIdGuid))
{
_policyRepository = policyRepository;
_policyService = policyService;
_organizationService = organizationService;
_organizationUserRepository = organizationUserRepository;
_userService = userService;
_currentContext = currentContext;
_globalSettings = globalSettings;
_organizationServiceDataProtector = dataProtectionProvider.CreateProtector(
"OrganizationServiceDataProtector");
throw new NotFoundException();
}
var policy = await _policyRepository.GetByOrganizationIdTypeAsync(orgIdGuid, (PolicyType)type);
if (policy == null)
{
throw new NotFoundException();
}
[HttpGet("{type}")]
public async Task<PolicyResponseModel> Get(string orgId, int type)
{
var orgIdGuid = new Guid(orgId);
if (!await _currentContext.ManagePolicies(orgIdGuid))
{
throw new NotFoundException();
}
var policy = await _policyRepository.GetByOrganizationIdTypeAsync(orgIdGuid, (PolicyType)type);
if (policy == null)
{
throw new NotFoundException();
}
return new PolicyResponseModel(policy);
}
return new PolicyResponseModel(policy);
[HttpGet("")]
public async Task<ListResponseModel<PolicyResponseModel>> Get(string orgId)
{
var orgIdGuid = new Guid(orgId);
if (!await _currentContext.ManagePolicies(orgIdGuid))
{
throw new NotFoundException();
}
[HttpGet("")]
public async Task<ListResponseModel<PolicyResponseModel>> Get(string orgId)
{
var orgIdGuid = new Guid(orgId);
if (!await _currentContext.ManagePolicies(orgIdGuid))
{
throw new NotFoundException();
}
var policies = await _policyRepository.GetManyByOrganizationIdAsync(orgIdGuid);
var responses = policies.Select(p => new PolicyResponseModel(p));
return new ListResponseModel<PolicyResponseModel>(responses);
}
var policies = await _policyRepository.GetManyByOrganizationIdAsync(orgIdGuid);
var responses = policies.Select(p => new PolicyResponseModel(p));
return new ListResponseModel<PolicyResponseModel>(responses);
[AllowAnonymous]
[HttpGet("token")]
public async Task<ListResponseModel<PolicyResponseModel>> GetByToken(string orgId, [FromQuery] string email,
[FromQuery] string token, [FromQuery] string organizationUserId)
{
var orgUserId = new Guid(organizationUserId);
var tokenValid = CoreHelpers.UserInviteTokenIsValid(_organizationServiceDataProtector, token,
email, orgUserId, _globalSettings);
if (!tokenValid)
{
throw new NotFoundException();
}
[AllowAnonymous]
[HttpGet("token")]
public async Task<ListResponseModel<PolicyResponseModel>> GetByToken(string orgId, [FromQuery] string email,
[FromQuery] string token, [FromQuery] string organizationUserId)
var orgIdGuid = new Guid(orgId);
var orgUser = await _organizationUserRepository.GetByIdAsync(orgUserId);
if (orgUser == null || orgUser.OrganizationId != orgIdGuid)
{
var orgUserId = new Guid(organizationUserId);
var tokenValid = CoreHelpers.UserInviteTokenIsValid(_organizationServiceDataProtector, token,
email, orgUserId, _globalSettings);
if (!tokenValid)
{
throw new NotFoundException();
}
var orgIdGuid = new Guid(orgId);
var orgUser = await _organizationUserRepository.GetByIdAsync(orgUserId);
if (orgUser == null || orgUser.OrganizationId != orgIdGuid)
{
throw new NotFoundException();
}
var policies = await _policyRepository.GetManyByOrganizationIdAsync(orgIdGuid);
var responses = policies.Where(p => p.Enabled).Select(p => new PolicyResponseModel(p));
return new ListResponseModel<PolicyResponseModel>(responses);
throw new NotFoundException();
}
[AllowAnonymous]
[HttpGet("invited-user")]
public async Task<ListResponseModel<PolicyResponseModel>> GetByInvitedUser(string orgId, [FromQuery] string userId)
{
var user = await _userService.GetUserByIdAsync(new Guid(userId));
if (user == null)
{
throw new UnauthorizedAccessException();
}
var orgIdGuid = new Guid(orgId);
var orgUsersByUserId = await _organizationUserRepository.GetManyByUserAsync(user.Id);
var orgUser = orgUsersByUserId.SingleOrDefault(u => u.OrganizationId == orgIdGuid);
if (orgUser == null)
{
throw new NotFoundException();
}
if (orgUser.Status != OrganizationUserStatusType.Invited)
{
throw new UnauthorizedAccessException();
}
var policies = await _policyRepository.GetManyByOrganizationIdAsync(orgIdGuid);
var responses = policies.Where(p => p.Enabled).Select(p => new PolicyResponseModel(p));
return new ListResponseModel<PolicyResponseModel>(responses);
}
var policies = await _policyRepository.GetManyByOrganizationIdAsync(orgIdGuid);
var responses = policies.Where(p => p.Enabled).Select(p => new PolicyResponseModel(p));
return new ListResponseModel<PolicyResponseModel>(responses);
[AllowAnonymous]
[HttpGet("invited-user")]
public async Task<ListResponseModel<PolicyResponseModel>> GetByInvitedUser(string orgId, [FromQuery] string userId)
{
var user = await _userService.GetUserByIdAsync(new Guid(userId));
if (user == null)
{
throw new UnauthorizedAccessException();
}
var orgIdGuid = new Guid(orgId);
var orgUsersByUserId = await _organizationUserRepository.GetManyByUserAsync(user.Id);
var orgUser = orgUsersByUserId.SingleOrDefault(u => u.OrganizationId == orgIdGuid);
if (orgUser == null)
{
throw new NotFoundException();
}
if (orgUser.Status != OrganizationUserStatusType.Invited)
{
throw new UnauthorizedAccessException();
}
[HttpPut("{type}")]
public async Task<PolicyResponseModel> Put(string orgId, int type, [FromBody] PolicyRequestModel model)
{
var orgIdGuid = new Guid(orgId);
if (!await _currentContext.ManagePolicies(orgIdGuid))
{
throw new NotFoundException();
}
var policy = await _policyRepository.GetByOrganizationIdTypeAsync(new Guid(orgId), (PolicyType)type);
if (policy == null)
{
policy = model.ToPolicy(orgIdGuid);
}
else
{
policy = model.ToPolicy(policy);
}
var policies = await _policyRepository.GetManyByOrganizationIdAsync(orgIdGuid);
var responses = policies.Where(p => p.Enabled).Select(p => new PolicyResponseModel(p));
return new ListResponseModel<PolicyResponseModel>(responses);
}
var userId = _userService.GetProperUserId(User);
await _policyService.SaveAsync(policy, _userService, _organizationService, userId);
return new PolicyResponseModel(policy);
[HttpPut("{type}")]
public async Task<PolicyResponseModel> Put(string orgId, int type, [FromBody] PolicyRequestModel model)
{
var orgIdGuid = new Guid(orgId);
if (!await _currentContext.ManagePolicies(orgIdGuid))
{
throw new NotFoundException();
}
var policy = await _policyRepository.GetByOrganizationIdTypeAsync(new Guid(orgId), (PolicyType)type);
if (policy == null)
{
policy = model.ToPolicy(orgIdGuid);
}
else
{
policy = model.ToPolicy(policy);
}
var userId = _userService.GetProperUserId(User);
await _policyService.SaveAsync(policy, _userService, _organizationService, userId);
return new PolicyResponseModel(policy);
}
}

View File

@ -9,87 +9,86 @@ using Bit.Core.Utilities;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Bit.Api.Controllers
namespace Bit.Api.Controllers;
[Route("providers/{providerId:guid}/organizations")]
[Authorize("Application")]
public class ProviderOrganizationsController : Controller
{
[Route("providers/{providerId:guid}/organizations")]
[Authorize("Application")]
public class ProviderOrganizationsController : Controller
private readonly IProviderOrganizationRepository _providerOrganizationRepository;
private readonly IProviderService _providerService;
private readonly IUserService _userService;
private readonly ICurrentContext _currentContext;
public ProviderOrganizationsController(
IProviderOrganizationRepository providerOrganizationRepository,
IProviderService providerService,
IUserService userService,
ICurrentContext currentContext)
{
_providerOrganizationRepository = providerOrganizationRepository;
_providerService = providerService;
_userService = userService;
_currentContext = currentContext;
}
private readonly IProviderOrganizationRepository _providerOrganizationRepository;
private readonly IProviderService _providerService;
private readonly IUserService _userService;
private readonly ICurrentContext _currentContext;
public ProviderOrganizationsController(
IProviderOrganizationRepository providerOrganizationRepository,
IProviderService providerService,
IUserService userService,
ICurrentContext currentContext)
[HttpGet("")]
public async Task<ListResponseModel<ProviderOrganizationOrganizationDetailsResponseModel>> Get(Guid providerId)
{
if (!_currentContext.AccessProviderOrganizations(providerId))
{
_providerOrganizationRepository = providerOrganizationRepository;
_providerService = providerService;
_userService = userService;
_currentContext = currentContext;
throw new NotFoundException();
}
[HttpGet("")]
public async Task<ListResponseModel<ProviderOrganizationOrganizationDetailsResponseModel>> Get(Guid providerId)
{
if (!_currentContext.AccessProviderOrganizations(providerId))
{
throw new NotFoundException();
}
var providerOrganizations = await _providerOrganizationRepository.GetManyDetailsByProviderAsync(providerId);
var responses = providerOrganizations.Select(o => new ProviderOrganizationOrganizationDetailsResponseModel(o));
return new ListResponseModel<ProviderOrganizationOrganizationDetailsResponseModel>(responses);
}
var providerOrganizations = await _providerOrganizationRepository.GetManyDetailsByProviderAsync(providerId);
var responses = providerOrganizations.Select(o => new ProviderOrganizationOrganizationDetailsResponseModel(o));
return new ListResponseModel<ProviderOrganizationOrganizationDetailsResponseModel>(responses);
[HttpPost("add")]
public async Task Add(Guid providerId, [FromBody] ProviderOrganizationAddRequestModel model)
{
if (!_currentContext.ManageProviderOrganizations(providerId))
{
throw new NotFoundException();
}
[HttpPost("add")]
public async Task Add(Guid providerId, [FromBody] ProviderOrganizationAddRequestModel model)
var userId = _userService.GetProperUserId(User).Value;
await _providerService.AddOrganization(providerId, model.OrganizationId, userId, model.Key);
}
[HttpPost("")]
[SelfHosted(NotSelfHostedOnly = true)]
public async Task<ProviderOrganizationResponseModel> Post(Guid providerId, [FromBody] ProviderOrganizationCreateRequestModel model)
{
var user = await _userService.GetUserByPrincipalAsync(User);
if (user == null)
{
if (!_currentContext.ManageProviderOrganizations(providerId))
{
throw new NotFoundException();
}
var userId = _userService.GetProperUserId(User).Value;
await _providerService.AddOrganization(providerId, model.OrganizationId, userId, model.Key);
throw new UnauthorizedAccessException();
}
[HttpPost("")]
[SelfHosted(NotSelfHostedOnly = true)]
public async Task<ProviderOrganizationResponseModel> Post(Guid providerId, [FromBody] ProviderOrganizationCreateRequestModel model)
if (!_currentContext.ManageProviderOrganizations(providerId))
{
var user = await _userService.GetUserByPrincipalAsync(User);
if (user == null)
{
throw new UnauthorizedAccessException();
}
if (!_currentContext.ManageProviderOrganizations(providerId))
{
throw new NotFoundException();
}
var organizationSignup = model.OrganizationCreateRequest.ToOrganizationSignup(user);
var result = await _providerService.CreateOrganizationAsync(providerId, organizationSignup, model.ClientOwnerEmail, user);
return new ProviderOrganizationResponseModel(result);
throw new NotFoundException();
}
[HttpDelete("{id:guid}")]
[HttpPost("{id:guid}/delete")]
public async Task Delete(Guid providerId, Guid id)
{
if (!_currentContext.ManageProviderOrganizations(providerId))
{
throw new NotFoundException();
}
var organizationSignup = model.OrganizationCreateRequest.ToOrganizationSignup(user);
var result = await _providerService.CreateOrganizationAsync(providerId, organizationSignup, model.ClientOwnerEmail, user);
return new ProviderOrganizationResponseModel(result);
}
var userId = _userService.GetProperUserId(User);
await _providerService.RemoveOrganizationAsync(providerId, id, userId.Value);
[HttpDelete("{id:guid}")]
[HttpPost("{id:guid}/delete")]
public async Task Delete(Guid providerId, Guid id)
{
if (!_currentContext.ManageProviderOrganizations(providerId))
{
throw new NotFoundException();
}
var userId = _userService.GetProperUserId(User);
await _providerService.RemoveOrganizationAsync(providerId, id, userId.Value);
}
}

View File

@ -9,192 +9,191 @@ using Bit.Core.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Bit.Api.Controllers
namespace Bit.Api.Controllers;
[Route("providers/{providerId:guid}/users")]
[Authorize("Application")]
public class ProviderUsersController : Controller
{
[Route("providers/{providerId:guid}/users")]
[Authorize("Application")]
public class ProviderUsersController : Controller
private readonly IProviderUserRepository _providerUserRepository;
private readonly IProviderService _providerService;
private readonly IUserService _userService;
private readonly ICurrentContext _currentContext;
public ProviderUsersController(
IProviderUserRepository providerUserRepository,
IProviderService providerService,
IUserService userService,
ICurrentContext currentContext)
{
private readonly IProviderUserRepository _providerUserRepository;
private readonly IProviderService _providerService;
private readonly IUserService _userService;
private readonly ICurrentContext _currentContext;
_providerUserRepository = providerUserRepository;
_providerService = providerService;
_userService = userService;
_currentContext = currentContext;
}
public ProviderUsersController(
IProviderUserRepository providerUserRepository,
IProviderService providerService,
IUserService userService,
ICurrentContext currentContext)
[HttpGet("{id:guid}")]
public async Task<ProviderUserResponseModel> Get(Guid providerId, Guid id)
{
var providerUser = await _providerUserRepository.GetByIdAsync(id);
if (providerUser == null || !_currentContext.ProviderManageUsers(providerUser.ProviderId))
{
_providerUserRepository = providerUserRepository;
_providerService = providerService;
_userService = userService;
_currentContext = currentContext;
throw new NotFoundException();
}
[HttpGet("{id:guid}")]
public async Task<ProviderUserResponseModel> Get(Guid providerId, Guid id)
{
var providerUser = await _providerUserRepository.GetByIdAsync(id);
if (providerUser == null || !_currentContext.ProviderManageUsers(providerUser.ProviderId))
{
throw new NotFoundException();
}
return new ProviderUserResponseModel(providerUser);
}
return new ProviderUserResponseModel(providerUser);
[HttpGet("")]
public async Task<ListResponseModel<ProviderUserUserDetailsResponseModel>> Get(Guid providerId)
{
if (!_currentContext.ProviderManageUsers(providerId))
{
throw new NotFoundException();
}
[HttpGet("")]
public async Task<ListResponseModel<ProviderUserUserDetailsResponseModel>> Get(Guid providerId)
{
if (!_currentContext.ProviderManageUsers(providerId))
{
throw new NotFoundException();
}
var providerUsers = await _providerUserRepository.GetManyDetailsByProviderAsync(providerId);
var responses = providerUsers.Select(o => new ProviderUserUserDetailsResponseModel(o));
return new ListResponseModel<ProviderUserUserDetailsResponseModel>(responses);
}
var providerUsers = await _providerUserRepository.GetManyDetailsByProviderAsync(providerId);
var responses = providerUsers.Select(o => new ProviderUserUserDetailsResponseModel(o));
return new ListResponseModel<ProviderUserUserDetailsResponseModel>(responses);
[HttpPost("invite")]
public async Task Invite(Guid providerId, [FromBody] ProviderUserInviteRequestModel model)
{
if (!_currentContext.ProviderManageUsers(providerId))
{
throw new NotFoundException();
}
[HttpPost("invite")]
public async Task Invite(Guid providerId, [FromBody] ProviderUserInviteRequestModel model)
{
if (!_currentContext.ProviderManageUsers(providerId))
{
throw new NotFoundException();
}
var invite = ProviderUserInviteFactory.CreateIntialInvite(model.Emails, model.Type.Value,
_userService.GetProperUserId(User).Value, providerId);
await _providerService.InviteUserAsync(invite);
}
var invite = ProviderUserInviteFactory.CreateIntialInvite(model.Emails, model.Type.Value,
_userService.GetProperUserId(User).Value, providerId);
await _providerService.InviteUserAsync(invite);
[HttpPost("reinvite")]
public async Task<ListResponseModel<ProviderUserBulkResponseModel>> BulkReinvite(Guid providerId, [FromBody] ProviderUserBulkRequestModel model)
{
if (!_currentContext.ProviderManageUsers(providerId))
{
throw new NotFoundException();
}
[HttpPost("reinvite")]
public async Task<ListResponseModel<ProviderUserBulkResponseModel>> BulkReinvite(Guid providerId, [FromBody] ProviderUserBulkRequestModel model)
{
if (!_currentContext.ProviderManageUsers(providerId))
{
throw new NotFoundException();
}
var invite = ProviderUserInviteFactory.CreateReinvite(model.Ids, _userService.GetProperUserId(User).Value, providerId);
var result = await _providerService.ResendInvitesAsync(invite);
return new ListResponseModel<ProviderUserBulkResponseModel>(
result.Select(t => new ProviderUserBulkResponseModel(t.Item1.Id, t.Item2)));
}
var invite = ProviderUserInviteFactory.CreateReinvite(model.Ids, _userService.GetProperUserId(User).Value, providerId);
var result = await _providerService.ResendInvitesAsync(invite);
return new ListResponseModel<ProviderUserBulkResponseModel>(
result.Select(t => new ProviderUserBulkResponseModel(t.Item1.Id, t.Item2)));
[HttpPost("{id:guid}/reinvite")]
public async Task Reinvite(Guid providerId, Guid id)
{
if (!_currentContext.ProviderManageUsers(providerId))
{
throw new NotFoundException();
}
[HttpPost("{id:guid}/reinvite")]
public async Task Reinvite(Guid providerId, Guid id)
{
if (!_currentContext.ProviderManageUsers(providerId))
{
throw new NotFoundException();
}
var invite = ProviderUserInviteFactory.CreateReinvite(new[] { id },
_userService.GetProperUserId(User).Value, providerId);
await _providerService.ResendInvitesAsync(invite);
}
var invite = ProviderUserInviteFactory.CreateReinvite(new[] { id },
_userService.GetProperUserId(User).Value, providerId);
await _providerService.ResendInvitesAsync(invite);
[HttpPost("{id:guid}/accept")]
public async Task Accept(Guid providerId, Guid id, [FromBody] ProviderUserAcceptRequestModel model)
{
var user = await _userService.GetUserByPrincipalAsync(User);
if (user == null)
{
throw new UnauthorizedAccessException();
}
[HttpPost("{id:guid}/accept")]
public async Task Accept(Guid providerId, Guid id, [FromBody] ProviderUserAcceptRequestModel model)
{
var user = await _userService.GetUserByPrincipalAsync(User);
if (user == null)
{
throw new UnauthorizedAccessException();
}
await _providerService.AcceptUserAsync(id, user, model.Token);
}
await _providerService.AcceptUserAsync(id, user, model.Token);
[HttpPost("{id:guid}/confirm")]
public async Task Confirm(Guid providerId, Guid id, [FromBody] ProviderUserConfirmRequestModel model)
{
if (!_currentContext.ProviderManageUsers(providerId))
{
throw new NotFoundException();
}
[HttpPost("{id:guid}/confirm")]
public async Task Confirm(Guid providerId, Guid id, [FromBody] ProviderUserConfirmRequestModel model)
{
if (!_currentContext.ProviderManageUsers(providerId))
{
throw new NotFoundException();
}
var userId = _userService.GetProperUserId(User);
await _providerService.ConfirmUsersAsync(providerId, new Dictionary<Guid, string> { [id] = model.Key }, userId.Value);
}
var userId = _userService.GetProperUserId(User);
await _providerService.ConfirmUsersAsync(providerId, new Dictionary<Guid, string> { [id] = model.Key }, userId.Value);
[HttpPost("confirm")]
public async Task<ListResponseModel<ProviderUserBulkResponseModel>> BulkConfirm(Guid providerId,
[FromBody] ProviderUserBulkConfirmRequestModel model)
{
if (!_currentContext.ProviderManageUsers(providerId))
{
throw new NotFoundException();
}
[HttpPost("confirm")]
public async Task<ListResponseModel<ProviderUserBulkResponseModel>> BulkConfirm(Guid providerId,
[FromBody] ProviderUserBulkConfirmRequestModel model)
var userId = _userService.GetProperUserId(User);
var results = await _providerService.ConfirmUsersAsync(providerId, model.ToDictionary(), userId.Value);
return new ListResponseModel<ProviderUserBulkResponseModel>(results.Select(r =>
new ProviderUserBulkResponseModel(r.Item1.Id, r.Item2)));
}
[HttpPost("public-keys")]
public async Task<ListResponseModel<ProviderUserPublicKeyResponseModel>> UserPublicKeys(Guid providerId, [FromBody] ProviderUserBulkRequestModel model)
{
if (!_currentContext.ProviderManageUsers(providerId))
{
if (!_currentContext.ProviderManageUsers(providerId))
{
throw new NotFoundException();
}
var userId = _userService.GetProperUserId(User);
var results = await _providerService.ConfirmUsersAsync(providerId, model.ToDictionary(), userId.Value);
return new ListResponseModel<ProviderUserBulkResponseModel>(results.Select(r =>
new ProviderUserBulkResponseModel(r.Item1.Id, r.Item2)));
throw new NotFoundException();
}
[HttpPost("public-keys")]
public async Task<ListResponseModel<ProviderUserPublicKeyResponseModel>> UserPublicKeys(Guid providerId, [FromBody] ProviderUserBulkRequestModel model)
{
if (!_currentContext.ProviderManageUsers(providerId))
{
throw new NotFoundException();
}
var result = await _providerUserRepository.GetManyPublicKeysByProviderUserAsync(providerId, model.Ids);
var responses = result.Select(r => new ProviderUserPublicKeyResponseModel(r.Id, r.UserId, r.PublicKey)).ToList();
return new ListResponseModel<ProviderUserPublicKeyResponseModel>(responses);
}
var result = await _providerUserRepository.GetManyPublicKeysByProviderUserAsync(providerId, model.Ids);
var responses = result.Select(r => new ProviderUserPublicKeyResponseModel(r.Id, r.UserId, r.PublicKey)).ToList();
return new ListResponseModel<ProviderUserPublicKeyResponseModel>(responses);
[HttpPut("{id:guid}")]
[HttpPost("{id:guid}")]
public async Task Put(Guid providerId, Guid id, [FromBody] ProviderUserUpdateRequestModel model)
{
if (!_currentContext.ProviderManageUsers(providerId))
{
throw new NotFoundException();
}
[HttpPut("{id:guid}")]
[HttpPost("{id:guid}")]
public async Task Put(Guid providerId, Guid id, [FromBody] ProviderUserUpdateRequestModel model)
var providerUser = await _providerUserRepository.GetByIdAsync(id);
if (providerUser == null || providerUser.ProviderId != providerId)
{
if (!_currentContext.ProviderManageUsers(providerId))
{
throw new NotFoundException();
}
var providerUser = await _providerUserRepository.GetByIdAsync(id);
if (providerUser == null || providerUser.ProviderId != providerId)
{
throw new NotFoundException();
}
var userId = _userService.GetProperUserId(User);
await _providerService.SaveUserAsync(model.ToProviderUser(providerUser), userId.Value);
throw new NotFoundException();
}
[HttpDelete("{id:guid}")]
[HttpPost("{id:guid}/delete")]
public async Task Delete(Guid providerId, Guid id)
{
if (!_currentContext.ProviderManageUsers(providerId))
{
throw new NotFoundException();
}
var userId = _userService.GetProperUserId(User);
await _providerService.SaveUserAsync(model.ToProviderUser(providerUser), userId.Value);
}
var userId = _userService.GetProperUserId(User);
await _providerService.DeleteUsersAsync(providerId, new[] { id }, userId.Value);
[HttpDelete("{id:guid}")]
[HttpPost("{id:guid}/delete")]
public async Task Delete(Guid providerId, Guid id)
{
if (!_currentContext.ProviderManageUsers(providerId))
{
throw new NotFoundException();
}
[HttpDelete("")]
[HttpPost("delete")]
public async Task<ListResponseModel<ProviderUserBulkResponseModel>> BulkDelete(Guid providerId, [FromBody] ProviderUserBulkRequestModel model)
{
if (!_currentContext.ProviderManageUsers(providerId))
{
throw new NotFoundException();
}
var userId = _userService.GetProperUserId(User);
await _providerService.DeleteUsersAsync(providerId, new[] { id }, userId.Value);
}
var userId = _userService.GetProperUserId(User);
var result = await _providerService.DeleteUsersAsync(providerId, model.Ids, userId.Value);
return new ListResponseModel<ProviderUserBulkResponseModel>(result.Select(r =>
new ProviderUserBulkResponseModel(r.Item1.Id, r.Item2)));
[HttpDelete("")]
[HttpPost("delete")]
public async Task<ListResponseModel<ProviderUserBulkResponseModel>> BulkDelete(Guid providerId, [FromBody] ProviderUserBulkRequestModel model)
{
if (!_currentContext.ProviderManageUsers(providerId))
{
throw new NotFoundException();
}
var userId = _userService.GetProperUserId(User);
var result = await _providerService.DeleteUsersAsync(providerId, model.Ids, userId.Value);
return new ListResponseModel<ProviderUserBulkResponseModel>(result.Select(r =>
new ProviderUserBulkResponseModel(r.Item1.Id, r.Item2)));
}
}

View File

@ -8,84 +8,83 @@ using Bit.Core.Settings;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Bit.Api.Controllers
namespace Bit.Api.Controllers;
[Route("providers")]
[Authorize("Application")]
public class ProvidersController : Controller
{
[Route("providers")]
[Authorize("Application")]
public class ProvidersController : Controller
private readonly IUserService _userService;
private readonly IProviderRepository _providerRepository;
private readonly IProviderService _providerService;
private readonly ICurrentContext _currentContext;
private readonly GlobalSettings _globalSettings;
public ProvidersController(IUserService userService, IProviderRepository providerRepository,
IProviderService providerService, ICurrentContext currentContext, GlobalSettings globalSettings)
{
private readonly IUserService _userService;
private readonly IProviderRepository _providerRepository;
private readonly IProviderService _providerService;
private readonly ICurrentContext _currentContext;
private readonly GlobalSettings _globalSettings;
_userService = userService;
_providerRepository = providerRepository;
_providerService = providerService;
_currentContext = currentContext;
_globalSettings = globalSettings;
}
public ProvidersController(IUserService userService, IProviderRepository providerRepository,
IProviderService providerService, ICurrentContext currentContext, GlobalSettings globalSettings)
[HttpGet("{id:guid}")]
public async Task<ProviderResponseModel> Get(Guid id)
{
if (!_currentContext.ProviderUser(id))
{
_userService = userService;
_providerRepository = providerRepository;
_providerService = providerService;
_currentContext = currentContext;
_globalSettings = globalSettings;
throw new NotFoundException();
}
[HttpGet("{id:guid}")]
public async Task<ProviderResponseModel> Get(Guid id)
var provider = await _providerRepository.GetByIdAsync(id);
if (provider == null)
{
if (!_currentContext.ProviderUser(id))
{
throw new NotFoundException();
}
var provider = await _providerRepository.GetByIdAsync(id);
if (provider == null)
{
throw new NotFoundException();
}
return new ProviderResponseModel(provider);
throw new NotFoundException();
}
[HttpPut("{id:guid}")]
[HttpPost("{id:guid}")]
public async Task<ProviderResponseModel> Put(Guid id, [FromBody] ProviderUpdateRequestModel model)
return new ProviderResponseModel(provider);
}
[HttpPut("{id:guid}")]
[HttpPost("{id:guid}")]
public async Task<ProviderResponseModel> Put(Guid id, [FromBody] ProviderUpdateRequestModel model)
{
if (!_currentContext.ProviderProviderAdmin(id))
{
if (!_currentContext.ProviderProviderAdmin(id))
{
throw new NotFoundException();
}
var provider = await _providerRepository.GetByIdAsync(id);
if (provider == null)
{
throw new NotFoundException();
}
await _providerService.UpdateAsync(model.ToProvider(provider, _globalSettings));
return new ProviderResponseModel(provider);
throw new NotFoundException();
}
[HttpPost("{id:guid}/setup")]
public async Task<ProviderResponseModel> Setup(Guid id, [FromBody] ProviderSetupRequestModel model)
var provider = await _providerRepository.GetByIdAsync(id);
if (provider == null)
{
if (!_currentContext.ProviderProviderAdmin(id))
{
throw new NotFoundException();
}
var provider = await _providerRepository.GetByIdAsync(id);
if (provider == null)
{
throw new NotFoundException();
}
var userId = _userService.GetProperUserId(User).Value;
var response =
await _providerService.CompleteSetupAsync(model.ToProvider(provider), userId, model.Token, model.Key);
return new ProviderResponseModel(response);
throw new NotFoundException();
}
await _providerService.UpdateAsync(model.ToProvider(provider, _globalSettings));
return new ProviderResponseModel(provider);
}
[HttpPost("{id:guid}/setup")]
public async Task<ProviderResponseModel> Setup(Guid id, [FromBody] ProviderSetupRequestModel model)
{
if (!_currentContext.ProviderProviderAdmin(id))
{
throw new NotFoundException();
}
var provider = await _providerRepository.GetByIdAsync(id);
if (provider == null)
{
throw new NotFoundException();
}
var userId = _userService.GetProperUserId(User).Value;
var response =
await _providerService.CompleteSetupAsync(model.ToProvider(provider), userId, model.Token, model.Key);
return new ProviderResponseModel(response);
}
}

View File

@ -7,109 +7,108 @@ using Bit.Core.Utilities;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Bit.Api.Controllers
namespace Bit.Api.Controllers;
[Route("push")]
[Authorize("Push")]
[SelfHosted(NotSelfHostedOnly = true)]
public class PushController : Controller
{
[Route("push")]
[Authorize("Push")]
[SelfHosted(NotSelfHostedOnly = true)]
public class PushController : Controller
private readonly IPushRegistrationService _pushRegistrationService;
private readonly IPushNotificationService _pushNotificationService;
private readonly IWebHostEnvironment _environment;
private readonly ICurrentContext _currentContext;
private readonly GlobalSettings _globalSettings;
public PushController(
IPushRegistrationService pushRegistrationService,
IPushNotificationService pushNotificationService,
IWebHostEnvironment environment,
ICurrentContext currentContext,
GlobalSettings globalSettings)
{
private readonly IPushRegistrationService _pushRegistrationService;
private readonly IPushNotificationService _pushNotificationService;
private readonly IWebHostEnvironment _environment;
private readonly ICurrentContext _currentContext;
private readonly GlobalSettings _globalSettings;
_currentContext = currentContext;
_environment = environment;
_pushRegistrationService = pushRegistrationService;
_pushNotificationService = pushNotificationService;
_globalSettings = globalSettings;
}
public PushController(
IPushRegistrationService pushRegistrationService,
IPushNotificationService pushNotificationService,
IWebHostEnvironment environment,
ICurrentContext currentContext,
GlobalSettings globalSettings)
[HttpPost("register")]
public async Task PostRegister([FromBody] PushRegistrationRequestModel model)
{
CheckUsage();
await _pushRegistrationService.CreateOrUpdateRegistrationAsync(model.PushToken, Prefix(model.DeviceId),
Prefix(model.UserId), Prefix(model.Identifier), model.Type);
}
[HttpDelete("{id}")]
public async Task Delete(string id)
{
CheckUsage();
await _pushRegistrationService.DeleteRegistrationAsync(Prefix(id));
}
[HttpPut("add-organization")]
public async Task PutAddOrganization([FromBody] PushUpdateRequestModel model)
{
CheckUsage();
await _pushRegistrationService.AddUserRegistrationOrganizationAsync(
model.DeviceIds.Select(d => Prefix(d)), Prefix(model.OrganizationId));
}
[HttpPut("delete-organization")]
public async Task PutDeleteOrganization([FromBody] PushUpdateRequestModel model)
{
CheckUsage();
await _pushRegistrationService.DeleteUserRegistrationOrganizationAsync(
model.DeviceIds.Select(d => Prefix(d)), Prefix(model.OrganizationId));
}
[HttpPost("send")]
public async Task PostSend([FromBody] PushSendRequestModel model)
{
CheckUsage();
if (!string.IsNullOrWhiteSpace(model.UserId))
{
_currentContext = currentContext;
_environment = environment;
_pushRegistrationService = pushRegistrationService;
_pushNotificationService = pushNotificationService;
_globalSettings = globalSettings;
await _pushNotificationService.SendPayloadToUserAsync(Prefix(model.UserId),
model.Type.Value, model.Payload, Prefix(model.Identifier), Prefix(model.DeviceId));
}
[HttpPost("register")]
public async Task PostRegister([FromBody] PushRegistrationRequestModel model)
else if (!string.IsNullOrWhiteSpace(model.OrganizationId))
{
CheckUsage();
await _pushRegistrationService.CreateOrUpdateRegistrationAsync(model.PushToken, Prefix(model.DeviceId),
Prefix(model.UserId), Prefix(model.Identifier), model.Type);
}
[HttpDelete("{id}")]
public async Task Delete(string id)
{
CheckUsage();
await _pushRegistrationService.DeleteRegistrationAsync(Prefix(id));
}
[HttpPut("add-organization")]
public async Task PutAddOrganization([FromBody] PushUpdateRequestModel model)
{
CheckUsage();
await _pushRegistrationService.AddUserRegistrationOrganizationAsync(
model.DeviceIds.Select(d => Prefix(d)), Prefix(model.OrganizationId));
}
[HttpPut("delete-organization")]
public async Task PutDeleteOrganization([FromBody] PushUpdateRequestModel model)
{
CheckUsage();
await _pushRegistrationService.DeleteUserRegistrationOrganizationAsync(
model.DeviceIds.Select(d => Prefix(d)), Prefix(model.OrganizationId));
}
[HttpPost("send")]
public async Task PostSend([FromBody] PushSendRequestModel model)
{
CheckUsage();
if (!string.IsNullOrWhiteSpace(model.UserId))
{
await _pushNotificationService.SendPayloadToUserAsync(Prefix(model.UserId),
model.Type.Value, model.Payload, Prefix(model.Identifier), Prefix(model.DeviceId));
}
else if (!string.IsNullOrWhiteSpace(model.OrganizationId))
{
await _pushNotificationService.SendPayloadToOrganizationAsync(Prefix(model.OrganizationId),
model.Type.Value, model.Payload, Prefix(model.Identifier), Prefix(model.DeviceId));
}
}
private string Prefix(string value)
{
if (string.IsNullOrWhiteSpace(value))
{
return null;
}
return $"{_currentContext.InstallationId.Value}_{value}";
}
private void CheckUsage()
{
if (CanUse())
{
return;
}
throw new BadRequestException("Not correctly configured for push relays.");
}
private bool CanUse()
{
if (_environment.IsDevelopment())
{
return true;
}
return _currentContext.InstallationId.HasValue && !_globalSettings.SelfHosted;
await _pushNotificationService.SendPayloadToOrganizationAsync(Prefix(model.OrganizationId),
model.Type.Value, model.Payload, Prefix(model.Identifier), Prefix(model.DeviceId));
}
}
private string Prefix(string value)
{
if (string.IsNullOrWhiteSpace(value))
{
return null;
}
return $"{_currentContext.InstallationId.Value}_{value}";
}
private void CheckUsage()
{
if (CanUse())
{
return;
}
throw new BadRequestException("Not correctly configured for push relays.");
}
private bool CanUse()
{
if (_environment.IsDevelopment())
{
return true;
}
return _currentContext.InstallationId.HasValue && !_globalSettings.SelfHosted;
}
}

View File

@ -7,61 +7,60 @@ using Bit.Core.Utilities;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Bit.Api.Controllers.SelfHosted
namespace Bit.Api.Controllers.SelfHosted;
[Route("organization/sponsorship/self-hosted")]
[Authorize("Application")]
[SelfHosted(SelfHostedOnly = true)]
public class SelfHostedOrganizationSponsorshipsController : Controller
{
[Route("organization/sponsorship/self-hosted")]
[Authorize("Application")]
[SelfHosted(SelfHostedOnly = true)]
public class SelfHostedOrganizationSponsorshipsController : Controller
private readonly IOrganizationRepository _organizationRepository;
private readonly IOrganizationUserRepository _organizationUserRepository;
private readonly IOrganizationSponsorshipRepository _organizationSponsorshipRepository;
private readonly ICreateSponsorshipCommand _offerSponsorshipCommand;
private readonly IRevokeSponsorshipCommand _revokeSponsorshipCommand;
private readonly ICurrentContext _currentContext;
public SelfHostedOrganizationSponsorshipsController(
ICreateSponsorshipCommand offerSponsorshipCommand,
IRevokeSponsorshipCommand revokeSponsorshipCommand,
IOrganizationRepository organizationRepository,
IOrganizationSponsorshipRepository organizationSponsorshipRepository,
IOrganizationUserRepository organizationUserRepository,
ICurrentContext currentContext
)
{
private readonly IOrganizationRepository _organizationRepository;
private readonly IOrganizationUserRepository _organizationUserRepository;
private readonly IOrganizationSponsorshipRepository _organizationSponsorshipRepository;
private readonly ICreateSponsorshipCommand _offerSponsorshipCommand;
private readonly IRevokeSponsorshipCommand _revokeSponsorshipCommand;
private readonly ICurrentContext _currentContext;
_offerSponsorshipCommand = offerSponsorshipCommand;
_revokeSponsorshipCommand = revokeSponsorshipCommand;
_organizationRepository = organizationRepository;
_organizationSponsorshipRepository = organizationSponsorshipRepository;
_organizationUserRepository = organizationUserRepository;
_currentContext = currentContext;
}
public SelfHostedOrganizationSponsorshipsController(
ICreateSponsorshipCommand offerSponsorshipCommand,
IRevokeSponsorshipCommand revokeSponsorshipCommand,
IOrganizationRepository organizationRepository,
IOrganizationSponsorshipRepository organizationSponsorshipRepository,
IOrganizationUserRepository organizationUserRepository,
ICurrentContext currentContext
)
[HttpPost("{sponsoringOrgId}/families-for-enterprise")]
public async Task CreateSponsorship(Guid sponsoringOrgId, [FromBody] OrganizationSponsorshipCreateRequestModel model)
{
await _offerSponsorshipCommand.CreateSponsorshipAsync(
await _organizationRepository.GetByIdAsync(sponsoringOrgId),
await _organizationUserRepository.GetByOrganizationAsync(sponsoringOrgId, _currentContext.UserId ?? default),
model.PlanSponsorshipType, model.SponsoredEmail, model.FriendlyName);
}
[HttpDelete("{sponsoringOrgId}")]
[HttpPost("{sponsoringOrgId}/delete")]
public async Task RevokeSponsorship(Guid sponsoringOrgId)
{
var orgUser = await _organizationUserRepository.GetByOrganizationAsync(sponsoringOrgId, _currentContext.UserId ?? default);
if (orgUser == null)
{
_offerSponsorshipCommand = offerSponsorshipCommand;
_revokeSponsorshipCommand = revokeSponsorshipCommand;
_organizationRepository = organizationRepository;
_organizationSponsorshipRepository = organizationSponsorshipRepository;
_organizationUserRepository = organizationUserRepository;
_currentContext = currentContext;
throw new BadRequestException("Unknown Organization User");
}
[HttpPost("{sponsoringOrgId}/families-for-enterprise")]
public async Task CreateSponsorship(Guid sponsoringOrgId, [FromBody] OrganizationSponsorshipCreateRequestModel model)
{
await _offerSponsorshipCommand.CreateSponsorshipAsync(
await _organizationRepository.GetByIdAsync(sponsoringOrgId),
await _organizationUserRepository.GetByOrganizationAsync(sponsoringOrgId, _currentContext.UserId ?? default),
model.PlanSponsorshipType, model.SponsoredEmail, model.FriendlyName);
}
var existingOrgSponsorship = await _organizationSponsorshipRepository
.GetBySponsoringOrganizationUserIdAsync(orgUser.Id);
[HttpDelete("{sponsoringOrgId}")]
[HttpPost("{sponsoringOrgId}/delete")]
public async Task RevokeSponsorship(Guid sponsoringOrgId)
{
var orgUser = await _organizationUserRepository.GetByOrganizationAsync(sponsoringOrgId, _currentContext.UserId ?? default);
if (orgUser == null)
{
throw new BadRequestException("Unknown Organization User");
}
var existingOrgSponsorship = await _organizationSponsorshipRepository
.GetBySponsoringOrganizationUserIdAsync(orgUser.Id);
await _revokeSponsorshipCommand.RevokeSponsorshipAsync(existingOrgSponsorship);
}
await _revokeSponsorshipCommand.RevokeSponsorshipAsync(existingOrgSponsorship);
}
}

View File

@ -16,323 +16,322 @@ using Bit.Core.Utilities;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Bit.Api.Controllers
namespace Bit.Api.Controllers;
[Route("sends")]
[Authorize("Application")]
public class SendsController : Controller
{
[Route("sends")]
[Authorize("Application")]
public class SendsController : Controller
private readonly ISendRepository _sendRepository;
private readonly IUserService _userService;
private readonly ISendService _sendService;
private readonly ISendFileStorageService _sendFileStorageService;
private readonly ILogger<SendsController> _logger;
private readonly GlobalSettings _globalSettings;
private readonly ICurrentContext _currentContext;
public SendsController(
ISendRepository sendRepository,
IUserService userService,
ISendService sendService,
ISendFileStorageService sendFileStorageService,
ILogger<SendsController> logger,
GlobalSettings globalSettings,
ICurrentContext currentContext)
{
private readonly ISendRepository _sendRepository;
private readonly IUserService _userService;
private readonly ISendService _sendService;
private readonly ISendFileStorageService _sendFileStorageService;
private readonly ILogger<SendsController> _logger;
private readonly GlobalSettings _globalSettings;
private readonly ICurrentContext _currentContext;
_sendRepository = sendRepository;
_userService = userService;
_sendService = sendService;
_sendFileStorageService = sendFileStorageService;
_logger = logger;
_globalSettings = globalSettings;
_currentContext = currentContext;
}
public SendsController(
ISendRepository sendRepository,
IUserService userService,
ISendService sendService,
ISendFileStorageService sendFileStorageService,
ILogger<SendsController> logger,
GlobalSettings globalSettings,
ICurrentContext currentContext)
[AllowAnonymous]
[HttpPost("access/{id}")]
public async Task<IActionResult> Access(string id, [FromBody] SendAccessRequestModel model)
{
// Uncomment whenever we want to require the `send-id` header
//if (!_currentContext.HttpContext.Request.Headers.ContainsKey("Send-Id") ||
// _currentContext.HttpContext.Request.Headers["Send-Id"] != id)
//{
// throw new BadRequestException("Invalid Send-Id header.");
//}
var guid = new Guid(CoreHelpers.Base64UrlDecode(id));
var (send, passwordRequired, passwordInvalid) =
await _sendService.AccessAsync(guid, model.Password);
if (passwordRequired)
{
_sendRepository = sendRepository;
_userService = userService;
_sendService = sendService;
_sendFileStorageService = sendFileStorageService;
_logger = logger;
_globalSettings = globalSettings;
_currentContext = currentContext;
return new UnauthorizedResult();
}
if (passwordInvalid)
{
await Task.Delay(2000);
throw new BadRequestException("Invalid password.");
}
if (send == null)
{
throw new NotFoundException();
}
[AllowAnonymous]
[HttpPost("access/{id}")]
public async Task<IActionResult> Access(string id, [FromBody] SendAccessRequestModel model)
var sendResponse = new SendAccessResponseModel(send, _globalSettings);
if (send.UserId.HasValue && !send.HideEmail.GetValueOrDefault())
{
// Uncomment whenever we want to require the `send-id` header
//if (!_currentContext.HttpContext.Request.Headers.ContainsKey("Send-Id") ||
// _currentContext.HttpContext.Request.Headers["Send-Id"] != id)
//{
// throw new BadRequestException("Invalid Send-Id header.");
//}
var creator = await _userService.GetUserByIdAsync(send.UserId.Value);
sendResponse.CreatorIdentifier = creator.Email;
}
return new ObjectResult(sendResponse);
}
var guid = new Guid(CoreHelpers.Base64UrlDecode(id));
var (send, passwordRequired, passwordInvalid) =
await _sendService.AccessAsync(guid, model.Password);
if (passwordRequired)
{
return new UnauthorizedResult();
}
if (passwordInvalid)
{
await Task.Delay(2000);
throw new BadRequestException("Invalid password.");
}
if (send == null)
{
throw new NotFoundException();
}
[AllowAnonymous]
[HttpPost("{encodedSendId}/access/file/{fileId}")]
public async Task<IActionResult> GetSendFileDownloadData(string encodedSendId,
string fileId, [FromBody] SendAccessRequestModel model)
{
// Uncomment whenever we want to require the `send-id` header
//if (!_currentContext.HttpContext.Request.Headers.ContainsKey("Send-Id") ||
// _currentContext.HttpContext.Request.Headers["Send-Id"] != encodedSendId)
//{
// throw new BadRequestException("Invalid Send-Id header.");
//}
var sendResponse = new SendAccessResponseModel(send, _globalSettings);
if (send.UserId.HasValue && !send.HideEmail.GetValueOrDefault())
{
var creator = await _userService.GetUserByIdAsync(send.UserId.Value);
sendResponse.CreatorIdentifier = creator.Email;
}
return new ObjectResult(sendResponse);
var sendId = new Guid(CoreHelpers.Base64UrlDecode(encodedSendId));
var send = await _sendRepository.GetByIdAsync(sendId);
if (send == null)
{
throw new BadRequestException("Could not locate send");
}
[AllowAnonymous]
[HttpPost("{encodedSendId}/access/file/{fileId}")]
public async Task<IActionResult> GetSendFileDownloadData(string encodedSendId,
string fileId, [FromBody] SendAccessRequestModel model)
var (url, passwordRequired, passwordInvalid) = await _sendService.GetSendFileDownloadUrlAsync(send, fileId,
model.Password);
if (passwordRequired)
{
// Uncomment whenever we want to require the `send-id` header
//if (!_currentContext.HttpContext.Request.Headers.ContainsKey("Send-Id") ||
// _currentContext.HttpContext.Request.Headers["Send-Id"] != encodedSendId)
//{
// throw new BadRequestException("Invalid Send-Id header.");
//}
var sendId = new Guid(CoreHelpers.Base64UrlDecode(encodedSendId));
var send = await _sendRepository.GetByIdAsync(sendId);
if (send == null)
{
throw new BadRequestException("Could not locate send");
}
var (url, passwordRequired, passwordInvalid) = await _sendService.GetSendFileDownloadUrlAsync(send, fileId,
model.Password);
if (passwordRequired)
{
return new UnauthorizedResult();
}
if (passwordInvalid)
{
await Task.Delay(2000);
throw new BadRequestException("Invalid password.");
}
if (send == null)
{
throw new NotFoundException();
}
return new ObjectResult(new SendFileDownloadDataResponseModel()
{
Id = fileId,
Url = url,
});
return new UnauthorizedResult();
}
if (passwordInvalid)
{
await Task.Delay(2000);
throw new BadRequestException("Invalid password.");
}
if (send == null)
{
throw new NotFoundException();
}
[HttpGet("{id}")]
public async Task<SendResponseModel> Get(string id)
return new ObjectResult(new SendFileDownloadDataResponseModel()
{
var userId = _userService.GetProperUserId(User).Value;
var send = await _sendRepository.GetByIdAsync(new Guid(id));
if (send == null || send.UserId != userId)
{
throw new NotFoundException();
}
Id = fileId,
Url = url,
});
}
return new SendResponseModel(send, _globalSettings);
[HttpGet("{id}")]
public async Task<SendResponseModel> Get(string id)
{
var userId = _userService.GetProperUserId(User).Value;
var send = await _sendRepository.GetByIdAsync(new Guid(id));
if (send == null || send.UserId != userId)
{
throw new NotFoundException();
}
[HttpGet("")]
public async Task<ListResponseModel<SendResponseModel>> Get()
return new SendResponseModel(send, _globalSettings);
}
[HttpGet("")]
public async Task<ListResponseModel<SendResponseModel>> Get()
{
var userId = _userService.GetProperUserId(User).Value;
var sends = await _sendRepository.GetManyByUserIdAsync(userId);
var responses = sends.Select(s => new SendResponseModel(s, _globalSettings));
return new ListResponseModel<SendResponseModel>(responses);
}
[HttpPost("")]
public async Task<SendResponseModel> Post([FromBody] SendRequestModel model)
{
model.ValidateCreation();
var userId = _userService.GetProperUserId(User).Value;
var send = model.ToSend(userId, _sendService);
await _sendService.SaveSendAsync(send);
return new SendResponseModel(send, _globalSettings);
}
[HttpPost("file")]
[Obsolete("Deprecated File Send API", false)]
[RequestSizeLimit(Constants.FileSize101mb)]
[DisableFormValueModelBinding]
public async Task<SendResponseModel> PostFile()
{
if (!Request?.ContentType.Contains("multipart/") ?? true)
{
var userId = _userService.GetProperUserId(User).Value;
var sends = await _sendRepository.GetManyByUserIdAsync(userId);
var responses = sends.Select(s => new SendResponseModel(s, _globalSettings));
return new ListResponseModel<SendResponseModel>(responses);
throw new BadRequestException("Invalid content.");
}
[HttpPost("")]
public async Task<SendResponseModel> Post([FromBody] SendRequestModel model)
Send send = null;
await Request.GetSendFileAsync(async (stream, fileName, model) =>
{
model.ValidateCreation();
var userId = _userService.GetProperUserId(User).Value;
var send = model.ToSend(userId, _sendService);
await _sendService.SaveSendAsync(send);
return new SendResponseModel(send, _globalSettings);
var (madeSend, madeData) = model.ToSend(userId, fileName, _sendService);
send = madeSend;
await _sendService.SaveFileSendAsync(send, madeData, model.FileLength.GetValueOrDefault(0));
await _sendService.UploadFileToExistingSendAsync(stream, send);
});
return new SendResponseModel(send, _globalSettings);
}
[HttpPost("file/v2")]
public async Task<SendFileUploadDataResponseModel> PostFile([FromBody] SendRequestModel model)
{
if (model.Type != SendType.File)
{
throw new BadRequestException("Invalid content.");
}
[HttpPost("file")]
[Obsolete("Deprecated File Send API", false)]
[RequestSizeLimit(Constants.FileSize101mb)]
[DisableFormValueModelBinding]
public async Task<SendResponseModel> PostFile()
if (!model.FileLength.HasValue)
{
if (!Request?.ContentType.Contains("multipart/") ?? true)
{
throw new BadRequestException("Invalid content.");
}
Send send = null;
await Request.GetSendFileAsync(async (stream, fileName, model) =>
{
model.ValidateCreation();
var userId = _userService.GetProperUserId(User).Value;
var (madeSend, madeData) = model.ToSend(userId, fileName, _sendService);
send = madeSend;
await _sendService.SaveFileSendAsync(send, madeData, model.FileLength.GetValueOrDefault(0));
await _sendService.UploadFileToExistingSendAsync(stream, send);
});
return new SendResponseModel(send, _globalSettings);
throw new BadRequestException("Invalid content. File size hint is required.");
}
[HttpPost("file/v2")]
public async Task<SendFileUploadDataResponseModel> PostFile([FromBody] SendRequestModel model)
if (model.FileLength.Value > SendService.MAX_FILE_SIZE)
{
if (model.Type != SendType.File)
{
throw new BadRequestException("Invalid content.");
}
if (!model.FileLength.HasValue)
{
throw new BadRequestException("Invalid content. File size hint is required.");
}
if (model.FileLength.Value > SendService.MAX_FILE_SIZE)
{
throw new BadRequestException($"Max file size is {SendService.MAX_FILE_SIZE_READABLE}.");
}
var userId = _userService.GetProperUserId(User).Value;
var (send, data) = model.ToSend(userId, model.File.FileName, _sendService);
var uploadUrl = await _sendService.SaveFileSendAsync(send, data, model.FileLength.Value);
return new SendFileUploadDataResponseModel
{
Url = uploadUrl,
FileUploadType = _sendFileStorageService.FileUploadType,
SendResponse = new SendResponseModel(send, _globalSettings)
};
throw new BadRequestException($"Max file size is {SendService.MAX_FILE_SIZE_READABLE}.");
}
[HttpGet("{id}/file/{fileId}")]
public async Task<SendFileUploadDataResponseModel> RenewFileUpload(string id, string fileId)
var userId = _userService.GetProperUserId(User).Value;
var (send, data) = model.ToSend(userId, model.File.FileName, _sendService);
var uploadUrl = await _sendService.SaveFileSendAsync(send, data, model.FileLength.Value);
return new SendFileUploadDataResponseModel
{
var userId = _userService.GetProperUserId(User).Value;
var sendId = new Guid(id);
var send = await _sendRepository.GetByIdAsync(sendId);
var fileData = JsonSerializer.Deserialize<SendFileData>(send?.Data);
Url = uploadUrl,
FileUploadType = _sendFileStorageService.FileUploadType,
SendResponse = new SendResponseModel(send, _globalSettings)
};
}
if (send == null || send.Type != SendType.File || (send.UserId.HasValue && send.UserId.Value != userId) ||
!send.UserId.HasValue || fileData.Id != fileId || fileData.Validated)
{
// Not found if Send isn't found, user doesn't have access, request is faulty,
// or we've already validated the file. This last is to emulate create-only blob permissions for Azure
throw new NotFoundException();
}
[HttpGet("{id}/file/{fileId}")]
public async Task<SendFileUploadDataResponseModel> RenewFileUpload(string id, string fileId)
{
var userId = _userService.GetProperUserId(User).Value;
var sendId = new Guid(id);
var send = await _sendRepository.GetByIdAsync(sendId);
var fileData = JsonSerializer.Deserialize<SendFileData>(send?.Data);
return new SendFileUploadDataResponseModel
{
Url = await _sendFileStorageService.GetSendFileUploadUrlAsync(send, fileId),
FileUploadType = _sendFileStorageService.FileUploadType,
SendResponse = new SendResponseModel(send, _globalSettings),
};
if (send == null || send.Type != SendType.File || (send.UserId.HasValue && send.UserId.Value != userId) ||
!send.UserId.HasValue || fileData.Id != fileId || fileData.Validated)
{
// Not found if Send isn't found, user doesn't have access, request is faulty,
// or we've already validated the file. This last is to emulate create-only blob permissions for Azure
throw new NotFoundException();
}
[HttpPost("{id}/file/{fileId}")]
[SelfHosted(SelfHostedOnly = true)]
[RequestSizeLimit(Constants.FileSize501mb)]
[DisableFormValueModelBinding]
public async Task PostFileForExistingSend(string id, string fileId)
return new SendFileUploadDataResponseModel
{
if (!Request?.ContentType.Contains("multipart/") ?? true)
{
throw new BadRequestException("Invalid content.");
}
Url = await _sendFileStorageService.GetSendFileUploadUrlAsync(send, fileId),
FileUploadType = _sendFileStorageService.FileUploadType,
SendResponse = new SendResponseModel(send, _globalSettings),
};
}
var send = await _sendRepository.GetByIdAsync(new Guid(id));
await Request.GetFileAsync(async (stream) =>
{
await _sendService.UploadFileToExistingSendAsync(stream, send);
});
[HttpPost("{id}/file/{fileId}")]
[SelfHosted(SelfHostedOnly = true)]
[RequestSizeLimit(Constants.FileSize501mb)]
[DisableFormValueModelBinding]
public async Task PostFileForExistingSend(string id, string fileId)
{
if (!Request?.ContentType.Contains("multipart/") ?? true)
{
throw new BadRequestException("Invalid content.");
}
[AllowAnonymous]
[HttpPost("file/validate/azure")]
public async Task<ObjectResult> AzureValidateFile()
var send = await _sendRepository.GetByIdAsync(new Guid(id));
await Request.GetFileAsync(async (stream) =>
{
await _sendService.UploadFileToExistingSendAsync(stream, send);
});
}
[AllowAnonymous]
[HttpPost("file/validate/azure")]
public async Task<ObjectResult> AzureValidateFile()
{
return await ApiHelpers.HandleAzureEvents(Request, new Dictionary<string, Func<EventGridEvent, Task>>
{
return await ApiHelpers.HandleAzureEvents(Request, new Dictionary<string, Func<EventGridEvent, Task>>
{
"Microsoft.Storage.BlobCreated", async (eventGridEvent) =>
{
"Microsoft.Storage.BlobCreated", async (eventGridEvent) =>
try
{
try
var blobName = eventGridEvent.Subject.Split($"{AzureSendFileStorageService.FilesContainerName}/blobs/")[1];
var sendId = AzureSendFileStorageService.SendIdFromBlobName(blobName);
var send = await _sendRepository.GetByIdAsync(new Guid(sendId));
if (send == null)
{
var blobName = eventGridEvent.Subject.Split($"{AzureSendFileStorageService.FilesContainerName}/blobs/")[1];
var sendId = AzureSendFileStorageService.SendIdFromBlobName(blobName);
var send = await _sendRepository.GetByIdAsync(new Guid(sendId));
if (send == null)
if (_sendFileStorageService is AzureSendFileStorageService azureSendFileStorageService)
{
if (_sendFileStorageService is AzureSendFileStorageService azureSendFileStorageService)
{
await azureSendFileStorageService.DeleteBlobAsync(blobName);
}
return;
await azureSendFileStorageService.DeleteBlobAsync(blobName);
}
await _sendService.ValidateSendFile(send);
}
catch (Exception e)
{
_logger.LogError(e, $"Uncaught exception occurred while handling event grid event: {JsonSerializer.Serialize(eventGridEvent)}");
return;
}
await _sendService.ValidateSendFile(send);
}
catch (Exception e)
{
_logger.LogError(e, $"Uncaught exception occurred while handling event grid event: {JsonSerializer.Serialize(eventGridEvent)}");
return;
}
}
});
}
[HttpPut("{id}")]
public async Task<SendResponseModel> Put(string id, [FromBody] SendRequestModel model)
{
model.ValidateEdit();
var userId = _userService.GetProperUserId(User).Value;
var send = await _sendRepository.GetByIdAsync(new Guid(id));
if (send == null || send.UserId != userId)
{
throw new NotFoundException();
}
});
}
await _sendService.SaveSendAsync(model.ToSend(send, _sendService));
return new SendResponseModel(send, _globalSettings);
}
[HttpPut("{id}/remove-password")]
public async Task<SendResponseModel> PutRemovePassword(string id)
[HttpPut("{id}")]
public async Task<SendResponseModel> Put(string id, [FromBody] SendRequestModel model)
{
model.ValidateEdit();
var userId = _userService.GetProperUserId(User).Value;
var send = await _sendRepository.GetByIdAsync(new Guid(id));
if (send == null || send.UserId != userId)
{
var userId = _userService.GetProperUserId(User).Value;
var send = await _sendRepository.GetByIdAsync(new Guid(id));
if (send == null || send.UserId != userId)
{
throw new NotFoundException();
}
send.Password = null;
await _sendService.SaveSendAsync(send);
return new SendResponseModel(send, _globalSettings);
throw new NotFoundException();
}
[HttpDelete("{id}")]
public async Task Delete(string id)
await _sendService.SaveSendAsync(model.ToSend(send, _sendService));
return new SendResponseModel(send, _globalSettings);
}
[HttpPut("{id}/remove-password")]
public async Task<SendResponseModel> PutRemovePassword(string id)
{
var userId = _userService.GetProperUserId(User).Value;
var send = await _sendRepository.GetByIdAsync(new Guid(id));
if (send == null || send.UserId != userId)
{
var userId = _userService.GetProperUserId(User).Value;
var send = await _sendRepository.GetByIdAsync(new Guid(id));
if (send == null || send.UserId != userId)
{
throw new NotFoundException();
}
await _sendService.DeleteSendAsync(send);
throw new NotFoundException();
}
send.Password = null;
await _sendService.SaveSendAsync(send);
return new SendResponseModel(send, _globalSettings);
}
[HttpDelete("{id}")]
public async Task Delete(string id)
{
var userId = _userService.GetProperUserId(User).Value;
var send = await _sendRepository.GetByIdAsync(new Guid(id));
if (send == null || send.UserId != userId)
{
throw new NotFoundException();
}
await _sendService.DeleteSendAsync(send);
}
}

View File

@ -4,47 +4,46 @@ using Bit.Core.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Bit.Api.Controllers
namespace Bit.Api.Controllers;
[Route("settings")]
[Authorize("Application")]
public class SettingsController : Controller
{
[Route("settings")]
[Authorize("Application")]
public class SettingsController : Controller
private readonly IUserService _userService;
public SettingsController(
IUserService userService)
{
private readonly IUserService _userService;
_userService = userService;
}
public SettingsController(
IUserService userService)
[HttpGet("domains")]
public async Task<DomainsResponseModel> GetDomains(bool excluded = true)
{
var user = await _userService.GetUserByPrincipalAsync(User);
if (user == null)
{
_userService = userService;
throw new UnauthorizedAccessException();
}
[HttpGet("domains")]
public async Task<DomainsResponseModel> GetDomains(bool excluded = true)
{
var user = await _userService.GetUserByPrincipalAsync(User);
if (user == null)
{
throw new UnauthorizedAccessException();
}
var response = new DomainsResponseModel(user, excluded);
return response;
}
var response = new DomainsResponseModel(user, excluded);
return response;
[HttpPut("domains")]
[HttpPost("domains")]
public async Task<DomainsResponseModel> PutDomains([FromBody] UpdateDomainsRequestModel model)
{
var user = await _userService.GetUserByPrincipalAsync(User);
if (user == null)
{
throw new UnauthorizedAccessException();
}
[HttpPut("domains")]
[HttpPost("domains")]
public async Task<DomainsResponseModel> PutDomains([FromBody] UpdateDomainsRequestModel model)
{
var user = await _userService.GetUserByPrincipalAsync(User);
if (user == null)
{
throw new UnauthorizedAccessException();
}
await _userService.SaveUserAsync(model.ToUser(user), true);
await _userService.SaveUserAsync(model.ToUser(user), true);
var response = new DomainsResponseModel(user);
return response;
}
var response = new DomainsResponseModel(user);
return response;
}
}

View File

@ -10,85 +10,84 @@ using Bit.Core.Settings;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Bit.Api.Controllers
namespace Bit.Api.Controllers;
[Route("sync")]
[Authorize("Application")]
public class SyncController : Controller
{
[Route("sync")]
[Authorize("Application")]
public class SyncController : Controller
private readonly IUserService _userService;
private readonly IFolderRepository _folderRepository;
private readonly ICipherRepository _cipherRepository;
private readonly ICollectionRepository _collectionRepository;
private readonly ICollectionCipherRepository _collectionCipherRepository;
private readonly IOrganizationUserRepository _organizationUserRepository;
private readonly IProviderUserRepository _providerUserRepository;
private readonly IPolicyRepository _policyRepository;
private readonly ISendRepository _sendRepository;
private readonly GlobalSettings _globalSettings;
public SyncController(
IUserService userService,
IFolderRepository folderRepository,
ICipherRepository cipherRepository,
ICollectionRepository collectionRepository,
ICollectionCipherRepository collectionCipherRepository,
IOrganizationUserRepository organizationUserRepository,
IProviderUserRepository providerUserRepository,
IPolicyRepository policyRepository,
ISendRepository sendRepository,
GlobalSettings globalSettings)
{
private readonly IUserService _userService;
private readonly IFolderRepository _folderRepository;
private readonly ICipherRepository _cipherRepository;
private readonly ICollectionRepository _collectionRepository;
private readonly ICollectionCipherRepository _collectionCipherRepository;
private readonly IOrganizationUserRepository _organizationUserRepository;
private readonly IProviderUserRepository _providerUserRepository;
private readonly IPolicyRepository _policyRepository;
private readonly ISendRepository _sendRepository;
private readonly GlobalSettings _globalSettings;
_userService = userService;
_folderRepository = folderRepository;
_cipherRepository = cipherRepository;
_collectionRepository = collectionRepository;
_collectionCipherRepository = collectionCipherRepository;
_organizationUserRepository = organizationUserRepository;
_providerUserRepository = providerUserRepository;
_policyRepository = policyRepository;
_sendRepository = sendRepository;
_globalSettings = globalSettings;
}
public SyncController(
IUserService userService,
IFolderRepository folderRepository,
ICipherRepository cipherRepository,
ICollectionRepository collectionRepository,
ICollectionCipherRepository collectionCipherRepository,
IOrganizationUserRepository organizationUserRepository,
IProviderUserRepository providerUserRepository,
IPolicyRepository policyRepository,
ISendRepository sendRepository,
GlobalSettings globalSettings)
[HttpGet("")]
public async Task<SyncResponseModel> Get([FromQuery] bool excludeDomains = false)
{
var user = await _userService.GetUserByPrincipalAsync(User);
if (user == null)
{
_userService = userService;
_folderRepository = folderRepository;
_cipherRepository = cipherRepository;
_collectionRepository = collectionRepository;
_collectionCipherRepository = collectionCipherRepository;
_organizationUserRepository = organizationUserRepository;
_providerUserRepository = providerUserRepository;
_policyRepository = policyRepository;
_sendRepository = sendRepository;
_globalSettings = globalSettings;
throw new BadRequestException("User not found.");
}
[HttpGet("")]
public async Task<SyncResponseModel> Get([FromQuery] bool excludeDomains = false)
{
var user = await _userService.GetUserByPrincipalAsync(User);
if (user == null)
{
throw new BadRequestException("User not found.");
}
var organizationUserDetails = await _organizationUserRepository.GetManyDetailsByUserAsync(user.Id,
OrganizationUserStatusType.Confirmed);
var providerUserDetails = await _providerUserRepository.GetManyDetailsByUserAsync(user.Id,
var organizationUserDetails = await _organizationUserRepository.GetManyDetailsByUserAsync(user.Id,
OrganizationUserStatusType.Confirmed);
var providerUserDetails = await _providerUserRepository.GetManyDetailsByUserAsync(user.Id,
ProviderUserStatusType.Confirmed);
var providerUserOrganizationDetails =
await _providerUserRepository.GetManyOrganizationDetailsByUserAsync(user.Id,
ProviderUserStatusType.Confirmed);
var providerUserOrganizationDetails =
await _providerUserRepository.GetManyOrganizationDetailsByUserAsync(user.Id,
ProviderUserStatusType.Confirmed);
var hasEnabledOrgs = organizationUserDetails.Any(o => o.Enabled);
var folders = await _folderRepository.GetManyByUserIdAsync(user.Id);
var ciphers = await _cipherRepository.GetManyByUserIdAsync(user.Id, hasEnabledOrgs);
var sends = await _sendRepository.GetManyByUserIdAsync(user.Id);
var hasEnabledOrgs = organizationUserDetails.Any(o => o.Enabled);
var folders = await _folderRepository.GetManyByUserIdAsync(user.Id);
var ciphers = await _cipherRepository.GetManyByUserIdAsync(user.Id, hasEnabledOrgs);
var sends = await _sendRepository.GetManyByUserIdAsync(user.Id);
IEnumerable<CollectionDetails> collections = null;
IDictionary<Guid, IGrouping<Guid, CollectionCipher>> collectionCiphersGroupDict = null;
IEnumerable<Policy> policies = null;
if (hasEnabledOrgs)
{
collections = await _collectionRepository.GetManyByUserIdAsync(user.Id);
var collectionCiphers = await _collectionCipherRepository.GetManyByUserIdAsync(user.Id);
collectionCiphersGroupDict = collectionCiphers.GroupBy(c => c.CipherId).ToDictionary(s => s.Key);
policies = await _policyRepository.GetManyByUserIdAsync(user.Id);
}
var userTwoFactorEnabled = await _userService.TwoFactorIsEnabledAsync(user);
var userHasPremiumFromOrganization = await _userService.HasPremiumFromOrganization(user);
var response = new SyncResponseModel(_globalSettings, user, userTwoFactorEnabled, userHasPremiumFromOrganization, organizationUserDetails,
providerUserDetails, providerUserOrganizationDetails, folders, collections, ciphers,
collectionCiphersGroupDict, excludeDomains, policies, sends);
return response;
IEnumerable<CollectionDetails> collections = null;
IDictionary<Guid, IGrouping<Guid, CollectionCipher>> collectionCiphersGroupDict = null;
IEnumerable<Policy> policies = null;
if (hasEnabledOrgs)
{
collections = await _collectionRepository.GetManyByUserIdAsync(user.Id);
var collectionCiphers = await _collectionCipherRepository.GetManyByUserIdAsync(user.Id);
collectionCiphersGroupDict = collectionCiphers.GroupBy(c => c.CipherId).ToDictionary(s => s.Key);
policies = await _policyRepository.GetManyByUserIdAsync(user.Id);
}
var userTwoFactorEnabled = await _userService.TwoFactorIsEnabledAsync(user);
var userHasPremiumFromOrganization = await _userService.HasPremiumFromOrganization(user);
var response = new SyncResponseModel(_globalSettings, user, userTwoFactorEnabled, userHasPremiumFromOrganization, organizationUserDetails,
providerUserDetails, providerUserOrganizationDetails, folders, collections, ciphers,
collectionCiphersGroupDict, excludeDomains, policies, sends);
return response;
}
}

View File

@ -16,443 +16,442 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
namespace Bit.Api.Controllers
namespace Bit.Api.Controllers;
[Route("two-factor")]
[Authorize("Web")]
public class TwoFactorController : Controller
{
[Route("two-factor")]
[Authorize("Web")]
public class TwoFactorController : Controller
private readonly IUserService _userService;
private readonly IOrganizationRepository _organizationRepository;
private readonly IOrganizationService _organizationService;
private readonly GlobalSettings _globalSettings;
private readonly UserManager<User> _userManager;
private readonly ICurrentContext _currentContext;
public TwoFactorController(
IUserService userService,
IOrganizationRepository organizationRepository,
IOrganizationService organizationService,
GlobalSettings globalSettings,
UserManager<User> userManager,
ICurrentContext currentContext)
{
private readonly IUserService _userService;
private readonly IOrganizationRepository _organizationRepository;
private readonly IOrganizationService _organizationService;
private readonly GlobalSettings _globalSettings;
private readonly UserManager<User> _userManager;
private readonly ICurrentContext _currentContext;
_userService = userService;
_organizationRepository = organizationRepository;
_organizationService = organizationService;
_globalSettings = globalSettings;
_userManager = userManager;
_currentContext = currentContext;
}
public TwoFactorController(
IUserService userService,
IOrganizationRepository organizationRepository,
IOrganizationService organizationService,
GlobalSettings globalSettings,
UserManager<User> userManager,
ICurrentContext currentContext)
[HttpGet("")]
public async Task<ListResponseModel<TwoFactorProviderResponseModel>> Get()
{
var user = await _userService.GetUserByPrincipalAsync(User);
if (user == null)
{
_userService = userService;
_organizationRepository = organizationRepository;
_organizationService = organizationService;
_globalSettings = globalSettings;
_userManager = userManager;
_currentContext = currentContext;
throw new UnauthorizedAccessException();
}
[HttpGet("")]
public async Task<ListResponseModel<TwoFactorProviderResponseModel>> Get()
{
var user = await _userService.GetUserByPrincipalAsync(User);
if (user == null)
{
throw new UnauthorizedAccessException();
}
var providers = user.GetTwoFactorProviders()?.Select(
p => new TwoFactorProviderResponseModel(p.Key, p.Value));
return new ListResponseModel<TwoFactorProviderResponseModel>(providers);
}
var providers = user.GetTwoFactorProviders()?.Select(
p => new TwoFactorProviderResponseModel(p.Key, p.Value));
return new ListResponseModel<TwoFactorProviderResponseModel>(providers);
[HttpGet("~/organizations/{id}/two-factor")]
public async Task<ListResponseModel<TwoFactorProviderResponseModel>> GetOrganization(string id)
{
var orgIdGuid = new Guid(id);
if (!await _currentContext.OrganizationAdmin(orgIdGuid))
{
throw new NotFoundException();
}
[HttpGet("~/organizations/{id}/two-factor")]
public async Task<ListResponseModel<TwoFactorProviderResponseModel>> GetOrganization(string id)
var organization = await _organizationRepository.GetByIdAsync(orgIdGuid);
if (organization == null)
{
var orgIdGuid = new Guid(id);
if (!await _currentContext.OrganizationAdmin(orgIdGuid))
{
throw new NotFoundException();
}
var organization = await _organizationRepository.GetByIdAsync(orgIdGuid);
if (organization == null)
{
throw new NotFoundException();
}
var providers = organization.GetTwoFactorProviders()?.Select(
p => new TwoFactorProviderResponseModel(p.Key, p.Value));
return new ListResponseModel<TwoFactorProviderResponseModel>(providers);
throw new NotFoundException();
}
[HttpPost("get-authenticator")]
public async Task<TwoFactorAuthenticatorResponseModel> GetAuthenticator([FromBody] SecretVerificationRequestModel model)
var providers = organization.GetTwoFactorProviders()?.Select(
p => new TwoFactorProviderResponseModel(p.Key, p.Value));
return new ListResponseModel<TwoFactorProviderResponseModel>(providers);
}
[HttpPost("get-authenticator")]
public async Task<TwoFactorAuthenticatorResponseModel> GetAuthenticator([FromBody] SecretVerificationRequestModel model)
{
var user = await CheckAsync(model, false);
var response = new TwoFactorAuthenticatorResponseModel(user);
return response;
}
[HttpPut("authenticator")]
[HttpPost("authenticator")]
public async Task<TwoFactorAuthenticatorResponseModel> PutAuthenticator(
[FromBody] UpdateTwoFactorAuthenticatorRequestModel model)
{
var user = await CheckAsync(model, false);
model.ToUser(user);
if (!await _userManager.VerifyTwoFactorTokenAsync(user,
CoreHelpers.CustomProviderName(TwoFactorProviderType.Authenticator), model.Token))
{
var user = await CheckAsync(model, false);
var response = new TwoFactorAuthenticatorResponseModel(user);
return response;
}
[HttpPut("authenticator")]
[HttpPost("authenticator")]
public async Task<TwoFactorAuthenticatorResponseModel> PutAuthenticator(
[FromBody] UpdateTwoFactorAuthenticatorRequestModel model)
{
var user = await CheckAsync(model, false);
model.ToUser(user);
if (!await _userManager.VerifyTwoFactorTokenAsync(user,
CoreHelpers.CustomProviderName(TwoFactorProviderType.Authenticator), model.Token))
{
await Task.Delay(2000);
throw new BadRequestException("Token", "Invalid token.");
}
await _userService.UpdateTwoFactorProviderAsync(user, TwoFactorProviderType.Authenticator);
var response = new TwoFactorAuthenticatorResponseModel(user);
return response;
}
[HttpPost("get-yubikey")]
public async Task<TwoFactorYubiKeyResponseModel> GetYubiKey([FromBody] SecretVerificationRequestModel model)
{
var user = await CheckAsync(model, true);
var response = new TwoFactorYubiKeyResponseModel(user);
return response;
}
[HttpPut("yubikey")]
[HttpPost("yubikey")]
public async Task<TwoFactorYubiKeyResponseModel> PutYubiKey([FromBody] UpdateTwoFactorYubicoOtpRequestModel model)
{
var user = await CheckAsync(model, true);
model.ToUser(user);
await ValidateYubiKeyAsync(user, nameof(model.Key1), model.Key1);
await ValidateYubiKeyAsync(user, nameof(model.Key2), model.Key2);
await ValidateYubiKeyAsync(user, nameof(model.Key3), model.Key3);
await ValidateYubiKeyAsync(user, nameof(model.Key4), model.Key4);
await ValidateYubiKeyAsync(user, nameof(model.Key5), model.Key5);
await _userService.UpdateTwoFactorProviderAsync(user, TwoFactorProviderType.YubiKey);
var response = new TwoFactorYubiKeyResponseModel(user);
return response;
}
[HttpPost("get-duo")]
public async Task<TwoFactorDuoResponseModel> GetDuo([FromBody] SecretVerificationRequestModel model)
{
var user = await CheckAsync(model, true);
var response = new TwoFactorDuoResponseModel(user);
return response;
}
[HttpPut("duo")]
[HttpPost("duo")]
public async Task<TwoFactorDuoResponseModel> PutDuo([FromBody] UpdateTwoFactorDuoRequestModel model)
{
var user = await CheckAsync(model, true);
try
{
var duoApi = new DuoApi(model.IntegrationKey, model.SecretKey, model.Host);
duoApi.JSONApiCall<object>("GET", "/auth/v2/check");
}
catch (DuoException)
{
throw new BadRequestException("Duo configuration settings are not valid. Please re-check the Duo Admin panel.");
}
model.ToUser(user);
await _userService.UpdateTwoFactorProviderAsync(user, TwoFactorProviderType.Duo);
var response = new TwoFactorDuoResponseModel(user);
return response;
}
[HttpPost("~/organizations/{id}/two-factor/get-duo")]
public async Task<TwoFactorDuoResponseModel> GetOrganizationDuo(string id,
[FromBody] SecretVerificationRequestModel model)
{
var user = await CheckAsync(model, false);
var orgIdGuid = new Guid(id);
if (!await _currentContext.ManagePolicies(orgIdGuid))
{
throw new NotFoundException();
}
var organization = await _organizationRepository.GetByIdAsync(orgIdGuid);
if (organization == null)
{
throw new NotFoundException();
}
var response = new TwoFactorDuoResponseModel(organization);
return response;
}
[HttpPut("~/organizations/{id}/two-factor/duo")]
[HttpPost("~/organizations/{id}/two-factor/duo")]
public async Task<TwoFactorDuoResponseModel> PutOrganizationDuo(string id,
[FromBody] UpdateTwoFactorDuoRequestModel model)
{
var user = await CheckAsync(model, false);
var orgIdGuid = new Guid(id);
if (!await _currentContext.ManagePolicies(orgIdGuid))
{
throw new NotFoundException();
}
var organization = await _organizationRepository.GetByIdAsync(orgIdGuid);
if (organization == null)
{
throw new NotFoundException();
}
try
{
var duoApi = new DuoApi(model.IntegrationKey, model.SecretKey, model.Host);
duoApi.JSONApiCall<object>("GET", "/auth/v2/check");
}
catch (DuoException)
{
throw new BadRequestException("Duo configuration settings are not valid. Please re-check the Duo Admin panel.");
}
model.ToOrganization(organization);
await _organizationService.UpdateTwoFactorProviderAsync(organization,
TwoFactorProviderType.OrganizationDuo);
var response = new TwoFactorDuoResponseModel(organization);
return response;
}
[HttpPost("get-webauthn")]
public async Task<TwoFactorWebAuthnResponseModel> GetWebAuthn([FromBody] SecretVerificationRequestModel model)
{
var user = await CheckAsync(model, true);
var response = new TwoFactorWebAuthnResponseModel(user);
return response;
}
[HttpPost("get-webauthn-challenge")]
public async Task<CredentialCreateOptions> GetWebAuthnChallenge([FromBody] SecretVerificationRequestModel model)
{
var user = await CheckAsync(model, true);
var reg = await _userService.StartWebAuthnRegistrationAsync(user);
return reg;
}
[HttpPut("webauthn")]
[HttpPost("webauthn")]
public async Task<TwoFactorWebAuthnResponseModel> PutWebAuthn([FromBody] TwoFactorWebAuthnRequestModel model)
{
var user = await CheckAsync(model, true);
var success = await _userService.CompleteWebAuthRegistrationAsync(
user, model.Id.Value, model.Name, model.DeviceResponse);
if (!success)
{
throw new BadRequestException("Unable to complete WebAuthn registration.");
}
var response = new TwoFactorWebAuthnResponseModel(user);
return response;
}
[HttpDelete("webauthn")]
public async Task<TwoFactorWebAuthnResponseModel> DeleteWebAuthn([FromBody] TwoFactorWebAuthnDeleteRequestModel model)
{
var user = await CheckAsync(model, true);
await _userService.DeleteWebAuthnKeyAsync(user, model.Id.Value);
var response = new TwoFactorWebAuthnResponseModel(user);
return response;
}
[HttpPost("get-email")]
public async Task<TwoFactorEmailResponseModel> GetEmail([FromBody] SecretVerificationRequestModel model)
{
var user = await CheckAsync(model, false);
var response = new TwoFactorEmailResponseModel(user);
return response;
}
[HttpPost("send-email")]
public async Task SendEmail([FromBody] TwoFactorEmailRequestModel model)
{
var user = await CheckAsync(model, false);
model.ToUser(user);
await _userService.SendTwoFactorEmailAsync(user);
}
[AllowAnonymous]
[HttpPost("send-email-login")]
public async Task SendEmailLogin([FromBody] TwoFactorEmailRequestModel model)
{
var user = await _userManager.FindByEmailAsync(model.Email.ToLowerInvariant());
if (user != null)
{
if (await _userService.VerifySecretAsync(user, model.Secret))
{
var isBecauseNewDeviceLogin = false;
if (user.GetTwoFactorProvider(TwoFactorProviderType.Email) is null
&&
await _userService.Needs2FABecauseNewDeviceAsync(user, model.DeviceIdentifier, null))
{
model.ToUser(user);
isBecauseNewDeviceLogin = true;
}
await _userService.SendTwoFactorEmailAsync(user, isBecauseNewDeviceLogin);
return;
}
}
await Task.Delay(2000);
throw new BadRequestException("Cannot send two-factor email.");
throw new BadRequestException("Token", "Invalid token.");
}
[HttpPut("email")]
[HttpPost("email")]
public async Task<TwoFactorEmailResponseModel> PutEmail([FromBody] UpdateTwoFactorEmailRequestModel model)
await _userService.UpdateTwoFactorProviderAsync(user, TwoFactorProviderType.Authenticator);
var response = new TwoFactorAuthenticatorResponseModel(user);
return response;
}
[HttpPost("get-yubikey")]
public async Task<TwoFactorYubiKeyResponseModel> GetYubiKey([FromBody] SecretVerificationRequestModel model)
{
var user = await CheckAsync(model, true);
var response = new TwoFactorYubiKeyResponseModel(user);
return response;
}
[HttpPut("yubikey")]
[HttpPost("yubikey")]
public async Task<TwoFactorYubiKeyResponseModel> PutYubiKey([FromBody] UpdateTwoFactorYubicoOtpRequestModel model)
{
var user = await CheckAsync(model, true);
model.ToUser(user);
await ValidateYubiKeyAsync(user, nameof(model.Key1), model.Key1);
await ValidateYubiKeyAsync(user, nameof(model.Key2), model.Key2);
await ValidateYubiKeyAsync(user, nameof(model.Key3), model.Key3);
await ValidateYubiKeyAsync(user, nameof(model.Key4), model.Key4);
await ValidateYubiKeyAsync(user, nameof(model.Key5), model.Key5);
await _userService.UpdateTwoFactorProviderAsync(user, TwoFactorProviderType.YubiKey);
var response = new TwoFactorYubiKeyResponseModel(user);
return response;
}
[HttpPost("get-duo")]
public async Task<TwoFactorDuoResponseModel> GetDuo([FromBody] SecretVerificationRequestModel model)
{
var user = await CheckAsync(model, true);
var response = new TwoFactorDuoResponseModel(user);
return response;
}
[HttpPut("duo")]
[HttpPost("duo")]
public async Task<TwoFactorDuoResponseModel> PutDuo([FromBody] UpdateTwoFactorDuoRequestModel model)
{
var user = await CheckAsync(model, true);
try
{
var user = await CheckAsync(model, false);
model.ToUser(user);
if (!await _userManager.VerifyTwoFactorTokenAsync(user,
CoreHelpers.CustomProviderName(TwoFactorProviderType.Email), model.Token))
{
await Task.Delay(2000);
throw new BadRequestException("Token", "Invalid token.");
}
await _userService.UpdateTwoFactorProviderAsync(user, TwoFactorProviderType.Email);
var response = new TwoFactorEmailResponseModel(user);
return response;
var duoApi = new DuoApi(model.IntegrationKey, model.SecretKey, model.Host);
duoApi.JSONApiCall<object>("GET", "/auth/v2/check");
}
catch (DuoException)
{
throw new BadRequestException("Duo configuration settings are not valid. Please re-check the Duo Admin panel.");
}
[HttpPut("disable")]
[HttpPost("disable")]
public async Task<TwoFactorProviderResponseModel> PutDisable([FromBody] TwoFactorProviderRequestModel model)
model.ToUser(user);
await _userService.UpdateTwoFactorProviderAsync(user, TwoFactorProviderType.Duo);
var response = new TwoFactorDuoResponseModel(user);
return response;
}
[HttpPost("~/organizations/{id}/two-factor/get-duo")]
public async Task<TwoFactorDuoResponseModel> GetOrganizationDuo(string id,
[FromBody] SecretVerificationRequestModel model)
{
var user = await CheckAsync(model, false);
var orgIdGuid = new Guid(id);
if (!await _currentContext.ManagePolicies(orgIdGuid))
{
var user = await CheckAsync(model, false);
await _userService.DisableTwoFactorProviderAsync(user, model.Type.Value, _organizationService);
var response = new TwoFactorProviderResponseModel(model.Type.Value, user);
return response;
throw new NotFoundException();
}
[HttpPut("~/organizations/{id}/two-factor/disable")]
[HttpPost("~/organizations/{id}/two-factor/disable")]
public async Task<TwoFactorProviderResponseModel> PutOrganizationDisable(string id,
[FromBody] TwoFactorProviderRequestModel model)
var organization = await _organizationRepository.GetByIdAsync(orgIdGuid);
if (organization == null)
{
var user = await CheckAsync(model, false);
var orgIdGuid = new Guid(id);
if (!await _currentContext.ManagePolicies(orgIdGuid))
{
throw new NotFoundException();
}
var organization = await _organizationRepository.GetByIdAsync(orgIdGuid);
if (organization == null)
{
throw new NotFoundException();
}
await _organizationService.DisableTwoFactorProviderAsync(organization, model.Type.Value);
var response = new TwoFactorProviderResponseModel(model.Type.Value, organization);
return response;
throw new NotFoundException();
}
[HttpPost("get-recover")]
public async Task<TwoFactorRecoverResponseModel> GetRecover([FromBody] SecretVerificationRequestModel model)
var response = new TwoFactorDuoResponseModel(organization);
return response;
}
[HttpPut("~/organizations/{id}/two-factor/duo")]
[HttpPost("~/organizations/{id}/two-factor/duo")]
public async Task<TwoFactorDuoResponseModel> PutOrganizationDuo(string id,
[FromBody] UpdateTwoFactorDuoRequestModel model)
{
var user = await CheckAsync(model, false);
var orgIdGuid = new Guid(id);
if (!await _currentContext.ManagePolicies(orgIdGuid))
{
var user = await CheckAsync(model, false);
var response = new TwoFactorRecoverResponseModel(user);
return response;
throw new NotFoundException();
}
[HttpPost("recover")]
[AllowAnonymous]
public async Task PostRecover([FromBody] TwoFactorRecoveryRequestModel model)
var organization = await _organizationRepository.GetByIdAsync(orgIdGuid);
if (organization == null)
{
if (!await _userService.RecoverTwoFactorAsync(model.Email, model.MasterPasswordHash, model.RecoveryCode,
_organizationService))
{
await Task.Delay(2000);
throw new BadRequestException(string.Empty, "Invalid information. Try again.");
}
throw new NotFoundException();
}
[HttpGet("get-device-verification-settings")]
public async Task<DeviceVerificationResponseModel> GetDeviceVerificationSettings()
try
{
var user = await _userService.GetUserByPrincipalAsync(User);
if (user == null)
{
throw new UnauthorizedAccessException();
}
if (User.Claims.HasSsoIdP())
{
return new DeviceVerificationResponseModel(false, false);
}
var canUserEditDeviceVerificationSettings = _userService.CanEditDeviceVerificationSettings(user);
return new DeviceVerificationResponseModel(canUserEditDeviceVerificationSettings, canUserEditDeviceVerificationSettings && user.UnknownDeviceVerificationEnabled);
var duoApi = new DuoApi(model.IntegrationKey, model.SecretKey, model.Host);
duoApi.JSONApiCall<object>("GET", "/auth/v2/check");
}
catch (DuoException)
{
throw new BadRequestException("Duo configuration settings are not valid. Please re-check the Duo Admin panel.");
}
[HttpPut("device-verification-settings")]
public async Task<DeviceVerificationResponseModel> PutDeviceVerificationSettings([FromBody] DeviceVerificationRequestModel model)
{
var user = await _userService.GetUserByPrincipalAsync(User);
if (user == null)
{
throw new UnauthorizedAccessException();
}
if (!_userService.CanEditDeviceVerificationSettings(user)
|| User.Claims.HasSsoIdP())
{
throw new InvalidOperationException("Can't update device verification settings");
}
model.ToOrganization(organization);
await _organizationService.UpdateTwoFactorProviderAsync(organization,
TwoFactorProviderType.OrganizationDuo);
var response = new TwoFactorDuoResponseModel(organization);
return response;
}
model.ToUser(user);
await _userService.SaveUserAsync(user);
return new DeviceVerificationResponseModel(true, user.UnknownDeviceVerificationEnabled);
[HttpPost("get-webauthn")]
public async Task<TwoFactorWebAuthnResponseModel> GetWebAuthn([FromBody] SecretVerificationRequestModel model)
{
var user = await CheckAsync(model, true);
var response = new TwoFactorWebAuthnResponseModel(user);
return response;
}
[HttpPost("get-webauthn-challenge")]
public async Task<CredentialCreateOptions> GetWebAuthnChallenge([FromBody] SecretVerificationRequestModel model)
{
var user = await CheckAsync(model, true);
var reg = await _userService.StartWebAuthnRegistrationAsync(user);
return reg;
}
[HttpPut("webauthn")]
[HttpPost("webauthn")]
public async Task<TwoFactorWebAuthnResponseModel> PutWebAuthn([FromBody] TwoFactorWebAuthnRequestModel model)
{
var user = await CheckAsync(model, true);
var success = await _userService.CompleteWebAuthRegistrationAsync(
user, model.Id.Value, model.Name, model.DeviceResponse);
if (!success)
{
throw new BadRequestException("Unable to complete WebAuthn registration.");
}
var response = new TwoFactorWebAuthnResponseModel(user);
return response;
}
private async Task<User> CheckAsync(SecretVerificationRequestModel model, bool premium)
[HttpDelete("webauthn")]
public async Task<TwoFactorWebAuthnResponseModel> DeleteWebAuthn([FromBody] TwoFactorWebAuthnDeleteRequestModel model)
{
var user = await CheckAsync(model, true);
await _userService.DeleteWebAuthnKeyAsync(user, model.Id.Value);
var response = new TwoFactorWebAuthnResponseModel(user);
return response;
}
[HttpPost("get-email")]
public async Task<TwoFactorEmailResponseModel> GetEmail([FromBody] SecretVerificationRequestModel model)
{
var user = await CheckAsync(model, false);
var response = new TwoFactorEmailResponseModel(user);
return response;
}
[HttpPost("send-email")]
public async Task SendEmail([FromBody] TwoFactorEmailRequestModel model)
{
var user = await CheckAsync(model, false);
model.ToUser(user);
await _userService.SendTwoFactorEmailAsync(user);
}
[AllowAnonymous]
[HttpPost("send-email-login")]
public async Task SendEmailLogin([FromBody] TwoFactorEmailRequestModel model)
{
var user = await _userManager.FindByEmailAsync(model.Email.ToLowerInvariant());
if (user != null)
{
var user = await _userService.GetUserByPrincipalAsync(User);
if (user == null)
if (await _userService.VerifySecretAsync(user, model.Secret))
{
throw new UnauthorizedAccessException();
}
var isBecauseNewDeviceLogin = false;
if (user.GetTwoFactorProvider(TwoFactorProviderType.Email) is null
&&
await _userService.Needs2FABecauseNewDeviceAsync(user, model.DeviceIdentifier, null))
{
model.ToUser(user);
isBecauseNewDeviceLogin = true;
}
if (!await _userService.VerifySecretAsync(user, model.Secret))
{
await Task.Delay(2000);
throw new BadRequestException(string.Empty, "User verification failed.");
}
if (premium && !(await _userService.CanAccessPremium(user)))
{
throw new BadRequestException("Premium status is required.");
}
return user;
}
private async Task ValidateYubiKeyAsync(User user, string name, string value)
{
if (string.IsNullOrWhiteSpace(value) || value.Length == 12)
{
await _userService.SendTwoFactorEmailAsync(user, isBecauseNewDeviceLogin);
return;
}
}
if (!await _userManager.VerifyTwoFactorTokenAsync(user,
CoreHelpers.CustomProviderName(TwoFactorProviderType.YubiKey), value))
{
await Task.Delay(2000);
throw new BadRequestException(name, $"{name} is invalid.");
}
else
{
await Task.Delay(500);
}
await Task.Delay(2000);
throw new BadRequestException("Cannot send two-factor email.");
}
[HttpPut("email")]
[HttpPost("email")]
public async Task<TwoFactorEmailResponseModel> PutEmail([FromBody] UpdateTwoFactorEmailRequestModel model)
{
var user = await CheckAsync(model, false);
model.ToUser(user);
if (!await _userManager.VerifyTwoFactorTokenAsync(user,
CoreHelpers.CustomProviderName(TwoFactorProviderType.Email), model.Token))
{
await Task.Delay(2000);
throw new BadRequestException("Token", "Invalid token.");
}
await _userService.UpdateTwoFactorProviderAsync(user, TwoFactorProviderType.Email);
var response = new TwoFactorEmailResponseModel(user);
return response;
}
[HttpPut("disable")]
[HttpPost("disable")]
public async Task<TwoFactorProviderResponseModel> PutDisable([FromBody] TwoFactorProviderRequestModel model)
{
var user = await CheckAsync(model, false);
await _userService.DisableTwoFactorProviderAsync(user, model.Type.Value, _organizationService);
var response = new TwoFactorProviderResponseModel(model.Type.Value, user);
return response;
}
[HttpPut("~/organizations/{id}/two-factor/disable")]
[HttpPost("~/organizations/{id}/two-factor/disable")]
public async Task<TwoFactorProviderResponseModel> PutOrganizationDisable(string id,
[FromBody] TwoFactorProviderRequestModel model)
{
var user = await CheckAsync(model, false);
var orgIdGuid = new Guid(id);
if (!await _currentContext.ManagePolicies(orgIdGuid))
{
throw new NotFoundException();
}
var organization = await _organizationRepository.GetByIdAsync(orgIdGuid);
if (organization == null)
{
throw new NotFoundException();
}
await _organizationService.DisableTwoFactorProviderAsync(organization, model.Type.Value);
var response = new TwoFactorProviderResponseModel(model.Type.Value, organization);
return response;
}
[HttpPost("get-recover")]
public async Task<TwoFactorRecoverResponseModel> GetRecover([FromBody] SecretVerificationRequestModel model)
{
var user = await CheckAsync(model, false);
var response = new TwoFactorRecoverResponseModel(user);
return response;
}
[HttpPost("recover")]
[AllowAnonymous]
public async Task PostRecover([FromBody] TwoFactorRecoveryRequestModel model)
{
if (!await _userService.RecoverTwoFactorAsync(model.Email, model.MasterPasswordHash, model.RecoveryCode,
_organizationService))
{
await Task.Delay(2000);
throw new BadRequestException(string.Empty, "Invalid information. Try again.");
}
}
[HttpGet("get-device-verification-settings")]
public async Task<DeviceVerificationResponseModel> GetDeviceVerificationSettings()
{
var user = await _userService.GetUserByPrincipalAsync(User);
if (user == null)
{
throw new UnauthorizedAccessException();
}
if (User.Claims.HasSsoIdP())
{
return new DeviceVerificationResponseModel(false, false);
}
var canUserEditDeviceVerificationSettings = _userService.CanEditDeviceVerificationSettings(user);
return new DeviceVerificationResponseModel(canUserEditDeviceVerificationSettings, canUserEditDeviceVerificationSettings && user.UnknownDeviceVerificationEnabled);
}
[HttpPut("device-verification-settings")]
public async Task<DeviceVerificationResponseModel> PutDeviceVerificationSettings([FromBody] DeviceVerificationRequestModel model)
{
var user = await _userService.GetUserByPrincipalAsync(User);
if (user == null)
{
throw new UnauthorizedAccessException();
}
if (!_userService.CanEditDeviceVerificationSettings(user)
|| User.Claims.HasSsoIdP())
{
throw new InvalidOperationException("Can't update device verification settings");
}
model.ToUser(user);
await _userService.SaveUserAsync(user);
return new DeviceVerificationResponseModel(true, user.UnknownDeviceVerificationEnabled);
}
private async Task<User> CheckAsync(SecretVerificationRequestModel model, bool premium)
{
var user = await _userService.GetUserByPrincipalAsync(User);
if (user == null)
{
throw new UnauthorizedAccessException();
}
if (!await _userService.VerifySecretAsync(user, model.Secret))
{
await Task.Delay(2000);
throw new BadRequestException(string.Empty, "User verification failed.");
}
if (premium && !(await _userService.CanAccessPremium(user)))
{
throw new BadRequestException("Premium status is required.");
}
return user;
}
private async Task ValidateYubiKeyAsync(User user, string name, string value)
{
if (string.IsNullOrWhiteSpace(value) || value.Length == 12)
{
return;
}
if (!await _userManager.VerifyTwoFactorTokenAsync(user,
CoreHelpers.CustomProviderName(TwoFactorProviderType.YubiKey), value))
{
await Task.Delay(2000);
throw new BadRequestException(name, $"{name} is invalid.");
}
else
{
await Task.Delay(500);
}
}
}

View File

@ -4,31 +4,30 @@ using Bit.Core.Repositories;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Bit.Api.Controllers
namespace Bit.Api.Controllers;
[Route("users")]
[Authorize("Application")]
public class UsersController : Controller
{
[Route("users")]
[Authorize("Application")]
public class UsersController : Controller
private readonly IUserRepository _userRepository;
public UsersController(
IUserRepository userRepository)
{
private readonly IUserRepository _userRepository;
_userRepository = userRepository;
}
public UsersController(
IUserRepository userRepository)
[HttpGet("{id}/public-key")]
public async Task<UserKeyResponseModel> Get(string id)
{
var guidId = new Guid(id);
var key = await _userRepository.GetPublicKeyAsync(guidId);
if (key == null)
{
_userRepository = userRepository;
throw new NotFoundException();
}
[HttpGet("{id}/public-key")]
public async Task<UserKeyResponseModel> Get(string id)
{
var guidId = new Guid(id);
var key = await _userRepository.GetPublicKeyAsync(guidId);
if (key == null)
{
throw new NotFoundException();
}
return new UserKeyResponseModel(guidId, key);
}
return new UserKeyResponseModel(guidId, key);
}
}

View File

@ -2,17 +2,16 @@
using Bit.Core.Jobs;
using Quartz;
namespace Bit.Api.Jobs
{
public class AliveJob : BaseJob
{
public AliveJob(ILogger<AliveJob> logger)
: base(logger) { }
namespace Bit.Api.Jobs;
protected override Task ExecuteJobAsync(IJobExecutionContext context)
{
_logger.LogInformation(Constants.BypassFiltersEventId, null, "It's alive!");
return Task.FromResult(0);
}
public class AliveJob : BaseJob
{
public AliveJob(ILogger<AliveJob> logger)
: base(logger) { }
protected override Task ExecuteJobAsync(IJobExecutionContext context)
{
_logger.LogInformation(Constants.BypassFiltersEventId, null, "It's alive!");
return Task.FromResult(0);
}
}

View File

@ -2,23 +2,22 @@
using Bit.Core.Services;
using Quartz;
namespace Bit.Api.Jobs
namespace Bit.Api.Jobs;
public class EmergencyAccessNotificationJob : BaseJob
{
public class EmergencyAccessNotificationJob : BaseJob
private readonly IServiceScopeFactory _serviceScopeFactory;
public EmergencyAccessNotificationJob(IServiceScopeFactory serviceScopeFactory, ILogger<EmergencyAccessNotificationJob> logger)
: base(logger)
{
private readonly IServiceScopeFactory _serviceScopeFactory;
_serviceScopeFactory = serviceScopeFactory;
}
public EmergencyAccessNotificationJob(IServiceScopeFactory serviceScopeFactory, ILogger<EmergencyAccessNotificationJob> logger)
: base(logger)
{
_serviceScopeFactory = serviceScopeFactory;
}
protected override async Task ExecuteJobAsync(IJobExecutionContext context)
{
using var scope = _serviceScopeFactory.CreateScope();
var emergencyAccessService = scope.ServiceProvider.GetService(typeof(IEmergencyAccessService)) as IEmergencyAccessService;
await emergencyAccessService.SendNotificationsAsync();
}
protected override async Task ExecuteJobAsync(IJobExecutionContext context)
{
using var scope = _serviceScopeFactory.CreateScope();
var emergencyAccessService = scope.ServiceProvider.GetService(typeof(IEmergencyAccessService)) as IEmergencyAccessService;
await emergencyAccessService.SendNotificationsAsync();
}
}

View File

@ -2,23 +2,22 @@
using Bit.Core.Services;
using Quartz;
namespace Bit.Api.Jobs
namespace Bit.Api.Jobs;
public class EmergencyAccessTimeoutJob : BaseJob
{
public class EmergencyAccessTimeoutJob : BaseJob
private readonly IServiceScopeFactory _serviceScopeFactory;
public EmergencyAccessTimeoutJob(IServiceScopeFactory serviceScopeFactory, ILogger<EmergencyAccessNotificationJob> logger)
: base(logger)
{
private readonly IServiceScopeFactory _serviceScopeFactory;
_serviceScopeFactory = serviceScopeFactory;
}
public EmergencyAccessTimeoutJob(IServiceScopeFactory serviceScopeFactory, ILogger<EmergencyAccessNotificationJob> logger)
: base(logger)
{
_serviceScopeFactory = serviceScopeFactory;
}
protected override async Task ExecuteJobAsync(IJobExecutionContext context)
{
using var scope = _serviceScopeFactory.CreateScope();
var emergencyAccessService = scope.ServiceProvider.GetService(typeof(IEmergencyAccessService)) as IEmergencyAccessService;
await emergencyAccessService.HandleTimedOutRequestsAsync();
}
protected override async Task ExecuteJobAsync(IJobExecutionContext context)
{
using var scope = _serviceScopeFactory.CreateScope();
var emergencyAccessService = scope.ServiceProvider.GetService(typeof(IEmergencyAccessService)) as IEmergencyAccessService;
await emergencyAccessService.HandleTimedOutRequestsAsync();
}
}

View File

@ -2,82 +2,81 @@
using Bit.Core.Settings;
using Quartz;
namespace Bit.Api.Jobs
namespace Bit.Api.Jobs;
public class JobsHostedService : BaseJobsHostedService
{
public class JobsHostedService : BaseJobsHostedService
public JobsHostedService(
GlobalSettings globalSettings,
IServiceProvider serviceProvider,
ILogger<JobsHostedService> logger,
ILogger<JobListener> listenerLogger)
: base(globalSettings, serviceProvider, logger, listenerLogger) { }
public override async Task StartAsync(CancellationToken cancellationToken)
{
public JobsHostedService(
GlobalSettings globalSettings,
IServiceProvider serviceProvider,
ILogger<JobsHostedService> logger,
ILogger<JobListener> listenerLogger)
: base(globalSettings, serviceProvider, logger, listenerLogger) { }
var everyTopOfTheHourTrigger = TriggerBuilder.Create()
.WithIdentity("EveryTopOfTheHourTrigger")
.StartNow()
.WithCronSchedule("0 0 * * * ?")
.Build();
var emergencyAccessNotificationTrigger = TriggerBuilder.Create()
.WithIdentity("EmergencyAccessNotificationTrigger")
.StartNow()
.WithCronSchedule("0 0 * * * ?")
.Build();
var emergencyAccessTimeoutTrigger = TriggerBuilder.Create()
.WithIdentity("EmergencyAccessTimeoutTrigger")
.StartNow()
.WithCronSchedule("0 0 * * * ?")
.Build();
var everyTopOfTheSixthHourTrigger = TriggerBuilder.Create()
.WithIdentity("EveryTopOfTheSixthHourTrigger")
.StartNow()
.WithCronSchedule("0 0 */6 * * ?")
.Build();
var everyTwelfthHourAndThirtyMinutesTrigger = TriggerBuilder.Create()
.WithIdentity("EveryTwelfthHourAndThirtyMinutesTrigger")
.StartNow()
.WithCronSchedule("0 30 */12 * * ?")
.Build();
var randomDailySponsorshipSyncTrigger = TriggerBuilder.Create()
.WithIdentity("RandomDailySponsorshipSyncTrigger")
.StartAt(DateBuilder.FutureDate(new Random().Next(24), IntervalUnit.Hour))
.WithSimpleSchedule(x => x
.WithIntervalInHours(24)
.RepeatForever())
.Build();
public override async Task StartAsync(CancellationToken cancellationToken)
var jobs = new List<Tuple<Type, ITrigger>>
{
var everyTopOfTheHourTrigger = TriggerBuilder.Create()
.WithIdentity("EveryTopOfTheHourTrigger")
.StartNow()
.WithCronSchedule("0 0 * * * ?")
.Build();
var emergencyAccessNotificationTrigger = TriggerBuilder.Create()
.WithIdentity("EmergencyAccessNotificationTrigger")
.StartNow()
.WithCronSchedule("0 0 * * * ?")
.Build();
var emergencyAccessTimeoutTrigger = TriggerBuilder.Create()
.WithIdentity("EmergencyAccessTimeoutTrigger")
.StartNow()
.WithCronSchedule("0 0 * * * ?")
.Build();
var everyTopOfTheSixthHourTrigger = TriggerBuilder.Create()
.WithIdentity("EveryTopOfTheSixthHourTrigger")
.StartNow()
.WithCronSchedule("0 0 */6 * * ?")
.Build();
var everyTwelfthHourAndThirtyMinutesTrigger = TriggerBuilder.Create()
.WithIdentity("EveryTwelfthHourAndThirtyMinutesTrigger")
.StartNow()
.WithCronSchedule("0 30 */12 * * ?")
.Build();
var randomDailySponsorshipSyncTrigger = TriggerBuilder.Create()
.WithIdentity("RandomDailySponsorshipSyncTrigger")
.StartAt(DateBuilder.FutureDate(new Random().Next(24), IntervalUnit.Hour))
.WithSimpleSchedule(x => x
.WithIntervalInHours(24)
.RepeatForever())
.Build();
new Tuple<Type, ITrigger>(typeof(AliveJob), everyTopOfTheHourTrigger),
new Tuple<Type, ITrigger>(typeof(EmergencyAccessNotificationJob), emergencyAccessNotificationTrigger),
new Tuple<Type, ITrigger>(typeof(EmergencyAccessTimeoutJob), emergencyAccessTimeoutTrigger),
new Tuple<Type, ITrigger>(typeof(ValidateUsersJob), everyTopOfTheSixthHourTrigger),
new Tuple<Type, ITrigger>(typeof(ValidateOrganizationsJob), everyTwelfthHourAndThirtyMinutesTrigger)
};
var jobs = new List<Tuple<Type, ITrigger>>
{
new Tuple<Type, ITrigger>(typeof(AliveJob), everyTopOfTheHourTrigger),
new Tuple<Type, ITrigger>(typeof(EmergencyAccessNotificationJob), emergencyAccessNotificationTrigger),
new Tuple<Type, ITrigger>(typeof(EmergencyAccessTimeoutJob), emergencyAccessTimeoutTrigger),
new Tuple<Type, ITrigger>(typeof(ValidateUsersJob), everyTopOfTheSixthHourTrigger),
new Tuple<Type, ITrigger>(typeof(ValidateOrganizationsJob), everyTwelfthHourAndThirtyMinutesTrigger)
};
if (_globalSettings.SelfHosted && _globalSettings.EnableCloudCommunication)
{
jobs.Add(new Tuple<Type, ITrigger>(typeof(SelfHostedSponsorshipSyncJob), randomDailySponsorshipSyncTrigger));
}
Jobs = jobs;
await base.StartAsync(cancellationToken);
if (_globalSettings.SelfHosted && _globalSettings.EnableCloudCommunication)
{
jobs.Add(new Tuple<Type, ITrigger>(typeof(SelfHostedSponsorshipSyncJob), randomDailySponsorshipSyncTrigger));
}
public static void AddJobsServices(IServiceCollection services, bool selfHosted)
Jobs = jobs;
await base.StartAsync(cancellationToken);
}
public static void AddJobsServices(IServiceCollection services, bool selfHosted)
{
if (selfHosted)
{
if (selfHosted)
{
services.AddTransient<SelfHostedSponsorshipSyncJob>();
}
services.AddTransient<AliveJob>();
services.AddTransient<EmergencyAccessNotificationJob>();
services.AddTransient<EmergencyAccessTimeoutJob>();
services.AddTransient<ValidateUsersJob>();
services.AddTransient<ValidateOrganizationsJob>();
services.AddTransient<SelfHostedSponsorshipSyncJob>();
}
services.AddTransient<AliveJob>();
services.AddTransient<EmergencyAccessNotificationJob>();
services.AddTransient<EmergencyAccessTimeoutJob>();
services.AddTransient<ValidateUsersJob>();
services.AddTransient<ValidateOrganizationsJob>();
}
}

View File

@ -7,59 +7,58 @@ using Bit.Core.Services;
using Bit.Core.Settings;
using Quartz;
namespace Bit.Api.Jobs
{
public class SelfHostedSponsorshipSyncJob : BaseJob
{
private readonly IServiceProvider _serviceProvider;
private IOrganizationRepository _organizationRepository;
private IOrganizationConnectionRepository _organizationConnectionRepository;
private readonly ILicensingService _licensingService;
private GlobalSettings _globalSettings;
namespace Bit.Api.Jobs;
public SelfHostedSponsorshipSyncJob(
IServiceProvider serviceProvider,
IOrganizationRepository organizationRepository,
IOrganizationConnectionRepository organizationConnectionRepository,
ILicensingService licensingService,
ILogger<SelfHostedSponsorshipSyncJob> logger,
GlobalSettings globalSettings)
: base(logger)
public class SelfHostedSponsorshipSyncJob : BaseJob
{
private readonly IServiceProvider _serviceProvider;
private IOrganizationRepository _organizationRepository;
private IOrganizationConnectionRepository _organizationConnectionRepository;
private readonly ILicensingService _licensingService;
private GlobalSettings _globalSettings;
public SelfHostedSponsorshipSyncJob(
IServiceProvider serviceProvider,
IOrganizationRepository organizationRepository,
IOrganizationConnectionRepository organizationConnectionRepository,
ILicensingService licensingService,
ILogger<SelfHostedSponsorshipSyncJob> logger,
GlobalSettings globalSettings)
: base(logger)
{
_serviceProvider = serviceProvider;
_organizationRepository = organizationRepository;
_organizationConnectionRepository = organizationConnectionRepository;
_licensingService = licensingService;
_globalSettings = globalSettings;
}
protected override async Task ExecuteJobAsync(IJobExecutionContext context)
{
if (!_globalSettings.EnableCloudCommunication)
{
_serviceProvider = serviceProvider;
_organizationRepository = organizationRepository;
_organizationConnectionRepository = organizationConnectionRepository;
_licensingService = licensingService;
_globalSettings = globalSettings;
_logger.LogInformation("Skipping Organization sync with cloud - Cloud communication is disabled in global settings");
return;
}
protected override async Task ExecuteJobAsync(IJobExecutionContext context)
var organizations = await _organizationRepository.GetManyByEnabledAsync();
using (var scope = _serviceProvider.CreateScope())
{
if (!_globalSettings.EnableCloudCommunication)
var syncCommand = scope.ServiceProvider.GetRequiredService<ISelfHostedSyncSponsorshipsCommand>();
foreach (var org in organizations)
{
_logger.LogInformation("Skipping Organization sync with cloud - Cloud communication is disabled in global settings");
return;
}
var organizations = await _organizationRepository.GetManyByEnabledAsync();
using (var scope = _serviceProvider.CreateScope())
{
var syncCommand = scope.ServiceProvider.GetRequiredService<ISelfHostedSyncSponsorshipsCommand>();
foreach (var org in organizations)
var connection = (await _organizationConnectionRepository.GetEnabledByOrganizationIdTypeAsync(org.Id, OrganizationConnectionType.CloudBillingSync)).FirstOrDefault();
if (connection != null)
{
var connection = (await _organizationConnectionRepository.GetEnabledByOrganizationIdTypeAsync(org.Id, OrganizationConnectionType.CloudBillingSync)).FirstOrDefault();
if (connection != null)
try
{
try
{
var config = connection.GetConfig<BillingSyncConfig>();
await syncCommand.SyncOrganization(org.Id, config.CloudOrganizationId, connection);
}
catch (Exception ex)
{
_logger.LogError(ex, $"Sponsorship sync for organization {org.Name} Failed");
}
var config = connection.GetConfig<BillingSyncConfig>();
await syncCommand.SyncOrganization(org.Id, config.CloudOrganizationId, connection);
}
catch (Exception ex)
{
_logger.LogError(ex, $"Sponsorship sync for organization {org.Name} Failed");
}
}
}

View File

@ -2,23 +2,22 @@
using Bit.Core.Services;
using Quartz;
namespace Bit.Api.Jobs
namespace Bit.Api.Jobs;
public class ValidateOrganizationsJob : BaseJob
{
public class ValidateOrganizationsJob : BaseJob
private readonly ILicensingService _licensingService;
public ValidateOrganizationsJob(
ILicensingService licensingService,
ILogger<ValidateOrganizationsJob> logger)
: base(logger)
{
private readonly ILicensingService _licensingService;
_licensingService = licensingService;
}
public ValidateOrganizationsJob(
ILicensingService licensingService,
ILogger<ValidateOrganizationsJob> logger)
: base(logger)
{
_licensingService = licensingService;
}
protected async override Task ExecuteJobAsync(IJobExecutionContext context)
{
await _licensingService.ValidateOrganizationsAsync();
}
protected async override Task ExecuteJobAsync(IJobExecutionContext context)
{
await _licensingService.ValidateOrganizationsAsync();
}
}

View File

@ -2,23 +2,22 @@
using Bit.Core.Services;
using Quartz;
namespace Bit.Api.Jobs
namespace Bit.Api.Jobs;
public class ValidateUsersJob : BaseJob
{
public class ValidateUsersJob : BaseJob
private readonly ILicensingService _licensingService;
public ValidateUsersJob(
ILicensingService licensingService,
ILogger<ValidateUsersJob> logger)
: base(logger)
{
private readonly ILicensingService _licensingService;
_licensingService = licensingService;
}
public ValidateUsersJob(
ILicensingService licensingService,
ILogger<ValidateUsersJob> logger)
: base(logger)
{
_licensingService = licensingService;
}
protected async override Task ExecuteJobAsync(IJobExecutionContext context)
{
await _licensingService.ValidateUsersAsync();
}
protected async override Task ExecuteJobAsync(IJobExecutionContext context)
{
await _licensingService.ValidateUsersAsync();
}
}

View File

@ -1,21 +1,20 @@
using Bit.Core.Models.Data;
using Bit.Core.Utilities;
namespace Bit.Api.Models
namespace Bit.Api.Models;
public class CipherAttachmentModel
{
public class CipherAttachmentModel
public CipherAttachmentModel() { }
public CipherAttachmentModel(CipherAttachment.MetaData data)
{
public CipherAttachmentModel() { }
public CipherAttachmentModel(CipherAttachment.MetaData data)
{
FileName = data.FileName;
Key = data.Key;
}
[EncryptedStringLength(1000)]
public string FileName { get; set; }
[EncryptedStringLength(1000)]
public string Key { get; set; }
FileName = data.FileName;
Key = data.Key;
}
[EncryptedStringLength(1000)]
public string FileName { get; set; }
[EncryptedStringLength(1000)]
public string Key { get; set; }
}

View File

@ -2,39 +2,38 @@
using Bit.Core.Models.Data;
using Bit.Core.Utilities;
namespace Bit.Api.Models
namespace Bit.Api.Models;
public class CipherCardModel
{
public class CipherCardModel
public CipherCardModel() { }
public CipherCardModel(CipherCardData data)
{
public CipherCardModel() { }
public CipherCardModel(CipherCardData data)
{
CardholderName = data.CardholderName;
Brand = data.Brand;
Number = data.Number;
ExpMonth = data.ExpMonth;
ExpYear = data.ExpYear;
Code = data.Code;
}
[EncryptedString]
[EncryptedStringLength(1000)]
public string CardholderName { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string Brand { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string Number { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string ExpMonth { get; set; }
[EncryptedString]
[StringLength(1000)]
public string ExpYear { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string Code { get; set; }
CardholderName = data.CardholderName;
Brand = data.Brand;
Number = data.Number;
ExpMonth = data.ExpMonth;
ExpYear = data.ExpYear;
Code = data.Code;
}
[EncryptedString]
[EncryptedStringLength(1000)]
public string CardholderName { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string Brand { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string Number { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string ExpMonth { get; set; }
[EncryptedString]
[StringLength(1000)]
public string ExpYear { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string Code { get; set; }
}

View File

@ -2,36 +2,35 @@
using Bit.Core.Models.Data;
using Bit.Core.Utilities;
namespace Bit.Api.Models
namespace Bit.Api.Models;
public class CipherFieldModel
{
public class CipherFieldModel
public CipherFieldModel() { }
public CipherFieldModel(CipherFieldData data)
{
public CipherFieldModel() { }
Type = data.Type;
Name = data.Name;
Value = data.Value;
LinkedId = data.LinkedId ?? null;
}
public CipherFieldModel(CipherFieldData data)
public FieldType Type { get; set; }
[EncryptedStringLength(1000)]
public string Name { get; set; }
[EncryptedStringLength(5000)]
public string Value { get; set; }
public int? LinkedId { get; set; }
public CipherFieldData ToCipherFieldData()
{
return new CipherFieldData
{
Type = data.Type;
Name = data.Name;
Value = data.Value;
LinkedId = data.LinkedId ?? null;
}
public FieldType Type { get; set; }
[EncryptedStringLength(1000)]
public string Name { get; set; }
[EncryptedStringLength(5000)]
public string Value { get; set; }
public int? LinkedId { get; set; }
public CipherFieldData ToCipherFieldData()
{
return new CipherFieldData
{
Type = Type,
Name = Name,
Value = Value,
LinkedId = LinkedId ?? null,
};
}
Type = Type,
Name = Name,
Value = Value,
LinkedId = LinkedId ?? null,
};
}
}

View File

@ -2,87 +2,86 @@
using Bit.Core.Models.Data;
using Bit.Core.Utilities;
namespace Bit.Api.Models
namespace Bit.Api.Models;
public class CipherIdentityModel
{
public class CipherIdentityModel
public CipherIdentityModel() { }
public CipherIdentityModel(CipherIdentityData data)
{
public CipherIdentityModel() { }
public CipherIdentityModel(CipherIdentityData data)
{
Title = data.Title;
FirstName = data.FirstName;
MiddleName = data.MiddleName;
LastName = data.LastName;
Address1 = data.Address1;
Address2 = data.Address2;
Address3 = data.Address3;
City = data.City;
State = data.State;
PostalCode = data.PostalCode;
Country = data.Country;
Company = data.Company;
Email = data.Email;
Phone = data.Phone;
SSN = data.SSN;
Username = data.Username;
PassportNumber = data.PassportNumber;
LicenseNumber = data.LicenseNumber;
}
[EncryptedString]
[EncryptedStringLength(1000)]
public string Title { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string FirstName { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string MiddleName { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string LastName { get; set; }
[EncryptedString]
[StringLength(1000)]
public string Address1 { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string Address2 { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string Address3 { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string City { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string State { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string PostalCode { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string Country { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string Company { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string Email { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string Phone { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string SSN { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string Username { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string PassportNumber { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string LicenseNumber { get; set; }
Title = data.Title;
FirstName = data.FirstName;
MiddleName = data.MiddleName;
LastName = data.LastName;
Address1 = data.Address1;
Address2 = data.Address2;
Address3 = data.Address3;
City = data.City;
State = data.State;
PostalCode = data.PostalCode;
Country = data.Country;
Company = data.Company;
Email = data.Email;
Phone = data.Phone;
SSN = data.SSN;
Username = data.Username;
PassportNumber = data.PassportNumber;
LicenseNumber = data.LicenseNumber;
}
[EncryptedString]
[EncryptedStringLength(1000)]
public string Title { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string FirstName { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string MiddleName { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string LastName { get; set; }
[EncryptedString]
[StringLength(1000)]
public string Address1 { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string Address2 { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string Address3 { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string City { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string State { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string PostalCode { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string Country { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string Company { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string Email { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string Phone { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string SSN { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string Username { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string PassportNumber { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string LicenseNumber { get; set; }
}

View File

@ -2,84 +2,83 @@
using Bit.Core.Models.Data;
using Bit.Core.Utilities;
namespace Bit.Api.Models
{
public class CipherLoginModel
{
public CipherLoginModel() { }
namespace Bit.Api.Models;
public CipherLoginModel(CipherLoginData data)
public class CipherLoginModel
{
public CipherLoginModel() { }
public CipherLoginModel(CipherLoginData data)
{
Uris = data.Uris?.Select(u => new CipherLoginUriModel(u))?.ToList();
if (!Uris?.Any() ?? true)
{
Uris = data.Uris?.Select(u => new CipherLoginUriModel(u))?.ToList();
if (!Uris?.Any() ?? true)
Uri = data.Uri;
}
Username = data.Username;
Password = data.Password;
PasswordRevisionDate = data.PasswordRevisionDate;
Totp = data.Totp;
AutofillOnPageLoad = data.AutofillOnPageLoad;
}
[EncryptedString]
[EncryptedStringLength(10000)]
public string Uri
{
get => Uris?.FirstOrDefault()?.Uri;
set
{
if (string.IsNullOrWhiteSpace(value))
{
Uri = data.Uri;
return;
}
Username = data.Username;
Password = data.Password;
PasswordRevisionDate = data.PasswordRevisionDate;
Totp = data.Totp;
AutofillOnPageLoad = data.AutofillOnPageLoad;
if (Uris == null)
{
Uris = new List<CipherLoginUriModel>();
}
Uris.Add(new CipherLoginUriModel(value));
}
}
public List<CipherLoginUriModel> Uris { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string Username { get; set; }
[EncryptedString]
[EncryptedStringLength(5000)]
public string Password { get; set; }
public DateTime? PasswordRevisionDate { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string Totp { get; set; }
public bool? AutofillOnPageLoad { get; set; }
public class CipherLoginUriModel
{
public CipherLoginUriModel() { }
public CipherLoginUriModel(string uri)
{
Uri = uri;
}
public CipherLoginUriModel(CipherLoginData.CipherLoginUriData uri)
{
Uri = uri.Uri;
Match = uri.Match;
}
[EncryptedString]
[EncryptedStringLength(10000)]
public string Uri
public string Uri { get; set; }
public UriMatchType? Match { get; set; } = null;
public CipherLoginData.CipherLoginUriData ToCipherLoginUriData()
{
get => Uris?.FirstOrDefault()?.Uri;
set
{
if (string.IsNullOrWhiteSpace(value))
{
return;
}
if (Uris == null)
{
Uris = new List<CipherLoginUriModel>();
}
Uris.Add(new CipherLoginUriModel(value));
}
}
public List<CipherLoginUriModel> Uris { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string Username { get; set; }
[EncryptedString]
[EncryptedStringLength(5000)]
public string Password { get; set; }
public DateTime? PasswordRevisionDate { get; set; }
[EncryptedString]
[EncryptedStringLength(1000)]
public string Totp { get; set; }
public bool? AutofillOnPageLoad { get; set; }
public class CipherLoginUriModel
{
public CipherLoginUriModel() { }
public CipherLoginUriModel(string uri)
{
Uri = uri;
}
public CipherLoginUriModel(CipherLoginData.CipherLoginUriData uri)
{
Uri = uri.Uri;
Match = uri.Match;
}
[EncryptedString]
[EncryptedStringLength(10000)]
public string Uri { get; set; }
public UriMatchType? Match { get; set; } = null;
public CipherLoginData.CipherLoginUriData ToCipherLoginUriData()
{
return new CipherLoginData.CipherLoginUriData { Uri = Uri, Match = Match, };
}
return new CipherLoginData.CipherLoginUriData { Uri = Uri, Match = Match, };
}
}
}

View File

@ -2,28 +2,27 @@
using Bit.Core.Models.Data;
using Bit.Core.Utilities;
namespace Bit.Api.Models
namespace Bit.Api.Models;
public class CipherPasswordHistoryModel
{
public class CipherPasswordHistoryModel
public CipherPasswordHistoryModel() { }
public CipherPasswordHistoryModel(CipherPasswordHistoryData data)
{
public CipherPasswordHistoryModel() { }
Password = data.Password;
LastUsedDate = data.LastUsedDate;
}
public CipherPasswordHistoryModel(CipherPasswordHistoryData data)
{
Password = data.Password;
LastUsedDate = data.LastUsedDate;
}
[EncryptedString]
[EncryptedStringLength(5000)]
[Required]
public string Password { get; set; }
[Required]
public DateTime? LastUsedDate { get; set; }
[EncryptedString]
[EncryptedStringLength(5000)]
[Required]
public string Password { get; set; }
[Required]
public DateTime? LastUsedDate { get; set; }
public CipherPasswordHistoryData ToCipherPasswordHistoryData()
{
return new CipherPasswordHistoryData { Password = Password, LastUsedDate = LastUsedDate.Value, };
}
public CipherPasswordHistoryData ToCipherPasswordHistoryData()
{
return new CipherPasswordHistoryData { Password = Password, LastUsedDate = LastUsedDate.Value, };
}
}

View File

@ -1,17 +1,16 @@
using Bit.Core.Enums;
using Bit.Core.Models.Data;
namespace Bit.Api.Models
namespace Bit.Api.Models;
public class CipherSecureNoteModel
{
public class CipherSecureNoteModel
public CipherSecureNoteModel() { }
public CipherSecureNoteModel(CipherSecureNoteData data)
{
public CipherSecureNoteModel() { }
public CipherSecureNoteModel(CipherSecureNoteData data)
{
Type = data.Type;
}
public SecureNoteType Type { get; set; }
Type = data.Type;
}
public SecureNoteType Type { get; set; }
}

View File

@ -1,19 +1,18 @@
using System.ComponentModel.DataAnnotations;
namespace Bit.Api.Models.Public
namespace Bit.Api.Models.Public;
public abstract class AssociationWithPermissionsBaseModel
{
public abstract class AssociationWithPermissionsBaseModel
{
/// <summary>
/// The associated object's unique identifier.
/// </summary>
/// <example>bfbc8338-e329-4dc0-b0c9-317c2ebf1a09</example>
[Required]
public Guid? Id { get; set; }
/// <summary>
/// When true, the read only permission will not allow the user or group to make changes to items.
/// </summary>
[Required]
public bool? ReadOnly { get; set; }
}
/// <summary>
/// The associated object's unique identifier.
/// </summary>
/// <example>bfbc8338-e329-4dc0-b0c9-317c2ebf1a09</example>
[Required]
public Guid? Id { get; set; }
/// <summary>
/// When true, the read only permission will not allow the user or group to make changes to items.
/// </summary>
[Required]
public bool? ReadOnly { get; set; }
}

View File

@ -1,14 +1,13 @@
using System.ComponentModel.DataAnnotations;
namespace Bit.Api.Models.Public
namespace Bit.Api.Models.Public;
public abstract class CollectionBaseModel
{
public abstract class CollectionBaseModel
{
/// <summary>
/// External identifier for reference or linking this collection to another system.
/// </summary>
/// <example>external_id_123456</example>
[StringLength(300)]
public string ExternalId { get; set; }
}
/// <summary>
/// External identifier for reference or linking this collection to another system.
/// </summary>
/// <example>external_id_123456</example>
[StringLength(300)]
public string ExternalId { get; set; }
}

View File

@ -1,27 +1,26 @@
using System.ComponentModel.DataAnnotations;
namespace Bit.Api.Models.Public
namespace Bit.Api.Models.Public;
public abstract class GroupBaseModel
{
public abstract class GroupBaseModel
{
/// <summary>
/// The name of the group.
/// </summary>
/// <example>Development Team</example>
[Required]
[StringLength(100)]
public string Name { get; set; }
/// <summary>
/// Determines if this group can access all collections within the organization, or only the associated
/// collections. If set to <c>true</c>, this option overrides any collection assignments.
/// </summary>
[Required]
public bool? AccessAll { get; set; }
/// <summary>
/// External identifier for reference or linking this group to another system, such as a user directory.
/// </summary>
/// <example>external_id_123456</example>
[StringLength(300)]
public string ExternalId { get; set; }
}
/// <summary>
/// The name of the group.
/// </summary>
/// <example>Development Team</example>
[Required]
[StringLength(100)]
public string Name { get; set; }
/// <summary>
/// Determines if this group can access all collections within the organization, or only the associated
/// collections. If set to <c>true</c>, this option overrides any collection assignments.
/// </summary>
[Required]
public bool? AccessAll { get; set; }
/// <summary>
/// External identifier for reference or linking this group to another system, such as a user directory.
/// </summary>
/// <example>external_id_123456</example>
[StringLength(300)]
public string ExternalId { get; set; }
}

View File

@ -3,59 +3,58 @@ using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
namespace Bit.Api.Models.Public
namespace Bit.Api.Models.Public;
public abstract class MemberBaseModel
{
public abstract class MemberBaseModel
public MemberBaseModel() { }
public MemberBaseModel(OrganizationUser user)
{
public MemberBaseModel() { }
public MemberBaseModel(OrganizationUser user)
if (user == null)
{
if (user == null)
{
throw new ArgumentNullException(nameof(user));
}
Type = user.Type;
AccessAll = user.AccessAll;
ExternalId = user.ExternalId;
ResetPasswordEnrolled = user.ResetPasswordKey != null;
throw new ArgumentNullException(nameof(user));
}
public MemberBaseModel(OrganizationUserUserDetails user)
{
if (user == null)
{
throw new ArgumentNullException(nameof(user));
}
Type = user.Type;
AccessAll = user.AccessAll;
ExternalId = user.ExternalId;
ResetPasswordEnrolled = user.ResetPasswordKey != null;
}
/// <summary>
/// The member's type (or role) within the organization.
/// </summary>
[Required]
public OrganizationUserType? Type { get; set; }
/// <summary>
/// Determines if this member can access all collections within the organization, or only the associated
/// collections. If set to <c>true</c>, this option overrides any collection assignments.
/// </summary>
[Required]
public bool? AccessAll { get; set; }
/// <summary>
/// External identifier for reference or linking this member to another system, such as a user directory.
/// </summary>
/// <example>external_id_123456</example>
[StringLength(300)]
public string ExternalId { get; set; }
/// <summary>
/// Returns <c>true</c> if the member has enrolled in Password Reset assistance within the organization
/// </summary>
[Required]
public bool ResetPasswordEnrolled { get; set; }
Type = user.Type;
AccessAll = user.AccessAll;
ExternalId = user.ExternalId;
ResetPasswordEnrolled = user.ResetPasswordKey != null;
}
public MemberBaseModel(OrganizationUserUserDetails user)
{
if (user == null)
{
throw new ArgumentNullException(nameof(user));
}
Type = user.Type;
AccessAll = user.AccessAll;
ExternalId = user.ExternalId;
ResetPasswordEnrolled = user.ResetPasswordKey != null;
}
/// <summary>
/// The member's type (or role) within the organization.
/// </summary>
[Required]
public OrganizationUserType? Type { get; set; }
/// <summary>
/// Determines if this member can access all collections within the organization, or only the associated
/// collections. If set to <c>true</c>, this option overrides any collection assignments.
/// </summary>
[Required]
public bool? AccessAll { get; set; }
/// <summary>
/// External identifier for reference or linking this member to another system, such as a user directory.
/// </summary>
/// <example>external_id_123456</example>
[StringLength(300)]
public string ExternalId { get; set; }
/// <summary>
/// Returns <c>true</c> if the member has enrolled in Password Reset assistance within the organization
/// </summary>
[Required]
public bool ResetPasswordEnrolled { get; set; }
}

View File

@ -1,17 +1,16 @@
using System.ComponentModel.DataAnnotations;
namespace Bit.Api.Models.Public
namespace Bit.Api.Models.Public;
public abstract class PolicyBaseModel
{
public abstract class PolicyBaseModel
{
/// <summary>
/// Determines if this policy is enabled and enforced.
/// </summary>
[Required]
public bool? Enabled { get; set; }
/// <summary>
/// Data for the policy.
/// </summary>
public Dictionary<string, object> Data { get; set; }
}
/// <summary>
/// Determines if this policy is enabled and enforced.
/// </summary>
[Required]
public bool? Enabled { get; set; }
/// <summary>
/// Data for the policy.
/// </summary>
public Dictionary<string, object> Data { get; set; }
}

View File

@ -1,16 +1,15 @@
using Bit.Core.Models.Data;
namespace Bit.Api.Models.Public.Request
namespace Bit.Api.Models.Public.Request;
public class AssociationWithPermissionsRequestModel : AssociationWithPermissionsBaseModel
{
public class AssociationWithPermissionsRequestModel : AssociationWithPermissionsBaseModel
public SelectionReadOnly ToSelectionReadOnly()
{
public SelectionReadOnly ToSelectionReadOnly()
return new SelectionReadOnly
{
return new SelectionReadOnly
{
Id = Id.Value,
ReadOnly = ReadOnly.Value
};
}
Id = Id.Value,
ReadOnly = ReadOnly.Value
};
}
}

View File

@ -1,18 +1,17 @@
using Bit.Core.Entities;
namespace Bit.Api.Models.Public.Request
{
public class CollectionUpdateRequestModel : CollectionBaseModel
{
/// <summary>
/// The associated groups that this collection is assigned to.
/// </summary>
public IEnumerable<AssociationWithPermissionsRequestModel> Groups { get; set; }
namespace Bit.Api.Models.Public.Request;
public Collection ToCollection(Collection existingCollection)
{
existingCollection.ExternalId = ExternalId;
return existingCollection;
}
public class CollectionUpdateRequestModel : CollectionBaseModel
{
/// <summary>
/// The associated groups that this collection is assigned to.
/// </summary>
public IEnumerable<AssociationWithPermissionsRequestModel> Groups { get; set; }
public Collection ToCollection(Collection existingCollection)
{
existingCollection.ExternalId = ExternalId;
return existingCollection;
}
}

View File

@ -1,50 +1,49 @@
using Bit.Core.Exceptions;
namespace Bit.Api.Models.Public.Request
namespace Bit.Api.Models.Public.Request;
public class EventFilterRequestModel
{
public class EventFilterRequestModel
/// <summary>
/// The start date. Must be less than the end date.
/// </summary>
public DateTime? Start { get; set; }
/// <summary>
/// The end date. Must be greater than the start date.
/// </summary>
public DateTime? End { get; set; }
/// <summary>
/// The unique identifier of the user that performed the event.
/// </summary>
public Guid? ActingUserId { get; set; }
/// <summary>
/// The unique identifier of the related item that the event describes.
/// </summary>
public Guid? ItemId { get; set; }
/// <summary>
/// A cursor for use in pagination.
/// </summary>
public string ContinuationToken { get; set; }
public Tuple<DateTime, DateTime> ToDateRange()
{
/// <summary>
/// The start date. Must be less than the end date.
/// </summary>
public DateTime? Start { get; set; }
/// <summary>
/// The end date. Must be greater than the start date.
/// </summary>
public DateTime? End { get; set; }
/// <summary>
/// The unique identifier of the user that performed the event.
/// </summary>
public Guid? ActingUserId { get; set; }
/// <summary>
/// The unique identifier of the related item that the event describes.
/// </summary>
public Guid? ItemId { get; set; }
/// <summary>
/// A cursor for use in pagination.
/// </summary>
public string ContinuationToken { get; set; }
public Tuple<DateTime, DateTime> ToDateRange()
if (!End.HasValue || !Start.HasValue)
{
if (!End.HasValue || !Start.HasValue)
{
End = DateTime.UtcNow.Date.AddDays(1).AddMilliseconds(-1);
Start = DateTime.UtcNow.Date.AddDays(-30);
}
else if (Start.Value > End.Value)
{
var newEnd = Start;
Start = End;
End = newEnd;
}
if ((End.Value - Start.Value) > TimeSpan.FromDays(367))
{
throw new BadRequestException("Date range must be < 367 days.");
}
return new Tuple<DateTime, DateTime>(Start.Value, End.Value);
End = DateTime.UtcNow.Date.AddDays(1).AddMilliseconds(-1);
Start = DateTime.UtcNow.Date.AddDays(-30);
}
else if (Start.Value > End.Value)
{
var newEnd = Start;
Start = End;
End = newEnd;
}
if ((End.Value - Start.Value) > TimeSpan.FromDays(367))
{
throw new BadRequestException("Date range must be < 367 days.");
}
return new Tuple<DateTime, DateTime>(Start.Value, End.Value);
}
}

View File

@ -1,28 +1,27 @@
using Bit.Core.Entities;
namespace Bit.Api.Models.Public.Request
namespace Bit.Api.Models.Public.Request;
public class GroupCreateUpdateRequestModel : GroupBaseModel
{
public class GroupCreateUpdateRequestModel : GroupBaseModel
/// <summary>
/// The associated collections that this group can access.
/// </summary>
public IEnumerable<AssociationWithPermissionsRequestModel> Collections { get; set; }
public Group ToGroup(Guid orgId)
{
/// <summary>
/// The associated collections that this group can access.
/// </summary>
public IEnumerable<AssociationWithPermissionsRequestModel> Collections { get; set; }
public Group ToGroup(Guid orgId)
return ToGroup(new Group
{
return ToGroup(new Group
{
OrganizationId = orgId
});
}
OrganizationId = orgId
});
}
public Group ToGroup(Group existingGroup)
{
existingGroup.Name = Name;
existingGroup.AccessAll = AccessAll.Value;
existingGroup.ExternalId = ExternalId;
return existingGroup;
}
public Group ToGroup(Group existingGroup)
{
existingGroup.Name = Name;
existingGroup.AccessAll = AccessAll.Value;
existingGroup.ExternalId = ExternalId;
return existingGroup;
}
}

View File

@ -2,22 +2,21 @@
using Bit.Core.Entities;
using Bit.Core.Utilities;
namespace Bit.Api.Models.Public.Request
{
public class MemberCreateRequestModel : MemberUpdateRequestModel
{
/// <summary>
/// The member's email address.
/// </summary>
/// <example>jsmith@example.com</example>
[Required]
[StringLength(256)]
[StrictEmailAddress]
public string Email { get; set; }
namespace Bit.Api.Models.Public.Request;
public override OrganizationUser ToOrganizationUser(OrganizationUser existingUser)
{
throw new NotImplementedException();
}
public class MemberCreateRequestModel : MemberUpdateRequestModel
{
/// <summary>
/// The member's email address.
/// </summary>
/// <example>jsmith@example.com</example>
[Required]
[StringLength(256)]
[StrictEmailAddress]
public string Email { get; set; }
public override OrganizationUser ToOrganizationUser(OrganizationUser existingUser)
{
throw new NotImplementedException();
}
}

View File

@ -1,20 +1,19 @@
using Bit.Core.Entities;
namespace Bit.Api.Models.Public.Request
{
public class MemberUpdateRequestModel : MemberBaseModel
{
/// <summary>
/// The associated collections that this member can access.
/// </summary>
public IEnumerable<AssociationWithPermissionsRequestModel> Collections { get; set; }
namespace Bit.Api.Models.Public.Request;
public virtual OrganizationUser ToOrganizationUser(OrganizationUser existingUser)
{
existingUser.Type = Type.Value;
existingUser.AccessAll = AccessAll.Value;
existingUser.ExternalId = ExternalId;
return existingUser;
}
public class MemberUpdateRequestModel : MemberBaseModel
{
/// <summary>
/// The associated collections that this member can access.
/// </summary>
public IEnumerable<AssociationWithPermissionsRequestModel> Collections { get; set; }
public virtual OrganizationUser ToOrganizationUser(OrganizationUser existingUser)
{
existingUser.Type = Type.Value;
existingUser.AccessAll = AccessAll.Value;
existingUser.ExternalId = ExternalId;
return existingUser;
}
}

View File

@ -4,108 +4,107 @@ using Bit.Core.Entities;
using Bit.Core.Models.Business;
using Bit.Core.Utilities;
namespace Bit.Api.Models.Public.Request
namespace Bit.Api.Models.Public.Request;
public class OrganizationImportRequestModel
{
public class OrganizationImportRequestModel
/// <summary>
/// Groups to import.
/// </summary>
public OrganizationImportGroupRequestModel[] Groups { get; set; }
/// <summary>
/// Members to import.
/// </summary>
public OrganizationImportMemberRequestModel[] Members { get; set; }
/// <summary>
/// Determines if the data in this request should overwrite or append to the existing organization data.
/// </summary>
[Required]
public bool? OverwriteExisting { get; set; }
/// <summary>
/// Indicates an import of over 2000 users and/or groups is expected
/// </summary>
public bool LargeImport { get; set; } = false;
public class OrganizationImportGroupRequestModel
{
/// <summary>
/// Groups to import.
/// </summary>
public OrganizationImportGroupRequestModel[] Groups { get; set; }
/// <summary>
/// Members to import.
/// </summary>
public OrganizationImportMemberRequestModel[] Members { get; set; }
/// <summary>
/// Determines if the data in this request should overwrite or append to the existing organization data.
/// The name of the group.
/// </summary>
/// <example>Development Team</example>
[Required]
public bool? OverwriteExisting { get; set; }
[StringLength(100)]
public string Name { get; set; }
/// <summary>
/// Indicates an import of over 2000 users and/or groups is expected
/// External identifier for reference or linking this group to another system, such as a user directory.
/// </summary>
public bool LargeImport { get; set; } = false;
/// <example>external_id_123456</example>
[Required]
[StringLength(300)]
[JsonConverter(typeof(PermissiveStringConverter))]
public string ExternalId { get; set; }
/// <summary>
/// The associated external ids for members in this group.
/// </summary>
[JsonConverter(typeof(PermissiveStringEnumerableConverter))]
public IEnumerable<string> MemberExternalIds { get; set; }
public class OrganizationImportGroupRequestModel
public ImportedGroup ToImportedGroup(Guid organizationId)
{
/// <summary>
/// The name of the group.
/// </summary>
/// <example>Development Team</example>
[Required]
[StringLength(100)]
public string Name { get; set; }
/// <summary>
/// External identifier for reference or linking this group to another system, such as a user directory.
/// </summary>
/// <example>external_id_123456</example>
[Required]
[StringLength(300)]
[JsonConverter(typeof(PermissiveStringConverter))]
public string ExternalId { get; set; }
/// <summary>
/// The associated external ids for members in this group.
/// </summary>
[JsonConverter(typeof(PermissiveStringEnumerableConverter))]
public IEnumerable<string> MemberExternalIds { get; set; }
public ImportedGroup ToImportedGroup(Guid organizationId)
var importedGroup = new ImportedGroup
{
var importedGroup = new ImportedGroup
Group = new Group
{
Group = new Group
{
OrganizationId = organizationId,
Name = Name,
ExternalId = ExternalId
},
ExternalUserIds = new HashSet<string>(MemberExternalIds)
};
OrganizationId = organizationId,
Name = Name,
ExternalId = ExternalId
},
ExternalUserIds = new HashSet<string>(MemberExternalIds)
};
return importedGroup;
}
return importedGroup;
}
}
public class OrganizationImportMemberRequestModel : IValidatableObject
{
/// <summary>
/// The member's email address. Required for non-deleted users.
/// </summary>
/// <example>jsmith@example.com</example>
[EmailAddress]
[StringLength(256)]
public string Email { get; set; }
/// <summary>
/// External identifier for reference or linking this member to another system, such as a user directory.
/// </summary>
/// <example>external_id_123456</example>
[Required]
[StringLength(300)]
[JsonConverter(typeof(PermissiveStringConverter))]
public string ExternalId { get; set; }
/// <summary>
/// Determines if this member should be removed from the organization during import.
/// </summary>
public bool Deleted { get; set; }
public ImportedOrganizationUser ToImportedOrganizationUser()
{
var importedUser = new ImportedOrganizationUser
{
Email = Email.ToLowerInvariant(),
ExternalId = ExternalId
};
return importedUser;
}
public class OrganizationImportMemberRequestModel : IValidatableObject
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
/// <summary>
/// The member's email address. Required for non-deleted users.
/// </summary>
/// <example>jsmith@example.com</example>
[EmailAddress]
[StringLength(256)]
public string Email { get; set; }
/// <summary>
/// External identifier for reference or linking this member to another system, such as a user directory.
/// </summary>
/// <example>external_id_123456</example>
[Required]
[StringLength(300)]
[JsonConverter(typeof(PermissiveStringConverter))]
public string ExternalId { get; set; }
/// <summary>
/// Determines if this member should be removed from the organization during import.
/// </summary>
public bool Deleted { get; set; }
public ImportedOrganizationUser ToImportedOrganizationUser()
if (string.IsNullOrWhiteSpace(Email) && !Deleted)
{
var importedUser = new ImportedOrganizationUser
{
Email = Email.ToLowerInvariant(),
ExternalId = ExternalId
};
return importedUser;
}
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (string.IsNullOrWhiteSpace(Email) && !Deleted)
{
yield return new ValidationResult("Email is required for enabled members.",
new string[] { nameof(Email) });
}
yield return new ValidationResult("Email is required for enabled members.",
new string[] { nameof(Email) });
}
}
}

View File

@ -1,23 +1,22 @@
using System.Text.Json;
using Bit.Core.Entities;
namespace Bit.Api.Models.Public.Request
{
public class PolicyUpdateRequestModel : PolicyBaseModel
{
public Policy ToPolicy(Guid orgId)
{
return ToPolicy(new Policy
{
OrganizationId = orgId
});
}
namespace Bit.Api.Models.Public.Request;
public virtual Policy ToPolicy(Policy existingPolicy)
public class PolicyUpdateRequestModel : PolicyBaseModel
{
public Policy ToPolicy(Guid orgId)
{
return ToPolicy(new Policy
{
existingPolicy.Enabled = Enabled.GetValueOrDefault();
existingPolicy.Data = Data != null ? JsonSerializer.Serialize(Data) : null;
return existingPolicy;
}
OrganizationId = orgId
});
}
public virtual Policy ToPolicy(Policy existingPolicy)
{
existingPolicy.Enabled = Enabled.GetValueOrDefault();
existingPolicy.Data = Data != null ? JsonSerializer.Serialize(Data) : null;
return existingPolicy;
}
}

View File

@ -1,10 +1,9 @@
namespace Bit.Api.Models.Public.Request
namespace Bit.Api.Models.Public.Request;
public class UpdateGroupIdsRequestModel
{
public class UpdateGroupIdsRequestModel
{
/// <summary>
/// The associated group ids that this object can access.
/// </summary>
public IEnumerable<Guid> GroupIds { get; set; }
}
/// <summary>
/// The associated group ids that this object can access.
/// </summary>
public IEnumerable<Guid> GroupIds { get; set; }
}

View File

@ -1,10 +1,9 @@
namespace Bit.Api.Models.Public.Request
namespace Bit.Api.Models.Public.Request;
public class UpdateMemberIdsRequestModel
{
public class UpdateMemberIdsRequestModel
{
/// <summary>
/// The associated member ids that have access to this object.
/// </summary>
public IEnumerable<Guid> MemberIds { get; set; }
}
/// <summary>
/// The associated member ids that have access to this object.
/// </summary>
public IEnumerable<Guid> MemberIds { get; set; }
}

View File

@ -1,17 +1,16 @@
using Bit.Core.Models.Data;
namespace Bit.Api.Models.Public.Response
namespace Bit.Api.Models.Public.Response;
public class AssociationWithPermissionsResponseModel : AssociationWithPermissionsBaseModel
{
public class AssociationWithPermissionsResponseModel : AssociationWithPermissionsBaseModel
public AssociationWithPermissionsResponseModel(SelectionReadOnly selection)
{
public AssociationWithPermissionsResponseModel(SelectionReadOnly selection)
if (selection == null)
{
if (selection == null)
{
throw new ArgumentNullException(nameof(selection));
}
Id = selection.Id;
ReadOnly = selection.ReadOnly;
throw new ArgumentNullException(nameof(selection));
}
Id = selection.Id;
ReadOnly = selection.ReadOnly;
}
}

View File

@ -2,40 +2,39 @@
using Bit.Core.Entities;
using Bit.Core.Models.Data;
namespace Bit.Api.Models.Public.Response
{
/// <summary>
/// A collection.
/// </summary>
public class CollectionResponseModel : CollectionBaseModel, IResponseModel
{
public CollectionResponseModel(Collection collection, IEnumerable<SelectionReadOnly> groups)
{
if (collection == null)
{
throw new ArgumentNullException(nameof(collection));
}
namespace Bit.Api.Models.Public.Response;
Id = collection.Id;
ExternalId = collection.ExternalId;
Groups = groups?.Select(c => new AssociationWithPermissionsResponseModel(c));
/// <summary>
/// A collection.
/// </summary>
public class CollectionResponseModel : CollectionBaseModel, IResponseModel
{
public CollectionResponseModel(Collection collection, IEnumerable<SelectionReadOnly> groups)
{
if (collection == null)
{
throw new ArgumentNullException(nameof(collection));
}
/// <summary>
/// String representing the object's type. Objects of the same type share the same properties.
/// </summary>
/// <example>collection</example>
[Required]
public string Object => "collection";
/// <summary>
/// The collection's unique identifier.
/// </summary>
/// <example>539a36c5-e0d2-4cf9-979e-51ecf5cf6593</example>
[Required]
public Guid Id { get; set; }
/// <summary>
/// The associated groups that this collection is assigned to.
/// </summary>
public IEnumerable<AssociationWithPermissionsResponseModel> Groups { get; set; }
Id = collection.Id;
ExternalId = collection.ExternalId;
Groups = groups?.Select(c => new AssociationWithPermissionsResponseModel(c));
}
/// <summary>
/// String representing the object's type. Objects of the same type share the same properties.
/// </summary>
/// <example>collection</example>
[Required]
public string Object => "collection";
/// <summary>
/// The collection's unique identifier.
/// </summary>
/// <example>539a36c5-e0d2-4cf9-979e-51ecf5cf6593</example>
[Required]
public Guid Id { get; set; }
/// <summary>
/// The associated groups that this collection is assigned to.
/// </summary>
public IEnumerable<AssociationWithPermissionsResponseModel> Groups { get; set; }
}

View File

@ -1,77 +1,76 @@
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Mvc.ModelBinding;
namespace Bit.Api.Models.Public.Response
namespace Bit.Api.Models.Public.Response;
public class ErrorResponseModel : IResponseModel
{
public class ErrorResponseModel : IResponseModel
public ErrorResponseModel(string message)
{
public ErrorResponseModel(string message)
{
Message = message;
}
public ErrorResponseModel(ModelStateDictionary modelState)
{
Message = "The request's model state is invalid.";
Errors = new Dictionary<string, IEnumerable<string>>();
var keys = modelState.Keys.ToList();
var values = modelState.Values.ToList();
for (var i = 0; i < values.Count; i++)
{
var value = values[i];
if (keys.Count <= i)
{
// Keys not available for some reason.
break;
}
var key = keys[i];
if (value.ValidationState != ModelValidationState.Invalid || value.Errors.Count == 0)
{
continue;
}
var errors = value.Errors.Select(e => e.ErrorMessage);
Errors.Add(key, errors);
}
}
public ErrorResponseModel(Dictionary<string, IEnumerable<string>> errors)
: this("Errors have occurred.", errors)
{ }
public ErrorResponseModel(string errorKey, string errorValue)
: this(errorKey, new string[] { errorValue })
{ }
public ErrorResponseModel(string errorKey, IEnumerable<string> errorValues)
: this(new Dictionary<string, IEnumerable<string>> { { errorKey, errorValues } })
{ }
public ErrorResponseModel(string message, Dictionary<string, IEnumerable<string>> errors)
{
Message = message;
Errors = errors;
}
/// <summary>
/// String representing the object's type. Objects of the same type share the same properties.
/// </summary>
/// <example>error</example>
[Required]
public string Object => "error";
/// <summary>
/// A human-readable message providing details about the error.
/// </summary>
/// <example>The request model is invalid.</example>
[Required]
public string Message { get; set; }
/// <summary>
/// If multiple errors occurred, they are listed in dictionary. Errors related to a specific
/// request parameter will include a dictionary key describing that parameter.
/// </summary>
public Dictionary<string, IEnumerable<string>> Errors { get; set; }
Message = message;
}
public ErrorResponseModel(ModelStateDictionary modelState)
{
Message = "The request's model state is invalid.";
Errors = new Dictionary<string, IEnumerable<string>>();
var keys = modelState.Keys.ToList();
var values = modelState.Values.ToList();
for (var i = 0; i < values.Count; i++)
{
var value = values[i];
if (keys.Count <= i)
{
// Keys not available for some reason.
break;
}
var key = keys[i];
if (value.ValidationState != ModelValidationState.Invalid || value.Errors.Count == 0)
{
continue;
}
var errors = value.Errors.Select(e => e.ErrorMessage);
Errors.Add(key, errors);
}
}
public ErrorResponseModel(Dictionary<string, IEnumerable<string>> errors)
: this("Errors have occurred.", errors)
{ }
public ErrorResponseModel(string errorKey, string errorValue)
: this(errorKey, new string[] { errorValue })
{ }
public ErrorResponseModel(string errorKey, IEnumerable<string> errorValues)
: this(new Dictionary<string, IEnumerable<string>> { { errorKey, errorValues } })
{ }
public ErrorResponseModel(string message, Dictionary<string, IEnumerable<string>> errors)
{
Message = message;
Errors = errors;
}
/// <summary>
/// String representing the object's type. Objects of the same type share the same properties.
/// </summary>
/// <example>error</example>
[Required]
public string Object => "error";
/// <summary>
/// A human-readable message providing details about the error.
/// </summary>
/// <example>The request model is invalid.</example>
[Required]
public string Message { get; set; }
/// <summary>
/// If multiple errors occurred, they are listed in dictionary. Errors related to a specific
/// request parameter will include a dictionary key describing that parameter.
/// </summary>
public Dictionary<string, IEnumerable<string>> Errors { get; set; }
}

View File

@ -2,92 +2,91 @@
using Bit.Core.Enums;
using Bit.Core.Models.Data;
namespace Bit.Api.Models.Public.Response
{
/// <summary>
/// An event log.
/// </summary>
public class EventResponseModel : IResponseModel
{
public EventResponseModel(IEvent ev)
{
if (ev == null)
{
throw new ArgumentNullException(nameof(ev));
}
namespace Bit.Api.Models.Public.Response;
Type = ev.Type;
ItemId = ev.CipherId;
CollectionId = ev.CollectionId;
GroupId = ev.GroupId;
PolicyId = ev.PolicyId;
MemberId = ev.OrganizationUserId;
ActingUserId = ev.ActingUserId;
Date = ev.Date;
Device = ev.DeviceType;
IpAddress = ev.IpAddress;
InstallationId = ev.InstallationId;
/// <summary>
/// An event log.
/// </summary>
public class EventResponseModel : IResponseModel
{
public EventResponseModel(IEvent ev)
{
if (ev == null)
{
throw new ArgumentNullException(nameof(ev));
}
/// <summary>
/// String representing the object's type. Objects of the same type share the same properties.
/// </summary>
/// <example>event</example>
[Required]
public string Object => "event";
/// <summary>
/// The type of event.
/// </summary>
[Required]
public EventType Type { get; set; }
/// <summary>
/// The unique identifier of the related item that the event describes.
/// </summary>
/// <example>3767a302-8208-4dc6-b842-030428a1cfad</example>
public Guid? ItemId { get; set; }
/// <summary>
/// The unique identifier of the related collection that the event describes.
/// </summary>
/// <example>bce212a4-25f3-4888-8a0a-4c5736d851e0</example>
public Guid? CollectionId { get; set; }
/// <summary>
/// The unique identifier of the related group that the event describes.
/// </summary>
/// <example>f29a2515-91d2-4452-b49b-5e8040e6b0f4</example>
public Guid? GroupId { get; set; }
/// <summary>
/// The unique identifier of the related policy that the event describes.
/// </summary>
/// <example>f29a2515-91d2-4452-b49b-5e8040e6b0f4</example>
public Guid? PolicyId { get; set; }
/// <summary>
/// The unique identifier of the related member that the event describes.
/// </summary>
/// <example>e68b8629-85eb-4929-92c0-b84464976ba4</example>
public Guid? MemberId { get; set; }
/// <summary>
/// The unique identifier of the user that performed the event.
/// </summary>
/// <example>a2549f79-a71f-4eb9-9234-eb7247333f94</example>
public Guid? ActingUserId { get; set; }
/// <summary>
/// The Unique identifier of the Installation that performed the event.
/// </summary>
/// <value></value>
public Guid? InstallationId { get; set; }
/// <summary>
/// The date/timestamp when the event occurred.
/// </summary>
[Required]
public DateTime Date { get; set; }
/// <summary>
/// The type of device used by the acting user when the event occurred.
/// </summary>
public DeviceType? Device { get; set; }
/// <summary>
/// The IP address of the acting user.
/// </summary>
/// <example>172.16.254.1</example>
public string IpAddress { get; set; }
Type = ev.Type;
ItemId = ev.CipherId;
CollectionId = ev.CollectionId;
GroupId = ev.GroupId;
PolicyId = ev.PolicyId;
MemberId = ev.OrganizationUserId;
ActingUserId = ev.ActingUserId;
Date = ev.Date;
Device = ev.DeviceType;
IpAddress = ev.IpAddress;
InstallationId = ev.InstallationId;
}
/// <summary>
/// String representing the object's type. Objects of the same type share the same properties.
/// </summary>
/// <example>event</example>
[Required]
public string Object => "event";
/// <summary>
/// The type of event.
/// </summary>
[Required]
public EventType Type { get; set; }
/// <summary>
/// The unique identifier of the related item that the event describes.
/// </summary>
/// <example>3767a302-8208-4dc6-b842-030428a1cfad</example>
public Guid? ItemId { get; set; }
/// <summary>
/// The unique identifier of the related collection that the event describes.
/// </summary>
/// <example>bce212a4-25f3-4888-8a0a-4c5736d851e0</example>
public Guid? CollectionId { get; set; }
/// <summary>
/// The unique identifier of the related group that the event describes.
/// </summary>
/// <example>f29a2515-91d2-4452-b49b-5e8040e6b0f4</example>
public Guid? GroupId { get; set; }
/// <summary>
/// The unique identifier of the related policy that the event describes.
/// </summary>
/// <example>f29a2515-91d2-4452-b49b-5e8040e6b0f4</example>
public Guid? PolicyId { get; set; }
/// <summary>
/// The unique identifier of the related member that the event describes.
/// </summary>
/// <example>e68b8629-85eb-4929-92c0-b84464976ba4</example>
public Guid? MemberId { get; set; }
/// <summary>
/// The unique identifier of the user that performed the event.
/// </summary>
/// <example>a2549f79-a71f-4eb9-9234-eb7247333f94</example>
public Guid? ActingUserId { get; set; }
/// <summary>
/// The Unique identifier of the Installation that performed the event.
/// </summary>
/// <value></value>
public Guid? InstallationId { get; set; }
/// <summary>
/// The date/timestamp when the event occurred.
/// </summary>
[Required]
public DateTime Date { get; set; }
/// <summary>
/// The type of device used by the acting user when the event occurred.
/// </summary>
public DeviceType? Device { get; set; }
/// <summary>
/// The IP address of the acting user.
/// </summary>
/// <example>172.16.254.1</example>
public string IpAddress { get; set; }
}

View File

@ -2,42 +2,41 @@
using Bit.Core.Entities;
using Bit.Core.Models.Data;
namespace Bit.Api.Models.Public.Response
{
/// <summary>
/// A user group.
/// </summary>
public class GroupResponseModel : GroupBaseModel, IResponseModel
{
public GroupResponseModel(Group group, IEnumerable<SelectionReadOnly> collections)
{
if (group == null)
{
throw new ArgumentNullException(nameof(group));
}
namespace Bit.Api.Models.Public.Response;
Id = group.Id;
Name = group.Name;
AccessAll = group.AccessAll;
ExternalId = group.ExternalId;
Collections = collections?.Select(c => new AssociationWithPermissionsResponseModel(c));
/// <summary>
/// A user group.
/// </summary>
public class GroupResponseModel : GroupBaseModel, IResponseModel
{
public GroupResponseModel(Group group, IEnumerable<SelectionReadOnly> collections)
{
if (group == null)
{
throw new ArgumentNullException(nameof(group));
}
/// <summary>
/// String representing the object's type. Objects of the same type share the same properties.
/// </summary>
/// <example>group</example>
[Required]
public string Object => "group";
/// <summary>
/// The group's unique identifier.
/// </summary>
/// <example>539a36c5-e0d2-4cf9-979e-51ecf5cf6593</example>
[Required]
public Guid Id { get; set; }
/// <summary>
/// The associated collections that this group can access.
/// </summary>
public IEnumerable<AssociationWithPermissionsResponseModel> Collections { get; set; }
Id = group.Id;
Name = group.Name;
AccessAll = group.AccessAll;
ExternalId = group.ExternalId;
Collections = collections?.Select(c => new AssociationWithPermissionsResponseModel(c));
}
/// <summary>
/// String representing the object's type. Objects of the same type share the same properties.
/// </summary>
/// <example>group</example>
[Required]
public string Object => "group";
/// <summary>
/// The group's unique identifier.
/// </summary>
/// <example>539a36c5-e0d2-4cf9-979e-51ecf5cf6593</example>
[Required]
public Guid Id { get; set; }
/// <summary>
/// The associated collections that this group can access.
/// </summary>
public IEnumerable<AssociationWithPermissionsResponseModel> Collections { get; set; }
}

View File

@ -1,7 +1,6 @@
namespace Bit.Api.Models.Public.Response
namespace Bit.Api.Models.Public.Response;
public interface IResponseModel
{
public interface IResponseModel
{
string Object { get; }
}
string Object { get; }
}

View File

@ -1,29 +1,28 @@
using System.ComponentModel.DataAnnotations;
namespace Bit.Api.Models.Public.Response
{
public class ListResponseModel<T> : IResponseModel where T : IResponseModel
{
public ListResponseModel(IEnumerable<T> data, string continuationToken = null)
{
Data = data;
ContinuationToken = continuationToken;
}
namespace Bit.Api.Models.Public.Response;
/// <summary>
/// String representing the object's type. Objects of the same type share the same properties.
/// </summary>
/// <example>list</example>
[Required]
public string Object => "list";
/// <summary>
/// An array containing the actual response elements, paginated by any request parameters.
/// </summary>
[Required]
public IEnumerable<T> Data { get; set; }
/// <summary>
/// A cursor for use in pagination.
/// </summary>
public string ContinuationToken { get; set; }
public class ListResponseModel<T> : IResponseModel where T : IResponseModel
{
public ListResponseModel(IEnumerable<T> data, string continuationToken = null)
{
Data = data;
ContinuationToken = continuationToken;
}
/// <summary>
/// String representing the object's type. Objects of the same type share the same properties.
/// </summary>
/// <example>list</example>
[Required]
public string Object => "list";
/// <summary>
/// An array containing the actual response elements, paginated by any request parameters.
/// </summary>
[Required]
public IEnumerable<T> Data { get; set; }
/// <summary>
/// A cursor for use in pagination.
/// </summary>
public string ContinuationToken { get; set; }
}

View File

@ -4,91 +4,90 @@ using Bit.Core.Enums;
using Bit.Core.Models.Data;
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
namespace Bit.Api.Models.Public.Response
namespace Bit.Api.Models.Public.Response;
/// <summary>
/// An organization member.
/// </summary>
public class MemberResponseModel : MemberBaseModel, IResponseModel
{
/// <summary>
/// An organization member.
/// </summary>
public class MemberResponseModel : MemberBaseModel, IResponseModel
public MemberResponseModel(OrganizationUser user, IEnumerable<SelectionReadOnly> collections)
: base(user)
{
public MemberResponseModel(OrganizationUser user, IEnumerable<SelectionReadOnly> collections)
: base(user)
if (user == null)
{
if (user == null)
{
throw new ArgumentNullException(nameof(user));
}
Id = user.Id;
UserId = user.UserId;
Email = user.Email;
Status = user.Status;
Collections = collections?.Select(c => new AssociationWithPermissionsResponseModel(c));
throw new ArgumentNullException(nameof(user));
}
public MemberResponseModel(OrganizationUserUserDetails user, bool twoFactorEnabled,
IEnumerable<SelectionReadOnly> collections)
: base(user)
{
if (user == null)
{
throw new ArgumentNullException(nameof(user));
}
Id = user.Id;
UserId = user.UserId;
Name = user.Name;
Email = user.Email;
TwoFactorEnabled = twoFactorEnabled;
Status = user.Status;
Collections = collections?.Select(c => new AssociationWithPermissionsResponseModel(c));
}
/// <summary>
/// String representing the object's type. Objects of the same type share the same properties.
/// </summary>
/// <example>member</example>
[Required]
public string Object => "member";
/// <summary>
/// The member's unique identifier within the organization.
/// </summary>
/// <example>539a36c5-e0d2-4cf9-979e-51ecf5cf6593</example>
[Required]
public Guid Id { get; set; }
/// <summary>
/// The member's unique identifier across Bitwarden.
/// </summary>
/// <example>48b47ee1-493e-4c67-aef7-014996c40eca</example>
[Required]
public Guid? UserId { get; set; }
/// <summary>
/// The member's name, set from their user account profile.
/// </summary>
/// <example>John Smith</example>
public string Name { get; set; }
/// <summary>
/// The member's email address.
/// </summary>
/// <example>jsmith@example.com</example>
[Required]
public string Email { get; set; }
/// <summary>
/// Returns <c>true</c> if the member has a two-step login method enabled on their user account.
/// </summary>
[Required]
public bool TwoFactorEnabled { get; set; }
/// <summary>
/// The member's status within the organization. All created members start with a status of "Invited".
/// Once a member accept's their invitation to join the organization, their status changes to "Accepted".
/// Accepted members are then "Confirmed" by an organization administrator. Once a member is "Confirmed",
/// their status can no longer change.
/// </summary>
[Required]
public OrganizationUserStatusType Status { get; set; }
/// <summary>
/// The associated collections that this member can access.
/// </summary>
public IEnumerable<AssociationWithPermissionsResponseModel> Collections { get; set; }
Id = user.Id;
UserId = user.UserId;
Email = user.Email;
Status = user.Status;
Collections = collections?.Select(c => new AssociationWithPermissionsResponseModel(c));
}
public MemberResponseModel(OrganizationUserUserDetails user, bool twoFactorEnabled,
IEnumerable<SelectionReadOnly> collections)
: base(user)
{
if (user == null)
{
throw new ArgumentNullException(nameof(user));
}
Id = user.Id;
UserId = user.UserId;
Name = user.Name;
Email = user.Email;
TwoFactorEnabled = twoFactorEnabled;
Status = user.Status;
Collections = collections?.Select(c => new AssociationWithPermissionsResponseModel(c));
}
/// <summary>
/// String representing the object's type. Objects of the same type share the same properties.
/// </summary>
/// <example>member</example>
[Required]
public string Object => "member";
/// <summary>
/// The member's unique identifier within the organization.
/// </summary>
/// <example>539a36c5-e0d2-4cf9-979e-51ecf5cf6593</example>
[Required]
public Guid Id { get; set; }
/// <summary>
/// The member's unique identifier across Bitwarden.
/// </summary>
/// <example>48b47ee1-493e-4c67-aef7-014996c40eca</example>
[Required]
public Guid? UserId { get; set; }
/// <summary>
/// The member's name, set from their user account profile.
/// </summary>
/// <example>John Smith</example>
public string Name { get; set; }
/// <summary>
/// The member's email address.
/// </summary>
/// <example>jsmith@example.com</example>
[Required]
public string Email { get; set; }
/// <summary>
/// Returns <c>true</c> if the member has a two-step login method enabled on their user account.
/// </summary>
[Required]
public bool TwoFactorEnabled { get; set; }
/// <summary>
/// The member's status within the organization. All created members start with a status of "Invited".
/// Once a member accept's their invitation to join the organization, their status changes to "Accepted".
/// Accepted members are then "Confirmed" by an organization administrator. Once a member is "Confirmed",
/// their status can no longer change.
/// </summary>
[Required]
public OrganizationUserStatusType Status { get; set; }
/// <summary>
/// The associated collections that this member can access.
/// </summary>
public IEnumerable<AssociationWithPermissionsResponseModel> Collections { get; set; }
}

View File

@ -3,45 +3,44 @@ using System.Text.Json;
using Bit.Core.Entities;
using Bit.Core.Enums;
namespace Bit.Api.Models.Public.Response
{
/// <summary>
/// A policy.
/// </summary>
public class PolicyResponseModel : PolicyBaseModel, IResponseModel
{
public PolicyResponseModel(Policy policy)
{
if (policy == null)
{
throw new ArgumentNullException(nameof(policy));
}
namespace Bit.Api.Models.Public.Response;
Id = policy.Id;
Type = policy.Type;
Enabled = policy.Enabled;
if (!string.IsNullOrWhiteSpace(policy.Data))
{
Data = JsonSerializer.Deserialize<Dictionary<string, object>>(policy.Data);
}
/// <summary>
/// A policy.
/// </summary>
public class PolicyResponseModel : PolicyBaseModel, IResponseModel
{
public PolicyResponseModel(Policy policy)
{
if (policy == null)
{
throw new ArgumentNullException(nameof(policy));
}
/// <summary>
/// String representing the object's type. Objects of the same type share the same properties.
/// </summary>
/// <example>policy</example>
[Required]
public string Object => "policy";
/// <summary>
/// The policy's unique identifier.
/// </summary>
/// <example>539a36c5-e0d2-4cf9-979e-51ecf5cf6593</example>
[Required]
public Guid Id { get; set; }
/// <summary>
/// The type of policy.
/// </summary>
[Required]
public PolicyType? Type { get; set; }
Id = policy.Id;
Type = policy.Type;
Enabled = policy.Enabled;
if (!string.IsNullOrWhiteSpace(policy.Data))
{
Data = JsonSerializer.Deserialize<Dictionary<string, object>>(policy.Data);
}
}
/// <summary>
/// String representing the object's type. Objects of the same type share the same properties.
/// </summary>
/// <example>policy</example>
[Required]
public string Object => "policy";
/// <summary>
/// The policy's unique identifier.
/// </summary>
/// <example>539a36c5-e0d2-4cf9-979e-51ecf5cf6593</example>
[Required]
public Guid Id { get; set; }
/// <summary>
/// The type of policy.
/// </summary>
[Required]
public PolicyType? Type { get; set; }
}

View File

@ -1,12 +1,11 @@
using System.ComponentModel.DataAnnotations;
namespace Bit.Api.Models.Request.Accounts
namespace Bit.Api.Models.Request.Accounts;
public class DeleteRecoverRequestModel
{
public class DeleteRecoverRequestModel
{
[Required]
[EmailAddress]
[StringLength(256)]
public string Email { get; set; }
}
[Required]
[EmailAddress]
[StringLength(256)]
public string Email { get; set; }
}

View File

@ -1,20 +1,19 @@
using System.ComponentModel.DataAnnotations;
using Bit.Core.Utilities;
namespace Bit.Api.Models.Request.Accounts
namespace Bit.Api.Models.Request.Accounts;
public class EmailRequestModel : SecretVerificationRequestModel
{
public class EmailRequestModel : SecretVerificationRequestModel
{
[Required]
[StrictEmailAddress]
[StringLength(256)]
public string NewEmail { get; set; }
[Required]
[StringLength(300)]
public string NewMasterPasswordHash { get; set; }
[Required]
public string Token { get; set; }
[Required]
public string Key { get; set; }
}
[Required]
[StrictEmailAddress]
[StringLength(256)]
public string NewEmail { get; set; }
[Required]
[StringLength(300)]
public string NewMasterPasswordHash { get; set; }
[Required]
public string Token { get; set; }
[Required]
public string Key { get; set; }
}

View File

@ -1,13 +1,12 @@
using System.ComponentModel.DataAnnotations;
using Bit.Core.Utilities;
namespace Bit.Api.Models.Request.Accounts
namespace Bit.Api.Models.Request.Accounts;
public class EmailTokenRequestModel : SecretVerificationRequestModel
{
public class EmailTokenRequestModel : SecretVerificationRequestModel
{
[Required]
[StrictEmailAddress]
[StringLength(256)]
public string NewEmail { get; set; }
}
[Required]
[StrictEmailAddress]
[StringLength(256)]
public string NewEmail { get; set; }
}

View File

@ -1,9 +1,8 @@
namespace Bit.Api.Models.Request.Accounts
namespace Bit.Api.Models.Request.Accounts;
public class ImportCiphersRequestModel
{
public class ImportCiphersRequestModel
{
public FolderRequestModel[] Folders { get; set; }
public CipherRequestModel[] Ciphers { get; set; }
public KeyValuePair<int, int>[] FolderRelationships { get; set; }
}
public FolderRequestModel[] Folders { get; set; }
public CipherRequestModel[] Ciphers { get; set; }
public KeyValuePair<int, int>[] FolderRelationships { get; set; }
}

View File

@ -1,30 +1,29 @@
using System.ComponentModel.DataAnnotations;
using Bit.Core.Enums;
namespace Bit.Api.Models.Request.Accounts
{
public class KdfRequestModel : PasswordRequestModel, IValidatableObject
{
[Required]
public KdfType? Kdf { get; set; }
[Required]
public int? KdfIterations { get; set; }
namespace Bit.Api.Models.Request.Accounts;
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
public class KdfRequestModel : PasswordRequestModel, IValidatableObject
{
[Required]
public KdfType? Kdf { get; set; }
[Required]
public int? KdfIterations { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (Kdf.HasValue && KdfIterations.HasValue)
{
if (Kdf.HasValue && KdfIterations.HasValue)
switch (Kdf.Value)
{
switch (Kdf.Value)
{
case KdfType.PBKDF2_SHA256:
if (KdfIterations.Value < 5000 || KdfIterations.Value > 2_000_000)
{
yield return new ValidationResult("KDF iterations must be between 5000 and 2000000.");
}
break;
default:
break;
}
case KdfType.PBKDF2_SHA256:
if (KdfIterations.Value < 5000 || KdfIterations.Value > 2_000_000)
{
yield return new ValidationResult("KDF iterations must be between 5000 and 2000000.");
}
break;
default:
break;
}
}
}

View File

@ -1,9 +1,8 @@
using Bit.Core.Enums;
namespace Bit.Api.Models.Request.Accounts
namespace Bit.Api.Models.Request.Accounts;
public class OrganizationApiKeyRequestModel : SecretVerificationRequestModel
{
public class OrganizationApiKeyRequestModel : SecretVerificationRequestModel
{
public OrganizationApiKeyType Type { get; set; }
}
public OrganizationApiKeyType Type { get; set; }
}

View File

@ -1,12 +1,11 @@
using System.ComponentModel.DataAnnotations;
namespace Bit.Api.Models.Request.Accounts
namespace Bit.Api.Models.Request.Accounts;
public class PasswordHintRequestModel
{
public class PasswordHintRequestModel
{
[Required]
[EmailAddress]
[StringLength(256)]
public string Email { get; set; }
}
[Required]
[EmailAddress]
[StringLength(256)]
public string Email { get; set; }
}

View File

@ -1,15 +1,14 @@
using System.ComponentModel.DataAnnotations;
namespace Bit.Api.Models.Request.Accounts
namespace Bit.Api.Models.Request.Accounts;
public class PasswordRequestModel : SecretVerificationRequestModel
{
public class PasswordRequestModel : SecretVerificationRequestModel
{
[Required]
[StringLength(300)]
public string NewMasterPasswordHash { get; set; }
[StringLength(50)]
public string MasterPasswordHint { get; set; }
[Required]
public string Key { get; set; }
}
[Required]
[StringLength(300)]
public string NewMasterPasswordHash { get; set; }
[StringLength(50)]
public string MasterPasswordHint { get; set; }
[Required]
public string Key { get; set; }
}

View File

@ -2,41 +2,40 @@
using Bit.Core.Settings;
using Enums = Bit.Core.Enums;
namespace Bit.Api.Models.Request.Accounts
namespace Bit.Api.Models.Request.Accounts;
public class PremiumRequestModel : IValidatableObject
{
public class PremiumRequestModel : IValidatableObject
[Required]
public Enums.PaymentMethodType? PaymentMethodType { get; set; }
public string PaymentToken { get; set; }
[Range(0, 99)]
public short? AdditionalStorageGb { get; set; }
public IFormFile License { get; set; }
public string Country { get; set; }
public string PostalCode { get; set; }
public bool Validate(GlobalSettings globalSettings)
{
[Required]
public Enums.PaymentMethodType? PaymentMethodType { get; set; }
public string PaymentToken { get; set; }
[Range(0, 99)]
public short? AdditionalStorageGb { get; set; }
public IFormFile License { get; set; }
public string Country { get; set; }
public string PostalCode { get; set; }
public bool Validate(GlobalSettings globalSettings)
if (!(License == null && !globalSettings.SelfHosted) ||
(License != null && globalSettings.SelfHosted))
{
if (!(License == null && !globalSettings.SelfHosted) ||
(License != null && globalSettings.SelfHosted))
{
return false;
}
return globalSettings.SelfHosted || !string.IsNullOrWhiteSpace(Country);
return false;
}
return globalSettings.SelfHosted || !string.IsNullOrWhiteSpace(Country);
}
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var creditType = PaymentMethodType.HasValue && PaymentMethodType.Value == Enums.PaymentMethodType.Credit;
if (string.IsNullOrWhiteSpace(PaymentToken) && !creditType && License == null)
{
var creditType = PaymentMethodType.HasValue && PaymentMethodType.Value == Enums.PaymentMethodType.Credit;
if (string.IsNullOrWhiteSpace(PaymentToken) && !creditType && License == null)
{
yield return new ValidationResult("Payment token or license is required.");
}
if (Country == "US" && string.IsNullOrWhiteSpace(PostalCode))
{
yield return new ValidationResult("Zip / postal code is required.",
new string[] { nameof(PostalCode) });
}
yield return new ValidationResult("Payment token or license is required.");
}
if (Country == "US" && string.IsNullOrWhiteSpace(PostalCode))
{
yield return new ValidationResult("Zip / postal code is required.",
new string[] { nameof(PostalCode) });
}
}
}

View File

@ -1,13 +1,12 @@
using System.ComponentModel.DataAnnotations;
namespace Bit.Api.Models.Request.Accounts
namespace Bit.Api.Models.Request.Accounts;
public class RegenerateTwoFactorRequestModel
{
public class RegenerateTwoFactorRequestModel
{
[Required]
public string MasterPasswordHash { get; set; }
[Required]
[StringLength(50)]
public string Token { get; set; }
}
[Required]
public string MasterPasswordHash { get; set; }
[Required]
[StringLength(50)]
public string Token { get; set; }
}

View File

@ -1,20 +1,19 @@
using System.ComponentModel.DataAnnotations;
namespace Bit.Api.Models.Request.Accounts
{
public class SecretVerificationRequestModel : IValidatableObject
{
[StringLength(300)]
public string MasterPasswordHash { get; set; }
public string OTP { get; set; }
public string Secret => !string.IsNullOrEmpty(MasterPasswordHash) ? MasterPasswordHash : OTP;
namespace Bit.Api.Models.Request.Accounts;
public virtual IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
public class SecretVerificationRequestModel : IValidatableObject
{
[StringLength(300)]
public string MasterPasswordHash { get; set; }
public string OTP { get; set; }
public string Secret => !string.IsNullOrEmpty(MasterPasswordHash) ? MasterPasswordHash : OTP;
public virtual IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (string.IsNullOrEmpty(Secret))
{
if (string.IsNullOrEmpty(Secret))
{
yield return new ValidationResult("MasterPasswordHash or OTP must be supplied.");
}
yield return new ValidationResult("MasterPasswordHash or OTP must be supplied.");
}
}
}

View File

@ -3,28 +3,27 @@ using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Models.Api.Request.Accounts;
namespace Bit.Api.Models.Request.Accounts
{
public class SetKeyConnectorKeyRequestModel
{
[Required]
public string Key { get; set; }
[Required]
public KeysRequestModel Keys { get; set; }
[Required]
public KdfType Kdf { get; set; }
[Required]
public int KdfIterations { get; set; }
[Required]
public string OrgIdentifier { get; set; }
namespace Bit.Api.Models.Request.Accounts;
public User ToUser(User existingUser)
{
existingUser.Kdf = Kdf;
existingUser.KdfIterations = KdfIterations;
existingUser.Key = Key;
Keys.ToUser(existingUser);
return existingUser;
}
public class SetKeyConnectorKeyRequestModel
{
[Required]
public string Key { get; set; }
[Required]
public KeysRequestModel Keys { get; set; }
[Required]
public KdfType Kdf { get; set; }
[Required]
public int KdfIterations { get; set; }
[Required]
public string OrgIdentifier { get; set; }
public User ToUser(User existingUser)
{
existingUser.Kdf = Kdf;
existingUser.KdfIterations = KdfIterations;
existingUser.Key = Key;
Keys.ToUser(existingUser);
return existingUser;
}
}

View File

@ -3,33 +3,32 @@ using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Models.Api.Request.Accounts;
namespace Bit.Api.Models.Request.Accounts
{
public class SetPasswordRequestModel
{
[Required]
[StringLength(300)]
public string MasterPasswordHash { get; set; }
[Required]
public string Key { get; set; }
[StringLength(50)]
public string MasterPasswordHint { get; set; }
[Required]
public KeysRequestModel Keys { get; set; }
[Required]
public KdfType Kdf { get; set; }
[Required]
public int KdfIterations { get; set; }
public string OrgIdentifier { get; set; }
namespace Bit.Api.Models.Request.Accounts;
public User ToUser(User existingUser)
{
existingUser.MasterPasswordHint = MasterPasswordHint;
existingUser.Kdf = Kdf;
existingUser.KdfIterations = KdfIterations;
existingUser.Key = Key;
Keys.ToUser(existingUser);
return existingUser;
}
public class SetPasswordRequestModel
{
[Required]
[StringLength(300)]
public string MasterPasswordHash { get; set; }
[Required]
public string Key { get; set; }
[StringLength(50)]
public string MasterPasswordHint { get; set; }
[Required]
public KeysRequestModel Keys { get; set; }
[Required]
public KdfType Kdf { get; set; }
[Required]
public int KdfIterations { get; set; }
public string OrgIdentifier { get; set; }
public User ToUser(User existingUser)
{
existingUser.MasterPasswordHint = MasterPasswordHint;
existingUser.Kdf = Kdf;
existingUser.KdfIterations = KdfIterations;
existingUser.Key = Key;
Keys.ToUser(existingUser);
return existingUser;
}
}

View File

@ -1,19 +1,18 @@
using System.ComponentModel.DataAnnotations;
namespace Bit.Api.Models.Request.Accounts
{
public class StorageRequestModel : IValidatableObject
{
[Required]
public short? StorageGbAdjustment { get; set; }
namespace Bit.Api.Models.Request.Accounts;
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
public class StorageRequestModel : IValidatableObject
{
[Required]
public short? StorageGbAdjustment { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (StorageGbAdjustment == 0)
{
if (StorageGbAdjustment == 0)
{
yield return new ValidationResult("Storage adjustment cannot be 0.",
new string[] { nameof(StorageGbAdjustment) });
}
yield return new ValidationResult("Storage adjustment cannot be 0.",
new string[] { nameof(StorageGbAdjustment) });
}
}
}

View File

@ -1,20 +1,19 @@
using System.ComponentModel.DataAnnotations;
namespace Bit.Api.Models.Request.Accounts
{
public class TaxInfoUpdateRequestModel : IValidatableObject
{
[Required]
public string Country { get; set; }
public string PostalCode { get; set; }
namespace Bit.Api.Models.Request.Accounts;
public virtual IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
public class TaxInfoUpdateRequestModel : IValidatableObject
{
[Required]
public string Country { get; set; }
public string PostalCode { get; set; }
public virtual IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (Country == "US" && string.IsNullOrWhiteSpace(PostalCode))
{
if (Country == "US" && string.IsNullOrWhiteSpace(PostalCode))
{
yield return new ValidationResult("Zip / postal code is required.",
new string[] { nameof(PostalCode) });
}
yield return new ValidationResult("Zip / postal code is required.",
new string[] { nameof(PostalCode) });
}
}
}

View File

@ -1,20 +1,19 @@
using System.ComponentModel.DataAnnotations;
namespace Bit.Api.Models.Request.Accounts
namespace Bit.Api.Models.Request.Accounts;
public class UpdateKeyRequestModel
{
public class UpdateKeyRequestModel
{
[Required]
[StringLength(300)]
public string MasterPasswordHash { get; set; }
[Required]
public IEnumerable<CipherWithIdRequestModel> Ciphers { get; set; }
[Required]
public IEnumerable<FolderWithIdRequestModel> Folders { get; set; }
public IEnumerable<SendWithIdRequestModel> Sends { get; set; }
[Required]
public string PrivateKey { get; set; }
[Required]
public string Key { get; set; }
}
[Required]
[StringLength(300)]
public string MasterPasswordHash { get; set; }
[Required]
public IEnumerable<CipherWithIdRequestModel> Ciphers { get; set; }
[Required]
public IEnumerable<FolderWithIdRequestModel> Folders { get; set; }
public IEnumerable<SendWithIdRequestModel> Sends { get; set; }
[Required]
public string PrivateKey { get; set; }
[Required]
public string Key { get; set; }
}

View File

@ -1,21 +1,20 @@
using System.ComponentModel.DataAnnotations;
using Bit.Core.Entities;
namespace Bit.Api.Models.Request.Accounts
{
public class UpdateProfileRequestModel
{
[StringLength(50)]
public string Name { get; set; }
[StringLength(50)]
[Obsolete("Changes will be made via the 'password' endpoint going forward.")]
public string MasterPasswordHint { get; set; }
namespace Bit.Api.Models.Request.Accounts;
public User ToUser(User existingUser)
{
existingUser.Name = Name;
existingUser.MasterPasswordHint = string.IsNullOrWhiteSpace(MasterPasswordHint) ? null : MasterPasswordHint;
return existingUser;
}
public class UpdateProfileRequestModel
{
[StringLength(50)]
public string Name { get; set; }
[StringLength(50)]
[Obsolete("Changes will be made via the 'password' endpoint going forward.")]
public string MasterPasswordHint { get; set; }
public User ToUser(User existingUser)
{
existingUser.Name = Name;
existingUser.MasterPasswordHint = string.IsNullOrWhiteSpace(MasterPasswordHint) ? null : MasterPasswordHint;
return existingUser;
}
}

View File

@ -1,11 +1,10 @@
using System.ComponentModel.DataAnnotations;
using Bit.Api.Models.Request.Organizations;
namespace Bit.Api.Models.Request.Accounts
namespace Bit.Api.Models.Request.Accounts;
public class UpdateTempPasswordRequestModel : OrganizationUserResetPasswordRequestModel
{
public class UpdateTempPasswordRequestModel : OrganizationUserResetPasswordRequestModel
{
[StringLength(50)]
public string MasterPasswordHint { get; set; }
}
[StringLength(50)]
public string MasterPasswordHint { get; set; }
}

View File

@ -1,12 +1,11 @@
using System.ComponentModel.DataAnnotations;
namespace Bit.Api.Models.Request.Accounts
namespace Bit.Api.Models.Request.Accounts;
public class VerifyDeleteRecoverRequestModel
{
public class VerifyDeleteRecoverRequestModel
{
[Required]
public string UserId { get; set; }
[Required]
public string Token { get; set; }
}
[Required]
public string UserId { get; set; }
[Required]
public string Token { get; set; }
}

View File

@ -1,12 +1,11 @@
using System.ComponentModel.DataAnnotations;
namespace Bit.Api.Models.Request.Accounts
namespace Bit.Api.Models.Request.Accounts;
public class VerifyEmailRequestModel
{
public class VerifyEmailRequestModel
{
[Required]
public string UserId { get; set; }
[Required]
public string Token { get; set; }
}
[Required]
public string UserId { get; set; }
[Required]
public string Token { get; set; }
}

View File

@ -1,10 +1,9 @@
using System.ComponentModel.DataAnnotations;
namespace Bit.Api.Models.Request.Accounts
namespace Bit.Api.Models.Request.Accounts;
public class VerifyOTPRequestModel
{
public class VerifyOTPRequestModel
{
[Required]
public string OTP { get; set; }
}
[Required]
public string OTP { get; set; }
}

View File

@ -1,10 +1,9 @@
namespace Bit.Api.Models.Request
namespace Bit.Api.Models.Request;
public class AttachmentRequestModel
{
public class AttachmentRequestModel
{
public string Key { get; set; }
public string FileName { get; set; }
public long FileSize { get; set; }
public bool AdminRequest { get; set; } = false;
}
public string Key { get; set; }
public string FileName { get; set; }
public long FileSize { get; set; }
public bool AdminRequest { get; set; } = false;
}

View File

@ -1,66 +1,65 @@
using System.ComponentModel.DataAnnotations;
using Bit.Core.Settings;
namespace Bit.Api.Models.Request
namespace Bit.Api.Models.Request;
public class BitPayInvoiceRequestModel : IValidatableObject
{
public class BitPayInvoiceRequestModel : IValidatableObject
public Guid? UserId { get; set; }
public Guid? OrganizationId { get; set; }
public bool Credit { get; set; }
[Required]
public decimal? Amount { get; set; }
public string ReturnUrl { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public BitPayLight.Models.Invoice.Invoice ToBitpayInvoice(GlobalSettings globalSettings)
{
public Guid? UserId { get; set; }
public Guid? OrganizationId { get; set; }
public bool Credit { get; set; }
[Required]
public decimal? Amount { get; set; }
public string ReturnUrl { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public BitPayLight.Models.Invoice.Invoice ToBitpayInvoice(GlobalSettings globalSettings)
var inv = new BitPayLight.Models.Invoice.Invoice
{
var inv = new BitPayLight.Models.Invoice.Invoice
Price = Convert.ToDouble(Amount.Value),
Currency = "USD",
RedirectUrl = ReturnUrl,
Buyer = new BitPayLight.Models.Invoice.Buyer
{
Price = Convert.ToDouble(Amount.Value),
Currency = "USD",
RedirectUrl = ReturnUrl,
Buyer = new BitPayLight.Models.Invoice.Buyer
{
Email = Email,
Name = Name
},
NotificationUrl = globalSettings.BitPay.NotificationUrl,
FullNotifications = true,
ExtendedNotifications = true
};
Email = Email,
Name = Name
},
NotificationUrl = globalSettings.BitPay.NotificationUrl,
FullNotifications = true,
ExtendedNotifications = true
};
var posData = string.Empty;
if (UserId.HasValue)
{
posData = "userId:" + UserId.Value;
}
else if (OrganizationId.HasValue)
{
posData = "organizationId:" + OrganizationId.Value;
}
if (Credit)
{
posData += ",accountCredit:1";
inv.ItemDesc = "Bitwarden Account Credit";
}
else
{
inv.ItemDesc = "Bitwarden";
}
inv.PosData = posData;
return inv;
var posData = string.Empty;
if (UserId.HasValue)
{
posData = "userId:" + UserId.Value;
}
else if (OrganizationId.HasValue)
{
posData = "organizationId:" + OrganizationId.Value;
}
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
if (Credit)
{
if (!UserId.HasValue && !OrganizationId.HasValue)
{
yield return new ValidationResult("User or Ooganization is required.");
}
posData += ",accountCredit:1";
inv.ItemDesc = "Bitwarden Account Credit";
}
else
{
inv.ItemDesc = "Bitwarden";
}
inv.PosData = posData;
return inv;
}
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (!UserId.HasValue && !OrganizationId.HasValue)
{
yield return new ValidationResult("User or Ooganization is required.");
}
}
}

View File

@ -1,11 +1,10 @@
using System.ComponentModel.DataAnnotations;
namespace Bit.Api.Models.Request
namespace Bit.Api.Models.Request;
public class CipherPartialRequestModel
{
public class CipherPartialRequestModel
{
[StringLength(36)]
public string FolderId { get; set; }
public bool Favorite { get; set; }
}
[StringLength(36)]
public string FolderId { get; set; }
public bool Favorite { get; set; }
}

View File

@ -8,342 +8,341 @@ using Core.Models.Data;
using NS = Newtonsoft.Json;
using NSL = Newtonsoft.Json.Linq;
namespace Bit.Api.Models.Request
namespace Bit.Api.Models.Request;
public class CipherRequestModel
{
public class CipherRequestModel
public CipherType Type { get; set; }
[StringLength(36)]
public string OrganizationId { get; set; }
public string FolderId { get; set; }
public bool Favorite { get; set; }
public CipherRepromptType Reprompt { get; set; }
[Required]
[EncryptedString]
[EncryptedStringLength(1000)]
public string Name { get; set; }
[EncryptedString]
[EncryptedStringLength(10000)]
public string Notes { get; set; }
public IEnumerable<CipherFieldModel> Fields { get; set; }
public IEnumerable<CipherPasswordHistoryModel> PasswordHistory { get; set; }
[Obsolete]
public Dictionary<string, string> Attachments { get; set; }
// TODO: Rename to Attachments whenever the above is finally removed.
public Dictionary<string, CipherAttachmentModel> Attachments2 { get; set; }
public CipherLoginModel Login { get; set; }
public CipherCardModel Card { get; set; }
public CipherIdentityModel Identity { get; set; }
public CipherSecureNoteModel SecureNote { get; set; }
public DateTime? LastKnownRevisionDate { get; set; } = null;
public CipherDetails ToCipherDetails(Guid userId, bool allowOrgIdSet = true)
{
public CipherType Type { get; set; }
[StringLength(36)]
public string OrganizationId { get; set; }
public string FolderId { get; set; }
public bool Favorite { get; set; }
public CipherRepromptType Reprompt { get; set; }
[Required]
[EncryptedString]
[EncryptedStringLength(1000)]
public string Name { get; set; }
[EncryptedString]
[EncryptedStringLength(10000)]
public string Notes { get; set; }
public IEnumerable<CipherFieldModel> Fields { get; set; }
public IEnumerable<CipherPasswordHistoryModel> PasswordHistory { get; set; }
[Obsolete]
public Dictionary<string, string> Attachments { get; set; }
// TODO: Rename to Attachments whenever the above is finally removed.
public Dictionary<string, CipherAttachmentModel> Attachments2 { get; set; }
public CipherLoginModel Login { get; set; }
public CipherCardModel Card { get; set; }
public CipherIdentityModel Identity { get; set; }
public CipherSecureNoteModel SecureNote { get; set; }
public DateTime? LastKnownRevisionDate { get; set; } = null;
public CipherDetails ToCipherDetails(Guid userId, bool allowOrgIdSet = true)
var hasOrgId = !string.IsNullOrWhiteSpace(OrganizationId);
var cipher = new CipherDetails
{
var hasOrgId = !string.IsNullOrWhiteSpace(OrganizationId);
var cipher = new CipherDetails
{
Type = Type,
UserId = !hasOrgId ? (Guid?)userId : null,
OrganizationId = allowOrgIdSet && hasOrgId ? new Guid(OrganizationId) : (Guid?)null,
Edit = true,
ViewPassword = true,
};
ToCipherDetails(cipher);
return cipher;
Type = Type,
UserId = !hasOrgId ? (Guid?)userId : null,
OrganizationId = allowOrgIdSet && hasOrgId ? new Guid(OrganizationId) : (Guid?)null,
Edit = true,
ViewPassword = true,
};
ToCipherDetails(cipher);
return cipher;
}
public CipherDetails ToCipherDetails(CipherDetails existingCipher)
{
existingCipher.FolderId = string.IsNullOrWhiteSpace(FolderId) ? null : (Guid?)new Guid(FolderId);
existingCipher.Favorite = Favorite;
ToCipher(existingCipher);
return existingCipher;
}
public Cipher ToCipher(Cipher existingCipher)
{
switch (existingCipher.Type)
{
case CipherType.Login:
var loginObj = NSL.JObject.FromObject(ToCipherLoginData(),
new NS.JsonSerializer { NullValueHandling = NS.NullValueHandling.Ignore });
// TODO: Switch to JsonNode in .NET 6 https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-use-dom-utf8jsonreader-utf8jsonwriter?pivots=dotnet-6-0
loginObj[nameof(CipherLoginData.Uri)]?.Parent?.Remove();
existingCipher.Data = loginObj.ToString(NS.Formatting.None);
break;
case CipherType.Card:
existingCipher.Data = JsonSerializer.Serialize(ToCipherCardData(), JsonHelpers.IgnoreWritingNull);
break;
case CipherType.Identity:
existingCipher.Data = JsonSerializer.Serialize(ToCipherIdentityData(), JsonHelpers.IgnoreWritingNull);
break;
case CipherType.SecureNote:
existingCipher.Data = JsonSerializer.Serialize(ToCipherSecureNoteData(), JsonHelpers.IgnoreWritingNull);
break;
default:
throw new ArgumentException("Unsupported type: " + nameof(Type) + ".");
}
public CipherDetails ToCipherDetails(CipherDetails existingCipher)
existingCipher.Reprompt = Reprompt;
var hasAttachments2 = (Attachments2?.Count ?? 0) > 0;
var hasAttachments = (Attachments?.Count ?? 0) > 0;
if (!hasAttachments2 && !hasAttachments)
{
existingCipher.FolderId = string.IsNullOrWhiteSpace(FolderId) ? null : (Guid?)new Guid(FolderId);
existingCipher.Favorite = Favorite;
ToCipher(existingCipher);
return existingCipher;
}
public Cipher ToCipher(Cipher existingCipher)
var attachments = existingCipher.GetAttachments();
if ((attachments?.Count ?? 0) == 0)
{
switch (existingCipher.Type)
{
case CipherType.Login:
var loginObj = NSL.JObject.FromObject(ToCipherLoginData(),
new NS.JsonSerializer { NullValueHandling = NS.NullValueHandling.Ignore });
// TODO: Switch to JsonNode in .NET 6 https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-use-dom-utf8jsonreader-utf8jsonwriter?pivots=dotnet-6-0
loginObj[nameof(CipherLoginData.Uri)]?.Parent?.Remove();
existingCipher.Data = loginObj.ToString(NS.Formatting.None);
break;
case CipherType.Card:
existingCipher.Data = JsonSerializer.Serialize(ToCipherCardData(), JsonHelpers.IgnoreWritingNull);
break;
case CipherType.Identity:
existingCipher.Data = JsonSerializer.Serialize(ToCipherIdentityData(), JsonHelpers.IgnoreWritingNull);
break;
case CipherType.SecureNote:
existingCipher.Data = JsonSerializer.Serialize(ToCipherSecureNoteData(), JsonHelpers.IgnoreWritingNull);
break;
default:
throw new ArgumentException("Unsupported type: " + nameof(Type) + ".");
}
existingCipher.Reprompt = Reprompt;
var hasAttachments2 = (Attachments2?.Count ?? 0) > 0;
var hasAttachments = (Attachments?.Count ?? 0) > 0;
if (!hasAttachments2 && !hasAttachments)
{
return existingCipher;
}
var attachments = existingCipher.GetAttachments();
if ((attachments?.Count ?? 0) == 0)
{
return existingCipher;
}
if (hasAttachments2)
{
foreach (var attachment in attachments.Where(a => Attachments2.ContainsKey(a.Key)))
{
var attachment2 = Attachments2[attachment.Key];
attachment.Value.FileName = attachment2.FileName;
attachment.Value.Key = attachment2.Key;
}
}
else if (hasAttachments)
{
foreach (var attachment in attachments.Where(a => Attachments.ContainsKey(a.Key)))
{
attachment.Value.FileName = Attachments[attachment.Key];
attachment.Value.Key = null;
}
}
existingCipher.SetAttachments(attachments);
return existingCipher;
}
public Cipher ToOrganizationCipher()
if (hasAttachments2)
{
if (string.IsNullOrWhiteSpace(OrganizationId))
foreach (var attachment in attachments.Where(a => Attachments2.ContainsKey(a.Key)))
{
throw new ArgumentNullException(nameof(OrganizationId));
}
return ToCipher(new Cipher
{
Type = Type,
OrganizationId = new Guid(OrganizationId)
});
}
public CipherDetails ToOrganizationCipherDetails(Guid orgId)
{
return ToCipherDetails(new CipherDetails
{
Type = Type,
OrganizationId = orgId,
Edit = true
});
}
private CipherLoginData ToCipherLoginData()
{
return new CipherLoginData
{
Name = Name,
Notes = Notes,
Fields = Fields?.Select(f => f.ToCipherFieldData()),
PasswordHistory = PasswordHistory?.Select(ph => ph.ToCipherPasswordHistoryData()),
Uris =
Login.Uris?.Where(u => u != null)
.Select(u => u.ToCipherLoginUriData()),
Username = Login.Username,
Password = Login.Password,
PasswordRevisionDate = Login.PasswordRevisionDate,
Totp = Login.Totp,
AutofillOnPageLoad = Login.AutofillOnPageLoad,
};
}
private CipherIdentityData ToCipherIdentityData()
{
return new CipherIdentityData
{
Name = Name,
Notes = Notes,
Fields = Fields?.Select(f => f.ToCipherFieldData()),
PasswordHistory = PasswordHistory?.Select(ph => ph.ToCipherPasswordHistoryData()),
Title = Identity.Title,
FirstName = Identity.FirstName,
MiddleName = Identity.MiddleName,
LastName = Identity.LastName,
Address1 = Identity.Address1,
Address2 = Identity.Address2,
Address3 = Identity.Address3,
City = Identity.City,
State = Identity.State,
PostalCode = Identity.PostalCode,
Country = Identity.Country,
Company = Identity.Company,
Email = Identity.Email,
Phone = Identity.Phone,
SSN = Identity.SSN,
Username = Identity.Username,
PassportNumber = Identity.PassportNumber,
LicenseNumber = Identity.LicenseNumber,
};
}
private CipherCardData ToCipherCardData()
{
return new CipherCardData
{
Name = Name,
Notes = Notes,
Fields = Fields?.Select(f => f.ToCipherFieldData()),
PasswordHistory = PasswordHistory?.Select(ph => ph.ToCipherPasswordHistoryData()),
CardholderName = Card.CardholderName,
Brand = Card.Brand,
Number = Card.Number,
ExpMonth = Card.ExpMonth,
ExpYear = Card.ExpYear,
Code = Card.Code,
};
}
private CipherSecureNoteData ToCipherSecureNoteData()
{
return new CipherSecureNoteData
{
Name = Name,
Notes = Notes,
Fields = Fields?.Select(f => f.ToCipherFieldData()),
PasswordHistory = PasswordHistory?.Select(ph => ph.ToCipherPasswordHistoryData()),
Type = SecureNote.Type,
};
}
}
public class CipherWithIdRequestModel : CipherRequestModel
{
[Required]
public Guid? Id { get; set; }
}
public class CipherCreateRequestModel : IValidatableObject
{
public IEnumerable<Guid> CollectionIds { get; set; }
[Required]
public CipherRequestModel Cipher { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (!string.IsNullOrWhiteSpace(Cipher.OrganizationId) && (!CollectionIds?.Any() ?? true))
{
yield return new ValidationResult("You must select at least one collection.",
new string[] { nameof(CollectionIds) });
var attachment2 = Attachments2[attachment.Key];
attachment.Value.FileName = attachment2.FileName;
attachment.Value.Key = attachment2.Key;
}
}
}
public class CipherShareRequestModel : IValidatableObject
{
[Required]
public IEnumerable<string> CollectionIds { get; set; }
[Required]
public CipherRequestModel Cipher { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
else if (hasAttachments)
{
if (string.IsNullOrWhiteSpace(Cipher.OrganizationId))
foreach (var attachment in attachments.Where(a => Attachments.ContainsKey(a.Key)))
{
yield return new ValidationResult("Cipher OrganizationId is required.",
new string[] { nameof(Cipher.OrganizationId) });
}
if (!CollectionIds?.Any() ?? true)
{
yield return new ValidationResult("You must select at least one collection.",
new string[] { nameof(CollectionIds) });
attachment.Value.FileName = Attachments[attachment.Key];
attachment.Value.Key = null;
}
}
existingCipher.SetAttachments(attachments);
return existingCipher;
}
public class CipherCollectionsRequestModel
public Cipher ToOrganizationCipher()
{
[Required]
public IEnumerable<string> CollectionIds { get; set; }
}
public class CipherBulkDeleteRequestModel
{
[Required]
public IEnumerable<string> Ids { get; set; }
public string OrganizationId { get; set; }
}
public class CipherBulkRestoreRequestModel
{
[Required]
public IEnumerable<string> Ids { get; set; }
}
public class CipherBulkMoveRequestModel
{
[Required]
public IEnumerable<string> Ids { get; set; }
public string FolderId { get; set; }
}
public class CipherBulkShareRequestModel : IValidatableObject
{
[Required]
public IEnumerable<string> CollectionIds { get; set; }
[Required]
public IEnumerable<CipherWithIdRequestModel> Ciphers { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
if (string.IsNullOrWhiteSpace(OrganizationId))
{
if (!Ciphers?.Any() ?? true)
{
yield return new ValidationResult("You must select at least one cipher.",
new string[] { nameof(Ciphers) });
}
else
{
var allHaveIds = true;
var organizationIds = new HashSet<string>();
foreach (var c in Ciphers)
{
organizationIds.Add(c.OrganizationId);
if (allHaveIds)
{
allHaveIds = !(!c.Id.HasValue || string.IsNullOrWhiteSpace(c.OrganizationId));
}
}
throw new ArgumentNullException(nameof(OrganizationId));
}
if (!allHaveIds)
{
yield return new ValidationResult("All Ciphers must have an Id and OrganizationId.",
new string[] { nameof(Ciphers) });
}
else if (organizationIds.Count != 1)
{
yield return new ValidationResult("All ciphers must be for the same organization.");
}
}
return ToCipher(new Cipher
{
Type = Type,
OrganizationId = new Guid(OrganizationId)
});
}
if (!CollectionIds?.Any() ?? true)
{
yield return new ValidationResult("You must select at least one collection.",
new string[] { nameof(CollectionIds) });
}
public CipherDetails ToOrganizationCipherDetails(Guid orgId)
{
return ToCipherDetails(new CipherDetails
{
Type = Type,
OrganizationId = orgId,
Edit = true
});
}
private CipherLoginData ToCipherLoginData()
{
return new CipherLoginData
{
Name = Name,
Notes = Notes,
Fields = Fields?.Select(f => f.ToCipherFieldData()),
PasswordHistory = PasswordHistory?.Select(ph => ph.ToCipherPasswordHistoryData()),
Uris =
Login.Uris?.Where(u => u != null)
.Select(u => u.ToCipherLoginUriData()),
Username = Login.Username,
Password = Login.Password,
PasswordRevisionDate = Login.PasswordRevisionDate,
Totp = Login.Totp,
AutofillOnPageLoad = Login.AutofillOnPageLoad,
};
}
private CipherIdentityData ToCipherIdentityData()
{
return new CipherIdentityData
{
Name = Name,
Notes = Notes,
Fields = Fields?.Select(f => f.ToCipherFieldData()),
PasswordHistory = PasswordHistory?.Select(ph => ph.ToCipherPasswordHistoryData()),
Title = Identity.Title,
FirstName = Identity.FirstName,
MiddleName = Identity.MiddleName,
LastName = Identity.LastName,
Address1 = Identity.Address1,
Address2 = Identity.Address2,
Address3 = Identity.Address3,
City = Identity.City,
State = Identity.State,
PostalCode = Identity.PostalCode,
Country = Identity.Country,
Company = Identity.Company,
Email = Identity.Email,
Phone = Identity.Phone,
SSN = Identity.SSN,
Username = Identity.Username,
PassportNumber = Identity.PassportNumber,
LicenseNumber = Identity.LicenseNumber,
};
}
private CipherCardData ToCipherCardData()
{
return new CipherCardData
{
Name = Name,
Notes = Notes,
Fields = Fields?.Select(f => f.ToCipherFieldData()),
PasswordHistory = PasswordHistory?.Select(ph => ph.ToCipherPasswordHistoryData()),
CardholderName = Card.CardholderName,
Brand = Card.Brand,
Number = Card.Number,
ExpMonth = Card.ExpMonth,
ExpYear = Card.ExpYear,
Code = Card.Code,
};
}
private CipherSecureNoteData ToCipherSecureNoteData()
{
return new CipherSecureNoteData
{
Name = Name,
Notes = Notes,
Fields = Fields?.Select(f => f.ToCipherFieldData()),
PasswordHistory = PasswordHistory?.Select(ph => ph.ToCipherPasswordHistoryData()),
Type = SecureNote.Type,
};
}
}
public class CipherWithIdRequestModel : CipherRequestModel
{
[Required]
public Guid? Id { get; set; }
}
public class CipherCreateRequestModel : IValidatableObject
{
public IEnumerable<Guid> CollectionIds { get; set; }
[Required]
public CipherRequestModel Cipher { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (!string.IsNullOrWhiteSpace(Cipher.OrganizationId) && (!CollectionIds?.Any() ?? true))
{
yield return new ValidationResult("You must select at least one collection.",
new string[] { nameof(CollectionIds) });
}
}
}
public class CipherShareRequestModel : IValidatableObject
{
[Required]
public IEnumerable<string> CollectionIds { get; set; }
[Required]
public CipherRequestModel Cipher { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (string.IsNullOrWhiteSpace(Cipher.OrganizationId))
{
yield return new ValidationResult("Cipher OrganizationId is required.",
new string[] { nameof(Cipher.OrganizationId) });
}
if (!CollectionIds?.Any() ?? true)
{
yield return new ValidationResult("You must select at least one collection.",
new string[] { nameof(CollectionIds) });
}
}
}
public class CipherCollectionsRequestModel
{
[Required]
public IEnumerable<string> CollectionIds { get; set; }
}
public class CipherBulkDeleteRequestModel
{
[Required]
public IEnumerable<string> Ids { get; set; }
public string OrganizationId { get; set; }
}
public class CipherBulkRestoreRequestModel
{
[Required]
public IEnumerable<string> Ids { get; set; }
}
public class CipherBulkMoveRequestModel
{
[Required]
public IEnumerable<string> Ids { get; set; }
public string FolderId { get; set; }
}
public class CipherBulkShareRequestModel : IValidatableObject
{
[Required]
public IEnumerable<string> CollectionIds { get; set; }
[Required]
public IEnumerable<CipherWithIdRequestModel> Ciphers { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (!Ciphers?.Any() ?? true)
{
yield return new ValidationResult("You must select at least one cipher.",
new string[] { nameof(Ciphers) });
}
else
{
var allHaveIds = true;
var organizationIds = new HashSet<string>();
foreach (var c in Ciphers)
{
organizationIds.Add(c.OrganizationId);
if (allHaveIds)
{
allHaveIds = !(!c.Id.HasValue || string.IsNullOrWhiteSpace(c.OrganizationId));
}
}
if (!allHaveIds)
{
yield return new ValidationResult("All Ciphers must have an Id and OrganizationId.",
new string[] { nameof(Ciphers) });
}
else if (organizationIds.Count != 1)
{
yield return new ValidationResult("All ciphers must be for the same organization.");
}
}
if (!CollectionIds?.Any() ?? true)
{
yield return new ValidationResult("You must select at least one collection.",
new string[] { nameof(CollectionIds) });
}
}
}

View File

@ -2,31 +2,30 @@
using Bit.Core.Entities;
using Bit.Core.Utilities;
namespace Bit.Api.Models.Request
namespace Bit.Api.Models.Request;
public class CollectionRequestModel
{
public class CollectionRequestModel
[Required]
[EncryptedString]
[EncryptedStringLength(1000)]
public string Name { get; set; }
[StringLength(300)]
public string ExternalId { get; set; }
public IEnumerable<SelectionReadOnlyRequestModel> Groups { get; set; }
public Collection ToCollection(Guid orgId)
{
[Required]
[EncryptedString]
[EncryptedStringLength(1000)]
public string Name { get; set; }
[StringLength(300)]
public string ExternalId { get; set; }
public IEnumerable<SelectionReadOnlyRequestModel> Groups { get; set; }
public Collection ToCollection(Guid orgId)
return ToCollection(new Collection
{
return ToCollection(new Collection
{
OrganizationId = orgId
});
}
OrganizationId = orgId
});
}
public Collection ToCollection(Collection existingCollection)
{
existingCollection.Name = Name;
existingCollection.ExternalId = ExternalId;
return existingCollection;
}
public Collection ToCollection(Collection existingCollection)
{
existingCollection.Name = Name;
existingCollection.ExternalId = ExternalId;
return existingCollection;
}
}

View File

@ -2,49 +2,48 @@
using Bit.Core.Entities;
using Bit.Core.Enums;
namespace Bit.Api.Models.Request
namespace Bit.Api.Models.Request;
public class DeviceRequestModel
{
public class DeviceRequestModel
[Required]
public DeviceType? Type { get; set; }
[Required]
[StringLength(50)]
public string Name { get; set; }
[Required]
[StringLength(50)]
public string Identifier { get; set; }
[StringLength(255)]
public string PushToken { get; set; }
public Device ToDevice(Guid? userId = null)
{
[Required]
public DeviceType? Type { get; set; }
[Required]
[StringLength(50)]
public string Name { get; set; }
[Required]
[StringLength(50)]
public string Identifier { get; set; }
[StringLength(255)]
public string PushToken { get; set; }
public Device ToDevice(Guid? userId = null)
return ToDevice(new Device
{
return ToDevice(new Device
{
UserId = userId == null ? default(Guid) : userId.Value
});
}
public Device ToDevice(Device existingDevice)
{
existingDevice.Name = Name;
existingDevice.Identifier = Identifier;
existingDevice.PushToken = PushToken;
existingDevice.Type = Type.Value;
return existingDevice;
}
UserId = userId == null ? default(Guid) : userId.Value
});
}
public class DeviceTokenRequestModel
public Device ToDevice(Device existingDevice)
{
[StringLength(255)]
public string PushToken { get; set; }
existingDevice.Name = Name;
existingDevice.Identifier = Identifier;
existingDevice.PushToken = PushToken;
existingDevice.Type = Type.Value;
public Device ToDevice(Device existingDevice)
{
existingDevice.PushToken = PushToken;
return existingDevice;
}
return existingDevice;
}
}
public class DeviceTokenRequestModel
{
[StringLength(255)]
public string PushToken { get; set; }
public Device ToDevice(Device existingDevice)
{
existingDevice.PushToken = PushToken;
return existingDevice;
}
}

View File

@ -1,17 +1,16 @@
using System.ComponentModel.DataAnnotations;
using Bit.Core.Entities;
namespace Bit.Api.Models.Request
{
public class DeviceVerificationRequestModel
{
[Required]
public bool UnknownDeviceVerificationEnabled { get; set; }
namespace Bit.Api.Models.Request;
public User ToUser(User user)
{
user.UnknownDeviceVerificationEnabled = UnknownDeviceVerificationEnabled;
return user;
}
public class DeviceVerificationRequestModel
{
[Required]
public bool UnknownDeviceVerificationEnabled { get; set; }
public User ToUser(User user)
{
user.UnknownDeviceVerificationEnabled = UnknownDeviceVerificationEnabled;
return user;
}
}

View File

@ -3,47 +3,46 @@ using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Utilities;
namespace Bit.Api.Models.Request
namespace Bit.Api.Models.Request;
public class EmergencyAccessInviteRequestModel
{
public class EmergencyAccessInviteRequestModel
{
[Required]
[StrictEmailAddress]
[StringLength(256)]
public string Email { get; set; }
[Required]
public EmergencyAccessType? Type { get; set; }
[Required]
public int WaitTimeDays { get; set; }
}
[Required]
[StrictEmailAddress]
[StringLength(256)]
public string Email { get; set; }
[Required]
public EmergencyAccessType? Type { get; set; }
[Required]
public int WaitTimeDays { get; set; }
}
public class EmergencyAccessUpdateRequestModel
{
[Required]
public EmergencyAccessType Type { get; set; }
[Required]
public int WaitTimeDays { get; set; }
public string KeyEncrypted { get; set; }
public class EmergencyAccessUpdateRequestModel
{
[Required]
public EmergencyAccessType Type { get; set; }
[Required]
public int WaitTimeDays { get; set; }
public string KeyEncrypted { get; set; }
public EmergencyAccess ToEmergencyAccess(EmergencyAccess existingEmergencyAccess)
public EmergencyAccess ToEmergencyAccess(EmergencyAccess existingEmergencyAccess)
{
// Ensure we only set keys for a confirmed emergency access.
if (!string.IsNullOrWhiteSpace(existingEmergencyAccess.KeyEncrypted) && !string.IsNullOrWhiteSpace(KeyEncrypted))
{
// Ensure we only set keys for a confirmed emergency access.
if (!string.IsNullOrWhiteSpace(existingEmergencyAccess.KeyEncrypted) && !string.IsNullOrWhiteSpace(KeyEncrypted))
{
existingEmergencyAccess.KeyEncrypted = KeyEncrypted;
}
existingEmergencyAccess.Type = Type;
existingEmergencyAccess.WaitTimeDays = WaitTimeDays;
return existingEmergencyAccess;
existingEmergencyAccess.KeyEncrypted = KeyEncrypted;
}
}
public class EmergencyAccessPasswordRequestModel
{
[Required]
[StringLength(300)]
public string NewMasterPasswordHash { get; set; }
[Required]
public string Key { get; set; }
existingEmergencyAccess.Type = Type;
existingEmergencyAccess.WaitTimeDays = WaitTimeDays;
return existingEmergencyAccess;
}
}
public class EmergencyAccessPasswordRequestModel
{
[Required]
[StringLength(300)]
public string NewMasterPasswordHash { get; set; }
[Required]
public string Key { get; set; }
}

View File

@ -2,32 +2,31 @@
using Bit.Core.Entities;
using Bit.Core.Utilities;
namespace Bit.Api.Models.Request
namespace Bit.Api.Models.Request;
public class FolderRequestModel
{
public class FolderRequestModel
[Required]
[EncryptedString]
[EncryptedStringLength(1000)]
public string Name { get; set; }
public Folder ToFolder(Guid userId)
{
[Required]
[EncryptedString]
[EncryptedStringLength(1000)]
public string Name { get; set; }
public Folder ToFolder(Guid userId)
return ToFolder(new Folder
{
return ToFolder(new Folder
{
UserId = userId
});
}
public Folder ToFolder(Folder existingFolder)
{
existingFolder.Name = Name;
return existingFolder;
}
UserId = userId
});
}
public class FolderWithIdRequestModel : FolderRequestModel
public Folder ToFolder(Folder existingFolder)
{
public Guid Id { get; set; }
existingFolder.Name = Name;
return existingFolder;
}
}
public class FolderWithIdRequestModel : FolderRequestModel
{
public Guid Id { get; set; }
}

View File

@ -1,33 +1,32 @@
using System.ComponentModel.DataAnnotations;
using Bit.Core.Entities;
namespace Bit.Api.Models.Request
namespace Bit.Api.Models.Request;
public class GroupRequestModel
{
public class GroupRequestModel
[Required]
[StringLength(100)]
public string Name { get; set; }
[Required]
public bool? AccessAll { get; set; }
[StringLength(300)]
public string ExternalId { get; set; }
public IEnumerable<SelectionReadOnlyRequestModel> Collections { get; set; }
public Group ToGroup(Guid orgId)
{
[Required]
[StringLength(100)]
public string Name { get; set; }
[Required]
public bool? AccessAll { get; set; }
[StringLength(300)]
public string ExternalId { get; set; }
public IEnumerable<SelectionReadOnlyRequestModel> Collections { get; set; }
public Group ToGroup(Guid orgId)
return ToGroup(new Group
{
return ToGroup(new Group
{
OrganizationId = orgId
});
}
OrganizationId = orgId
});
}
public Group ToGroup(Group existingGroup)
{
existingGroup.Name = Name;
existingGroup.AccessAll = AccessAll.Value;
existingGroup.ExternalId = ExternalId;
return existingGroup;
}
public Group ToGroup(Group existingGroup)
{
existingGroup.Name = Name;
existingGroup.AccessAll = AccessAll.Value;
existingGroup.ExternalId = ExternalId;
return existingGroup;
}
}

Some files were not shown because too many files have changed in this diff Show More