1
0
mirror of https://github.com/bitwarden/server.git synced 2025-04-09 15:18:13 -05:00
bitwarden/test/Api.Test/Tools/Controllers/ImportCiphersControllerTests.cs
Daniel James Smith 34358acf61
Fix user context on importing into individual vaults ()
Pass in the current userId instead of trying to infer it from the folders or ciphers passed into the ImportCiphersCommand

Kudos go to @MJebran who pointed this out on https://github.com/bitwarden/server/pull/4896

Co-authored-by: Daniel James Smith <djsmith85@users.noreply.github.com>
2025-03-07 15:09:54 +01:00

365 lines
15 KiB
C#

using System.Security.Claims;
using AutoFixture;
using Bit.Api.Models.Request;
using Bit.Api.Tools.Controllers;
using Bit.Api.Tools.Models.Request.Accounts;
using Bit.Api.Tools.Models.Request.Organizations;
using Bit.Api.Vault.AuthorizationHandlers.Collections;
using Bit.Api.Vault.Models.Request;
using Bit.Core.Context;
using Bit.Core.Entities;
using Bit.Core.Exceptions;
using Bit.Core.Repositories;
using Bit.Core.Tools.ImportFeatures.Interfaces;
using Bit.Core.Vault.Entities;
using Bit.Core.Vault.Models.Data;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
using Microsoft.AspNetCore.Authorization;
using NSubstitute;
using Xunit;
using GlobalSettings = Bit.Core.Settings.GlobalSettings;
namespace Bit.Api.Test.Tools.Controllers;
[ControllerCustomize(typeof(ImportCiphersController))]
[SutProviderCustomize]
public class ImportCiphersControllerTests
{
/*************************
* PostImport - Individual
*************************/
[Theory, BitAutoData]
public async Task PostImportIndividual_ImportCiphersRequestModel_BadRequestException(SutProvider<ImportCiphersController> sutProvider, IFixture fixture)
{
// Arrange
sutProvider.GetDependency<Core.Settings.GlobalSettings>()
.SelfHosted = false;
var ciphers = fixture.CreateMany<CipherRequestModel>(7001).ToArray();
var model = new ImportCiphersRequestModel
{
Ciphers = ciphers,
FolderRelationships = null,
Folders = null
};
// Act
var exception = await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.PostImport(model));
// Assert
Assert.Equal("You cannot import this much data at once.", exception.Message);
}
[Theory, BitAutoData]
public async Task PostImportIndividual_ImportCiphersRequestModel_Success(User user,
IFixture fixture, SutProvider<ImportCiphersController> sutProvider)
{
// Arrange
sutProvider.GetDependency<GlobalSettings>()
.SelfHosted = false;
sutProvider.GetDependency<Bit.Core.Services.IUserService>()
.GetProperUserId(Arg.Any<ClaimsPrincipal>())
.Returns(user.Id);
var request = fixture.Build<ImportCiphersRequestModel>()
.With(x => x.Ciphers, fixture.Build<CipherRequestModel>()
.With(c => c.OrganizationId, Guid.NewGuid().ToString())
.With(c => c.FolderId, Guid.NewGuid().ToString())
.CreateMany(1).ToArray())
.Create();
// Act
await sutProvider.Sut.PostImport(request);
// Assert
await sutProvider.GetDependency<IImportCiphersCommand>()
.Received()
.ImportIntoIndividualVaultAsync(
Arg.Any<List<Folder>>(),
Arg.Any<List<CipherDetails>>(),
Arg.Any<IEnumerable<KeyValuePair<int, int>>>(),
user.Id
);
}
/****************************
* PostImport - Organization
****************************/
[Theory, BitAutoData]
public async Task PostImportOrganization_ImportOrganizationCiphersRequestModel_BadRequestException(SutProvider<ImportCiphersController> sutProvider, IFixture fixture)
{
// Arrange
var globalSettings = sutProvider.GetDependency<Core.Settings.GlobalSettings>();
globalSettings.SelfHosted = false;
globalSettings.ImportCiphersLimitation = new GlobalSettings.ImportCiphersLimitationSettings()
{ // limits are set in appsettings.json, making values small for test to run faster.
CiphersLimit = 200,
CollectionsLimit = 400,
CollectionRelationshipsLimit = 20
};
var ciphers = fixture.CreateMany<CipherRequestModel>(201).ToArray();
var model = new ImportOrganizationCiphersRequestModel
{
Collections = null,
Ciphers = ciphers,
CollectionRelationships = null
};
// Act
var exception = await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.PostImport(Arg.Any<string>(), model));
// Assert
Assert.Equal("You cannot import this much data at once.", exception.Message);
}
[Theory, BitAutoData]
public async Task PostImportOrganization_ImportOrganizationCiphersRequestModel_Succeeds(
SutProvider<ImportCiphersController> sutProvider,
IFixture fixture,
User user)
{
// Arrange
var orgId = "AD89E6F8-4E84-4CFE-A978-256CC0DBF974";
var orgIdGuid = Guid.Parse(orgId);
var existingCollections = fixture.CreateMany<CollectionWithIdRequestModel>(2).ToArray();
sutProvider.GetDependency<GlobalSettings>().SelfHosted = false;
sutProvider.GetDependency<Bit.Core.Services.IUserService>()
.GetProperUserId(Arg.Any<ClaimsPrincipal>())
.Returns(user.Id);
var request = fixture.Build<ImportOrganizationCiphersRequestModel>()
.With(x => x.Ciphers, fixture.Build<CipherRequestModel>()
.With(c => c.OrganizationId, Guid.NewGuid().ToString())
.With(c => c.FolderId, Guid.NewGuid().ToString())
.CreateMany(1).ToArray())
.With(y => y.Collections, fixture.Build<CollectionWithIdRequestModel>()
.With(c => c.Id, orgIdGuid)
.CreateMany(1).ToArray())
.Create();
// AccessImportExport permission setup
sutProvider.GetDependency<ICurrentContext>()
.AccessImportExport(Arg.Any<Guid>())
.Returns(false);
// BulkCollectionOperations.ImportCiphers permission setup
sutProvider.GetDependency<IAuthorizationService>()
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(),
Arg.Any<IEnumerable<Collection>>(),
Arg.Is<IEnumerable<IAuthorizationRequirement>>(reqs => reqs.Contains(BulkCollectionOperations.ImportCiphers)))
.Returns(AuthorizationResult.Success());
// BulkCollectionOperations.Create permission setup
sutProvider.GetDependency<IAuthorizationService>()
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(),
Arg.Any<IEnumerable<Collection>>(),
Arg.Is<IEnumerable<IAuthorizationRequirement>>(reqs => reqs.Contains(BulkCollectionOperations.Create)))
.Returns(AuthorizationResult.Success());
sutProvider.GetDependency<ICollectionRepository>()
.GetManyByOrganizationIdAsync(orgIdGuid)
.Returns(existingCollections.Select(c => new Collection { Id = orgIdGuid }).ToList());
// Act
await sutProvider.Sut.PostImport(orgId, request);
// Assert
await sutProvider.GetDependency<IImportCiphersCommand>()
.Received(1)
.ImportIntoOrganizationalVaultAsync(
Arg.Any<List<Collection>>(),
Arg.Any<List<CipherDetails>>(),
Arg.Any<IEnumerable<KeyValuePair<int, int>>>(),
Arg.Any<Guid>());
}
[Theory, BitAutoData]
public async Task PostImportOrganization_WithAccessImportExport_Succeeds(
SutProvider<ImportCiphersController> sutProvider,
IFixture fixture,
User user)
{
// Arrange
var orgId = "AD89E6F8-4E84-4CFE-A978-256CC0DBF974";
var orgIdGuid = Guid.Parse(orgId);
var existingCollections = fixture.CreateMany<CollectionWithIdRequestModel>(2).ToArray();
sutProvider.GetDependency<GlobalSettings>().SelfHosted = false;
sutProvider.GetDependency<Bit.Core.Services.IUserService>()
.GetProperUserId(Arg.Any<ClaimsPrincipal>())
.Returns(user.Id);
var request = fixture.Build<ImportOrganizationCiphersRequestModel>()
.With(x => x.Ciphers, fixture.Build<CipherRequestModel>()
.With(c => c.OrganizationId, Guid.NewGuid().ToString())
.With(c => c.FolderId, Guid.NewGuid().ToString())
.CreateMany(1).ToArray())
.With(y => y.Collections, fixture.Build<CollectionWithIdRequestModel>()
.With(c => c.Id, orgIdGuid)
.CreateMany(1).ToArray())
.Create();
// AccessImportExport permission setup
sutProvider.GetDependency<ICurrentContext>()
.AccessImportExport(Arg.Any<Guid>())
.Returns(false);
// BulkCollectionOperations.ImportCiphers permission setup
sutProvider.GetDependency<IAuthorizationService>()
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(),
Arg.Any<IEnumerable<Collection>>(),
Arg.Is<IEnumerable<IAuthorizationRequirement>>(reqs => reqs.Contains(BulkCollectionOperations.ImportCiphers)))
.Returns(AuthorizationResult.Success());
// BulkCollectionOperations.Create permission setup
sutProvider.GetDependency<IAuthorizationService>()
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(),
Arg.Any<IEnumerable<Collection>>(),
Arg.Is<IEnumerable<IAuthorizationRequirement>>(reqs => reqs.Contains(BulkCollectionOperations.Create)))
.Returns(AuthorizationResult.Success());
sutProvider.GetDependency<ICollectionRepository>()
.GetManyByOrganizationIdAsync(orgIdGuid)
.Returns(existingCollections.Select(c => new Collection { Id = orgIdGuid }).ToList());
// Act
await sutProvider.Sut.PostImport(orgId, request);
// Assert
await sutProvider.GetDependency<IImportCiphersCommand>()
.Received(1)
.ImportIntoOrganizationalVaultAsync(
Arg.Any<List<Collection>>(),
Arg.Any<List<CipherDetails>>(),
Arg.Any<IEnumerable<KeyValuePair<int, int>>>(),
Arg.Any<Guid>());
}
[Theory, BitAutoData]
public async Task PostImportOrganization_WithExistingCollectionsAndWithoutImportCiphersPermissions_NotFoundException(
SutProvider<ImportCiphersController> sutProvider,
IFixture fixture,
User user)
{
// Arrange
var orgId = "AD89E6F8-4E84-4CFE-A978-256CC0DBF974";
var orgIdGuid = Guid.Parse(orgId);
var existingCollections = fixture.CreateMany<CollectionWithIdRequestModel>(2).ToArray();
sutProvider.GetDependency<GlobalSettings>().SelfHosted = false;
sutProvider.GetDependency<Bit.Core.Services.IUserService>()
.GetProperUserId(Arg.Any<ClaimsPrincipal>())
.Returns(user.Id);
var request = fixture.Build<ImportOrganizationCiphersRequestModel>()
.With(x => x.Ciphers, fixture.Build<CipherRequestModel>()
.With(c => c.OrganizationId, Guid.NewGuid().ToString())
.With(c => c.FolderId, Guid.NewGuid().ToString())
.CreateMany(1).ToArray())
.With(y => y.Collections, fixture.Build<CollectionWithIdRequestModel>()
.With(c => c.Id, orgIdGuid)
.CreateMany(1).ToArray())
.Create();
// AccessImportExport permission setup
sutProvider.GetDependency<ICurrentContext>()
.AccessImportExport(Arg.Any<Guid>())
.Returns(false);
// BulkCollectionOperations.ImportCiphers permission setup
sutProvider.GetDependency<IAuthorizationService>()
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(),
Arg.Any<IEnumerable<Collection>>(),
Arg.Is<IEnumerable<IAuthorizationRequirement>>(reqs =>
reqs.Contains(BulkCollectionOperations.ImportCiphers)))
.Returns(AuthorizationResult.Failed());
// BulkCollectionOperations.Create permission setup
sutProvider.GetDependency<IAuthorizationService>()
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(),
Arg.Any<IEnumerable<Collection>>(),
Arg.Is<IEnumerable<IAuthorizationRequirement>>(reqs =>
reqs.Contains(BulkCollectionOperations.Create)))
.Returns(AuthorizationResult.Success());
sutProvider.GetDependency<ICollectionRepository>()
.GetManyByOrganizationIdAsync(orgIdGuid)
.Returns(existingCollections.Select(c => new Collection { Id = orgIdGuid }).ToList());
// Act
var exception = await Assert.ThrowsAsync<Bit.Core.Exceptions.NotFoundException>(() =>
sutProvider.Sut.PostImport(orgId, request));
// Assert
Assert.IsType<Bit.Core.Exceptions.NotFoundException>(exception);
}
[Theory, BitAutoData]
public async Task PostImportOrganization_WithoutCreatePermissions_NotFoundException(
SutProvider<ImportCiphersController> sutProvider,
IFixture fixture,
User user)
{
// Arrange
var orgId = "AD89E6F8-4E84-4CFE-A978-256CC0DBF974";
var orgIdGuid = Guid.Parse(orgId);
var existingCollections = fixture.CreateMany<CollectionWithIdRequestModel>(2).ToArray();
sutProvider.GetDependency<GlobalSettings>().SelfHosted = false;
sutProvider.GetDependency<Bit.Core.Services.IUserService>()
.GetProperUserId(Arg.Any<ClaimsPrincipal>())
.Returns(user.Id);
var request = fixture.Build<ImportOrganizationCiphersRequestModel>()
.With(x => x.Ciphers, fixture.Build<CipherRequestModel>()
.With(c => c.OrganizationId, Guid.NewGuid().ToString())
.With(c => c.FolderId, Guid.NewGuid().ToString())
.CreateMany(1).ToArray())
.With(y => y.Collections, fixture.Build<CollectionWithIdRequestModel>()
.With(c => c.Id, orgIdGuid)
.CreateMany(1).ToArray())
.Create();
// AccessImportExport permission setup
sutProvider.GetDependency<ICurrentContext>()
.AccessImportExport(Arg.Any<Guid>())
.Returns(false);
// BulkCollectionOperations.ImportCiphers permission setup
sutProvider.GetDependency<IAuthorizationService>()
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(),
Arg.Any<IEnumerable<Collection>>(),
Arg.Is<IEnumerable<IAuthorizationRequirement>>(reqs =>
reqs.Contains(BulkCollectionOperations.ImportCiphers)))
.Returns(AuthorizationResult.Success());
// BulkCollectionOperations.Create permission setup
sutProvider.GetDependency<IAuthorizationService>()
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(),
Arg.Any<IEnumerable<Collection>>(),
Arg.Is<IEnumerable<IAuthorizationRequirement>>(reqs =>
reqs.Contains(BulkCollectionOperations.Create)))
.Returns(AuthorizationResult.Failed());
sutProvider.GetDependency<ICollectionRepository>()
.GetManyByOrganizationIdAsync(orgIdGuid)
.Returns(existingCollections.Select(c => new Collection { Id = orgIdGuid }).ToList());
// Act
var exception = await Assert.ThrowsAsync<Bit.Core.Exceptions.NotFoundException>(() =>
sutProvider.Sut.PostImport(orgId, request));
// Assert
Assert.IsType<Bit.Core.Exceptions.NotFoundException>(exception);
}
}