-
-
Notifications
You must be signed in to change notification settings - Fork 445
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add shared users between tenants #57
Comments
This could be solved by using the DB storage driver, storing users in a table on the central database and making the session Auth guard use that DB connection. Though relationships could be problematic. |
Yes please. Once that is in place, then a user impersonation can by done. |
If i'm OK without having relations between users in the central database and the tenant databases, but I want relations between the users table and the tenants/domains tables, whats your suggested implementation? user-tenant or user-domain ? Is a user the same as a tenant? Can you give a basic example? |
Like what? What do you wish to use that relation for? |
For example in a hotel management saas where a user can have one or more hotels. |
I've been thinking about this feature for some time and with multi-database tenancy, the best thing I could think of was having like a sync service between tenants. You'd have the central database that holds the global information about the user:
And then tenant databases could have a more complex schema:
When you'd update this model in any database, the keys held in the central database would get synced to all databases. This comes with a lot of complexity and is basically at the bottom of my priority list when it comes to this package's features. I'm not even sure if it's in scope of the package, or whether it should be a separate package/up to the user to implement. The alternative to this approach is cross-DB queries, which aren't a first-class feature in Laravel, and are problematic if you use multiple hosts for your DBs (this is a common motivation for the multi-database model of tenancy). I think the 2.x codebase might be flexible enough to let me add support for single-database tenancy, which makes shared users a non-problem. |
If "each hotel is a tenant" (makes sense), then you would probably be thinking that they need only 1 domain, ie: But if you have all your admin users of that tenant "inside" a database within the tenant's tables, and not in the "central" (onboarding/support/admin) |
@drbyte I see, so domains are for flexibility in case its needed. @stancl I was thinking about a simpler solution (for my use case). All users are in the central table (admins and tenant specific users) and they authenticate against the central database. So I think my question is how to give permission to a specific user to a tenant. My thinking is, a regular user logs in in the central domain (saas.com) and is redirected to tenant (tenant1.saas.com). An admin user also logs in in the central domain, but he is presented with a dashboard and all the tenants he admins, then he can also go to tenant1.saas.com if he wants. Also possible to show the login form directly in the tenant domain but always authenticate against the central users table. |
Ok so I found a solution that works for my specific use case.
class User extends Authenticatable
{
protected $connection = 'mysql';
}
// config/session.php
'domain' => env('SESSION_DOMAIN', null), // .env
SESSION_DOMAIN=.mysite.test // notice the "." before the domain Now !!!: Of course we now must check if the user have permissions to access the tenant! This is very useful if users have access to multiple tenants as they can switch tenants without the need to login again. |
I think this is a good case only when you are using path identification because you do not want to share sessions between subdomains. Your users should access the system from:
then they will be able to pick the tenant they want to access to in case they have access to more than one and get redirected to:
If you are using domain identification you should have access to the tenant database only. Then you access the tenant app from:
I think this feature (shared users between tenants) SHOULD NOT be added to this package though. It's a business decision you have to handle from your app. That's why opened a ticket to Support Path Identification. See: #173 |
@rrpadilla Path identification might be a nice addition, but path identification and authentication are two separate things. Each application should be able to choose how implement each, and not be forced to use a specific authentication method because of the identification method. Also, If you use path identification then shared sessions are irrelevant because all tenants are on the same domain, so they automatically share sessions. For my application I do want to share sessions between subdomains, so I can have a single Other applications might want only admins to login to the central database and have separate users tables for each domain. Again, it depends on the requirements of each SaaS. |
Right, using the central connection on your User model solves a part of this problem, but if you need to use Eloquent relationships or make a JOIN query with some tables from within the tenant database, it won't work. Depends on how much functionality you need with the shared users. |
@stancl I can maybe add to this thread that we have run a SaaS Platform for about 6 years now with multi db setup. I would say that im quite happy with the
I would not live without it. Just my two cent on this topic. |
@viezel Can you elaborate on that setup? Sounds interesting. |
Sure. So our master dB contains a user table and an tenants_users table with the relationship. A user can belong to multiple tenants. Now each user has a unique global id. This means stuff like SSO in multiple tenants is possible due to this global user id. Anything specific you want me to elaborate on? |
To show a simple example of a user that is part of 2 tenants.
|
@viezel is there a specific reason you need the users table in each tenant? In my case I have all users in Central and I authenticate using the central connection and for everything else I use the Tenant connection. The downside is that you can't have a user foregin key in tenant that points to central but you already have |
It would definitely be better to store the users in the central database, but that would not let you have relationships to tables in the tenant databases (technically you could do that, since most RDBMS support cross-database queries, but it's quite hacky, doesn't let you use foreign keys, and isn't natively supported by Eloquent). So even though it takes much more space, syncing the users between databases is the only way to have relationships in a nice way. The uuid seems like a great solution. Time-based, to prevent race conditions (hopefully that's enough and multiple requests coming in the same second won't be an issue, I'll have to check that), and optionally stored as binary for faster indexing and space usage. And then an optional |
Yes you loose the users relationships with a single users table, so I guess it will depend on each usecase/preference, I think both are valid options. Regarding UUIDs, the collision probability is very very low. Even if the requests come in the same microsecond the UUIDs will be different as there is additional randomness added and also an internal counter implemented for extreme cases. |
To authenticate users against the central database, do you just make the auth guard use that DB? Would be nice if we supported both approaches in 2.3. Both synced users & auth against central DB.
Sounds great. I'll look into using uuids and hopefully I can push a branch with this functionality in the next few days. Thanks for the feedback, it's really appreciated. |
I still don’t understand what the issue with the same user I master db and tenant db. |
I know this might sound silly but i can't wrap my mind around why we need to use uuid or two ids one global and one local. The way I see this, is that everyone creating a user whether through a tenant or at the central level they must create the user first at the central db users table and then the created user will be synced with a queue job (on save) down to the Tenant table and the same user id from the central table will be synced as the id on the tenant users table. There is no way two users can have same id as they all are going through the central db(one table which has auto increment on) and we are using that same id on the tenants user table. Central DB: Tenant A Tenant B |
@zeidanbm you wrote The concept is two way based. |
This is why I asked you about getting the global id. You said:
which implies that you always create users in the central database first. |
oh that was not the intention. Im just quite focused on decoupling tenant and central, so they can function individually. |
Then to have (some) shared users, using uuids seems like the only option. |
@viezel Do you use a |
@stancl yes i do. |
But it’s queued since the tenant could import 1000 users. Then it’s handy that those extra central db queries happens async |
This feature comes with a ton of complexity. Hopefully I can make an official solution but I'm starting to feel like there are so many ways to implement this that I should let users do this. For example:
I'll focus on some smaller tasks now (e.g. Nova tenant CRUD, Passport integration) and do this after that. Need to think about all these edge cases. |
@stancl totally agree. |
btw, this implementation #57 (comment) will do just fine. People can choose to use that trait or built their own. |
I don't think there's anything that needs to be changed in the package to let you implement this yourself. The implementation in that comment had some issues that I've resolved, but I still need to take care of many edge cases before opening a pull request. |
Thanks for such a great package! I was reading through the redirect docs and will be using a redirect to redirect the customer to their domain after signup. Is there a preferred way to add their account info to the tenant database? Should I add a custom handler for the I'm considering storing tenant admins in a central |
Hi @neovive. I think you're looking for this: #194 (comment) You create the "user" as a record in the |
Has anyone considered using/ has successfully used Laravel Passport on the central app to solve this issue? Just wondering if it would be possible before I go down the rabbit hole. TIA |
@metadeck I would not do that if I were you. Remember that tenant id is in the core of what you do. |
@viezel Thanks for your input across all of this thread. I think we'll go with the global user id and sync approach. |
Implemented in v3. |
I see how that works. How did you solve RBAC @jonagoldman ? |
@cyrillkalita I have the roles and permissions tables also in the central database, together with the users and tenants. I use an additional 'tenant_id' foreign key in the roles table so a user can have different roles for different tenants. It can be tricky but it works for me. |
How would I go about using this in v3? I am currently developing a sass application with v3 and this is exactly what I am needing. I can't find any documentation on how to use shared users between master and tenants. Thanks in advanced. |
@peter-brennan imagine a standard many-to-many: what would you do? Attach one model to another? Ok. So when you make a central user, it is attached to a tenant. And when you invite/ register the user with the same email in another tenant, find that central model and attach it to a new tenant. When you need to delete a user, after you are done on a tenant level, detach central user from your tenant. You can also verify if the central use doesn't have any other tenants attached - and remove it from central users table. Does that make sense? |
Yes perfect sense thank you @cyrillkalita. If I run into any troubles I will be sure to shoot you guys a line. Thank you. |
* parallelize migration-related commands * Fix code style (php-cs-fixer) --------- Co-authored-by: PHP CS Fixer <phpcsfixer@example.com>
Like Stripe administration.
The text was updated successfully, but these errors were encountered: