Base solution for your next web application
Open Closed

Multi-tenancy in Anonymous mode shows data for wrong tenant #1906


User avatar
0
leop created

I'm seeing an issue where data for Tenant_A is shown for Tenant_B in the case where user is Anonymous. It appears that either incorrect TenantId is being read from TenantCache for Anonymous users, or some other issue is present - or maybe I'm incorrectly surfacing the TenantId in my Session object. Incorrect TenantId is present in AbpSession object in DbPerTenantConnnectionStringResolver - GetCurrentTenantId() method.

This behavior does not happen for users that are signed in.

A little about my setup... I set up tenant detection by hostname using SaasKit (<a class="postlink" href="https://github.com/saaskit/saaskit">https://github.com/saaskit/saaskit</a>), which works quite well.

SaasKit plugs into ConfigureServices in Startup.cs where it resolves the tenant using hostname via TenantManager - at the point I create the Tenant object (load it using TenantManager) and makes it available via IoC.

public IServiceProvider ConfigureServices(IServiceCollection services)
        {
            services.AddMultitenancy<Tenant, TenantResolverService>();

            //MVC
            services.AddMvc(options =>
            {
                options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
            });
            ...
            options.IocManager.Register<IAbpSession, MtfSession>(DependencyLifeStyle.Transient);

TenantResolverService:

public class TenantResolverService : ITenantResolver<Tenant>
    {
        private readonly TenantManager _tenantManager;
        public TenantResolverService(TenantManager tenantManager)
        {
            _tenantManager = tenantManager;
        }

        public Task<TenantContext<Tenant>> ResolveAsync(HttpContext context)
        {
            TenantContext<Tenant> tenantContext = null;

            var hostname = context.Request.Host.Value;
            var hostnameParts = hostname.Split('.');

            var tenant = _tenantManager.FindByHostnameAsync(hostname).Result;

            if (tenant == null && hostnameParts.Length > 0)
            {
                var subdomain = hostnameParts[0];
                tenant = _tenantManager.FindByTenancyNameAsync(subdomain).Result;
            }

            if (tenant != null)
            {
                tenantContext = new TenantContext<Tenant>(tenant);
            }
            return Task.FromResult(tenantContext);
        }
    }

I extend the ClaimsAbpSession class (IAbpSession) to return TenantId from the Tenant object created above.

public class MtfSession : ClaimsAbpSession
    {
        private readonly Tenant _tenant;

        public override int? TenantId
        {
            get
            {
                if (base.PrincipalAccessor.Principal.Identity.IsAuthenticated)
                {
                    return base.TenantId;
                }

                if (_tenant != null)
                {
                    // Special case TenantID 1, this is the Host tenant
                    return _tenant.Id == MultiTenancyConsts.DefaultTenantId ? new int?() : _tenant.Id;
                }
                
                return base.TenantId;   
            }
        }

        public MtfSession(IMultiTenancyConfig multiTenancy, Tenant tenant) : base(multiTenancy)
        {
            _tenant = tenant;
        }
    }

If I inject my session object directly into a View, it always shows correct Tenant info - however, when session object reads TenantId from DbPerTenantConnnectionStringResolver.cs, it shows incorrect TenantId.

Any help with this is really appreciated!!


2 Answer(s)
  • User Avatar
    0
    hikalkan created
    Support Team

    Hi,

    There are some problems with your implementation. ClaimsAbpSession is singleton, so MtfSession is singleton inherently. So, the Tenant object will be shared from all requests. That's why it's mixed (you are calling base.TenantId if user is authenticated, thus no problem occurs). You could set Tenant object to HttpContext.Items, so you could get it in the MtfSession.

    Anyway, these are all not needed actually. AspNet Zero solution has already infrastructure does the same stuff: TenantIdAccessor and SubdomainTenancyNameFinder. You could do it with changing this classes. Why do you use another library which may conflict.

  • User Avatar
    0
    hikalkan created
    Support Team

    Sorry, you are not using aspnet zero. but the first method I explained will work for you. Have a nice day.