/// <summary>
        /// Adds and configures a ClaimsStrategy to the application where the TenantClaimName configuration registered elsewhere.
        /// </summary>
        /// <remarks>
        /// The claims strategy does not rely on TenantContext.  Therefore, it is not necessary to
        /// register the tenant context here.  The only thing it relies on is a tenant claim name and this
        /// is registered via a TenantConfiguration object which is injected into the TenantConfigurations
        /// object.  The TenantConfigurations object will handle duplicates in case app settings are also
        /// registered and have a tenant claim name value.
        /// </remarks>
        /// <param name="builder"></param>
        /// <returns></returns>
        public static FinbuckleMultiTenantBuilder WithClaimsStrategy(this FinbuckleMultiTenantBuilder builder)
        {
            // register the strategy with the built in finbuckly custom strategy
            builder.WithStrategy <ClaimsStrategy>(ServiceLifetime.Scoped);

            return(builder);
        }
        /// <summary>
        /// Adds and configures a BasePathStrategy to the application.
        /// </summary>
        /// <returns>The same MultiTenantBuilder passed into the method.></returns>
        public static FinbuckleMultiTenantBuilder <TTenantInfo> WithBasePathStrategy <TTenantInfo>(
            this FinbuckleMultiTenantBuilder <TTenantInfo> builder, Action <BasePathStrategyOptions> configureOptions)
            where TTenantInfo : class, ITenantInfo, new()
        {
            builder.Services.Configure(configureOptions);
            builder.Services.Configure <MultiTenantOptions>(options =>
            {
                var origOnTenantResolved        = options.Events.OnTenantResolved;
                options.Events.OnTenantResolved = tenantResolvedContext =>
                {
                    var httpContext = tenantResolvedContext.Context as HttpContext ??
                                      throw new MultiTenantException("BasePathStrategy expects HttpContext.");

                    if (tenantResolvedContext.StrategyType == typeof(BasePathStrategy) &&
                        httpContext.RequestServices.GetRequiredService <IOptions <BasePathStrategyOptions> >().Value
                        .RebaseAspNetCorePathBase)
                    {
                        httpContext.Request.Path.StartsWithSegments($"/{tenantResolvedContext.TenantInfo?.Identifier}",
                                                                    out var matched, out var
                                                                    newPath);
                        httpContext.Request.PathBase = Path.Combine(httpContext.Request.PathBase, matched);
                        httpContext.Request.Path     = newPath;
                    }

                    return(origOnTenantResolved(tenantResolvedContext));
                };
            });

            return(builder.WithStrategy <BasePathStrategy>(ServiceLifetime.Singleton));
        }
 /// <summary>
 /// Adds and configures a ClaimStrategy to the application.
 /// </summary>
 /// <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>
 public static FinbuckleMultiTenantBuilder <TTenantInfo> WithClaimStrategy <TTenantInfo>(
     this FinbuckleMultiTenantBuilder <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 a ClaimsStrategy to the application where the TenantClaimName is registered via IConfigurationSection.
        /// </summary>
        /// <remarks>
        /// The claims strategy does not rely on TenantContext.  Therefore, it is not necessary to
        /// register the tenant context here.  The only thing it relies on is a tenant claim name and this
        /// is registered via a TenantConfiguration object which is injected into the TenantConfigurations
        /// object.  The TenantConfigurations object will handle duplicates in case app settings are also
        /// registered and have a tenant claim name value.
        /// </remarks>
        /// <param name="builder"></param>
        /// <param name="configurationSection">The configuration section containing a value for <see cref="Constants.TenantClaimName"/>.</param>
        /// <returns></returns>
        public static FinbuckleMultiTenantBuilder WithClaimsStrategy(this FinbuckleMultiTenantBuilder builder, IConfigurationSection configurationSection)
        {
            // add the configuration section and configuration wrapper
            builder.Services.AddTenantConfigurations(configurationSection);
            // register the strategy with the built in finbuckly custom strategy
            builder.WithStrategy <ClaimsStrategy>(ServiceLifetime.Scoped);

            return(builder);
        }
        /// <summary>
        /// Adds and configures a HostStrategy to the application.
        /// </summary>
        /// <param name="template">The template for determining the tenant identifier in the host.</param>
        /// <returns>The same MultiTenantBuilder passed into the method.</returns>
        public static FinbuckleMultiTenantBuilder <TTenantInfo> WithHostStrategy <TTenantInfo>(this FinbuckleMultiTenantBuilder <TTenantInfo> builder, string template)
            where TTenantInfo : class, ITenantInfo, new()
        {
            if (string.IsNullOrWhiteSpace(template))
            {
                throw new ArgumentException("Invalid value for \"template\"", nameof(template));
            }

            return(builder.WithStrategy <HostStrategy>(ServiceLifetime.Singleton, new object[] { template }));
        }
        /// <summary>
        /// Adds and configures a StaticStrategy to the application.
        /// </summary>
        /// <param name="identifier">The tenant identifier to use for all tenant resolution.</param>
        /// <returns>The same MultiTenantBuilder passed into the method.</returns>
        public static FinbuckleMultiTenantBuilder WithStaticStrategy(this FinbuckleMultiTenantBuilder builder,
                                                                     string identifier)
        {
            if (string.IsNullOrWhiteSpace(identifier))
            {
                throw new ArgumentException("Invalid value for \"identifier\"", nameof(identifier));
            }

            return(builder.WithStrategy <StaticStrategy>(ServiceLifetime.Singleton, new object[] { identifier }));;
        }
        /// <summary>
        /// Adds and configures a DelegateStrategy to the application.
        /// </summary>
        /// <param name="doStrategy">The delegate implementing the strategy.</returns>
        public static FinbuckleMultiTenantBuilder WithDelegateStrategy(this FinbuckleMultiTenantBuilder builder,
                                                                       Func <object, Task <string> > doStrategy)
        {
            if (doStrategy == null)
            {
                throw new ArgumentNullException(nameof(doStrategy));
            }

            return(builder.WithStrategy <DelegateStrategy>(ServiceLifetime.Singleton, new object[] { doStrategy }));
        }
        /// <summary>
        /// Adds and configures a StaticStrategy to the application.
        /// </summary>
        /// <param name="builder">The builder instance.</param>
        /// <param name="identifier">The tenant identifier to use for all tenant resolution.</param>
        public static FinbuckleMultiTenantBuilder <TTenantInfo> WithStaticStrategy <TTenantInfo>(this FinbuckleMultiTenantBuilder <TTenantInfo> builder,
                                                                                                 string identifier)
            where TTenantInfo : class, ITenantInfo, new()
        {
            if (string.IsNullOrWhiteSpace(identifier))
            {
                throw new ArgumentNullException(nameof(identifier), "Invalid value for \"identifier\"");
            }

            return(builder.WithStrategy <StaticStrategy>(ServiceLifetime.Singleton, new object[] { identifier }));
        }
        /// <summary>
        /// Adds and configures a DelegateStrategy to the application.
        /// </summary>
        /// <param name="builder">The builder instance.</param>
        /// <param name="doStrategy">The delegate implementing the strategy.</param>
        public static FinbuckleMultiTenantBuilder <TTenantInfo> WithDelegateStrategy <TTenantInfo>(this FinbuckleMultiTenantBuilder <TTenantInfo> builder,
                                                                                                   Func <object, Task <string?> > doStrategy)
            where TTenantInfo : class, ITenantInfo, new()
        {
            if (doStrategy == null)
            {
                throw new ArgumentNullException(nameof(doStrategy));
            }

            return(builder.WithStrategy <DelegateStrategy>(ServiceLifetime.Singleton, new object[] { doStrategy }));
        }
        /// <summary>
        /// Adds and configures a FormStrategy to the application with a singleton configuration.
        /// </summary>
        /// <remarks>
        /// The form strategy does not rely on TenantContext.  Therefore, it is not necessary to
        /// register the tenant context here.  The only thing it relies on is a FormStrategyConfiguration
        /// object.  This object is either added as a parameter or injected via IOptionsSnapshot.
        /// </remarks>
        /// <param name="builder"></param>
        /// <param name="formStrategyConfiguration">A static form strategy configuration</param>
        /// <returns></returns>
        public static FinbuckleMultiTenantBuilder WithFormStrategy(this FinbuckleMultiTenantBuilder builder, FormStrategyConfiguration formStrategyConfiguration)
        {
            // validate the configuration section
            ValidateFormStrategyConfiguration(formStrategyConfiguration);

            // wrap the configuration value in a IOptionsSnapshot object and register it
            builder.Services.AddSingleton <IOptionsSnapshot <FormStrategyConfiguration> >(new OptionSnapshotFormStrategyConfiguration(formStrategyConfiguration));

            // register the strategy with the built in finbuckly custom strategy
            builder.WithStrategy <FormStrategy>(ServiceLifetime.Scoped);
            return(builder);
        }
        /// <summary>
        /// Adds and configures a ClaimsStrategy to the application.
        /// </summary>
        /// <remarks>
        /// The claims strategy does not rely on TenantContext.  Therefore, it is not necessary to
        /// register the tenant context here.  The only thing it relies on is a tenant claim name and this
        /// is registered via a TenantConfiguration object which is injected into the TenantConfigurations
        /// object.  The TenantConfigurations object will handle duplicates in case app settings are also
        /// registered and have a tenant claim name value.
        /// </remarks>
        /// <param name="builder"></param>
        /// <param name="tenantClaimName">The name of the claim holding the tenant id.</param>
        /// <returns></returns>
        public static FinbuckleMultiTenantBuilder WithClaimsStrategy(this FinbuckleMultiTenantBuilder builder, string tenantClaimName)
        {
            // add the claim name as a manual configuration
            builder.Services.AddSingleton <ITenantConfiguration>(new TenantConfiguration()
            {
                Key = Constants.TenantClaimName, Value = tenantClaimName
            });
            // add the configuration wrapper
            builder.Services.AddTenantConfigurations();
            // register the strategy with the built in finbuckly custom strategy
            builder.WithStrategy <ClaimsStrategy>(ServiceLifetime.Scoped);

            return(builder);
        }
        /// <summary>
        /// Adds and configures a RouteStrategy to the application.
        /// </summary>
        /// <param name="tenantParam">The name of the route parameter used to determine the tenant identifier.</param>
        /// <param name="configRoutes">Delegate to configure the routes.</param>
        /// <returns>The same MultiTenantBuilder passed into the method.</returns>
        public static FinbuckleMultiTenantBuilder WithRouteStrategy(this FinbuckleMultiTenantBuilder builder,
                                                                    string tenantParam,
                                                                    Action <IRouteBuilder> configRoutes)
        {
            if (string.IsNullOrWhiteSpace(tenantParam))
            {
                throw new ArgumentException("Invalud value for \"tenantParam\"", nameof(tenantParam));
            }

            if (configRoutes == null)
            {
                throw new ArgumentNullException(nameof(configRoutes));
            }

            return(builder.WithStrategy <RouteStrategy>(ServiceLifetime.Singleton, new object[] { tenantParam, configRoutes }));
        }
        /// <summary>
        /// Adds and configures a FormStrategy to the application with a configuration section configuration which reloads with changes.
        /// </summary>
        /// <remarks>
        /// The form strategy does not rely on TenantContext.  Therefore, it is not necessary to
        /// register the tenant context here.  The only thing it relies on is a FormStrategyConfiguration
        /// object.  This object is either added as a parameter or injected via IOptionsSnapshot.
        /// </remarks>
        /// <param name="builder"></param>
        /// <param name="formStrategyConfiguration">The configuration section defining the form strategy configuration and resolved with IOptionsSnapshot<></param>
        /// <returns></returns>
        public static FinbuckleMultiTenantBuilder WithFormStrategy(this FinbuckleMultiTenantBuilder builder, IConfigurationSection configurationSection)
        {
            // validate the configuration section
            var formStrategyConfiguration = new FormStrategyConfiguration();

            configurationSection.Bind(formStrategyConfiguration);

            ValidateFormStrategyConfiguration(formStrategyConfiguration);

            // register configuration so it will reload with changes
            builder.Services.Configure <FormStrategyConfiguration>(configurationSection);

            // register the strategy with the built in finbuckly custom strategy
            builder.WithStrategy <FormStrategy>(ServiceLifetime.Scoped);

            return(builder);
        }
 /// <summary>
 /// Adds and configures a Header to the application.
 /// </summary>
 /// <param name="builder">MultiTenantBuilder instance.</param>
 /// <param name="tenantKey">The template for determining the tenant identifier in the host.</param>
 /// <returns>The same MultiTenantBuilder passed into the method.</returns>
 public static FinbuckleMultiTenantBuilder <TTenantInfo> WithHeaderStrategy <TTenantInfo>(
     this FinbuckleMultiTenantBuilder <TTenantInfo> builder, string tenantKey)
     where TTenantInfo : class, ITenantInfo, new()
 {
     return(builder.WithStrategy <HeaderStrategy>(ServiceLifetime.Singleton, tenantKey));
 }
 /// <summary>
 /// Adds and configures a RemoteAuthenticationCallbackStrategy to the application.
 /// </summary>
 /// <returns>The same MultiTenantBuilder passed into the method.</returns>
 public static FinbuckleMultiTenantBuilder <TTenantInfo> WithRemoteAuthenticationCallbackStrategy <TTenantInfo>(
     this FinbuckleMultiTenantBuilder <TTenantInfo> builder)
     where TTenantInfo : class, ITenantInfo, new()
 {
     return(builder.WithStrategy <RemoteAuthenticationCallbackStrategy>(ServiceLifetime.Singleton));
 }
 /// <summary>
 /// Adds and configures a SessionStrategy to the application.
 /// </summary>
 /// <param name="builder">MultiTenantBuilder instance.</param>
 /// <param name="tenantKey">The session key to use.</param>
 /// <returns>The same MultiTenantBuilder passed into the method.</returns>
 public static FinbuckleMultiTenantBuilder <TTenantInfo> WithSessionStrategy <TTenantInfo>(
     this FinbuckleMultiTenantBuilder <TTenantInfo> builder, string tenantKey)
     where TTenantInfo : class, ITenantInfo, new()
 => builder.WithStrategy <SessionStrategy>(ServiceLifetime.Singleton, tenantKey);
 /// <summary>
 /// Adds and configures a SessionStrategy to the application.
 /// </summary>
 /// <param name="builder">MultiTenantBuilder instance.</param>
 /// <returns>The same MultiTenantBuilder passed into the method.</returns>
 public static FinbuckleMultiTenantBuilder <TTenantInfo> WithSessionStrategy <TTenantInfo>(
     this FinbuckleMultiTenantBuilder <TTenantInfo> builder)
     where TTenantInfo : class, ITenantInfo, new()
 => builder.WithStrategy <SessionStrategy>(ServiceLifetime.Singleton, Constants.TenantToken);
 /// <summary>
 /// Adds and configures a ClaimStrategy with tenantKey "__tenant__" to the application.
 /// </summary>
 /// <returns>The same MultiTenantBuilder passed into the method.</returns>
 public static FinbuckleMultiTenantBuilder <TTenantInfo> WithClaimStrategy <TTenantInfo>(this FinbuckleMultiTenantBuilder <TTenantInfo> builder) where TTenantInfo : class, ITenantInfo, new()
 {
     return(builder.WithStrategy <ClaimStrategy>(ServiceLifetime.Singleton, Constants.TenantToken));
 }
 /// <summary>
 /// Adds and configures a BasePathStrategy to the application.
 /// </summary>
 /// <returns>The same MultiTenantBuilder passed into the method.></returns>
 public static FinbuckleMultiTenantBuilder <TTenantInfo> WithBasePathStrategy <TTenantInfo>(this FinbuckleMultiTenantBuilder <TTenantInfo> builder)
     where TTenantInfo : class, ITenantInfo, new()
 => builder.WithStrategy <BasePathStrategy>(ServiceLifetime.Singleton);
 /// <summary>
 /// Adds and configures a BasePathStrategy to the application.
 /// </summary>
 /// <returns>The same MultiTenantBuilder passed into the method.></returns>
 public static FinbuckleMultiTenantBuilder WithBasePathStrategy(this FinbuckleMultiTenantBuilder builder)
 => builder.WithStrategy <BasePathStrategy>(ServiceLifetime.Singleton);