mirror of
https://github.com/bitwarden/server.git
synced 2024-12-28 17:57:37 +01:00
[AC-1782] Import can manage (#3453)
* Changed Import permissions validation to check if the user CanCreate a Collection * Corrected authorized to import validation allowing import without collections when the user is admin * Added validation to check if user can import ciphers into existing collections * swapped feature flag flexible collections with org property * Removed unused feature service from ImportCiphersController * Improved code readability * added null protection against empty org when checking for FlexibleCollections flag
This commit is contained in:
parent
114b72d738
commit
c2b4ee7eac
@ -1,6 +1,8 @@
|
||||
using Bit.Api.Tools.Models.Request.Accounts;
|
||||
using Bit.Api.Tools.Models.Request.Organizations;
|
||||
using Bit.Api.Vault.AuthorizationHandlers.Collections;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
@ -20,20 +22,28 @@ public class ImportCiphersController : Controller
|
||||
private readonly ICurrentContext _currentContext;
|
||||
private readonly ILogger<ImportCiphersController> _logger;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly ICollectionRepository _collectionRepository;
|
||||
private readonly IAuthorizationService _authorizationService;
|
||||
private readonly IOrganizationRepository _organizationRepository;
|
||||
|
||||
public ImportCiphersController(
|
||||
ICollectionCipherRepository collectionCipherRepository,
|
||||
ICipherService cipherService,
|
||||
IUserService userService,
|
||||
ICurrentContext currentContext,
|
||||
ILogger<ImportCiphersController> logger,
|
||||
GlobalSettings globalSettings)
|
||||
GlobalSettings globalSettings,
|
||||
ICollectionRepository collectionRepository,
|
||||
IAuthorizationService authorizationService,
|
||||
IOrganizationRepository organizationRepository)
|
||||
{
|
||||
_cipherService = cipherService;
|
||||
_userService = userService;
|
||||
_currentContext = currentContext;
|
||||
_logger = logger;
|
||||
_globalSettings = globalSettings;
|
||||
_collectionRepository = collectionRepository;
|
||||
_authorizationService = authorizationService;
|
||||
_organizationRepository = organizationRepository;
|
||||
}
|
||||
|
||||
[HttpPost("import")]
|
||||
@ -64,14 +74,59 @@ public class ImportCiphersController : Controller
|
||||
}
|
||||
|
||||
var orgId = new Guid(organizationId);
|
||||
if (!await _currentContext.AccessImportExport(orgId))
|
||||
var collections = model.Collections.Select(c => c.ToCollection(orgId)).ToList();
|
||||
|
||||
|
||||
//An User is allowed to import if CanCreate Collections or has AccessToImportExport
|
||||
var authorized = await CheckOrgImportPermission(collections, orgId);
|
||||
|
||||
if (!authorized)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var userId = _userService.GetProperUserId(User).Value;
|
||||
var collections = model.Collections.Select(c => c.ToCollection(orgId)).ToList();
|
||||
var ciphers = model.Ciphers.Select(l => l.ToOrganizationCipherDetails(orgId)).ToList();
|
||||
await _cipherService.ImportCiphersAsync(collections, ciphers, model.CollectionRelationships, userId);
|
||||
}
|
||||
|
||||
private async Task<bool> CheckOrgImportPermission(List<Collection> collections, Guid orgId)
|
||||
{
|
||||
//Users are allowed to import if they have the AccessToImportExport permission
|
||||
if (await _currentContext.AccessImportExport(orgId))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//If flexible collections is disabled the user cannot continue with the import
|
||||
var orgFlexibleCollections = await _organizationRepository.GetByIdAsync(orgId);
|
||||
if (!orgFlexibleCollections?.FlexibleCollections ?? false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//Users allowed to import if they CanCreate Collections
|
||||
if (!(await _authorizationService.AuthorizeAsync(User, collections, BulkCollectionOperations.Create)).Succeeded)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//Calling Repository instead of Service as we want to get all the collections, regardless of permission
|
||||
//Permissions check will be done later on AuthorizationService
|
||||
var orgCollectionIds =
|
||||
(await _collectionRepository.GetManyByOrganizationIdAsync(orgId))
|
||||
.Select(c => c.Id)
|
||||
.ToHashSet();
|
||||
|
||||
//We need to verify if the user is trying to import into existing collections
|
||||
var existingCollections = collections.Where(tc => orgCollectionIds.Contains(tc.Id));
|
||||
|
||||
//When importing into existing collection, we need to verify if the user has permissions
|
||||
if (existingCollections.Any() && !(await _authorizationService.AuthorizeAsync(User, existingCollections, BulkCollectionOperations.ImportCiphers)).Succeeded)
|
||||
{
|
||||
return false;
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -76,6 +76,7 @@ public class BulkCollectionAuthorizationHandler : BulkAuthorizationHandler<BulkC
|
||||
|
||||
case not null when requirement == BulkCollectionOperations.Update:
|
||||
case not null when requirement == BulkCollectionOperations.ModifyAccess:
|
||||
case not null when requirement == BulkCollectionOperations.ImportCiphers:
|
||||
await CanUpdateCollectionAsync(context, requirement, resources, org);
|
||||
break;
|
||||
|
||||
|
@ -18,4 +18,5 @@ public static class BulkCollectionOperations
|
||||
/// </summary>
|
||||
public static readonly BulkCollectionOperationRequirement ModifyAccess = new() { Name = nameof(ModifyAccess) };
|
||||
public static readonly BulkCollectionOperationRequirement Delete = new() { Name = nameof(Delete) };
|
||||
public static readonly BulkCollectionOperationRequirement ImportCiphers = new() { Name = nameof(ImportCiphers) };
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user