1
0
mirror of https://github.com/bitwarden/server.git synced 2024-11-21 12:05:42 +01:00

[SM-1256] Add BulkSecretAuthorizationHandler (#4099)

* Add AccessToSecretsAsync to the repository

* Add BulkSecretAuthorizationHandler

* Update controller to use the new authz handler

* Add integration test coverage
This commit is contained in:
Thomas Avery 2024-07-09 10:06:33 -05:00 committed by GitHub
parent 313eef49f0
commit acc4808509
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 484 additions and 72 deletions

View File

@ -0,0 +1,63 @@
#nullable enable
using Bit.Core.Context;
using Bit.Core.SecretsManager.AuthorizationRequirements;
using Bit.Core.SecretsManager.Entities;
using Bit.Core.SecretsManager.Queries.Interfaces;
using Bit.Core.SecretsManager.Repositories;
using Microsoft.AspNetCore.Authorization;
namespace Bit.Commercial.Core.SecretsManager.AuthorizationHandlers.Secrets;
public class
BulkSecretAuthorizationHandler : AuthorizationHandler<BulkSecretOperationRequirement, IReadOnlyList<Secret>>
{
private readonly IAccessClientQuery _accessClientQuery;
private readonly ICurrentContext _currentContext;
private readonly ISecretRepository _secretRepository;
public BulkSecretAuthorizationHandler(ICurrentContext currentContext, IAccessClientQuery accessClientQuery,
ISecretRepository secretRepository)
{
_currentContext = currentContext;
_accessClientQuery = accessClientQuery;
_secretRepository = secretRepository;
}
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context,
BulkSecretOperationRequirement requirement,
IReadOnlyList<Secret> resources)
{
// Ensure all secrets belong to the same organization.
var organizationId = resources[0].OrganizationId;
if (resources.Any(secret => secret.OrganizationId != organizationId) ||
!_currentContext.AccessSecretsManager(organizationId))
{
return;
}
switch (requirement)
{
case not null when requirement == BulkSecretOperations.ReadAll:
await CanReadAllAsync(context, requirement, resources, organizationId);
break;
default:
throw new ArgumentException("Unsupported operation requirement type provided.", nameof(requirement));
}
}
private async Task CanReadAllAsync(AuthorizationHandlerContext context,
BulkSecretOperationRequirement requirement, IReadOnlyList<Secret> resources, Guid organizationId)
{
var (accessClient, userId) = await _accessClientQuery.GetAccessClientAsync(context.User, organizationId);
var secretsAccess =
await _secretRepository.AccessToSecretsAsync(resources.Select(s => s.Id), userId, accessClient);
if (secretsAccess.Count == resources.Count &&
secretsAccess.All(a => a.Value.Read))
{
context.Succeed(requirement);
}
}
}

View File

@ -43,6 +43,7 @@ public static class SecretsManagerCollectionExtensions
services.AddScoped<IAuthorizationHandler, ServiceAccountGrantedPoliciesAuthorizationHandler>();
services.AddScoped<IAuthorizationHandler, ProjectServiceAccountsAccessPoliciesAuthorizationHandler>();
services.AddScoped<IAuthorizationHandler, SecretAccessPoliciesUpdatesAuthorizationHandler>();
services.AddScoped<IAuthorizationHandler, BulkSecretAuthorizationHandler>();
services.AddScoped<IAccessClientQuery, AccessClientQuery>();
services.AddScoped<IMaxProjectsQuery, MaxProjectsQuery>();
services.AddScoped<ISameOrganizationQuery, SameOrganizationQuery>();

View File

