Finbuckle / Finbuckle.MultiTenant

Finbuckle.MultiTenant is an open-source multitenancy middleware library for .NET. It enables tenant resolution, per-tenant app behavior, and per-tenant data isolation.
https://www.finbuckle.com/multitenant
Apache License 2.0
1.32k stars 267 forks source link

HttpRemoteStore - configure placement of tenant identifier #232

Closed clinically-au closed 4 years ago

clinically-au commented 4 years ago

Hi Andrew

Thanks for your work (again!) - going from strength to strength.

I am implementing the HttpRemoteStore using an Azure function, and am using function-level security, which requires passing in a secret key. The URL I pass in to .WithHttpRemoteStore() looks something like: https://internalapi.my.app/GetTenantInfo?code=[SECRET KEY]&identifier=

I then strip off the leading / that Finbuckle automatically prepends the tenant identifier with.

It would be great if the URL I pass to .WithHttpRemoteStore() could allow custom placement of the tenant identifier e.g. https://my.app/GetTenantInfo/{tenant}/?code=[SECRET KEY]

I think it would allow a lot more flexibility with HttpRemoteStore.

Thanks for considering it Wojt

PS for anyone interested, the Azure function looks like this:

using System.Threading.Tasks;
using Finbuckle.MultiTenant;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;

namespace MyNamespace.AzureFunctions.Tenants
{
    public class GetTenantInfo
    {
        private readonly IMultiTenantStore _store;

        public GetTenantInfo(IMultiTenantStore store)
        {
            _store = store;
        }

        [FunctionName("GetTenantInfo")]
        public async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "get", Route = null)]
            HttpRequest req, ILogger log)
        {
            var identifier = req.Query["identifier"].ToString();

            if (identifier.StartsWith("/")) identifier = identifier.Remove(0, 1);

            if (string.IsNullOrEmpty(identifier))
            {
                log.LogWarning($"API.GetTenantInfo: Received empty request for tenant identifier.");
                return new NotFoundObjectResult($"API.GetTenantInfo: No tenant identifier received.");
            }

            log.LogInformation($"API.GetTenantInfo: Received request for tenant {identifier}");

            TenantInfo tenantInfo = await _store.TryGetByIdentifierAsync(identifier);

            if (tenantInfo == null)
            {
                log.LogWarning($"API.GetTenantInfo: Received request for non-existent tenant {identifier}.");
                return new NotFoundObjectResult($"API.GetTenantInfo: Tenant '{identifier}' does not exist.");
            }

            return new OkObjectResult(tenantInfo);
        }
    }
}

And Configure() in Startup.cs includes this to read the MultiTenantStore from a json file (NB ensure you publish the json file to the server).

        public override void Configure(IFunctionsHostBuilder builder)
        {
            var executioncontextoptions = builder.Services.BuildServiceProvider()
                .GetService<IOptions<ExecutionContextOptions>>().Value;
            var currentDirectory = executioncontextoptions.AppDirectory;

            var config = new ConfigurationBuilder()
                .SetBasePath(currentDirectory)
                .AddJsonFile("tenants.json", optional: false, reloadOnChange: true)
                .AddEnvironmentVariables()
                .Build();

            builder.Services.AddMultiTenant().WithConfigurationStore(config, "TenantStore");
        }
clinically-au commented 4 years ago

Actually, looking at the source code, i see that the endpoint template can include {__tenant__}

Feel free to close (but it may be useful to add that to the sample).