views:

137

answers:

5

I have a Django app which has a single-account model. We are converting this to be multi-account, so almost every model will have a ForeignKey(Account).

What is the easiest way to make sure that each Account (each account is in its own subdomain) can only access its own data? We have a middleware that populates the subdomain, and the current account on each request.

We can do this the hard way, by adding a filter(...., account = request.account) in all of our views. This is not desirable as,

  1. filter(...., account = request.account) will be added to all of the queries, making this non-dry, repetative and error-prone.
  2. Bigger risk is if there is a missing filter, anywhere it is security risk.
A: 

This snippet might put you in the right direction. I believe row level permissions are on the todo list for 1.2 also, not 100% sure on that one though.

Baresi
A: 

Is there some huge reason why you can't just write a function that automatically inserts the session account into the query and takes all your other parameters as arguments?

Jasconius
How would you get `request.session` in the models or manager without explicitly passing it with each query. If I am going that way, I might just `filter` on each call.
uswaretech
A: 

Are you using django.contrib.auth?

If you are, just make Account a ForeignKey(User, unique=true) and point all your models at User

ie. ForeignKey(User)

Also, Take a look at the django Auth Docs

edit: I think I understand your concern a little better now...

Instead of doing

my_model.objects.filter(user=request.user)

just do:

request.user.my_model_set.all()
Jiaaro
Yes, but that would still mean, I need to put an explicit `filter` everywhere.
uswaretech
but you can just start from the user object: `user.object1_set.all()` instead of `object1.objects.filter(user=user)`
Jiaaro
+1  A: 

I don't think there is any clear winner, especially if you consider that not all queries will need to be filtered by account. Also consider the old threadlocals trick is considered unreliable which means the only way to do automatic insertion of filter parameters would be with middleware I guess... but that also seems unreliable and complex to me.

I also haven't come up with a good way to make a query manager that can help here, but it might be possible.

So, I think the best solution for a "multi-tenant" database is just to make sure all your queries are filtered by account. You can do this with:

  • Debug mode middleware such as Middleware: Record ownership screener

  • In your tests check the sql generated by any tests and verify the account field is in the query. You could also include "other account" data in your test fixtures that your test would make sure do not show up in any query results.

  • Making sure all queries are checked for the filter during code review

Certainly not pretty, but the best I've been able to do so far.

Van Gale
A: 

Wow, I'm having the exact same problem. Here's the best answer I got:

http://stackoverflow.com/questions/2134549/django-how-would-one-organize-this-big-model-manager-design-mess#answer-2135929

orokusaki