@ -299,6 +299,22 @@ public class SecretRepository : Repository<Core.SecretsManager.Entities.Secret,
return policy == null ? (false, false) : (policy.Read, policy.Write);
}
public async Task<Dictionary<Guid, (bool Read, bool Write)>> AccessToSecretsAsync(
IEnumerable<Guid> ids,
Guid userId,
AccessClientType accessType)
{
await using var scope = ServiceScopeFactory.CreateAsyncScope();
var dbContext = GetDatabaseContext(scope);
var secrets = dbContext.Secret
.Where(s => ids.Contains(s.Id));
var accessQuery = BuildSecretAccessQuery(secrets, userId, accessType);
return await accessQuery.ToDictionaryAsync(sa => sa.Id, sa => (sa.Read, sa.Write));
}
public async Task EmptyTrash(DateTime currentDate, uint deleteAfterThisNumberOfDays)
{
using var scope = ServiceScopeFactory.CreateScope();

View File

@ -0,0 +1,224 @@
#nullable enable
using System.Reflection;
using System.Security.Claims;
using Bit.Commercial.Core.SecretsManager.AuthorizationHandlers.Secrets;
using Bit.Core.Context;
using Bit.Core.Enums;
using Bit.Core.SecretsManager.AuthorizationRequirements;
using Bit.Core.SecretsManager.Entities;
using Bit.Core.SecretsManager.Queries.Interfaces;
using Bit.Core.SecretsManager.Repositories;
using Bit.Core.Test.SecretsManager.AutoFixture.ProjectsFixture;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
using Microsoft.AspNetCore.Authorization;
using NSubstitute;
using Xunit;
namespace Bit.Commercial.Core.Test.SecretsManager.AuthorizationHandlers.Secrets;
[SutProviderCustomize]
[ProjectCustomize]
public class BulkSecretAuthorizationHandlerTests
{
[Fact]
public void BulkSecretOperations_OnlyPublicStatic()
{
var publicStaticFields = typeof(BulkSecretOperations).GetFields(BindingFlags.Public | BindingFlags.Static);
var allFields = typeof(BulkSecretOperations).GetFields();
Assert.Equal(publicStaticFields.Length, allFields.Length);
}
[Theory]
[BitAutoData]
public async Task Handler_MisMatchedOrganizations_DoesNotSucceed(
SutProvider<BulkSecretAuthorizationHandler> sutProvider, List<Secret> resources,
ClaimsPrincipal claimsPrincipal)
{
var requirement = BulkSecretOperations.ReadAll;
resources[0].OrganizationId = Guid.NewGuid();
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(Arg.Any<Guid>())
.ReturnsForAnyArgs(true);
var authzContext = new AuthorizationHandlerContext(new List<IAuthorizationRequirement> { requirement },
claimsPrincipal, resources);
await sutProvider.Sut.HandleAsync(authzContext);
Assert.False(authzContext.HasSucceeded);
}
[Theory]
[BitAutoData]
public async Task Handler_NoAccessToSecretsManager_DoesNotSucceed(
SutProvider<BulkSecretAuthorizationHandler> sutProvider, List<Secret> resources,
ClaimsPrincipal claimsPrincipal)
{
var requirement = BulkSecretOperations.ReadAll;
resources = SetSameOrganization(resources);
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(Arg.Any<Guid>())
.ReturnsForAnyArgs(false);
var authzContext = new AuthorizationHandlerContext(new List<IAuthorizationRequirement> { requirement },
claimsPrincipal, resources);
await sutProvider.Sut.HandleAsync(authzContext);
Assert.False(authzContext.HasSucceeded);
}
[Theory]
[BitAutoData]
public async Task Handler_UnsupportedSecretOperationRequirement_Throws(
SutProvider<BulkSecretAuthorizationHandler> sutProvider, List<Secret> resources,
ClaimsPrincipal claimsPrincipal)
{
var requirement = new BulkSecretOperationRequirement();
resources = SetSameOrganization(resources);
SetupUserSubstitutes(sutProvider, AccessClientType.User, resources.First().OrganizationId);
var authzContext = new AuthorizationHandlerContext(new List<IAuthorizationRequirement> { requirement },
claimsPrincipal, resources);
await Assert.ThrowsAsync<ArgumentException>(() => sutProvider.Sut.HandleAsync(authzContext));
}
[Theory]
[BitAutoData(AccessClientType.User)]
[BitAutoData(AccessClientType.NoAccessCheck)]
[BitAutoData(AccessClientType.ServiceAccount)]
public async Task Handler_NoAccessToSecrets_DoesNotSucceed(
AccessClientType accessClientType,
SutProvider<BulkSecretAuthorizationHandler> sutProvider, List<Secret> resources,
ClaimsPrincipal claimsPrincipal)
{
var requirement = BulkSecretOperations.ReadAll;
resources = SetSameOrganization(resources);
var secretIds =
SetupSecretAccessRequest(sutProvider, resources, accessClientType, resources.First().OrganizationId);
sutProvider.GetDependency<ISecretRepository>()
.AccessToSecretsAsync(Arg.Any<IEnumerable<Guid>>(), Arg.Any<Guid>(), Arg.Any<AccessClientType>())
.Returns(secretIds.ToDictionary(id => id, _ => (false, false)));
var authzContext = new AuthorizationHandlerContext(new List<IAuthorizationRequirement> { requirement },
claimsPrincipal, resources);
await sutProvider.Sut.HandleAsync(authzContext);
Assert.False(authzContext.HasSucceeded);
}
[Theory]
[BitAutoData(AccessClientType.User)]
[BitAutoData(AccessClientType.NoAccessCheck)]
[BitAutoData(AccessClientType.ServiceAccount)]
public async Task Handler_HasAccessToSomeSecrets_DoesNotSucceed(
AccessClientType accessClientType,
SutProvider<BulkSecretAuthorizationHandler> sutProvider, List<Secret> resources,
ClaimsPrincipal claimsPrincipal)
{
var requirement = BulkSecretOperations.ReadAll;
resources = SetSameOrganization(resources);
var secretIds =
SetupSecretAccessRequest(sutProvider, resources, accessClientType, resources.First().OrganizationId);
var accessResult = secretIds.ToDictionary(secretId => secretId, _ => (false, false));
accessResult[secretIds.First()] = (true, true);
sutProvider.GetDependency<ISecretRepository>()
.AccessToSecretsAsync(Arg.Any<IEnumerable<Guid>>(), Arg.Any<Guid>(), Arg.Any<AccessClientType>())
.Returns(accessResult);
var authzContext = new AuthorizationHandlerContext(new List<IAuthorizationRequirement> { requirement },
claimsPrincipal, resources);
await sutProvider.Sut.HandleAsync(authzContext);
Assert.False(authzContext.HasSucceeded);
}
[Theory]
[BitAutoData(AccessClientType.User)]
[BitAutoData(AccessClientType.NoAccessCheck)]
[BitAutoData(AccessClientType.ServiceAccount)]
public async Task Handler_PartialAccessReturn_DoesNotSucceed(
AccessClientType accessClientType,
SutProvider<BulkSecretAuthorizationHandler> sutProvider, List<Secret> resources,
ClaimsPrincipal claimsPrincipal)
{
var requirement = BulkSecretOperations.ReadAll;
resources = SetSameOrganization(resources);
var secretIds =
SetupSecretAccessRequest(sutProvider, resources, accessClientType, resources.First().OrganizationId);
var accessResult = secretIds.ToDictionary(secretId => secretId, _ => (false, false));
accessResult.Remove(secretIds.First());
sutProvider.GetDependency<ISecretRepository>()
.AccessToSecretsAsync(Arg.Any<IEnumerable<Guid>>(), Arg.Any<Guid>(), Arg.Any<AccessClientType>())
.Returns(accessResult);
var authzContext = new AuthorizationHandlerContext(new List<IAuthorizationRequirement> { requirement },
claimsPrincipal, resources);
await sutProvider.Sut.HandleAsync(authzContext);
Assert.False(authzContext.HasSucceeded);
}
[Theory]
[BitAutoData(AccessClientType.User)]
[BitAutoData(AccessClientType.NoAccessCheck)]
[BitAutoData(AccessClientType.ServiceAccount)]
public async Task Handler_HasAccessToAllSecrets_Success(
AccessClientType accessClientType,
SutProvider<BulkSecretAuthorizationHandler> sutProvider, List<Secret> resources,
ClaimsPrincipal claimsPrincipal)
{
var requirement = BulkSecretOperations.ReadAll;
resources = SetSameOrganization(resources);
var secretIds =
SetupSecretAccessRequest(sutProvider, resources, accessClientType, resources.First().OrganizationId);
var accessResult = secretIds.ToDictionary(secretId => secretId, _ => (true, true));
sutProvider.GetDependency<ISecretRepository>()
.AccessToSecretsAsync(Arg.Any<IEnumerable<Guid>>(), Arg.Any<Guid>(), Arg.Any<AccessClientType>())
.Returns(accessResult);
var authzContext = new AuthorizationHandlerContext(new List<IAuthorizationRequirement> { requirement },
claimsPrincipal, resources);
await sutProvider.Sut.HandleAsync(authzContext);
Assert.True(authzContext.HasSucceeded);
}
private static List<Secret> SetSameOrganization(List<Secret> secrets)
{
var organizationId = secrets.First().OrganizationId;
foreach (var secret in secrets)
{
secret.OrganizationId = organizationId;
}
return secrets;
}
private static void SetupUserSubstitutes(
SutProvider<BulkSecretAuthorizationHandler> sutProvider,
AccessClientType accessClientType,
Guid organizationId,
Guid userId = new())
{
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(organizationId)
.Returns(true);
sutProvider.GetDependency<IAccessClientQuery>().GetAccessClientAsync(default, organizationId)
.ReturnsForAnyArgs((accessClientType, userId));
}
private static List<Guid> SetupSecretAccessRequest(
SutProvider<BulkSecretAuthorizationHandler> sutProvider,
IEnumerable<Secret> resources,
AccessClientType accessClientType,
Guid organizationId,
Guid userId = new())
{
SetupUserSubstitutes(sutProvider, accessClientType, organizationId, userId);
return resources.Select(s => s.Id).ToList();
}
}

View File

@ -260,25 +260,13 @@ public class SecretsController : Controller
throw new NotFoundException();
}
// Ensure all secrets belong to the same organization.
var organizationId = secrets.First().OrganizationId;
if (secrets.Any(secret => secret.OrganizationId != organizationId) ||
!_currentContext.AccessSecretsManager(organizationId))
var authorizationResult = await _authorizationService.AuthorizeAsync(User, secrets, BulkSecretOperations.ReadAll);
if (!authorizationResult.Succeeded)
{
throw new NotFoundException();
}
foreach (var secret in secrets)
{
var authorizationResult = await _authorizationService.AuthorizeAsync(User, secret, SecretOperations.Read);
if (!authorizationResult.Succeeded)
{
throw new NotFoundException();
}
}
await LogSecretsRetrievalAsync(organizationId, secrets);
await LogSecretsRetrievalAsync(secrets.First().OrganizationId, secrets);
var responses = secrets.Select(s => new BaseSecretResponseModel(s));
return new ListResponseModel<BaseSecretResponseModel>(responses);

