mirror of
https://github.com/bitwarden/server.git
synced 2025-12-11 12:59:33 -06:00
164 lines
6.7 KiB
C#
164 lines
6.7 KiB
C#
// FIXME: Update this file to be null safe and then delete the line below
|
|
#nullable disable
|
|
|
|
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;
|
|
using Bit.Core.Settings;
|
|
using Bit.Core.Tools.ImportFeatures.Interfaces;
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
|
|
namespace Bit.Api.Tools.Controllers;
|
|
|
|
[Route("ciphers")]
|
|
[Authorize("Application")]
|
|
public class ImportCiphersController : Controller
|
|
{
|
|
private readonly IUserService _userService;
|
|
private readonly ICurrentContext _currentContext;
|
|
private readonly ILogger<ImportCiphersController> _logger;
|
|
private readonly GlobalSettings _globalSettings;
|
|
private readonly ICollectionRepository _collectionRepository;
|
|
private readonly IAuthorizationService _authorizationService;
|
|
private readonly IImportCiphersCommand _importCiphersCommand;
|
|
|
|
public ImportCiphersController(
|
|
IUserService userService,
|
|
ICurrentContext currentContext,
|
|
ILogger<ImportCiphersController> logger,
|
|
GlobalSettings globalSettings,
|
|
ICollectionRepository collectionRepository,
|
|
IAuthorizationService authorizationService,
|
|
IImportCiphersCommand importCiphersCommand)
|
|
{
|
|
_userService = userService;
|
|
_currentContext = currentContext;
|
|
_logger = logger;
|
|
_globalSettings = globalSettings;
|
|
_collectionRepository = collectionRepository;
|
|
_authorizationService = authorizationService;
|
|
_importCiphersCommand = importCiphersCommand;
|
|
}
|
|
|
|
[HttpPost("import")]
|
|
public async Task PostImport([FromBody] ImportCiphersRequestModel model)
|
|
{
|
|
if (!_globalSettings.SelfHosted &&
|
|
(model.Ciphers.Count() > 7000 || model.FolderRelationships.Count() > 7000 ||
|
|
model.Folders.Count() > 2000))
|
|
{
|
|
throw new BadRequestException("You cannot import this much data at once.");
|
|
}
|
|
|
|
var userId = _userService.GetProperUserId(User).Value;
|
|
var folders = model.Folders.Select(f => f.ToFolder(userId)).ToList();
|
|
var ciphers = model.Ciphers.Select(c => c.ToCipherDetails(userId, false)).ToList();
|
|
await _importCiphersCommand.ImportIntoIndividualVaultAsync(folders, ciphers, model.FolderRelationships, userId);
|
|
}
|
|
|
|
[HttpPost("import-organization")]
|
|
public async Task PostImportOrganization([FromQuery] string organizationId,
|
|
[FromBody] ImportOrganizationCiphersRequestModel model)
|
|
{
|
|
if (!_globalSettings.SelfHosted &&
|
|
(model.Ciphers.Count() > _globalSettings.ImportCiphersLimitation.CiphersLimit ||
|
|
model.CollectionRelationships.Count() > _globalSettings.ImportCiphersLimitation.CollectionRelationshipsLimit ||
|
|
model.Collections.Count() > _globalSettings.ImportCiphersLimitation.CollectionsLimit))
|
|
{
|
|
throw new BadRequestException("You cannot import this much data at once.");
|
|
}
|
|
|
|
var orgId = new Guid(organizationId);
|
|
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 BadRequestException("Not enough privileges to import into this organization.");
|
|
}
|
|
|
|
var userId = _userService.GetProperUserId(User).Value;
|
|
var ciphers = model.Ciphers.Select(l => l.ToOrganizationCipherDetails(orgId)).ToList();
|
|
await _importCiphersCommand.ImportIntoOrganizationalVaultAsync(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;
|
|
}
|
|
|
|
//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();
|
|
|
|
// when there are no collections, then we can import
|
|
if (collections.Count == 0)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// are we trying to import into existing collections?
|
|
var existingCollections = collections.Where(tc => orgCollectionIds.Contains(tc.Id));
|
|
|
|
// are we trying to create new collections?
|
|
var hasNewCollections = collections.Any(tc => !orgCollectionIds.Contains(tc.Id));
|
|
|
|
// suppose we have both new and existing collections
|
|
if (hasNewCollections && existingCollections.Any())
|
|
{
|
|
// since we are creating new collection, user must have import/manage and create collection permission
|
|
if ((await _authorizationService.AuthorizeAsync(User, collections, BulkCollectionOperations.Create)).Succeeded
|
|
&& (await _authorizationService.AuthorizeAsync(User, existingCollections, BulkCollectionOperations.ImportCiphers)).Succeeded)
|
|
{
|
|
// can import collections and create new ones
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
// user does not have permission to import
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// suppose we have new collections and none of our collections exist
|
|
if (hasNewCollections && !existingCollections.Any())
|
|
{
|
|
// user is trying to create new collections
|
|
// we need to check if the user has permission to create collections
|
|
if ((await _authorizationService.AuthorizeAsync(User, collections, BulkCollectionOperations.Create)).Succeeded)
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
// user does not have permission to create new collections
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// in many import formats, we don't create collections, we just import ciphers into an existing collection
|
|
|
|
// When importing, we need to verify if the user has ImportCiphers permission
|
|
if (existingCollections.Any() && (await _authorizationService.AuthorizeAsync(User, existingCollections, BulkCollectionOperations.ImportCiphers)).Succeeded)
|
|
{
|
|
return true;
|
|
};
|
|
|
|
return false;
|
|
}
|
|
}
|