views:

49

answers:

4

I'm trying to secure an application so that users can only see objects which are assigned to them. I've got a custom QuerySet which works for this, but I'm trying to find a way to force the use of this additional functionality. Here is my Model:

class Inquiry(models.Model):   
    ts = models.DateTimeField(auto_now_add=True)
    assigned_to_user = models.ForeignKey(User,
            blank=True,
            null=True,
            related_name="assigned_inquiries")
    objects = CustomQuerySetManager()
    class QuerySet(QuerySet):
        def for_user(self, user):       
            return self.filter(assigned_to_user=user)

(The CustomQuerySetManager is documented over here, if it is important.)

I'm trying to force everything to use this filtering, so that other methods will raise an exception. For example:

Inquiry.objects.all() ## Should raise an exception.
Inquiry.objects.filter(pk=69) ## Should raise an exception.
Inquiry.objects.for_user(request.user).filter(pk=69) ## Should work.
inqs = Inquiry.objects.for_user(request.user) ## Should work.
inqs.filter(pk=69) ## Should work.

It seems to me that there should be a way to force the security of these objects by allowing only certain users to access them.

I am not concerned with how this might impact the admin interface.

A: 

AFAIK, Django is not really ready to provide security on domain objects / DAO level.

I'm not aware of any method for "force all queries to be filteret through this condition". You can certainly overwrite all QuerySet methods to ensure this, but it's kinda hacky and tedious.

Common way to do it is to secure code in views. This is hacky for someone used to do AOP, but it kinda works.

Almad
A: 

I'm assuming the reason you're doing this is because you want to make sure that you or another developer never forget to filter X model by the user.

I think the proper way to solve this isn't necessarily to make it throw an exception but to make user of Django's testing framework and to write tests to make sure that views/whatever don't return other user's data.

T. Stone
A: 

I would suggest isolating all your queryset generation in model methods, which then makes it easier to see bad queries during code reviews and easier to develop tests that check the security. If you need to enforce some kind of checking, then maybe Record Ownership screener middleware would be helpful. Otherwise try an alternative approach like django-authority.

(From questions How to enforce account based separation in Django and How would one organize this...)

Van Gale
A: 

Everything in a manager runs through the function get_query_set(). Can't you use that as a chokepoint and check for the presence of some argument that must be set in all cases? So if you don't have a viewing_user keyword argument, you throw an exception. Something like:

def get_query_set(self):
    if not self.viewing_user:
        raise MyCustomException
    return super(CustomQuerySetManager, self).get_query_set()
Tom