View File

@ -0,0 +1,12 @@
using Microsoft.AspNetCore.Authorization.Infrastructure;
namespace Bit.Core.SecretsManager.AuthorizationRequirements;
public class BulkSecretOperationRequirement : OperationAuthorizationRequirement
{
}
public static class BulkSecretOperations
{
public static readonly BulkSecretOperationRequirement ReadAll = new() { Name = nameof(ReadAll) };
}

View File

@ -21,6 +21,7 @@ public interface ISecretRepository
Task RestoreManyByIdAsync(IEnumerable<Guid> ids);
Task<IEnumerable<Secret>> ImportAsync(IEnumerable<Secret> secrets);
Task<(bool Read, bool Write)> AccessToSecretAsync(Guid id, Guid userId, AccessClientType accessType);
Task<Dictionary<Guid, (bool Read, bool Write)>> AccessToSecretsAsync(IEnumerable<Guid> ids, Guid userId, AccessClientType accessType);
Task EmptyTrash(DateTime nowTime, uint deleteAfterThisNumberOfDays);
Task<int> GetSecretsCountByOrganizationIdAsync(Guid organizationId);
}

View File

@ -81,6 +81,12 @@ public class NoopSecretRepository : ISecretRepository
return Task.FromResult((false, false));
}
public Task<Dictionary<Guid, (bool Read, bool Write)>> AccessToSecretsAsync(IEnumerable<Guid> ids,
Guid userId, AccessClientType accessType)
{
return Task.FromResult(null as Dictionary<Guid, (bool Read, bool Write)>);
}
public Task EmptyTrash(DateTime nowTime, uint deleteAfterThisNumberOfDays)
{
return Task.FromResult(0);

View File

@ -741,44 +741,83 @@ public class SecretsControllerTests : IClassFixture<ApiApplicationFactory>, IAsy
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
[Theory]
[InlineData(PermissionType.RunAsAdmin)]
[InlineData(PermissionType.RunAsUserWithPermission)]
public async Task GetSecretsByIds_Success(PermissionType permissionType)
[Fact]
public async Task GetSecretsByIds_SecretsNotInTheSameOrganization_NotFound()
{
var (org, _) = await _organizationHelper.Initialize(true, true, true);
await _loginHelper.LoginAsync(_email);
var (project, secretIds) = await CreateSecretsAsync(org.Id);
if (permissionType == PermissionType.RunAsUserWithPermission)
{
var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true);
await _loginHelper.LoginAsync(email);
var accessPolicies = new List<BaseAccessPolicy>
{
new UserProjectAccessPolicy
{
GrantedProjectId = project.Id, OrganizationUserId = orgUser.Id, Read = true, Write = true,
},
};
await _accessPolicyRepository.CreateManyAsync(accessPolicies);
}
else
{
var (email, _) = await _organizationHelper.CreateNewUser(OrganizationUserType.Admin, true);
await _loginHelper.LoginAsync(email);
}
var otherOrg = await _organizationHelper.CreateSmOrganizationAsync();
var (_, secretIds) = await CreateSecretsAsync(org.Id);
var (_, diffOrgSecrets) = await CreateSecretsAsync(otherOrg.Id, 1);
secretIds.AddRange(diffOrgSecrets);
var request = new GetSecretsRequestModel { Ids = secretIds };
var response = await _client.PostAsJsonAsync("/secrets/get-by-ids", request);
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
[Theory]
[InlineData(false)]
[InlineData(true)]
public async Task GetSecretsByIds_SecretsNonExistent_NotFound(bool partial)
{
var (org, _) = await _organizationHelper.Initialize(true, true, true);
await _loginHelper.LoginAsync(_email);
var ids = new List<Guid>();
if (partial)
{
var (_, secretIds) = await CreateSecretsAsync(org.Id);
ids = secretIds;
ids.Add(Guid.NewGuid());
}
var request = new GetSecretsRequestModel { Ids = ids };
var response = await _client.PostAsJsonAsync("/secrets/get-by-ids", request);
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
[Theory]
[InlineData(true, false)]
[InlineData(true, true)]
[InlineData(false, false)]
[InlineData(false, true)]
public async Task GetSecretsByIds_NoAccess_NotFound(bool runAsServiceAccount, bool partialAccess)
{
var (org, _) = await _organizationHelper.Initialize(true, true, true);
var request = await SetupNoAccessRequestAsync(org.Id, runAsServiceAccount, partialAccess);
var response = await _client.PostAsJsonAsync("/secrets/get-by-ids", request);
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
[Theory]
[InlineData(PermissionType.RunAsAdmin)]
[InlineData(PermissionType.RunAsUserWithPermission)]
[InlineData(PermissionType.RunAsServiceAccountWithPermission)]
public async Task GetSecretsByIds_Success(PermissionType permissionType)
{
var (org, _) = await _organizationHelper.Initialize(true, true, true);
await _loginHelper.LoginAsync(_email);
var request = await SetupGetSecretsByIdsRequestAsync(org.Id, permissionType);
var response = await _client.PostAsJsonAsync("/secrets/get-by-ids", request);
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadFromJsonAsync<ListResponseModel<BaseSecretResponseModel>>();
Assert.NotNull(result);
Assert.NotEmpty(result.Data);
Assert.Equal(secretIds.Count, result.Data.Count());
Assert.Equal(request.Ids.Count(), result.Data.Count());
Assert.All(result.Data, data => Assert.Equal(_mockEncryptedString, data.Value));
Assert.All(result.Data, data => Assert.Equal(_mockEncryptedString, data.Key));
Assert.All(result.Data, data => Assert.Equal(_mockEncryptedString, data.Note));
Assert.All(result.Data, data => Assert.Equal(org.Id, data.OrganizationId));
}
@ -1161,4 +1200,94 @@ public class SecretsControllerTests : IClassFixture<ApiApplicationFactory>, IAsy
return (secret, request);
}
private async Task<GetSecretsRequestModel> SetupGetSecretsByIdsRequestAsync(Guid organizationId,
PermissionType permissionType)
{
var (project, secretIds) = await CreateSecretsAsync(organizationId);
if (permissionType == PermissionType.RunAsUserWithPermission)
{
var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true);
await _loginHelper.LoginAsync(email);
var accessPolicies = new List<BaseAccessPolicy>
{
new UserProjectAccessPolicy
{
GrantedProjectId = project.Id, OrganizationUserId = orgUser.Id, Read = true, Write = true
}
};
await _accessPolicyRepository.CreateManyAsync(accessPolicies);
}
if (permissionType == PermissionType.RunAsServiceAccountWithPermission)
{
var apiKeyDetails = await _organizationHelper.CreateNewServiceAccountApiKeyAsync();
await _loginHelper.LoginWithApiKeyAsync(apiKeyDetails);
var accessPolicies = new List<BaseAccessPolicy>
{
new ServiceAccountProjectAccessPolicy
{
GrantedProjectId = project.Id,
ServiceAccountId = apiKeyDetails.ApiKey.ServiceAccountId,
Read = true,
Write = true
}
};
await _accessPolicyRepository.CreateManyAsync(accessPolicies);
}
return new GetSecretsRequestModel { Ids = secretIds };
}
private async Task<GetSecretsRequestModel> SetupNoAccessRequestAsync(Guid organizationId, bool runAsServiceAccount,
bool partialAccess)
{
var (_, secretIds) = await CreateSecretsAsync(organizationId);
if (runAsServiceAccount)
{
var apiKeyDetails = await _organizationHelper.CreateNewServiceAccountApiKeyAsync();
await _loginHelper.LoginWithApiKeyAsync(apiKeyDetails);
if (partialAccess)
{
var accessPolicies = new List<BaseAccessPolicy>
{
new ServiceAccountSecretAccessPolicy
{
GrantedSecretId = secretIds[0],
ServiceAccountId = apiKeyDetails.ApiKey.ServiceAccountId,
Read = true,
Write = true
}
};
await _accessPolicyRepository.CreateManyAsync(accessPolicies);
}
}
else
{
var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true);
await _loginHelper.LoginAsync(email);
if (partialAccess)
{
var accessPolicies = new List<BaseAccessPolicy>
{
new UserSecretAccessPolicy
{
GrantedSecretId = secretIds[0],
OrganizationUserId = orgUser.Id,
Read = true,
Write = true
}
};
await _accessPolicyRepository.CreateManyAsync(accessPolicies);
}
}
return new GetSecretsRequestModel { Ids = secretIds };
}
}

