Open Closed

TenantConnectionString not changed on CustomTenantResolver using AccessToken #1445


0
Mohammad created

Hello

public class AccessTokenTenantResolver : HttpTenantResolveContributorBase
    {
        public override string Name => "AccessToken";

        protected override Task<string> GetTenantIdOrNameFromHttpContextOrNullAsync(ITenantResolveContext context, HttpContext httpContext)
        {
           
            string accessCode = httpContext.Request.Headers["Authorization"];

            if (!string.IsNullOrEmpty(accessCode))
            {
                var token = accessCode.Split(" ")[1];
                var tokenHandler = new JwtSecurityTokenHandler();
                var tokens = tokenHandler.ReadJwtToken(token);
                var client_id = tokens.Claims.FirstOrDefault(x => x.Type == "client_id").Value;
                var scopes = tokens.Claims.FirstOrDefault(x => x.Type == "scope").Value;
                
            }

            return Task.FromResult("39fb7b60-0ba6-8109-70b1-a049e5f25575");

            //throw new NotImplementedException();
        }
    }

I have this custom tenant resolver which resolves the tenant based on the client_id from the AccessToken. The Current Tenant Information is populated properly however the connection string of the tenant is not changed based on the connection string set in the database.


20 Answer(s)
  • 0
    maliming created
    Support Team

    hi

    n. The Current Tenant Information is populated properly

    Do you mean the ICurrentTenant is changed? Can you confirm?

    the connection string of the tenant is not changed based on the connection string set in the database.

    Can you explain in details?

  • 0
    Mohammad created
    • Do you mean the ICurrentTenant is changed? Can you confirm? Yes. It shows the proper tenant

    • The connection string of the tenant is not changed based on the connection string set in the database. I am using seperate database per tenant. The connection string is stored in the database for each tenant database. While running the query the connection string is not taken from the database.

    E.g The Identity Server Project Has seperate connection strings for each module

    "ConnectionStrings": {
        "Default": "Host=xxx.xxx.xxx.xxx;Port=5432;Database=Tenant1;Username=postgres;Password=xxxxxx;",
        "AbpIdentityServer": "Host=xxx.xxx.xxx.xxx;Port=5432;Database=Identity;Username=postgres;Password=xxxxxx;",
        "AbpIdentity": "Host=xxx.xxx.xxx.xxx;Port=5432;Database=Identity;Username=postgres;Password=xxxxxx;",
    }
    
    

    and if I set a Default Connection String for the Tenant in the database (AbpTenantConnectionStrings Table) When I Try to login to this tenant the Identity Server throws errors

    Npgsql.PostgresException (0x80004005): 42P01: relation "AbpUsers" does not exist

    For some reason the abp tries to find the AbpUsers table in Tenant1 Database Instead of searching for it in Identity Database.

  • 0
    maliming created
    Support Team

    hi

    Can you copy the code of MultiTenantConnectionStringResolver to debug to see what happend?

    https://github.com/abpframework/abp/blob/dev/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/MultiTenantConnectionStringResolver.cs

  • 0
    Mohammad created

    Hi

    The current tenant id is null in MultiTenantConnectionStringResolver even after the custom tenant resolver has executed also it is null when the api is executing.

    I am on version 4.2.2

  • 0
    maliming created
    Support Team

    HI

    That's why I need you to confirm the ICurrentTenant .

    So your AccessTokenTenantResolver is not working.

    The framework has some build in TenantResolver https://docs.abp.io/en/abp/latest/Multi-Tenancy#determining-the-current-tenant

    CurrentUserTenantResolveContributor: Gets the tenant id from claims of the current user, if the current user has logged in. This should always be the first contributor for the security.

  • 0
    Mohammad created

    I have disabled AccessTokenTenantResolver and trying to access the application it still cannot determine the tenant connection string from the database.

    It only happens when a connection string for a tenant is set in the database.

    Can you please do a remote session and check. I have been facing this issue since i upgraded to 4.2.2

    Thanks

  • 0
    maliming created
    Support Team

    I have disabled AccessTokenTenantResolver and trying to access the application it still cannot determine the tenant connection string from the database. It only happens when a connection string for a tenant is set in the database.

    hi

    Can you check the MultiTenantConnectionStringResolver ? I also check it if we remotely.

  • 0
    Mohammad created

    I have checked MultiTenantConnectionStringResolver.

    I'll share zoom details on email.

  • 0
    maliming created
    Support Team

    hi Mohammad

    I have checked. This is by design.

    Your tenant's default connection string should contain the module tables that support multi-tenancy.

    The tenant's default connection string means default, So it will no longer look for the connection string on the host side.

    If you don't want this behavior, you can customize MultiTenantConnectionStringResolver for some special cases of your.

  • 0
    Mohammad created

    Hi Ma

    Can you provide a sample code to acheive this using MultiTenantConnectionStringResolver ?

    Thanks

  • 0
    maliming created
    Support Team

    hi

    These few lines of code return the tenant's default connection string, you can call return await base.ResolveAsync(connectionStringName); for some situations, which will get the host's connection string.

    https://github.com/abpframework/abp/blob/dev/framework/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/MultiTenantConnectionStringResolver.cs#L76-L79

  • 0
    Mohammad created

    Hi Ma.

    I was debugging the same. There is a very wierd behaviour with the MultiTenantConnectionStringResolver code.

    For 1 request it asks to resolve AbpPermissionManagement, AbpAuditLogging on the 2nd request it Asks to resolve only AbpAuditLogging for the same page request.

    I was trying a workaround. where I set a custom name for the tenant connection string. So when the connectionstring is required by the dbcontext it will try to resolve using the MultiTenantConnectionStringResolver.

    I have two modules which work on multitenany

    1. ProductManagement
    2. Intelligence

    So according the the code behaviour It should try to resolve connection string for ProductManagement and Intelligence. But it only tries to resolve ProductManagement.

    This thread also has the same issues https://support.abp.io/QA/Questions/335/Steps-to-create-Single-Deployment---Multiple-Database-Multi-Tenant-Solution

    If you can connect to me remotely I can show you the debug.

    Thanks

  • 0
    maliming created
    Support Team

    For 1 request it asks to resolve AbpPermissionManagement, AbpAuditLogging on the 2nd request it Asks to resolve only AbpAuditLogging for the same page request.

    I think this is because the Permissions are cached. So we don't need read it from db for 2nd request.

    So according the the code behaviour It should try to resolve connection string for ProductManagement and Intelligence. But it only tries to resolve ProductManagement.

    It will only be triggered when Intelligence-related repositories are used and the _currentTenant.Id not be null.

    Your entity also need IsMultiTenant https://github.com/abpframework/abp/blob/c4e92a3431787b1a6727a615768a7a2906a9ba69/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/EfCoreRepository.cs#L52

  • 0
    Mohammad created

    For 1 request it asks to resolve AbpPermissionManagement, AbpAuditLogging on the 2nd request it Asks to resolve only AbpAuditLogging for the same page request.

    I think this is because the Permissions are cached. So we don't need read it from db for 2nd request.

    So according the the code behaviour It should try to resolve connection string for ProductManagement and Intelligence. But it only tries to resolve ProductManagement.

    It will only be triggered when Intelligence-related repositories are used and the _currentTenant.Id not be null.

    Your entity also need IsMultiTenant

    I am using Single database per tenant.

    1. Should My entities implement IMultitenant?

    https://github.com/abpframework/abp/blob/c4e92a3431787b1a6727a615768a7a2906a9ba69/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/EfCoreRepository.cs#L52

  • 0
    maliming created
    Support Team

    The entity inherits IMultiTenant which means it is multi-tenant, and will try to get the connection string of the tenant.

  • 0
    Mohammad created

    The entity inherits IMultiTenant which means it is multi-tenant, and will try to get the connection string of the tenant.

    In v4.0 It was working without Inheriting IMultiTenant https://github.com/abpframework/abp/blob/rel-4.0/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/EfCoreRepository.cs

    I think its a change that has been made in the new version?

  • 0
    maliming created
    Support Team

    See https://github.com/abpframework/abp/pull/7509

  • 0
    Mohammad created

    So what would you suggest in my case for Seperate database per tenant?

    1. Should I inherit all entities with IMultiTenant? If I do this then I'll have to store the tenantid in each table? Which doesnt serve the purpose of having separate databases per tenant.
  • 0
    maliming created
    Support Team

    You should implement the IMultiTenant interface for your entities to make them multi-tenancy ready.

    https://docs.abp.io/en/abp/latest/Multi-Tenancy

  • 0
    Mohammad created

    Thanks