diff --git a/src/Idmt.Plugin/Configuration/IdmtOptions.cs b/src/Idmt.Plugin/Configuration/IdmtOptions.cs index 7366e5e..34818e6 100644 --- a/src/Idmt.Plugin/Configuration/IdmtOptions.cs +++ b/src/Idmt.Plugin/Configuration/IdmtOptions.cs @@ -174,16 +174,10 @@ public static class IdmtMultiTenantStrategy public const string Route = "route"; public const string BasePath = "basepath"; - // Strategy options keys - - public const string HeaderKeyOption = "HeaderKey"; - public const string ClaimOption = "ClaimType"; - public const string RouteParameterOption = "RouteParameter"; - // Strategy options defaults - public const string DefaultHeaderName = "__tenant__"; - public const string DefaultClaimType = "tenant"; + public const string DefaultHeader = "__tenant__"; + public const string DefaultClaim = "tenant"; public const string DefaultRouteParameter = "__tenant__"; } diff --git a/src/Idmt.Plugin/Extensions/ServiceCollectionExtensions.cs b/src/Idmt.Plugin/Extensions/ServiceCollectionExtensions.cs index 2465dd6..c1fcf49 100644 --- a/src/Idmt.Plugin/Extensions/ServiceCollectionExtensions.cs +++ b/src/Idmt.Plugin/Extensions/ServiceCollectionExtensions.cs @@ -185,18 +185,18 @@ private static void ConfigureMultiTenant(IServiceCollection services, IdmtOption { case IdmtMultiTenantStrategy.Header: builder.WithHeaderStrategy( - idmtOptions.MultiTenant.StrategyOptions.GetValueOrDefault(IdmtMultiTenantStrategy.HeaderKeyOption, IdmtMultiTenantStrategy.DefaultHeaderName)); + idmtOptions.MultiTenant.StrategyOptions.GetValueOrDefault(IdmtMultiTenantStrategy.Header, IdmtMultiTenantStrategy.DefaultHeader)); break; case IdmtMultiTenantStrategy.Route: builder.WithRouteStrategy( - idmtOptions.MultiTenant.StrategyOptions.GetValueOrDefault(IdmtMultiTenantStrategy.RouteParameterOption, IdmtMultiTenantStrategy.DefaultRouteParameter), + idmtOptions.MultiTenant.StrategyOptions.GetValueOrDefault(IdmtMultiTenantStrategy.Route, IdmtMultiTenantStrategy.DefaultRouteParameter), useTenantAmbientRouteValue: true); break; case IdmtMultiTenantStrategy.Claim: builder.WithClaimStrategy( - idmtOptions.MultiTenant.StrategyOptions.GetValueOrDefault(IdmtMultiTenantStrategy.ClaimOption, IdmtMultiTenantStrategy.DefaultClaimType)); + idmtOptions.MultiTenant.StrategyOptions.GetValueOrDefault(IdmtMultiTenantStrategy.Claim, IdmtMultiTenantStrategy.DefaultClaim)); break; case IdmtMultiTenantStrategy.BasePath: diff --git a/src/Idmt.Plugin/Features/Auth/Login.cs b/src/Idmt.Plugin/Features/Auth/Login.cs index 2268f23..e73c362 100644 --- a/src/Idmt.Plugin/Features/Auth/Login.cs +++ b/src/Idmt.Plugin/Features/Auth/Login.cs @@ -1,4 +1,5 @@ using Finbuckle.MultiTenant.Abstractions; +using Idmt.Plugin.Configuration; using Idmt.Plugin.Models; using Idmt.Plugin.Validation; using Microsoft.AspNetCore.Authentication; diff --git a/src/Idmt.Plugin/Middleware/ValidateBearerTokenTenantMiddleware.cs b/src/Idmt.Plugin/Middleware/ValidateBearerTokenTenantMiddleware.cs index 68ae323..322ab64 100644 --- a/src/Idmt.Plugin/Middleware/ValidateBearerTokenTenantMiddleware.cs +++ b/src/Idmt.Plugin/Middleware/ValidateBearerTokenTenantMiddleware.cs @@ -44,8 +44,8 @@ private bool ValidateTokenTenant( // Get the tenant claim type from configuration var tenantClaimType = idmtOptions.Value.MultiTenant.StrategyOptions.GetValueOrDefault( - IdmtMultiTenantStrategy.ClaimOption, - IdmtMultiTenantStrategy.DefaultClaimType); + IdmtMultiTenantStrategy.Claim, + IdmtMultiTenantStrategy.DefaultClaim); // Extract tenant claim from token var tokenTenantClaim = context.User.FindFirst(tenantClaimType)?.Value; diff --git a/src/Idmt.Plugin/Services/CurrentUserService.cs b/src/Idmt.Plugin/Services/CurrentUserService.cs index 040d395..cbaf8f0 100644 --- a/src/Idmt.Plugin/Services/CurrentUserService.cs +++ b/src/Idmt.Plugin/Services/CurrentUserService.cs @@ -28,10 +28,10 @@ internal sealed class CurrentUserService( multiTenantContextAccessor.MultiTenantContext?.TenantInfo?.Id; public string? TenantIdentifier => - User?.FindFirstValue(idmtOptions.Value.MultiTenant.StrategyOptions.GetValueOrDefault(IdmtMultiTenantStrategy.ClaimOption, IdmtMultiTenantStrategy.DefaultClaimType)) ?? + User?.FindFirstValue(idmtOptions.Value.MultiTenant.StrategyOptions.GetValueOrDefault(IdmtMultiTenantStrategy.Claim, IdmtMultiTenantStrategy.DefaultClaim)) ?? multiTenantContextAccessor.MultiTenantContext?.TenantInfo?.Identifier; - public bool IsActive => User?.FindFirstValue("is_active") == "true"; + public bool IsActive => string.Equals(User?.FindFirstValue("is_active"), "true", StringComparison.OrdinalIgnoreCase); public bool IsInRole(string role) => User?.IsInRole(role) ?? false; diff --git a/src/Idmt.Plugin/Services/IdmtUserClaimsPrincipalFactory.cs b/src/Idmt.Plugin/Services/IdmtUserClaimsPrincipalFactory.cs index 21d3b64..710f83e 100644 --- a/src/Idmt.Plugin/Services/IdmtUserClaimsPrincipalFactory.cs +++ b/src/Idmt.Plugin/Services/IdmtUserClaimsPrincipalFactory.cs @@ -24,7 +24,7 @@ protected override async Task GenerateClaimsAsync(IdmtUser user) // Add tenant claim for multi-tenant strategies (header, claim, route) // This ensures token validation includes tenant context - var claimKey = idmtOptions.Value.MultiTenant.StrategyOptions.GetValueOrDefault(IdmtMultiTenantStrategy.ClaimOption, IdmtMultiTenantStrategy.DefaultClaimType); + var claimKey = idmtOptions.Value.MultiTenant.StrategyOptions.GetValueOrDefault(IdmtMultiTenantStrategy.Claim, IdmtMultiTenantStrategy.DefaultClaim); // Try to get tenant info from store using user's TenantId var tenantInfo = await tenantStore.GetAsync(user.TenantId) ?? throw new InvalidOperationException($"Tenant information not found for tenant ID: {user.TenantId}. User ID: {user.Id}"); diff --git a/src/samples/Idmt.BasicSample/appsettings.Development.json b/src/samples/Idmt.BasicSample/appsettings.Development.json index 5eecbed..8417a63 100644 --- a/src/samples/Idmt.BasicSample/appsettings.Development.json +++ b/src/samples/Idmt.BasicSample/appsettings.Development.json @@ -8,7 +8,11 @@ "Idmt": { "MultiTenant": { "DefaultTenantId": "system-tenant", - "Strategies": ["header", "claim"] + "Strategies": ["header", "claim"], + "StrategyOptions": { + "header": "__tenant-identifier__", + "claim": "tenant-identifier" + } } } -} +} \ No newline at end of file diff --git a/src/samples/Idmt.BasicSample/appsettings.json b/src/samples/Idmt.BasicSample/appsettings.json index ed7a5e5..8b5a746 100644 --- a/src/samples/Idmt.BasicSample/appsettings.json +++ b/src/samples/Idmt.BasicSample/appsettings.json @@ -9,7 +9,11 @@ "Idmt": { "MultiTenant": { "DefaultTenantId": "system-tenant", - "Strategies": ["header", "claim"] + "Strategies": ["header", "claim"], + "StrategyOptions": { + "header": "__tenant-identifier__", + "claim": "tenant-identifier" + } } } -} +} \ No newline at end of file diff --git a/src/tests/Idmt.BasicSample.Tests/IdmtApiFactory.cs b/src/tests/Idmt.BasicSample.Tests/IdmtApiFactory.cs index 5ec3e56..a9d0409 100644 --- a/src/tests/Idmt.BasicSample.Tests/IdmtApiFactory.cs +++ b/src/tests/Idmt.BasicSample.Tests/IdmtApiFactory.cs @@ -11,6 +11,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; using Moq; namespace Idmt.BasicSample.Tests; @@ -92,13 +93,17 @@ public HttpClient CreateClientWithTenant(string? tenantId = null, bool allowAuto AllowAutoRedirect = allowAutoRedirect, }); - if (_strategies.Contains(IdmtMultiTenantStrategy.Route)) + var idmtOptions = Services.GetRequiredService>().Value; + + var strategies = idmtOptions.MultiTenant.Strategies; + + if (strategies.Contains(IdmtMultiTenantStrategy.Route)) { client.BaseAddress = new Uri($"http://localhost/{tenantId}/"); } - if (_strategies.Contains(IdmtMultiTenantStrategy.Header)) + if (strategies.Contains(IdmtMultiTenantStrategy.Header)) { - client.DefaultRequestHeaders.TryAddWithoutValidation(IdmtMultiTenantStrategy.DefaultHeaderName, tenantId); + client.DefaultRequestHeaders.TryAddWithoutValidation(idmtOptions.MultiTenant.StrategyOptions.GetValueOrDefault(IdmtMultiTenantStrategy.Header, IdmtMultiTenantStrategy.DefaultHeader), tenantId); } return client; } diff --git a/src/tests/Idmt.UnitTests/Services/CoreServicesTests.cs b/src/tests/Idmt.UnitTests/Services/CoreServicesTests.cs index 6d929c6..a4bec0f 100644 --- a/src/tests/Idmt.UnitTests/Services/CoreServicesTests.cs +++ b/src/tests/Idmt.UnitTests/Services/CoreServicesTests.cs @@ -111,7 +111,7 @@ public void TenantIdentifier_ReturnsTenantIdentifier_WhenClaimExists() var user = new System.Security.Claims.ClaimsPrincipal( new System.Security.Claims.ClaimsIdentity( [ - new System.Security.Claims.Claim(IdmtMultiTenantStrategy.DefaultClaimType, tenantId) + new System.Security.Claims.Claim(IdmtMultiTenantStrategy.DefaultClaim, tenantId) ])); _service.SetCurrentUser(user, "127.0.0.1", "TestAgent/1.0"); @@ -165,7 +165,7 @@ public void TenantIdentifier_ReturnsTenantIdentifier_WhenCustomClaimTypeIsConfig { StrategyOptions = new Dictionary { - { IdmtMultiTenantStrategy.ClaimOption, customClaimType } + { IdmtMultiTenantStrategy.Claim, customClaimType } } } }; diff --git a/src/tests/Idmt.UnitTests/Services/IdmtUserClaimsPrincipalFactoryTests.cs b/src/tests/Idmt.UnitTests/Services/IdmtUserClaimsPrincipalFactoryTests.cs index 8abc9b2..89376e8 100644 --- a/src/tests/Idmt.UnitTests/Services/IdmtUserClaimsPrincipalFactoryTests.cs +++ b/src/tests/Idmt.UnitTests/Services/IdmtUserClaimsPrincipalFactoryTests.cs @@ -176,7 +176,7 @@ public async Task CreateAsync_AddsTenantClaim_WithDefaultClaimType() var identity = await CallGenerateClaimsAsync(user); - var tenantClaim = identity.FindFirst(IdmtMultiTenantStrategy.DefaultClaimType); + var tenantClaim = identity.FindFirst(IdmtMultiTenantStrategy.DefaultClaim); Assert.NotNull(tenantClaim); // The factory adds tenantInfo.Identifier, not tenantId Assert.Equal(tenantIdentifier, tenantClaim.Value); @@ -196,7 +196,7 @@ public async Task CreateAsync_AddsTenantClaim_WithCustomClaimType() { StrategyOptions = new Dictionary { - { IdmtMultiTenantStrategy.ClaimOption, customClaimType } + { IdmtMultiTenantStrategy.Claim, customClaimType } } } }; @@ -236,7 +236,7 @@ public async Task CreateAsync_AddsTenantClaim_WithCustomClaimType() Assert.Equal(tenantIdentifier, tenantClaim.Value); // Verify default claim type is not present - var defaultTenantClaim = identity.FindFirst(IdmtMultiTenantStrategy.DefaultClaimType); + var defaultTenantClaim = identity.FindFirst(IdmtMultiTenantStrategy.DefaultClaim); Assert.Null(defaultTenantClaim); } @@ -302,7 +302,7 @@ public async Task CreateAsync_AddsAllCustomClaims() Assert.NotNull(isActiveClaim); Assert.Equal("True", isActiveClaim.Value); - var tenantClaim = identity.FindFirst(IdmtMultiTenantStrategy.DefaultClaimType); + var tenantClaim = identity.FindFirst(IdmtMultiTenantStrategy.DefaultClaim); Assert.NotNull(tenantClaim); // The factory adds tenantInfo.Identifier, not tenantId Assert.Equal(tenantIdentifier, tenantClaim.Value);