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(() => 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(() => 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(() => 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]) private class CustomAuthorizeAttribute : AuthorizeAttribute { } }