View File

@ -412,30 +412,6 @@ public class SecretsControllerTests
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.GetSecretsByIdsAsync(request));
}
[Theory]
[BitAutoData]
public async Task GetSecretsByIds_OrganizationMisMatch_ThrowsNotFound(SutProvider<SecretsController> sutProvider,
List<Secret> data)
{
var (ids, request) = BuildGetSecretsRequestModel(data);
sutProvider.GetDependency<ISecretRepository>().GetManyByIds(Arg.Is(ids)).ReturnsForAnyArgs(data);
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.GetSecretsByIdsAsync(request));
}
[Theory]
[BitAutoData]
public async Task GetSecretsByIds_NoAccessToSecretsManager_ThrowsNotFound(
SutProvider<SecretsController> sutProvider, List<Secret> data)
{
var (ids, request) = BuildGetSecretsRequestModel(data);
var organizationId = SetOrganizations(ref data);
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(Arg.Is(organizationId))
.ReturnsForAnyArgs(false);
sutProvider.GetDependency<ISecretRepository>().GetManyByIds(Arg.Is(ids)).ReturnsForAnyArgs(data);
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.GetSecretsByIdsAsync(request));
}
[Theory]
[BitAutoData]
public async Task GetSecretsByIds_AccessDenied_ThrowsNotFound(SutProvider<SecretsController> sutProvider,
@ -445,10 +421,8 @@ public class SecretsControllerTests
var organizationId = SetOrganizations(ref data);
sutProvider.GetDependency<ISecretRepository>().GetManyByIds(Arg.Is(ids)).ReturnsForAnyArgs(data);
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(Arg.Is(organizationId))
.ReturnsForAnyArgs(true);
sutProvider.GetDependency<IAuthorizationService>()
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(), data.First(),
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(), data,
Arg.Any<IEnumerable<IAuthorizationRequirement>>()).ReturnsForAnyArgs(AuthorizationResult.Failed());
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.GetSecretsByIdsAsync(request));
@ -462,10 +436,8 @@ public class SecretsControllerTests
var organizationId = SetOrganizations(ref data);
sutProvider.GetDependency<ISecretRepository>().GetManyByIds(Arg.Is(ids)).ReturnsForAnyArgs(data);
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(Arg.Is(organizationId))
.ReturnsForAnyArgs(true);
sutProvider.GetDependency<IAuthorizationService>()
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(), data.First(),
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(), data,
Arg.Any<IEnumerable<IAuthorizationRequirement>>()).ReturnsForAnyArgs(AuthorizationResult.Success());
var results = await sutProvider.Sut.GetSecretsByIdsAsync(request);