From 883a66ef5e6c52d7507dfa50fbb7c756a50c6149 Mon Sep 17 00:00:00 2001 From: Thomas Rittson Date: Thu, 27 Mar 2025 11:45:10 +1000 Subject: [PATCH] Add tests --- .../OrganizationRequirementHandler.cs | 2 - .../OrganizationRequirementHelpers.cs | 2 +- .../OrganizationRequirementHandlerTests.cs | 92 +++++++++++++++++++ 3 files changed, 93 insertions(+), 3 deletions(-) create mode 100644 test/Api.Test/AdminConsole/Authorization/OrganizationRequirementHandlerTests.cs diff --git a/src/Api/AdminConsole/Authorization/OrganizationRequirementHandler.cs b/src/Api/AdminConsole/Authorization/OrganizationRequirementHandler.cs index 987c0f236a..040cd63491 100644 --- a/src/Api/AdminConsole/Authorization/OrganizationRequirementHandler.cs +++ b/src/Api/AdminConsole/Authorization/OrganizationRequirementHandler.cs @@ -11,8 +11,6 @@ namespace Bit.Api.AdminConsole.Authorization; /// Retrieves the Organization ID from the route and then passes it to the requirement's AuthorizeAsync callback to /// determine whether the action is authorized. /// -/// -/// public class OrganizationRequirementHandler( ICurrentContext currentContext, IProviderOrganizationContext providerOrganizationContext, diff --git a/src/Api/AdminConsole/Authorization/OrganizationRequirementHelpers.cs b/src/Api/AdminConsole/Authorization/OrganizationRequirementHelpers.cs index 5449810953..4c7b9c4941 100644 --- a/src/Api/AdminConsole/Authorization/OrganizationRequirementHelpers.cs +++ b/src/Api/AdminConsole/Authorization/OrganizationRequirementHelpers.cs @@ -12,7 +12,7 @@ public static class OrganizationRequirementHelpers } httpContextAccessor.HttpContext.GetRouteData().Values.TryGetValue("orgId", out var orgIdParam); - if (!Guid.TryParse(orgIdParam?.ToString(), out var orgId)) + if (orgIdParam == null || !Guid.TryParse(orgIdParam.ToString(), out var orgId)) { return null; } diff --git a/test/Api.Test/AdminConsole/Authorization/OrganizationRequirementHandlerTests.cs b/test/Api.Test/AdminConsole/Authorization/OrganizationRequirementHandlerTests.cs new file mode 100644 index 0000000000..fc156c7eac --- /dev/null +++ b/test/Api.Test/AdminConsole/Authorization/OrganizationRequirementHandlerTests.cs @@ -0,0 +1,92 @@ +using System.Security.Claims; +using Bit.Api.AdminConsole.Authorization; +using Bit.Api.AdminConsole.Context; +using Bit.Test.Common.AutoFixture; +using Bit.Test.Common.AutoFixture.Attributes; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using NSubstitute; +using Xunit; + +namespace Bit.Api.Test.AdminConsole.Authorization; + +[SutProviderCustomize] +public class OrganizationRequirementHandlerTests +{ + [Theory, BitAutoData] + public async Task IfNoOrganizationId_Throws(SutProvider sutProvider) + { + // Arrange + ArrangeRouteValues(sutProvider, null); // no orgId in route + var testRequirement = Substitute.For(); + var authContext = new AuthorizationHandlerContext([testRequirement], new ClaimsPrincipal(), null); + + // Act + var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.HandleAsync(authContext)); + Assert.Contains("No organizationId found", exception.Message); + Assert.False(authContext.HasSucceeded); + } + + [Theory, BitAutoData] + public async Task IfInvalidOrganizationId_Throws(SutProvider sutProvider) + { + // Arrange + ArrangeRouteValues(sutProvider, "malformed guid"); + var testRequirement = Substitute.For(); + var authContext = new AuthorizationHandlerContext([testRequirement], new ClaimsPrincipal(), null); + + // Act + var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.HandleAsync(authContext)); + Assert.Contains("No organizationId found", exception.Message); + Assert.False(authContext.HasSucceeded); + } + + [Theory, BitAutoData] + public async Task DoesNotAuthorize_IfAuthorizeAsync_ReturnsFalse(SutProvider sutProvider, Guid organizationId) + { + // Arrange route values + ArrangeRouteValues(sutProvider, organizationId.ToString()); + + // Arrange requirement + var testRequirement = Substitute.For(); + testRequirement + .AuthorizeAsync(organizationId, null, Arg.Any()) + .ReturnsForAnyArgs(false); + var authContext = new AuthorizationHandlerContext([testRequirement], new ClaimsPrincipal(), null); + + // Act + await sutProvider.Sut.HandleAsync(authContext); + + // Assert + await testRequirement.Received(1).AuthorizeAsync(organizationId, null, Arg.Any()); + Assert.False(authContext.HasSucceeded); + } + + [Theory, BitAutoData] + public async Task Authorizes_IfAuthorizeAsync_ReturnsTrue(SutProvider sutProvider, Guid organizationId) + { + // Arrange route values + ArrangeRouteValues(sutProvider, organizationId.ToString()); + + // Arrange requirement + var testRequirement = Substitute.For(); + testRequirement + .AuthorizeAsync(organizationId, null, Arg.Any()) + .ReturnsForAnyArgs(true); + var authContext = new AuthorizationHandlerContext([testRequirement], new ClaimsPrincipal(), null); + + // Act + await sutProvider.Sut.HandleAsync(authContext); + + // Assert + await testRequirement.Received(1).AuthorizeAsync(organizationId, null, Arg.Any()); + Assert.True(authContext.HasSucceeded); + } + + private static void ArrangeRouteValues(SutProvider sutProvider, string orgIdRouteValue) + { + var httpContext = new DefaultHttpContext(); + httpContext.Request.RouteValues["orgId"] = orgIdRouteValue; + sutProvider.GetDependency().HttpContext = httpContext; + } +}