diff --git a/src/Api/Tools/Controllers/OrganizationExportController.cs b/src/Api/Tools/Controllers/OrganizationExportController.cs index b3c0643b28..fff417d4e6 100644 --- a/src/Api/Tools/Controllers/OrganizationExportController.cs +++ b/src/Api/Tools/Controllers/OrganizationExportController.cs @@ -1,11 +1,18 @@ using Bit.Api.Models.Response; using Bit.Api.Tools.Models.Response; using Bit.Api.Vault.Models.Response; +using Bit.Core; +using Bit.Core.AdminConsole.OrganizationFeatures.Shared.Authorization; using Bit.Core.Context; using Bit.Core.Entities; +using Bit.Core.Exceptions; +using Bit.Core.Repositories; using Bit.Core.Services; using Bit.Core.Settings; +using Bit.Core.Tools.Authorization; using Bit.Core.Vault.Models.Data; +using Bit.Core.Vault.Queries; +using Bit.Core.Vault.Repositories; using Bit.Core.Vault.Services; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -21,24 +28,44 @@ public class OrganizationExportController : Controller private readonly ICollectionService _collectionService; private readonly ICipherService _cipherService; private readonly GlobalSettings _globalSettings; + private readonly IFeatureService _featureService; + private readonly IAuthorizationService _authorizationService; + private readonly IOrganizationCiphersQuery _organizationCiphersQuery; + private readonly ICipherRepository _cipherRepository; + private readonly ICollectionRepository _collectionRepository; public OrganizationExportController( ICurrentContext currentContext, ICipherService cipherService, ICollectionService collectionService, IUserService userService, - GlobalSettings globalSettings) + GlobalSettings globalSettings, + IFeatureService featureService, + IAuthorizationService authorizationService, + IOrganizationCiphersQuery organizationCiphersQuery, + ICipherRepository cipherRepository, + ICollectionRepository collectionRepository) { _currentContext = currentContext; _cipherService = cipherService; _collectionService = collectionService; _userService = userService; _globalSettings = globalSettings; + _featureService = featureService; + _authorizationService = authorizationService; + _organizationCiphersQuery = organizationCiphersQuery; + _cipherRepository = cipherRepository; + _collectionRepository = collectionRepository; } [HttpGet("export")] public async Task Export(Guid organizationId) { + if (_featureService.IsEnabled(FeatureFlagKeys.PM11360RemoveProviderExportPermission)) + { + return await Export_vNext(organizationId); + } + var userId = _userService.GetProperUserId(User).Value; IEnumerable orgCollections = await _collectionService.GetOrganizationCollectionsAsync(organizationId); @@ -65,6 +92,37 @@ public class OrganizationExportController : Controller return Ok(organizationExportListResponseModel); } + private async Task Export_vNext(Guid organizationId) + { + var canExportAll = await _authorizationService.AuthorizeAsync(User, new OrganizationScope(organizationId), + VaultExportOperations.ExportWholeVault); + if (canExportAll.Succeeded) + { + var allOrganizationCiphers = await _organizationCiphersQuery.GetAllOrganizationCiphers(organizationId); + var allCollections = await _collectionRepository.GetManyByOrganizationIdAsync(organizationId); + return Ok(new OrganizationExportResponseModel(allOrganizationCiphers, allCollections, _globalSettings)); + } + + var canExportManaged = await _authorizationService.AuthorizeAsync(User, new OrganizationScope(organizationId), + VaultExportOperations.ExportManagedCollections); + if (canExportManaged.Succeeded) + { + var userId = _userService.GetProperUserId(User)!.Value; + + var allCollections = await _collectionRepository.GetManyByUserIdAsync(userId); + var managedCollections = allCollections.Where(c => c.OrganizationId == organizationId && c.Manage).ToList(); + + var managedCollectionIds = managedCollections.Select(c => c.Id).ToHashSet(); + var allOrganizationCiphers = await _organizationCiphersQuery.GetAllOrganizationCiphers(organizationId); + var managedCiphers = allOrganizationCiphers.Where(c => c.CollectionIds.Intersect(managedCollectionIds).Any()); + + return Ok(new OrganizationExportResponseModel(managedCiphers, managedCollections, _globalSettings)); + } + + // Unauthorized + throw new NotFoundException(); + } + private ListResponseModel GetOrganizationCollectionsResponse(IEnumerable orgCollections) { var collections = orgCollections.Select(c => new CollectionResponseModel(c)); diff --git a/src/Api/Tools/Models/Response/OrganizationExportResponseModel.cs b/src/Api/Tools/Models/Response/OrganizationExportResponseModel.cs index a4b35d8de1..5fd7e821cf 100644 --- a/src/Api/Tools/Models/Response/OrganizationExportResponseModel.cs +++ b/src/Api/Tools/Models/Response/OrganizationExportResponseModel.cs @@ -1,6 +1,9 @@ using Bit.Api.Models.Response; using Bit.Api.Vault.Models.Response; +using Bit.Core.Entities; using Bit.Core.Models.Api; +using Bit.Core.Settings; +using Bit.Core.Vault.Models.Data; namespace Bit.Api.Tools.Models.Response; @@ -10,6 +13,13 @@ public class OrganizationExportResponseModel : ResponseModel { } + public OrganizationExportResponseModel(IEnumerable ciphers, + IEnumerable collections, GlobalSettings globalSettings) : this() + { + Ciphers = ciphers.Select(c => new CipherMiniDetailsResponseModel(c, globalSettings)); + Collections = collections.Select(c => new CollectionResponseModel(c)); + } + public IEnumerable Collections { get; set; } public IEnumerable Ciphers { get; set; } diff --git a/src/Api/Vault/Models/Response/CipherResponseModel.cs b/src/Api/Vault/Models/Response/CipherResponseModel.cs index 10b77274b5..207017227a 100644 --- a/src/Api/Vault/Models/Response/CipherResponseModel.cs +++ b/src/Api/Vault/Models/Response/CipherResponseModel.cs @@ -166,5 +166,12 @@ public class CipherMiniDetailsResponseModel : CipherMiniResponseModel CollectionIds = cipher.CollectionIds ?? new List(); } + public CipherMiniDetailsResponseModel(CipherOrganizationDetailsWithCollections cipher, + GlobalSettings globalSettings, string obj = "cipherMiniDetails") + : base(cipher, globalSettings, cipher.OrganizationUseTotp, obj) + { + CollectionIds = cipher.CollectionIds ?? new List(); + } + public IEnumerable CollectionIds { get; set; } } diff --git a/src/Core/Constants.cs b/src/Core/Constants.cs index 3e40eb7a16..6439b8d9f7 100644 --- a/src/Core/Constants.cs +++ b/src/Core/Constants.cs @@ -154,6 +154,7 @@ public static class FeatureFlagKeys public const string NewDeviceVerificationPermanentDismiss = "new-device-permanent-dismiss"; public const string SecurityTasks = "security-tasks"; public const string PM14401_ScaleMSPOnClientOrganizationUpdate = "PM-14401-scale-msp-on-client-organization-update"; + public const string PM11360RemoveProviderExportPermission = "pm-11360-remove-provider-export-permission"; public static List GetAllKeys() {