1

I'm running a Django shop where we serve each our clients an object graph which is completely separate from the graphs of all the other clients. The data is moderately sensitive, so I don't want any of it to leak from one client to another, nor for one client to delete or alter another client's data.

I would like to structure my code such that I by default write code which adheres to the security requirements (No hard guarantees necessary), but lets me override them when I know I need to.

My main fear is that in a Twig.objects.get(...), I forget to add client=request.client, and likewise for Leaf.objects.get where I have to check that twig__client=request.client. This quickly becomes error-prone and complicated.

What are some good ways to get around my own forgetfulness? How do I make this a thing I don't have to think about?

Charles
  • 50,943
  • 13
  • 104
  • 142
Jonas Kölker
  • 7,680
  • 3
  • 44
  • 51

2 Answers2

0

One candidate solution I have in mind is this:

  • Set the default object manager as DANGER = models.Manager() on my abstract base class(es).
  • Have a method ok(request) on said base classes which applies .filter(leaf__twig__branch__trunk__root__client=request.client) as applicable.
  • use MyModel.ok(request) instead of MyModel.objects wherever feasible.

Can this be improved upon? One not so nice issue is when a view calls a model method, e.g. branch.get_twigs_with_fruit, I now have to either pass a request for it to run through ok or I have to invoke DANGER. I like neither :-\

Is there some way of getting access to the current request? I think that might mitigate the situation...

Jonas Kölker
  • 7,680
  • 3
  • 44
  • 51
0

Ill explain a different problem I had however I think the solution might be something to look into.

Once I was working on a project to visualize data where I needed to have a really big table which will store all the data for all visualizations. That turned out to be a big problem because I would have to do things like Model.objects.filter(visualization=5) which was just not very elegant and not efficient.

To make things simpler and more efficient I ended up creating dynamic models on the fly. Essentially I would create a separate table in the db on the fly and then store a data only for that one visualization in that. My code is something like:

def get_model_class(table_name):
    class ModelBase(ModelBase):
        def __new__(cls, name, bases, attrs):
            name = '{}_{}'.format(name, table_name)
            return super(ModelBase, cls).__new__(cls, name, bases, attrs)


    class Data(models.Model):
        # fields here
        __metaclass__ = ModelBase
        class Meta(object):
            db_table = table_name

    return Data

dynamic_model = get_model_class('foo')

This was useful for my purposes because it allowed queries to be much faster but getting back to your issue I think something like this can be useful because this will make sure that each client's data is separate not only via a foreign key, but is actually separated in the db.

Using this method is pretty straight forward except before using the model, you have to call the function to get it for each client. To make things more efficient you can cache/memoize the results of the function call so that it does not have to recompute the same thing more than once.

miki725
  • 27,207
  • 17
  • 105
  • 121
  • Just checking: what you're suggesting is to have one `twig` table for each client, and likewise for branch, trunk, etc., then select which table to go to dynamically via your above code? Did I hear you correctly? – Jonas Kölker Feb 17 '13 at 05:14
  • Yes. Have a different table for each client. Not sure if it's appropriate in your situation but just a though. – miki725 Feb 17 '13 at 05:17