Skip to content

Commit

Permalink
feat: add WithHttpContextStrategy (#934)
Browse files Browse the repository at this point in the history
  • Loading branch information
AndrewTriesToCode authored Jan 29, 2025
1 parent a18a935 commit e6aeb7c
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 20 deletions.
1 change: 1 addition & 0 deletions docs/ConfigurationAndUsage.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ in the order registered. See [MultiTenant Strategies](Strategies) for more infor
- `WithDelegateStrategy<TContext, TTenantInfo>`
- `WithHeaderStrategy`
- `WithHostStrategy`
- `WithHttpContextStrategy`
- `WithRouteStrategy`
- `WithSessionStrategy`
- `WithStaticStrategy`
Expand Down
31 changes: 28 additions & 3 deletions docs/Strategies.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ builder.Services.AddMultiTenant<TenantInfo>()
Uses a provided `Func<object, Task<string>>` to determine the tenant. For example the lambda
function `async context => "initech"` would use "initech" as the identifier when resolving the tenant for every request.
This strategy is good to use for testing or simple logic. This strategy is configured multiple times and will run in the
order configured.
This strategy is good to use for testing or simple logic. This strategy can be used multiple times and will run
in the order configured.

Configure by calling `WithDelegateStrategy` after `AddMultiTenant<TTenantInfo>` A `Func<object, Task<string?>>`is passed
in which will be used with each request to resolve the tenant. A lambda or async lambda can be used as the parameter.
Expand Down Expand Up @@ -91,6 +91,30 @@ builder.Services.AddMultiTenant<TenantInfo>()
})...
```

## HttpContext Strategy

> NuGet package: Finbuckle.MultiTenant.AspNetCore
Uses a delegate that takes an `HttpContext` parameter to determine the tenant identifier. When used with the ASP.NET
Core middleware each request's`HttpConeext` is passed to the strategy. This strategy can be used multiple times and will
run in the order configured. Tenant resolution will ignore this strategy if the context is not of the correct type.

Configure by calling `WithHttpContextStrategy` after `AddMultiTenant<TTenantInfo>`:

```csharp
builder.Services.AddMultiTenant<TenantInfo>()
.WithHttpContextStrategy(async httpContext =>
{
var identifier = httpContext.Request.Query["tenant"];

// query value will be empty if the value didn't exist in the request
if(identifier == string.Empty)
return null;

return identifier;
})...
```

## Base Path Strategy

> NuGet package: Finbuckle.MultiTenant.AspNetCore
Expand Down Expand Up @@ -244,7 +268,8 @@ builder.Services.AddMultiTenant<TenantInfo>()

> NuGet package: Finbuckle.MultiTenant.AspNetCore
Uses an HTTP request header to determine the tenant identifier. By default, the header with key `__tenant__` is used, but
Uses an HTTP request header to determine the tenant identifier. By default, the header with key `__tenant__` is used,
but
a custom key can also be used.

Configure by calling `WithHeaderStrategy` after `AddMultiTenant<TTenantInfo>`. An overload to accept a custom claim type
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ public static class MultiTenantBuilderExtensions
/// <summary>
/// Configures authentication options to enable per-tenant behavior.
/// </summary>
/// <returns>The same MultiTenantBuilder passed into the method.</returns>
/// <typeparam name="TTenantInfo">The ITenantInfo implementation type.</typeparam>
/// <returns>The <see cref="MultiTenantBuilder&lt;TTenantInfo&gt;"/> so that additional calls can be chained.</returns>
public static MultiTenantBuilder<TTenantInfo> WithPerTenantAuthentication<TTenantInfo>(
this MultiTenantBuilder<TTenantInfo> builder)
where TTenantInfo : class, ITenantInfo, new()
Expand All @@ -39,9 +40,10 @@ public static MultiTenantBuilder<TTenantInfo> WithPerTenantAuthentication<TTenan
/// <summary>
/// Configures per-tenant authentication behavior.
/// </summary>
/// <typeparam name="TTenantInfo">The ITenantInfo implementation type.</typeparam>
/// <param name="builder">MultiTenantBuilder instance.</param>
/// <param name="config">Authentication options config.</param>
/// <returns>The same MultiTenantBuilder passed into the method.</returns>
/// <returns>The <see cref="MultiTenantBuilder&lt;TTenantInfo&gt;"/> so that additional calls can be chained.</returns>
// ReSharper disable once MemberCanBePrivate.Global
public static MultiTenantBuilder<TTenantInfo> WithPerTenantAuthentication<TTenantInfo>(
this MultiTenantBuilder<TTenantInfo> builder, Action<MultiTenantAuthenticationOptions> config)
Expand All @@ -57,7 +59,8 @@ public static MultiTenantBuilder<TTenantInfo> WithPerTenantAuthentication<TTenan
/// <summary>
/// Configures conventional functionality for per-tenant authentication.
/// </summary>
/// <returns>The same MultiTenantBuilder passed into the method.</returns>
/// <typeparam name="TTenantInfo">The ITenantInfo implementation type.</typeparam>
/// <returns>The <see cref="MultiTenantBuilder&lt;TTenantInfo&gt;"/> so that additional calls can be chained.</returns>
public static MultiTenantBuilder<TTenantInfo> WithPerTenantAuthenticationConventions<TTenantInfo>(
this MultiTenantBuilder<TTenantInfo> builder)
where TTenantInfo : class, ITenantInfo, new()
Expand Down Expand Up @@ -139,9 +142,10 @@ public static MultiTenantBuilder<TTenantInfo> WithPerTenantAuthenticationConvent
/// <summary>
/// Configures core functionality for per-tenant authentication.
/// </summary>
/// <typeparam name="TTenantInfo">The ITenantInfo implementation type.</typeparam>
/// <param name="builder">MultiTenantBuilder instance.</param>
/// <param name="config">Authentication options config</param>
/// <returns>The same MultiTenantBuilder passed into the method.</returns>
/// <returns>The <see cref="MultiTenantBuilder&lt;TTenantInfo&gt;"/> so that additional calls can be chained.</returns>
public static MultiTenantBuilder<TTenantInfo> WithPerTenantAuthenticationCore<TTenantInfo>(
this MultiTenantBuilder<TTenantInfo> builder, Action<MultiTenantAuthenticationOptions>? config =
null)
Expand All @@ -167,8 +171,9 @@ public static MultiTenantBuilder<TTenantInfo> WithPerTenantAuthenticationCore<TT
/// <summary>
/// Adds and configures a SessionStrategy to the application.
/// </summary>
/// <typeparam name="TTenantInfo">The ITenantInfo implementation type.</typeparam>
/// <param name="builder">MultiTenantBuilder instance.</param>
/// <returns>The same MultiTenantBuilder passed into the method.</returns>
/// <returns>The <see cref="MultiTenantBuilder&lt;TTenantInfo&gt;"/> so that additional calls can be chained.</returns>
public static MultiTenantBuilder<TTenantInfo> WithSessionStrategy<TTenantInfo>(
this MultiTenantBuilder<TTenantInfo> builder)
where TTenantInfo : class, ITenantInfo, new()
Expand All @@ -177,9 +182,10 @@ public static MultiTenantBuilder<TTenantInfo> WithSessionStrategy<TTenantInfo>(
/// <summary>
/// Adds and configures a SessionStrategy to the application.
/// </summary>
/// <typeparam name="TTenantInfo">The ITenantInfo implementation type.</typeparam>
/// <param name="builder">MultiTenantBuilder instance.</param>
/// <param name="tenantKey">The session key to use.</param>
/// <returns>The same MultiTenantBuilder passed into the method.</returns>
/// <returns>The <see cref="MultiTenantBuilder&lt;TTenantInfo&gt;"/> so that additional calls can be chained.</returns>
public static MultiTenantBuilder<TTenantInfo> WithSessionStrategy<TTenantInfo>(
this MultiTenantBuilder<TTenantInfo> builder, string tenantKey)
where TTenantInfo : class, ITenantInfo, new()
Expand All @@ -188,7 +194,8 @@ public static MultiTenantBuilder<TTenantInfo> WithSessionStrategy<TTenantInfo>(
/// <summary>
/// Adds and configures a RemoteAuthenticationCallbackStrategy to the application.
/// </summary>
/// <returns>The same MultiTenantBuilder passed into the method.</returns>
/// <typeparam name="TTenantInfo">The ITenantInfo implementation type.</typeparam>
/// <returns>The <see cref="MultiTenantBuilder&lt;TTenantInfo&gt;"/> so that additional calls can be chained.</returns>
public static MultiTenantBuilder<TTenantInfo> WithRemoteAuthenticationCallbackStrategy<TTenantInfo>(
this MultiTenantBuilder<TTenantInfo> builder)
where TTenantInfo : class, ITenantInfo, new()
Expand All @@ -199,6 +206,7 @@ public static MultiTenantBuilder<TTenantInfo> WithRemoteAuthenticationCallbackSt
/// <summary>
/// Adds and configures a BasePathStrategy to the application.
/// </summary>
/// <typeparam name="TTenantInfo">The ITenantInfo implementation type.</typeparam>
/// <returns>The same MultiTenantBuilder passed into the method.></returns>
public static MultiTenantBuilder<TTenantInfo> WithBasePathStrategy<TTenantInfo>(
this MultiTenantBuilder<TTenantInfo> builder)
Expand All @@ -208,6 +216,7 @@ public static MultiTenantBuilder<TTenantInfo> WithBasePathStrategy<TTenantInfo>(
/// <summary>
/// Adds and configures a BasePathStrategy to the application.
/// </summary>
/// <typeparam name="TTenantInfo">The ITenantInfo implementation type.</typeparam>
/// <returns>The same MultiTenantBuilder passed into the method.></returns>
public static MultiTenantBuilder<TTenantInfo> WithBasePathStrategy<TTenantInfo>(
this MultiTenantBuilder<TTenantInfo> builder, Action<BasePathStrategyOptions> configureOptions)
Expand Down Expand Up @@ -241,7 +250,8 @@ resolutionCompletedContext.Context is HttpContext httpContext &&
/// <summary>
/// Adds and configures a RouteStrategy with a route parameter Constants.TenantToken to the application.
/// </summary>
/// <returns>The same MultiTenantBuilder passed into the method.</returns>
/// <typeparam name="TTenantInfo">The ITenantInfo implementation type.</typeparam>
/// <returns>The <see cref="MultiTenantBuilder&lt;TTenantInfo&gt;"/> so that additional calls can be chained.</returns>
public static MultiTenantBuilder<TTenantInfo> WithRouteStrategy<TTenantInfo>(
this MultiTenantBuilder<TTenantInfo> builder)
where TTenantInfo : class, ITenantInfo, new()
Expand All @@ -250,9 +260,10 @@ public static MultiTenantBuilder<TTenantInfo> WithRouteStrategy<TTenantInfo>(
/// <summary>
/// Adds and configures a RouteStrategy to the application.
/// </summary>
/// <typeparam name="TTenantInfo">The ITenantInfo implementation type.</typeparam>
/// <param name="builder">MultiTenantBuilder instance.</param>
/// <param name="tenantParam">The name of the route parameter used to determine the tenant identifier.</param>
/// <returns>The same MultiTenantBuilder passed into the method.</returns>
/// <returns>The <see cref="MultiTenantBuilder&lt;TTenantInfo&gt;"/> so that additional calls can be chained.</returns>
public static MultiTenantBuilder<TTenantInfo> WithRouteStrategy<TTenantInfo>(
this MultiTenantBuilder<TTenantInfo> builder, string tenantParam)
where TTenantInfo : class, ITenantInfo, new()
Expand All @@ -264,12 +275,12 @@ public static MultiTenantBuilder<TTenantInfo> WithRouteStrategy<TTenantInfo>(

return builder.WithStrategy<RouteStrategy>(ServiceLifetime.Singleton, tenantParam);
}
// #endif

/// <summary>
/// Adds and configures a HostStrategy with template "__tenant__.*" to the application.
/// </summary>
/// <returns>The same MultiTenantBuilder passed into the method.</returns>
/// <typeparam name="TTenantInfo">The ITenantInfo implementation type.</typeparam>
/// <returns>The <see cref="MultiTenantBuilder&lt;TTenantInfo&gt;"/> so that additional calls can be chained.</returns>
public static MultiTenantBuilder<TTenantInfo> WithHostStrategy<TTenantInfo>(
this MultiTenantBuilder<TTenantInfo> builder)
where TTenantInfo : class, ITenantInfo, new()
Expand All @@ -278,9 +289,10 @@ public static MultiTenantBuilder<TTenantInfo> WithHostStrategy<TTenantInfo>(
/// <summary>
/// Adds and configures a HostStrategy to the application.
/// </summary>
/// <typeparam name="TTenantInfo">The ITenantInfo implementation type.</typeparam>
/// <param name="builder">MultiTenantBuilder instance.</param>
/// <param name="template">The template for determining the tenant identifier in the host.</param>
/// <returns>The same MultiTenantBuilder passed into the method.</returns>
/// <returns>The <see cref="MultiTenantBuilder&lt;TTenantInfo&gt;"/> so that additional calls can be chained.</returns>
public static MultiTenantBuilder<TTenantInfo> WithHostStrategy<TTenantInfo>(
this MultiTenantBuilder<TTenantInfo> builder, string template)
where TTenantInfo : class, ITenantInfo, new()
Expand All @@ -296,7 +308,8 @@ public static MultiTenantBuilder<TTenantInfo> WithHostStrategy<TTenantInfo>(
/// <summary>
/// Adds and configures a ClaimStrategy for claim name "__tenant__" to the application. Uses the default authentication handler scheme.
/// </summary>
/// <returns>The same MultiTenantBuilder passed into the method.</returns>
/// <typeparam name="TTenantInfo">The ITenantInfo implementation type.</typeparam>
/// <returns>The <see cref="MultiTenantBuilder&lt;TTenantInfo&gt;"/> so that additional calls can be chained.</returns>
public static MultiTenantBuilder<TTenantInfo> WithClaimStrategy<TTenantInfo>(
this MultiTenantBuilder<TTenantInfo> builder) where TTenantInfo : class, ITenantInfo, new()
{
Expand All @@ -306,9 +319,10 @@ public static MultiTenantBuilder<TTenantInfo> WithClaimStrategy<TTenantInfo>(
/// <summary>
/// Adds and configures a ClaimStrategy to the application. Uses the default authentication handler scheme.
/// </summary>
/// <typeparam name="TTenantInfo">The ITenantInfo implementation type.</typeparam>
/// <param name="builder">MultiTenantBuilder instance.</param>
/// <param name="tenantKey">Claim name for determining the tenant identifier.</param>
/// <returns>The same MultiTenantBuilder passed into the method.</returns>
/// <returns>The <see cref="MultiTenantBuilder&lt;TTenantInfo&gt;"/> so that additional calls can be chained.</returns>
// ReSharper disable once MemberCanBePrivate.Global
public static MultiTenantBuilder<TTenantInfo> WithClaimStrategy<TTenantInfo>(
this MultiTenantBuilder<TTenantInfo> builder, string tenantKey)
Expand All @@ -321,17 +335,32 @@ public static MultiTenantBuilder<TTenantInfo> WithClaimStrategy<TTenantInfo>(
/// <summary>
/// Adds and configures a ClaimStrategy to the application.
/// </summary>
/// <typeparam name="TTenantInfo">The ITenantInfo implementation type.</typeparam>
/// <param name="builder">MultiTenantBuilder instance.</param>
/// <param name="tenantKey">Claim name for determining the tenant identifier.</param>
/// <param name="authenticationScheme">The authentication scheme to check for claims.</param>
/// <returns>The same MultiTenantBuilder passed into the method.</returns>
/// <returns>The <see cref="MultiTenantBuilder&lt;TTenantInfo&gt;"/> so that additional calls can be chained.</returns>
public static MultiTenantBuilder<TTenantInfo> WithClaimStrategy<TTenantInfo>(
this MultiTenantBuilder<TTenantInfo> builder, string tenantKey, string authenticationScheme)
where TTenantInfo : class, ITenantInfo, new()
{
BypassSessionPrincipalValidation(builder);
return builder.WithStrategy<ClaimStrategy>(ServiceLifetime.Singleton, tenantKey, authenticationScheme);
}

/// <summary>
/// Adds and configures an HttpContext delegate strategy to the application.
/// </summary>
/// <typeparam name="TTenantInfo">The ITenantInfo implementation type.</typeparam>
/// <param name="builder">MultiTenantBuilder instance.</param>
/// <param name="doStrategy">The delegate to execute to determine the tenant identifier.</param>
/// <returns>The <see cref="MultiTenantBuilder&lt;TTenantInfo&gt;"/> so that additional calls can be chained.</returns>
public static MultiTenantBuilder<TTenantInfo> WithHttpContextStrategy<TTenantInfo>(
this MultiTenantBuilder<TTenantInfo> builder, Func<HttpContext, Task<string?>> doStrategy)
where TTenantInfo : class, ITenantInfo, new()
{
return builder.WithDelegateStrategy<HttpContext, TTenantInfo>(doStrategy);
}

private static void BypassSessionPrincipalValidation<TTenantInfo>(
MultiTenantBuilder<TTenantInfo> builder)
Expand All @@ -355,7 +384,7 @@ private static void BypassSessionPrincipalValidation<TTenantInfo>(
/// <summary>
/// Adds and configures a HeaderStrategy with using HTTP header key "__tenant__" to the application.
/// </summary>
/// <returns>The same MultiTenantBuilder passed into the method.</returns>
/// <returns>The <see cref="MultiTenantBuilder&lt;TTenantInfo&gt;"/> so that additional calls can be chained.</returns>
public static MultiTenantBuilder<TTenantInfo> WithHeaderStrategy<TTenantInfo>(
this MultiTenantBuilder<TTenantInfo> builder) where TTenantInfo : class, ITenantInfo, new()
{
Expand All @@ -367,7 +396,7 @@ public static MultiTenantBuilder<TTenantInfo> WithHeaderStrategy<TTenantInfo>(
/// </summary>
/// <param name="builder">MultiTenantBuilder instance.</param>
/// <param name="tenantKey">The HTTP header key for determining the tenant identifier in the request.</param>
/// <returns>The same MultiTenantBuilder passed into the method.</returns>
/// <returns>The <see cref="MultiTenantBuilder&lt;TTenantInfo&gt;"/> so that additional calls can be chained.</returns>
public static MultiTenantBuilder<TTenantInfo> WithHeaderStrategy<TTenantInfo>(
this MultiTenantBuilder<TTenantInfo> builder, string tenantKey)
where TTenantInfo : class, ITenantInfo, new()
Expand Down

0 comments on commit e6aeb7c

Please sign in to comment.