Files
server/test/Api.Test/Utilities/ControllerAuthorizationTestHelpersTests.cs
Thomas Rittson bb5506e934 [PM-34595] Update provider controllers to use authz attribute (#7450)
* Also decouple ProviderClientsController from Billing Team code

* Also add noop authorize attribute where no additional
  authorization is required

* Also remove unused ICurrentContext getters
2026-04-17 07:49:49 +10:00

155 lines
5.1 KiB
C#

using Bit.Api.AdminConsole.Authorization;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Xunit;
namespace Bit.Api.Test.Utilities;
public class ControllerAuthorizationTestHelpersTests
{
[Fact]
public void AssertAllHttpMethodsHaveAuthorization_ControllerMissingClassLevelAuthorize_Throws()
{
var exception = Assert.Throws<Xunit.Sdk.FailException>(() =>
ControllerAuthorizationTestHelpers.AssertAllHttpMethodsHaveAuthorization(
typeof(ControllerWithoutClassAuthorize)));
Assert.Contains("missing required class-level [Authorize] attribute", exception.Message);
Assert.Contains("ControllerWithoutClassAuthorize", exception.Message);
}
[Fact]
public void AssertAllHttpMethodsHaveAuthorization_MethodMissingAuthorization_Throws()
{
var exception = Assert.Throws<Xunit.Sdk.FailException>(() =>
ControllerAuthorizationTestHelpers.AssertAllHttpMethodsHaveAuthorization(
typeof(ControllerWithUnauthorizedMethod)));
Assert.Contains("3 HTTP action method(s) without method-level authorization", exception.Message);
Assert.Contains("GetUnauthorized ([HttpGet])", exception.Message);
Assert.Contains("PostUnauthorized ([HttpPost])", exception.Message);
Assert.Contains("PutUnauthorized ([HttpPut])", exception.Message);
Assert.Contains("ControllerWithUnauthorizedMethod", exception.Message);
}
[Fact]
public void AssertAllHttpMethodsHaveAuthorization_AllMethodsProperlyAuthorized_DoesNotThrow()
{
ControllerAuthorizationTestHelpers.AssertAllHttpMethodsHaveAuthorization(
typeof(ControllerWithProperAuthorization));
}
[Fact]
public void AssertAllHttpMethodsHaveAuthorization_MethodWithAllowAnonymous_DoesNotThrow()
{
ControllerAuthorizationTestHelpers.AssertAllHttpMethodsHaveAuthorization(
typeof(ControllerWithAllowAnonymous));
}
[Fact]
public void AssertAllHttpMethodsHaveAuthorization_MethodWithNoopAuthorize_DoesNotThrow()
{
ControllerAuthorizationTestHelpers.AssertAllHttpMethodsHaveAuthorization(
typeof(ControllerWithNoopAuthorize));
}
[Fact]
public void AssertAllHttpMethodsHaveAuthorization_ControllerWithNoHttpMethods_Throws()
{
var exception = Assert.Throws<Xunit.Sdk.FailException>(() =>
ControllerAuthorizationTestHelpers.AssertAllHttpMethodsHaveAuthorization(
typeof(ControllerWithNoHttpMethods)));
Assert.Contains("has no HTTP action methods", exception.Message);
}
// Controller missing class-level [Authorize]
private class ControllerWithoutClassAuthorize : ControllerBase
{
[HttpGet]
[CustomAuthorize]
public OkResult Get() => Ok();
}
// Controller with class-level [Authorize] but methods missing method-level authorization
[Authorize]
private class ControllerWithUnauthorizedMethod : ControllerBase
{
[HttpGet("authorized")]
[CustomAuthorize]
public OkResult GetAuthorized() => Ok();
[HttpGet("unauthorized")]
public OkResult GetUnauthorized() => Ok();
[HttpPost("unauthorized")]
public OkResult PostUnauthorized() => Ok();
[HttpPut("unauthorized")]
public OkResult PutUnauthorized() => Ok();
// Non-HTTP method should be ignored
public OkResult NonHttpMethod() => Ok();
}
// Controller with proper authorization on all methods
[Authorize]
private class ControllerWithProperAuthorization : ControllerBase
{
[HttpGet("custom")]
[CustomAuthorize]
public OkResult GetWithCustom() => Ok();
[HttpPost("custom")]
[CustomAuthorize]
public OkResult PostWithCustom() => Ok();
[HttpDelete("custom")]
[CustomAuthorize]
public OkResult DeleteWithCustom() => Ok();
}
// Controller with AllowAnonymous (which is valid method-level authorization)
[Authorize]
private class ControllerWithAllowAnonymous : ControllerBase
{
[HttpGet("anonymous")]
[AllowAnonymous]
public OkResult GetAnonymous() => Ok();
[HttpPost("protected")]
[CustomAuthorize]
public OkResult PostProtected() => Ok();
[HttpGet("mixed")]
[AllowAnonymous]
public OkResult GetMixed() => Ok();
}
// Controller with [NoopAuthorize] on a method (authenticated, no additional authz)
[Authorize]
private class ControllerWithNoopAuthorize : ControllerBase
{
[HttpGet("noop")]
[NoopAuthorize]
public OkResult GetWithNoop() => Ok();
[HttpPost("protected")]
[CustomAuthorize]
public OkResult PostProtected() => Ok();
}
// Controller with no HTTP methods
[Authorize]
private class ControllerWithNoHttpMethods : ControllerBase
{
public OkResult NotAnHttpMethod() => Ok();
public void AnotherNonHttpMethod() { }
}
// Custom authorize attribute for testing (mimics [Authorize<T>])
private class CustomAuthorizeAttribute : AuthorizeAttribute
{
}
}