From 86b56c9341028ce888cca8f4c553b3339071dd66 Mon Sep 17 00:00:00 2001 From: williambza Date: Thu, 9 Jul 2026 09:45:37 +0200 Subject: [PATCH] Add improvements to authorization settings --- .../OpenIdConnect/OpenIdConnectAssertions.cs | 8 +++++++- .../OpenIdConnect/When_authentication_is_enabled.cs | 3 ++- .../OpenIdConnectSettings.cs | 7 +++++++ .../Settings/OpenIdConnectSettingsTests.cs | 12 ++++++++++++ .../Authentication/AuthenticationController.cs | 2 ++ 5 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/ServiceControl.AcceptanceTesting/OpenIdConnect/OpenIdConnectAssertions.cs b/src/ServiceControl.AcceptanceTesting/OpenIdConnect/OpenIdConnectAssertions.cs index a1007fcc5b..5525b9271f 100644 --- a/src/ServiceControl.AcceptanceTesting/OpenIdConnect/OpenIdConnectAssertions.cs +++ b/src/ServiceControl.AcceptanceTesting/OpenIdConnect/OpenIdConnectAssertions.cs @@ -105,7 +105,8 @@ public static async Task AssertAuthConfigurationResponse( string expectedClientId = null, string expectedAuthority = null, string expectedAudience = null, - string expectedApiScopes = null) + string expectedApiScopes = null, + bool expectedRoleBasedAuthorizationEnabled = false) { Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK), "Authentication configuration endpoint should return 200 OK"); @@ -120,6 +121,11 @@ public static async Task AssertAuthConfigurationResponse( "Response should contain 'enabled' property"); Assert.That(enabledProperty.GetBoolean(), Is.EqualTo(expectedEnabled), $"'enabled' should be {expectedEnabled}"); + + Assert.That(root.TryGetProperty("role_based_authorization_enabled", out var roleBasedAuthorizationEnabledProperty), Is.True, + "Response should contain 'role_based_authorization_enabled' property"); + Assert.That(roleBasedAuthorizationEnabledProperty.GetBoolean(), Is.EqualTo(expectedRoleBasedAuthorizationEnabled), + $"'role_based_authorization_enabled' should be {expectedRoleBasedAuthorizationEnabled}"); } // Note: API uses snake_case JSON serialization (client_id, api_scopes) diff --git a/src/ServiceControl.AcceptanceTests/Security/OpenIdConnect/When_authentication_is_enabled.cs b/src/ServiceControl.AcceptanceTests/Security/OpenIdConnect/When_authentication_is_enabled.cs index 51009e48ea..93342685f1 100644 --- a/src/ServiceControl.AcceptanceTests/Security/OpenIdConnect/When_authentication_is_enabled.cs +++ b/src/ServiceControl.AcceptanceTests/Security/OpenIdConnect/When_authentication_is_enabled.cs @@ -74,7 +74,8 @@ await OpenIdConnectAssertions.AssertAuthConfigurationResponse( expectedEnabled: true, expectedClientId: TestClientId, expectedAudience: TestAudience, - expectedApiScopes: TestApiScopes); + expectedApiScopes: TestApiScopes, + expectedRoleBasedAuthorizationEnabled: true); } [Test] diff --git a/src/ServiceControl.Infrastructure/OpenIdConnectSettings.cs b/src/ServiceControl.Infrastructure/OpenIdConnectSettings.cs index 3f80fa8550..058a946168 100644 --- a/src/ServiceControl.Infrastructure/OpenIdConnectSettings.cs +++ b/src/ServiceControl.Infrastructure/OpenIdConnectSettings.cs @@ -154,6 +154,13 @@ public OpenIdConnectSettings(SettingsRootNamespace rootNamespace, bool validateC void Validate(bool requireServicePulseSettings) { + if (!Enabled && RoleBasedAuthorizationEnabled) + { + var message = "Authentication.RoleBasedAuthorizationEnabled cannot be true when Authentication.Enabled is false. Role-based authorization requires authentication to be enabled."; + logger.LogCritical(message); + throw new Exception(message); + } + if (Enabled) { ValidateRequiredSettings(requireServicePulseSettings); diff --git a/src/ServiceControl.UnitTests/Infrastructure/Settings/OpenIdConnectSettingsTests.cs b/src/ServiceControl.UnitTests/Infrastructure/Settings/OpenIdConnectSettingsTests.cs index 2e3fd20641..d1a588a517 100644 --- a/src/ServiceControl.UnitTests/Infrastructure/Settings/OpenIdConnectSettingsTests.cs +++ b/src/ServiceControl.UnitTests/Infrastructure/Settings/OpenIdConnectSettingsTests.cs @@ -29,6 +29,7 @@ public void TearDown() Environment.SetEnvironmentVariable("SERVICECONTROL_AUTHENTICATION_VALIDATELIFETIME", null); Environment.SetEnvironmentVariable("SERVICECONTROL_AUTHENTICATION_VALIDATEISSUERSIGNINGKEY", null); Environment.SetEnvironmentVariable("SERVICECONTROL_AUTHENTICATION_REQUIREHTTPSMETADATA", null); + Environment.SetEnvironmentVariable("SERVICECONTROL_AUTHENTICATION_ROLEBASEDAUTHORIZATIONENABLED", null); Environment.SetEnvironmentVariable("SERVICECONTROL_AUTHENTICATION_SERVICEPULSE_CLIENTID", null); Environment.SetEnvironmentVariable("SERVICECONTROL_AUTHENTICATION_SERVICEPULSE_APISCOPES", null); Environment.SetEnvironmentVariable("SERVICECONTROL_AUTHENTICATION_SERVICEPULSE_AUTHORITY", null); @@ -49,6 +50,7 @@ public void Should_have_correct_defaults() Assert.That(settings.ValidateLifetime, Is.True); Assert.That(settings.ValidateIssuerSigningKey, Is.True); Assert.That(settings.RequireHttpsMetadata, Is.True); + Assert.That(settings.RoleBasedAuthorizationEnabled, Is.False); Assert.That(settings.ServicePulseClientId, Is.Null); Assert.That(settings.ServicePulseApiScopes, Is.Null); Assert.That(settings.ServicePulseAuthority, Is.Null); @@ -265,6 +267,16 @@ public void Should_succeed_without_service_pulse_settings_when_not_required() } } + [Test] + public void Should_throw_when_role_based_authorization_enabled_without_authentication_enabled() + { + Environment.SetEnvironmentVariable("SERVICECONTROL_AUTHENTICATION_ROLEBASEDAUTHORIZATIONENABLED", "true"); + + var ex = Assert.Throws(() => new OpenIdConnectSettings(TestNamespace, validateConfiguration: true, requireServicePulseSettings: false)); + + Assert.That(ex.Message, Does.Contain("RoleBasedAuthorizationEnabled cannot be true when Authentication.Enabled is false")); + } + [Test] public void Should_not_validate_when_disabled() { diff --git a/src/ServiceControl/Authentication/AuthenticationController.cs b/src/ServiceControl/Authentication/AuthenticationController.cs index 467a7494fa..9a878f568a 100644 --- a/src/ServiceControl/Authentication/AuthenticationController.cs +++ b/src/ServiceControl/Authentication/AuthenticationController.cs @@ -17,6 +17,7 @@ public ActionResult Configuration() var info = new AuthConfig { Enabled = settings.OpenIdConnectSettings.Enabled, + RoleBasedAuthorizationEnabled = settings.OpenIdConnectSettings.RoleBasedAuthorizationEnabled, ClientId = settings.OpenIdConnectSettings.ServicePulseClientId, Authority = settings.OpenIdConnectSettings.ServicePulseAuthority, Audience = settings.OpenIdConnectSettings.Audience, @@ -34,6 +35,7 @@ public ActionResult Configuration() public class AuthConfig { public bool Enabled { get; set; } + public bool RoleBasedAuthorizationEnabled { get; set; } public string ClientId { get; set; } public string Authority { get; set; } public string Audience { get; set; }