mirror of
https://github.com/bitwarden/server
synced 2025-12-06 00:03:34 +00:00
[PM-26772] Importing Archived Ciphers (#6452)
* persist archive date for importing ciphers * throw error if a user imports archived ciphers into an organization * remove extra semi-colon * set archive date for initial tests to avoid error thrown * refactor ArchivedDate query * add test for throwing for importing archived ciphers into a organization * remove folder and organization id from test * remove unneeded org id and null out the folderid
This commit is contained in:
@@ -74,10 +74,14 @@ public class ImportCiphersController : Controller
|
||||
throw new BadRequestException("You cannot import this much data at once.");
|
||||
}
|
||||
|
||||
if (model.Ciphers.Any(c => c.ArchivedDate.HasValue))
|
||||
{
|
||||
throw new BadRequestException("You cannot import archived items into an organization.");
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -156,7 +160,7 @@ public class ImportCiphersController : Controller
|
||||
if (existingCollections.Any() && (await _authorizationService.AuthorizeAsync(User, existingCollections, BulkCollectionOperations.ImportCiphers)).Succeeded)
|
||||
{
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -218,6 +218,8 @@ public static class BulkResourceCreationService
|
||||
ciphersTable.Columns.Add(revisionDateColumn);
|
||||
var deletedDateColumn = new DataColumn(nameof(c.DeletedDate), typeof(DateTime));
|
||||
ciphersTable.Columns.Add(deletedDateColumn);
|
||||
var archivedDateColumn = new DataColumn(nameof(c.ArchivedDate), typeof(DateTime));
|
||||
ciphersTable.Columns.Add(archivedDateColumn);
|
||||
var repromptColumn = new DataColumn(nameof(c.Reprompt), typeof(short));
|
||||
ciphersTable.Columns.Add(repromptColumn);
|
||||
var keyColummn = new DataColumn(nameof(c.Key), typeof(string));
|
||||
@@ -247,6 +249,7 @@ public static class BulkResourceCreationService
|
||||
row[creationDateColumn] = cipher.CreationDate;
|
||||
row[revisionDateColumn] = cipher.RevisionDate;
|
||||
row[deletedDateColumn] = cipher.DeletedDate.HasValue ? (object)cipher.DeletedDate : DBNull.Value;
|
||||
row[archivedDateColumn] = cipher.ArchivedDate.HasValue ? cipher.ArchivedDate : DBNull.Value;
|
||||
row[repromptColumn] = cipher.Reprompt.HasValue ? cipher.Reprompt.Value : DBNull.Value;
|
||||
row[keyColummn] = cipher.Key;
|
||||
|
||||
|
||||
@@ -75,6 +75,7 @@ public class ImportCiphersControllerTests
|
||||
.With(x => x.Ciphers, fixture.Build<CipherRequestModel>()
|
||||
.With(c => c.OrganizationId, Guid.NewGuid().ToString())
|
||||
.With(c => c.FolderId, Guid.NewGuid().ToString())
|
||||
.With(c => c.ArchivedDate, (DateTime?)null)
|
||||
.CreateMany(1).ToArray())
|
||||
.Create();
|
||||
|
||||
@@ -92,6 +93,37 @@ public class ImportCiphersControllerTests
|
||||
);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task PostImportIndividual_WithArchivedDate_SavesArchivedDate(User user,
|
||||
IFixture fixture, SutProvider<ImportCiphersController> sutProvider)
|
||||
{
|
||||
var archivedDate = DateTime.UtcNow;
|
||||
sutProvider.GetDependency<GlobalSettings>()
|
||||
.SelfHosted = false;
|
||||
|
||||
sutProvider.GetDependency<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.ArchivedDate, archivedDate)
|
||||
.With(c => c.FolderId, (string)null)
|
||||
.CreateMany(1).ToArray())
|
||||
.Create();
|
||||
|
||||
await sutProvider.Sut.PostImport(request);
|
||||
|
||||
await sutProvider.GetDependency<IImportCiphersCommand>()
|
||||
.Received()
|
||||
.ImportIntoIndividualVaultAsync(
|
||||
Arg.Any<List<Folder>>(),
|
||||
Arg.Is<List<CipherDetails>>(ciphers => ciphers.First().ArchivedDate == archivedDate),
|
||||
Arg.Any<IEnumerable<KeyValuePair<int, int>>>(),
|
||||
user.Id
|
||||
);
|
||||
}
|
||||
|
||||
/****************************
|
||||
* PostImport - Organization
|
||||
****************************/
|
||||
@@ -156,6 +188,7 @@ public class ImportCiphersControllerTests
|
||||
.With(x => x.Ciphers, fixture.Build<CipherRequestModel>()
|
||||
.With(c => c.OrganizationId, Guid.NewGuid().ToString())
|
||||
.With(c => c.FolderId, Guid.NewGuid().ToString())
|
||||
.With(c => c.ArchivedDate, (DateTime?)null)
|
||||
.CreateMany(1).ToArray())
|
||||
.With(y => y.Collections, fixture.Build<CollectionWithIdRequestModel>()
|
||||
.With(c => c.Id, orgIdGuid)
|
||||
@@ -227,6 +260,7 @@ public class ImportCiphersControllerTests
|
||||
.With(x => x.Ciphers, fixture.Build<CipherRequestModel>()
|
||||
.With(c => c.OrganizationId, Guid.NewGuid().ToString())
|
||||
.With(c => c.FolderId, Guid.NewGuid().ToString())
|
||||
.With(c => c.ArchivedDate, (DateTime?)null)
|
||||
.CreateMany(1).ToArray())
|
||||
.With(y => y.Collections, fixture.Build<CollectionWithIdRequestModel>()
|
||||
.With(c => c.Id, orgIdGuid)
|
||||
@@ -291,6 +325,7 @@ public class ImportCiphersControllerTests
|
||||
.With(x => x.Ciphers, fixture.Build<CipherRequestModel>()
|
||||
.With(c => c.OrganizationId, Guid.NewGuid().ToString())
|
||||
.With(c => c.FolderId, Guid.NewGuid().ToString())
|
||||
.With(c => c.ArchivedDate, (DateTime?)null)
|
||||
.CreateMany(1).ToArray())
|
||||
.With(y => y.Collections, fixture.Build<CollectionWithIdRequestModel>()
|
||||
.With(c => c.Id, orgIdGuid)
|
||||
@@ -354,6 +389,7 @@ public class ImportCiphersControllerTests
|
||||
.With(x => x.Ciphers, fixture.Build<CipherRequestModel>()
|
||||
.With(c => c.OrganizationId, Guid.NewGuid().ToString())
|
||||
.With(c => c.FolderId, Guid.NewGuid().ToString())
|
||||
.With(c => c.ArchivedDate, (DateTime?)null)
|
||||
.CreateMany(1).ToArray())
|
||||
.With(y => y.Collections, fixture.Build<CollectionWithIdRequestModel>()
|
||||
.With(c => c.Id, orgIdGuid)
|
||||
@@ -423,6 +459,7 @@ public class ImportCiphersControllerTests
|
||||
Ciphers = fixture.Build<CipherRequestModel>()
|
||||
.With(_ => _.OrganizationId, orgId.ToString())
|
||||
.With(_ => _.FolderId, Guid.NewGuid().ToString())
|
||||
.With(_ => _.ArchivedDate, (DateTime?)null)
|
||||
.CreateMany(2).ToArray(),
|
||||
CollectionRelationships = new List<KeyValuePair<int, int>>().ToArray(),
|
||||
};
|
||||
@@ -499,6 +536,7 @@ public class ImportCiphersControllerTests
|
||||
Ciphers = fixture.Build<CipherRequestModel>()
|
||||
.With(_ => _.OrganizationId, orgId.ToString())
|
||||
.With(_ => _.FolderId, Guid.NewGuid().ToString())
|
||||
.With(_ => _.ArchivedDate, (DateTime?)null)
|
||||
.CreateMany(2).ToArray(),
|
||||
CollectionRelationships = new List<KeyValuePair<int, int>>().ToArray(),
|
||||
};
|
||||
@@ -578,6 +616,7 @@ public class ImportCiphersControllerTests
|
||||
Ciphers = fixture.Build<CipherRequestModel>()
|
||||
.With(_ => _.OrganizationId, orgId.ToString())
|
||||
.With(_ => _.FolderId, Guid.NewGuid().ToString())
|
||||
.With(_ => _.ArchivedDate, (DateTime?)null)
|
||||
.CreateMany(2).ToArray(),
|
||||
CollectionRelationships = new List<KeyValuePair<int, int>>().ToArray(),
|
||||
};
|
||||
@@ -651,6 +690,7 @@ public class ImportCiphersControllerTests
|
||||
Ciphers = fixture.Build<CipherRequestModel>()
|
||||
.With(_ => _.OrganizationId, orgId.ToString())
|
||||
.With(_ => _.FolderId, Guid.NewGuid().ToString())
|
||||
.With(_ => _.ArchivedDate, (DateTime?)null)
|
||||
.CreateMany(2).ToArray(),
|
||||
CollectionRelationships = new List<KeyValuePair<int, int>>().ToArray(),
|
||||
};
|
||||
@@ -720,6 +760,7 @@ public class ImportCiphersControllerTests
|
||||
Ciphers = fixture.Build<CipherRequestModel>()
|
||||
.With(_ => _.OrganizationId, orgId.ToString())
|
||||
.With(_ => _.FolderId, Guid.NewGuid().ToString())
|
||||
.With(_ => _.ArchivedDate, (DateTime?)null)
|
||||
.CreateMany(2).ToArray(),
|
||||
CollectionRelationships = new List<KeyValuePair<int, int>>().ToArray(),
|
||||
};
|
||||
@@ -765,6 +806,63 @@ public class ImportCiphersControllerTests
|
||||
Arg.Any<Guid>());
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task PostImportOrganization_ThrowsException_WhenAnyCipherIsArchived(
|
||||
SutProvider<ImportCiphersController> sutProvider,
|
||||
IFixture fixture,
|
||||
User user
|
||||
)
|
||||
{
|
||||
var orgId = Guid.NewGuid();
|
||||
|
||||
sutProvider.GetDependency<GlobalSettings>()
|
||||
.SelfHosted = false;
|
||||
sutProvider.GetDependency<GlobalSettings>()
|
||||
.ImportCiphersLimitation = _organizationCiphersLimitations;
|
||||
|
||||
SetupUserService(sutProvider, user);
|
||||
|
||||
var ciphers = fixture.Build<CipherRequestModel>()
|
||||
.With(_ => _.ArchivedDate, DateTime.UtcNow)
|
||||
.CreateMany(2).ToArray();
|
||||
|
||||
var request = new ImportOrganizationCiphersRequestModel
|
||||
{
|
||||
Collections = new List<CollectionWithIdRequestModel>().ToArray(),
|
||||
Ciphers = ciphers,
|
||||
CollectionRelationships = new List<KeyValuePair<int, int>>().ToArray(),
|
||||
};
|
||||
|
||||
sutProvider.GetDependency<ICurrentContext>()
|
||||
.AccessImportExport(Arg.Any<Guid>())
|
||||
.Returns(false);
|
||||
|
||||
sutProvider.GetDependency<IAuthorizationService>()
|
||||
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(),
|
||||
Arg.Any<IEnumerable<Collection>>(),
|
||||
Arg.Is<IEnumerable<IAuthorizationRequirement>>(reqs =>
|
||||
reqs.Contains(BulkCollectionOperations.ImportCiphers)))
|
||||
.Returns(AuthorizationResult.Failed());
|
||||
|
||||
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(orgId)
|
||||
.Returns(new List<Collection>());
|
||||
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(async () =>
|
||||
{
|
||||
await sutProvider.Sut.PostImportOrganization(orgId.ToString(), request);
|
||||
});
|
||||
|
||||
Assert.Equal("You cannot import archived items into an organization.", exception.Message);
|
||||
}
|
||||
|
||||
private static void SetupUserService(SutProvider<ImportCiphersController> sutProvider, User user)
|
||||
{
|
||||
// This is a workaround for the NSubstitute issue with ambiguous arguments
|
||||
|
||||
Reference in New Issue
Block a user