Skip to content
Merged
8 changes: 6 additions & 2 deletions src/Api/Tools/Controllers/ImportCiphersController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,14 @@ public async Task PostImportOrganization([FromQuery] string organizationId,
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)
Expand Down Expand Up @@ -156,7 +160,7 @@ private async Task<bool> CheckOrgImportPermission(List<Collection> collections,
if (existingCollections.Any() && (await _authorizationService.AuthorizeAsync(User, existingCollections, BulkCollectionOperations.ImportCiphers)).Succeeded)
{
return true;
};
}

return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,8 @@ private static DataTable BuildCiphersTable(SqlBulkCopy bulkCopy, IEnumerable<Cip
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));
Expand Down Expand Up @@ -247,6 +249,7 @@ private static DataTable BuildCiphersTable(SqlBulkCopy bulkCopy, IEnumerable<Cip
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;

Expand Down
98 changes: 98 additions & 0 deletions test/Api.Test/Tools/Controllers/ImportCiphersControllerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ public async Task PostImportIndividual_ImportCiphersRequestModel_Success(User us
.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();

Expand All @@ -92,6 +93,37 @@ await sutProvider.GetDependency<IImportCiphersCommand>()
);
}

[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
****************************/
Expand Down Expand Up @@ -156,6 +188,7 @@ public async Task PostImportOrganization_ImportOrganizationCiphersRequestModel_S
.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)
Expand Down Expand Up @@ -227,6 +260,7 @@ public async Task PostImportOrganization_WithAccessImportExport_Succeeds(
.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)
Expand Down Expand Up @@ -291,6 +325,7 @@ public async Task PostImportOrganization_WithExistingCollectionsAndWithoutImport
.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)
Expand Down Expand Up @@ -354,6 +389,7 @@ public async Task PostImportOrganization_WithoutCreatePermissions_ThrowsExceptio
.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)
Expand Down Expand Up @@ -423,6 +459,7 @@ public async Task PostImportOrganization_CanCreateChildCollectionsWithCreateAndI
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(),
};
Expand Down Expand Up @@ -499,6 +536,7 @@ public async Task PostImportOrganization_CannotCreateChildCollectionsWithoutCrea
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(),
};
Expand Down Expand Up @@ -578,6 +616,7 @@ public async Task PostImportOrganization_ImportIntoNewCollectionWithCreatePermis
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(),
};
Expand Down Expand Up @@ -651,6 +690,7 @@ public async Task PostImportOrganization_ImportIntoExistingCollectionWithImportP
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(),
};
Expand Down Expand Up @@ -720,6 +760,7 @@ public async Task PostImportOrganization_ImportWithNoCollectionsWithCreatePermis
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(),
};
Expand Down Expand Up @@ -765,6 +806,63 @@ await sutProvider.GetDependency<IImportCiphersCommand>()
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
Expand Down
Loading