djangodjango-rest-framework

Multi tenancy for consumer facing application


I'm building a consumer-facing application using Django and DRF that will have user accounts. I'd like to integrate some multi-tenancy into it so that all data is separated by tenant(user). I know that I could simply filter by user in queryset but I feel that this approach is a bit more secure. I am also aware of the django-tenant and django-tenant-schema pkgs but I'd like to try and avoid outside pkgs if possible.

Basically where there is a main tenants-table all other tables have a foreign-key pointing to that table. Does that sound right?

So basically something like this:

class Tenant(models.Model):
    tenant_id = f'{...}'
    ....


from tenant.models import Tenant
class User(AbstractBaseUser, PermissionsMixin):
    user_id = f'{....}'
    tenant_id = models.ForeignKey(Tenant, ...)
    ...



from tenant.models import Tenant
class Profile(models.Model):
    profile_id = f'{...}'
    user_id = f'{...}'
    tenant_id = models.ForeignKey(Tenant, ...)
    ...

Solution

  • That is indeed one way to do multi-tenancy. django-tenants has a concise summary of how one might approach multi-tenancy:

    1 - Isolated Approach: Separate Databases. Each tenant has its own database.
    2 - Semi Isolated Approach: Shared Database, Separate Schemas. One database for all tenants, but one schema per tenant.
    3 - Shared Approach: Shared Database, Shared Schema. All tenants share the same database and schema. There is a main tenant-table, where all other tables have a foreign key pointing to.

    #3 is the one you are talking about.

    However, you said this:

    I'd like to integrate some multi-tenancy into it so that all data is separated by tenant(user). I know that I could simply filter by user in queryset but I feel that this approach is a bit more secure.

    If you are saying that a tenant is equivalent to a single user, then your conclusion is wrong. The foreignkey pointing to User and the foreignkey pointing to Tenant serve the exact same purpose, and filtering data on tenant is not (even a little bit) more secure than filtering on user - they are exactly identical in form, function and security.

    There is no difference at all, security-wise, between the following two calls:

    customers = Customer.objects.filter(user=user)
    
    customers = Customer.objects.filter(tenant=user.tenant)
    

    If more than a single user can belong to the same tenant, then you might need a Tenant table. But for the sake of clarity: that's for function, not for security.

    If leaking data between tenants is your concern, extending the foreignkey chain isn't going to have any impact (except make your application harder to maintain, which in turn can make it more difficult for you to make sure everything works as it should).