1
0
mirror of https://github.com/bitwarden/server synced 2025-12-16 00:03:54 +00:00

[SM-949] Add endpoint to fetch events by service account (#3336)

* Add ability to fetch events by service account

* Extract GetDateRange into ApiHelpers util

* Add dapper implementation

* Add EF repo implementation

* Add authz handler case

* unit + integration tests for controller

* swap to read check

* Adding comments

* Fix integration tests from merge

* Enabled SM events controller for self-hosting
This commit is contained in:
Thomas Avery
2023-10-19 16:57:14 -05:00
committed by GitHub
parent c1cf07d764
commit 728cd1c0b5
15 changed files with 461 additions and 28 deletions

View File

@@ -0,0 +1,71 @@
using System.Net;
using System.Net.Http.Headers;
using Bit.Api.IntegrationTest.Factories;
using Bit.Core.SecretsManager.Entities;
using Bit.Core.SecretsManager.Repositories;
using Xunit;
namespace Bit.Api.IntegrationTest.SecretsManager.Controllers;
public class SecretsManagerEventsControllerTests : IClassFixture<ApiApplicationFactory>, IAsyncLifetime
{
private const string _mockEncryptedString =
"2.3Uk+WNBIoU5xzmVFNcoWzz==|1MsPIYuRfdOHfu/0uY6H2Q==|/98sp4wb6pHP1VTZ9JcNCYgQjEUMFPlqJgCwRk1YXKg=";
private readonly HttpClient _client;
private readonly ApiApplicationFactory _factory;
private readonly IServiceAccountRepository _serviceAccountRepository;
private string _email = null!;
private SecretsManagerOrganizationHelper _organizationHelper = null!;
public SecretsManagerEventsControllerTests(ApiApplicationFactory factory)
{
_factory = factory;
_client = _factory.CreateClient();
_serviceAccountRepository = _factory.GetService<IServiceAccountRepository>();
}
public async Task InitializeAsync()
{
_email = $"integration-test{Guid.NewGuid()}@bitwarden.com";
await _factory.LoginWithNewAccount(_email);
_organizationHelper = new SecretsManagerOrganizationHelper(_factory, _email);
}
public Task DisposeAsync()
{
_client.Dispose();
return Task.CompletedTask;
}
private async Task LoginAsync(string email)
{
var tokens = await _factory.LoginAsync(email);
_client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", tokens.Token);
}
[Theory]
[InlineData(false, false, false)]
[InlineData(false, false, true)]
[InlineData(false, true, false)]
[InlineData(false, true, true)]
[InlineData(true, false, false)]
[InlineData(true, false, true)]
[InlineData(true, true, false)]
public async Task GetServiceAccountEvents_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets, bool organizationEnabled)
{
var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets, organizationEnabled);
await LoginAsync(_email);
var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount
{
OrganizationId = org.Id,
Name = _mockEncryptedString
});
var response = await _client.GetAsync($"/sm/events/service-accounts/{serviceAccount.Id}");
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
}

View File

@@ -0,0 +1,79 @@
using System.Security.Claims;
using Bit.Api.SecretsManager.Controllers;
using Bit.Core.Exceptions;
using Bit.Core.Models.Data;
using Bit.Core.Repositories;
using Bit.Core.SecretsManager.Entities;
using Bit.Core.SecretsManager.Repositories;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
using Microsoft.AspNetCore.Authorization;
using NSubstitute;
using Xunit;
namespace Bit.Api.Test.SecretsManager.Controllers;
[ControllerCustomize(typeof(SecretsManagerEventsController))]
[SutProviderCustomize]
[JsonDocumentCustomize]
public class SecretsManagerEventsControllerTests
{
[Theory]
[BitAutoData]
public async void GetServiceAccountEvents_NoAccess_Throws(SutProvider<SecretsManagerEventsController> sutProvider,
ServiceAccount data)
{
sutProvider.GetDependency<IServiceAccountRepository>().GetByIdAsync(default).ReturnsForAnyArgs(data);
sutProvider.GetDependency<IAuthorizationService>()
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(), data,
Arg.Any<IEnumerable<IAuthorizationRequirement>>()).ReturnsForAnyArgs(AuthorizationResult.Failed());
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.GetServiceAccountEventsAsync(data.Id));
await sutProvider.GetDependency<IEventRepository>().DidNotReceiveWithAnyArgs()
.GetManyByOrganizationServiceAccountAsync(Arg.Any<Guid>(), Arg.Any<Guid>(), Arg.Any<DateTime>(),
Arg.Any<DateTime>(), Arg.Any<PageOptions>());
}
[Theory]
[BitAutoData]
public async void GetServiceAccountEvents_DateRangeOver_Throws(
SutProvider<SecretsManagerEventsController> sutProvider,
ServiceAccount data)
{
sutProvider.GetDependency<IServiceAccountRepository>().GetByIdAsync(default).ReturnsForAnyArgs(data);
sutProvider.GetDependency<IAuthorizationService>()
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(), data,
Arg.Any<IEnumerable<IAuthorizationRequirement>>()).ReturnsForAnyArgs(AuthorizationResult.Success());
var start = DateTime.UtcNow.AddYears(-1);
var end = DateTime.UtcNow.AddYears(1);
await Assert.ThrowsAsync<BadRequestException>(() =>
sutProvider.Sut.GetServiceAccountEventsAsync(data.Id, start, end));
await sutProvider.GetDependency<IEventRepository>().DidNotReceiveWithAnyArgs()
.GetManyByOrganizationServiceAccountAsync(Arg.Any<Guid>(), Arg.Any<Guid>(), Arg.Any<DateTime>(),
Arg.Any<DateTime>(), Arg.Any<PageOptions>());
}
[Theory]
[BitAutoData]
public async void GetServiceAccountEvents_Success(SutProvider<SecretsManagerEventsController> sutProvider,
ServiceAccount data)
{
sutProvider.GetDependency<IServiceAccountRepository>().GetByIdAsync(default).ReturnsForAnyArgs(data);
sutProvider.GetDependency<IAuthorizationService>()
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(), data,
Arg.Any<IEnumerable<IAuthorizationRequirement>>()).ReturnsForAnyArgs(AuthorizationResult.Success());
sutProvider.GetDependency<IEventRepository>()
.GetManyByOrganizationServiceAccountAsync(default, default, default, default, default)
.ReturnsForAnyArgs(new PagedResult<IEvent>());
await sutProvider.Sut.GetServiceAccountEventsAsync(data.Id);
await sutProvider.GetDependency<IEventRepository>().Received(1)
.GetManyByOrganizationServiceAccountAsync(data.OrganizationId, data.Id, Arg.Any<DateTime>(),
Arg.Any<DateTime>(), Arg.Any<PageOptions>());
}
}