Corvia / django-tenant-users

Adds global user authentication and tenant-specific permissions to django-tenants.
https://django-tenant-users.rtfd.io
MIT License
343 stars 65 forks source link

Struggling with order of operations between django-tenants and django-tenant-users #690

Open andyl-astardigital opened 5 days ago

andyl-astardigital commented 5 days ago

Hi folks,

I'm hoping to get some guidance on how to get tenants and users running correctly.

What I've done:

Issues I keep bumping up against:

I am mega keen to use this setup as it promises to be everything I need in my SaaS application but I've spent days chasing my tail and second guessing myself.

Could someone do me a massive favour and spell out how they got this stuff setup, please?

  1. How complete does the setup of django-tenants have to be before setting up django-tenant-users?
  2. At what point should I be able to succesfully create the public schema?
  3. Does the owner of a new tenant have to exist already? (I've massively confused myself on this as I've sen the library both complain about the user not existing and already existing!)

If someone spells the config and steps out I will write the readme and PR it in as it looks like lots of others are also struggling with the current getting started info.

Thanks in advance,

Andy

Dresdn commented 5 days ago

Hi Andy,

1. How complete does the setup of django-tenants have to be before setting up django-tenant-users?

Everything until running the migrate command. Don't makemigrations or migrate until DTU setup is complete.

2. At what point should I be able to succesfully create the public schema?

At the order called out in the docs.

3. Does the owner of a new tenant have to exist already? (I've massively confused myself on this as I've sen the library both complain about the user not existing and already existing!)

Yes. Looks like there's a typo there. Go off the code sample, not the docs :D You can also see the provision_tenant() function for more info.

Extra Thoughts

What I think is happening is you're setting up django-tenants, creating the tables, and then going through the django-tenant-users setup. This will definitely cause some problems, and there have been some other reports of circular dependencies. There definitely will be some nuance to implementing this into an existing project, but starting fresh, you shouldn't have any issues.

User Model

Since you are starting fresh, one thing I would recommend doing is not using tenant_users.tenants.models.UserProfile as it's pretty opinionated on how the user model is structured. I've had a PR I've been meaning to clean up and commit to give a better base model, but for now, I would suggest creating your own, inherit from AbstractBaseUser, PermissionsMixinFacade, and use the default manager and tenants relationship.

Hope this helps. If not, let me know!

andyl-astardigital commented 4 days ago

Brilliant! Thank you for coming back to me so quickly.

The steps I followed:

  1. Run through django-tenants setup but DO NOT run makemigrations or migrate
  2. Run through django-tenant-users setup but DO NOT migrate yet
  3. Instead of the dtu UserProfile create a custom user model and use the PermissionsMixinFacade, add in the manager from dtu and setup the relation to tenants, User must have is_active and email fields
  4. Run makemigrations (make sure there is an existing migrations folder with init.py)
  5. Run migrate
  6. use the dtu create_public_tenant which also creates the user (note the password, is_super_user, is_staff and the tenant_extra_data fields)
    from tenant_users.tenants.utils import create_public_tenant
    create_public_tenant(domain_url="localhost", owner_email="owner@evil.com", is_superuser=True, is_staff=True, 
    tenant_extra_data={"access_end":"2099-1-1"}, password="Evil!!!")

    NOTE FOR OTHERS: create_public_tenant does create a new user unlike provision_tenant NOTE FOR OTHERS: if there are issues further down the stack in this call it is not atomic, for instance I had some missing bits for my broader auth setup (using all_auth) that when failed left the user created, tenant not created, etc

I'm now able to get things up and running including the public admin site access.

I do have one more issue though: I can't log in to the admin sites of the subdomain tenants. I have their admin site defined as a new url under the tenant apps, I can get to the login screen but it never lets me log in (saying the details are wrong). I added this new tenant through the public admin site and made the owner the same as the one above.

What I'm expecting is to be able to add a tenant via the public admin site then be able to log in to the tenant admin via the tenant_admin url. Is there anything special I'm missing or misunderstanding? I'd rather not command dive in Prod if I don't have to.

Big thanks, again, for getting me un-stuck!

Dresdn commented 3 days ago

NOTE FOR OTHERS: create_public_tenant does create a new user unlike provision_tenant NOTE FOR OTHERS: if there are issues further down the stack in this call it is not atomic, for instance I had some missing bits for my broader auth setup (using all_auth) that when failed left the user created, tenant not created, etc

Sounds like great PRs ;)

I do have one more issue though: I can't log in to the admin sites of the subdomain tenants. I have their admin site defined as a new url under the tenant apps, I can get to the login screen but it never lets me log in (saying the details are wrong). I added this new tenant through the public admin site and made the owner the same as the one above.

What I'm expecting is to be able to add a tenant via the public admin site then be able to log in to the tenant admin via the tenant_admin url. Is there anything special I'm missing or misunderstanding? I'd rather not command dive in Prod if I don't have to.

Are you setting the permissions? Keep in the mind the whole point of this app is to have tenant-specific permissions with a global User account. When creating a tenant with provision_tenant(), there are param options for setting is_staff and is_superuser. If you create a tenant, and want to add the user to that tenant using tenant_instance.add_user(my_user), there are also params there to set the default permissions that get created in that tenant's specific UserTenantPermissions object.

Big thanks, again, for getting me un-stuck!

Sure thing!