using Bit.Api.SecretsManager.Controllers; using Bit.Api.SecretsManager.Models.Request; using Bit.Core.Auth.Identity; using Bit.Core.Context; using Bit.Core.Entities; using Bit.Core.Exceptions; using Bit.Core.Repositories; using Bit.Core.SecretsManager.Entities; using Bit.Core.SecretsManager.Repositories; using Bit.Core.Services; using Bit.Core.Test.SecretsManager.AutoFixture.SecretsFixture; using Bit.Test.Common.AutoFixture; using Bit.Test.Common.AutoFixture.Attributes; using NSubstitute; using Xunit; namespace Bit.Api.Test.SecretsManager.Controllers; [ControllerCustomize(typeof(SecretVersionsController))] [SutProviderCustomize] [SecretCustomize] public class SecretVersionsControllerTests { [Theory] [BitAutoData] public async Task GetVersionsBySecretId_SecretNotFound_Throws( SutProvider sutProvider, Guid secretId) { sutProvider.GetDependency().GetByIdAsync(secretId).Returns((Secret?)null); await Assert.ThrowsAsync(() => sutProvider.Sut.GetVersionsBySecretIdAsync(secretId)); } [Theory] [BitAutoData] public async Task GetVersionsBySecretId_NoAccess_Throws( SutProvider sutProvider, Secret secret) { sutProvider.GetDependency().GetByIdAsync(secret.Id).Returns(secret); sutProvider.GetDependency().AccessSecretsManager(secret.OrganizationId).Returns(false); await Assert.ThrowsAsync(() => sutProvider.Sut.GetVersionsBySecretIdAsync(secret.Id)); } [Theory] [BitAutoData] public async Task GetVersionsBySecretId_NoReadAccess_Throws( SutProvider sutProvider, Secret secret, Guid userId) { sutProvider.GetDependency().GetByIdAsync(secret.Id).Returns(secret); sutProvider.GetDependency().AccessSecretsManager(secret.OrganizationId).Returns(true); sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency().OrganizationAdmin(secret.OrganizationId).Returns(false); sutProvider.GetDependency().AccessToSecretAsync(secret.Id, userId, default) .ReturnsForAnyArgs((false, false)); await Assert.ThrowsAsync(() => sutProvider.Sut.GetVersionsBySecretIdAsync(secret.Id)); } [Theory] [BitAutoData] public async Task GetVersionsBySecretId_Success( SutProvider sutProvider, Secret secret, List versions, Guid userId) { sutProvider.GetDependency().GetByIdAsync(secret.Id).Returns(secret); sutProvider.GetDependency().AccessSecretsManager(secret.OrganizationId).Returns(true); sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency().OrganizationAdmin(secret.OrganizationId).Returns(false); sutProvider.GetDependency().AccessToSecretAsync(secret.Id, userId, default) .ReturnsForAnyArgs((true, false)); foreach (var version in versions) { version.SecretId = secret.Id; } sutProvider.GetDependency().GetManyBySecretIdAsync(secret.Id).Returns(versions); var result = await sutProvider.Sut.GetVersionsBySecretIdAsync(secret.Id); Assert.Equal(versions.Count, result.Data.Count()); await sutProvider.GetDependency().Received(1) .GetManyBySecretIdAsync(Arg.Is(secret.Id)); } [Theory] [BitAutoData] public async Task GetById_VersionNotFound_Throws( SutProvider sutProvider, Guid versionId) { sutProvider.GetDependency().GetByIdAsync(versionId).Returns((SecretVersion?)null); await Assert.ThrowsAsync(() => sutProvider.Sut.GetByIdAsync(versionId)); } [Theory] [BitAutoData] public async Task GetById_Success( SutProvider sutProvider, SecretVersion version, Secret secret, Guid userId) { version.SecretId = secret.Id; sutProvider.GetDependency().GetByIdAsync(version.Id).Returns(version); sutProvider.GetDependency().GetByIdAsync(secret.Id).Returns(secret); sutProvider.GetDependency().AccessSecretsManager(secret.OrganizationId).Returns(true); sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency().OrganizationAdmin(secret.OrganizationId).Returns(false); sutProvider.GetDependency().AccessToSecretAsync(secret.Id, userId, default) .ReturnsForAnyArgs((true, false)); var result = await sutProvider.Sut.GetByIdAsync(version.Id); Assert.Equal(version.Id, result.Id); Assert.Equal(version.SecretId, result.SecretId); } [Theory] [BitAutoData] public async Task RestoreVersion_NoWriteAccess_Throws( SutProvider sutProvider, Secret secret, SecretVersion version, RestoreSecretVersionRequestModel request, Guid userId) { version.SecretId = secret.Id; request.VersionId = version.Id; sutProvider.GetDependency().GetByIdAsync(secret.Id).Returns(secret); sutProvider.GetDependency().AccessSecretsManager(secret.OrganizationId).Returns(true); sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency().OrganizationAdmin(secret.OrganizationId).Returns(false); sutProvider.GetDependency().AccessToSecretAsync(secret.Id, userId, default) .ReturnsForAnyArgs((true, false)); await Assert.ThrowsAsync(() => sutProvider.Sut.RestoreVersionAsync(secret.Id, request)); } [Theory] [BitAutoData] public async Task RestoreVersion_VersionNotFound_Throws( SutProvider sutProvider, Secret secret, RestoreSecretVersionRequestModel request, Guid userId) { sutProvider.GetDependency().GetByIdAsync(secret.Id).Returns(secret); sutProvider.GetDependency().AccessSecretsManager(secret.OrganizationId).Returns(true); sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency().OrganizationAdmin(secret.OrganizationId).Returns(true); sutProvider.GetDependency().AccessToSecretAsync(secret.Id, userId, default) .ReturnsForAnyArgs((true, true)); sutProvider.GetDependency().GetByIdAsync(request.VersionId).Returns((SecretVersion?)null); await Assert.ThrowsAsync(() => sutProvider.Sut.RestoreVersionAsync(secret.Id, request)); } [Theory] [BitAutoData] public async Task RestoreVersion_VersionBelongsToDifferentSecret_Throws( SutProvider sutProvider, Secret secret, SecretVersion version, RestoreSecretVersionRequestModel request, Guid userId) { version.SecretId = Guid.NewGuid(); // Different secret request.VersionId = version.Id; sutProvider.GetDependency().GetByIdAsync(secret.Id).Returns(secret); sutProvider.GetDependency().AccessSecretsManager(secret.OrganizationId).Returns(true); sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency().OrganizationAdmin(secret.OrganizationId).Returns(true); sutProvider.GetDependency().AccessToSecretAsync(secret.Id, userId, default) .ReturnsForAnyArgs((true, true)); sutProvider.GetDependency().GetByIdAsync(request.VersionId).Returns(version); await Assert.ThrowsAsync(() => sutProvider.Sut.RestoreVersionAsync(secret.Id, request)); } [Theory] [BitAutoData] public async Task RestoreVersion_Success( SutProvider sutProvider, Secret secret, SecretVersion version, RestoreSecretVersionRequestModel request, Guid userId, OrganizationUser organizationUser) { version.SecretId = secret.Id; request.VersionId = version.Id; var versionValue = version.Value; organizationUser.OrganizationId = secret.OrganizationId; organizationUser.UserId = userId; sutProvider.GetDependency().GetByIdAsync(secret.Id).Returns(secret); sutProvider.GetDependency().AccessSecretsManager(secret.OrganizationId).Returns(true); sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency().OrganizationAdmin(secret.OrganizationId).Returns(true); sutProvider.GetDependency().AccessToSecretAsync(secret.Id, userId, default) .ReturnsForAnyArgs((true, true)); sutProvider.GetDependency().GetByIdAsync(request.VersionId).Returns(version); sutProvider.GetDependency() .GetByOrganizationAsync(secret.OrganizationId, userId).Returns(organizationUser); sutProvider.GetDependency().UpdateAsync(Arg.Any()).Returns(x => x.Arg()); var result = await sutProvider.Sut.RestoreVersionAsync(secret.Id, request); await sutProvider.GetDependency().Received(1) .UpdateAsync(Arg.Is(s => s.Value == versionValue)); } [Theory] [BitAutoData] public async Task BulkDelete_EmptyIds_Throws( SutProvider sutProvider) { await Assert.ThrowsAsync(() => sutProvider.Sut.BulkDeleteAsync(new List())); } [Theory] [BitAutoData] public async Task BulkDelete_VersionNotFound_Throws( SutProvider sutProvider, List ids) { sutProvider.GetDependency().GetByIdAsync(ids[0]).Returns((SecretVersion?)null); await Assert.ThrowsAsync(() => sutProvider.Sut.BulkDeleteAsync(ids)); } [Theory] [BitAutoData] public async Task BulkDelete_NoWriteAccess_Throws( SutProvider sutProvider, List versions, Secret secret, Guid userId) { var ids = versions.Select(v => v.Id).ToList(); foreach (var version in versions) { version.SecretId = secret.Id; sutProvider.GetDependency().GetByIdAsync(version.Id).Returns(version); } sutProvider.GetDependency().GetManyByIds(Arg.Any>()) .Returns(new List { secret }); sutProvider.GetDependency().AccessSecretsManager(secret.OrganizationId).Returns(true); sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency().OrganizationAdmin(secret.OrganizationId).Returns(false); sutProvider.GetDependency().AccessToSecretAsync(secret.Id, userId, default) .ReturnsForAnyArgs((true, false)); await Assert.ThrowsAsync(() => sutProvider.Sut.BulkDeleteAsync(ids)); } [Theory] [BitAutoData] public async Task BulkDelete_Success( SutProvider sutProvider, List versions, Secret secret, Guid userId) { var ids = versions.Select(v => v.Id).ToList(); foreach (var version in versions) { version.SecretId = secret.Id; } sutProvider.GetDependency().GetManyByIdsAsync(ids).Returns(versions); sutProvider.GetDependency().GetManyByIds(Arg.Any>()) .Returns(new List { secret }); sutProvider.GetDependency().AccessSecretsManager(secret.OrganizationId).Returns(true); sutProvider.GetDependency().IdentityClientType.Returns(IdentityClientType.ServiceAccount); sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId); sutProvider.GetDependency().OrganizationAdmin(secret.OrganizationId).Returns(true); sutProvider.GetDependency().AccessToSecretAsync(secret.Id, userId, default) .ReturnsForAnyArgs((true, true)); await sutProvider.Sut.BulkDeleteAsync(ids); await sutProvider.GetDependency().Received(1) .DeleteManyByIdAsync(Arg.Is>(x => x.SequenceEqual(ids))); } }