Multi Tenancy for Authentication

I am trying to use Multi Tenancy.
Form ORMLite there is no problem with it, but I use OrmLiteAuthRepository like below, it is not working.

var dbFact = new OrmLiteConnectionFactory(Cache.conStr, MySqlDialect.Provider);
            container.Register<IDbConnectionFactory>(dbFact);

            container.Register<IUserAuthRepository>(c =>
                new OrmLiteAuthRepository(c.Resolve<IDbConnectionFactory>())
                {
                    UseDistinctRoleTables = true,
                });
            var authRepo = (OrmLiteAuthRepository)container.Resolve<IUserAuthRepository>();

It does not even get DB connection from the overriden method on AppHost

public override IDbConnection GetDbConnection(IRequest req = null)
        {
            return TryResolve<IDbConnectionFactory>().OpenDbConnectionString(Cache.conStr.Replace("Database=db",
                    "Database=db_" + req.Headers["X-Company-Key"]));
        }

You can only specify the namedConnection for OrmLiteAuthRepository at registration, e.g:

container.Register<IAuthRepository>(c =>
  new OrmLiteAuthRepository(c.Resolve<IDbConnectionFactory>(), namedConnection:"AuthDB") {
      UseDistinctRoleTables = true,
  });

It doesn’t have access to the IRequest to be able to support switching to different DB’s at runtime at only supports specifying which named connection to use at registration.

If there is no way to change named connection’s Connection String, so it does not fit my situation.

Or I have to put my authentication information in separate database. but then I won’t be able to use RefId column to join my User or other related table.

So I need to find a way to handle this, thank you for your help.

I think I found it, with the help of this stackoverflow question, it was possible to change connection string with a connection filter like;

dbFact.RegisterConnection("auth",Cache.conStr,MySqlDialect.Provider);
OrmLiteConnectionFactory.NamedConnections["auth"] = new OrmLiteConnectionFactory(
    Cache.conStr, MySqlDialect.Provider, true)
    {
        ConnectionFilter = x => {
            var req = HostContext.AppHost.TryGetCurrentRequest();
            if (req?.Headers["X-Company-Key"] == null || 
                req?.Headers["X-Company-Key"] == "null")
                return base.GetDbConnection(eq.req());
            else
                return TryResolve<IDbConnectionFactory>().OpenDbConnectionString(
                    Cache.conStr.Replace("Database=db",
                    "Database=db_" + req.Headers["X-Esneq-Company-Key"]));
            }
    };
container.Register<IUserAuthRepository>(c =>
    new OrmLiteAuthRepository(c.Resolve<IDbConnectionFactory>(),"auth") {
        UseDistinctRoleTables = true,
    });

And It worked, thank you again

Hi I’ve just completed a major refactor so we can implement a Multitenancy OrmLite AuthProvider properly without relying on the Request Context Singleton (which only works in ASP.NET). Now all access to IAuthRepository goes through HostContext.AppHost.GetAuthRepository(IRequest) which similar to the other dependencies can be overridden.

One of the things we need to be mindful of is when OrmLite AuthRepository gets called on startup to create the schema, it doesn’t have a IRequest context and in this case needs to go through and create the schema for all Multitenancy db’s.

Whilst when it’s called at runtime it does have access to the IRequest and can just use the current db connection.

So to handle this I’ve created a new OrmLiteAuthRepositoryMultitenancy class with 2 constructors, with the default registration getting called on startup which should be configured with all the connection strings of all your Multitenancy DB’s that you want it to create scheams for, e.g:

var connStrings = new [] {
     "company1ConnectionString",
     "company2ConnectionString",
    //etc
}

container.Register<IAuthRepository>(c =>
  new OrmLiteAuthRepositoryMultitenancy(c.Resolve<IDbConnectionFactory>(), connStrings)
  {
      UseDistinctRoleTables = true,
  });

You can just pass in an empty array if you’ve already created schema’s for all db’s and don’t want to check/create them.

Then to handle the normal use-case of getting called at runtime, you can get it to use the current Multitenancy DB connection at runtime by overriding GetAuthRepository() in your AppHost, e.g:

public override IAuthRepository GetAuthRepository(IRequest req = null)
{
    return req != null
        ? new OrmLiteAuthRepositoryMultitenancy(GetDbConnection(req))
        : TryResolve<IAuthRepository>();
}

So now when GetAuthRepository() is called within the context of a request it uses the same Multitenancy DB as your other services, otherwise when its called outside, it uses the default DB registration (which can only be called to initialize the schemas).

This change is now available from v4.0.61 that’s now on MyGet. I would appreciate it if you can look at using this new support support for Multitenancy DB AuthRepository and let me know if it does what you need, thanks.

3 Likes

Hi mythz, I am in different project for 2 days. I will try this new feature on saturday, and inform you.

Thank you, for your help in advance.

1 Like

I could not successfully get the new overridable method GetAuthRepository for the new version (4.0.61)

I cleared the cache, manually delete the 4.0.61 version nuget packages in folder, downgraded to 4.0.60 and get the new myget 4.0.61 but nothing worked.

Am I missing something? Can you check again if it is available in 4.0.61 the GetAuthRepository in AppHost?

I am getting error “no suitable method found to override” for the code below:

public class AppHost : AppHostBase
    {
        public static string HDR_COMPANY_KEY = "X-Esneq-Company-Key";
        /// <summary>
        /// Default constructor.
        /// Base constructor requires a name and assembly to locate web service classes. 
        /// </summary>
        public AppHost()
            : base("EsneqSS", typeof(ObjectServices).Assembly)
        {

        }
        public override IAuthRepository GetAuthRepository(IRequest req = null)
        {
            return req != null
                ? new OrmLiteAuthRepositoryMultitenancy(GetDbConnection(req))
                : TryResolve<IAuthRepository>();
        }

The v4.0.61 pre-release packages are only on MyGet, you need to add ServiceStack’s MyGet packages feed in order to be able to download ServiceStack’s pre-release packages on MyGet.

I am already using MyGet to get latest version.

If you had a previous v4.0.61 of MyGet installed you will need to clear your NuGet packages cache.