mirror of
https://github.com/bitwarden/server.git
synced 2025-05-29 23:34:53 -05: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.Accounts;
|
||||||
using Bit.Api.Tools.Models.Request.Organizations;
|
using Bit.Api.Tools.Models.Request.Organizations;
|
||||||
|
using Bit.Api.Vault.AuthorizationHandlers.Collections;
|
||||||
using Bit.Core.Context;
|
using Bit.Core.Context;
|
||||||
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
@ -20,20 +22,28 @@ public class ImportCiphersController : Controller
|
|||||||
private readonly ICurrentContext _currentContext;
|
private readonly ICurrentContext _currentContext;
|
||||||
private readonly ILogger<ImportCiphersController> _logger;
|
private readonly ILogger<ImportCiphersController> _logger;
|
||||||
private readonly GlobalSettings _globalSettings;
|
private readonly GlobalSettings _globalSettings;
|
||||||
|
private readonly ICollectionRepository _collectionRepository;
|
||||||
|
private readonly IAuthorizationService _authorizationService;
|
||||||
|
private readonly IOrganizationRepository _organizationRepository;
|
||||||
|
|
||||||
public ImportCiphersController(
|
public ImportCiphersController(
|
||||||
ICollectionCipherRepository collectionCipherRepository,
|
|
||||||
ICipherService cipherService,
|
ICipherService cipherService,
|
||||||
IUserService userService,
|
IUserService userService,
|
||||||
ICurrentContext currentContext,
|
ICurrentContext currentContext,
|
||||||
ILogger<ImportCiphersController> logger,
|
ILogger<ImportCiphersController> logger,
|
||||||
GlobalSettings globalSettings)
|
GlobalSettings globalSettings,
|
||||||
|
ICollectionRepository collectionRepository,
|
||||||
|
IAuthorizationService authorizationService,
|
||||||
|
IOrganizationRepository organizationRepository)
|
||||||
{
|
{
|
||||||
_cipherService = cipherService;
|
_cipherService = cipherService;
|
||||||
_userService = userService;
|
_userService = userService;
|
||||||
_currentContext = currentContext;
|
_currentContext = currentContext;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_globalSettings = globalSettings;
|
_globalSettings = globalSettings;
|
||||||
|
_collectionRepository = collectionRepository;
|
||||||
|
_authorizationService = authorizationService;
|
||||||
|
_organizationRepository = organizationRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("import")]
|
[HttpPost("import")]
|
||||||
@ -64,14 +74,59 @@ public class ImportCiphersController : Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
var orgId = new Guid(organizationId);
|
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();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var userId = _userService.GetProperUserId(User).Value;
|
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();
|
var ciphers = model.Ciphers.Select(l => l.ToOrganizationCipherDetails(orgId)).ToList();
|
||||||
await _cipherService.ImportCiphersAsync(collections, ciphers, model.CollectionRelationships, userId);
|
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.Update:
|
||||||
case not null when requirement == BulkCollectionOperations.ModifyAccess:
|
case not null when requirement == BulkCollectionOperations.ModifyAccess:
|
||||||
|
case not null when requirement == BulkCollectionOperations.ImportCiphers:
|
||||||
await CanUpdateCollectionAsync(context, requirement, resources, org);
|
await CanUpdateCollectionAsync(context, requirement, resources, org);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -18,4 +18,5 @@ public static class BulkCollectionOperations
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static readonly BulkCollectionOperationRequirement ModifyAccess = new() { Name = nameof(ModifyAccess) };
|
public static readonly BulkCollectionOperationRequirement ModifyAccess = new() { Name = nameof(ModifyAccess) };
|
||||||
public static readonly BulkCollectionOperationRequirement Delete = new() { Name = nameof(Delete) };
|
public static readonly BulkCollectionOperationRequirement Delete = new() { Name = nameof(Delete) };
|
||||||
|
public static readonly BulkCollectionOperationRequirement ImportCiphers = new() { Name = nameof(ImportCiphers) };
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user