Restrict access to only owned content django
Asked Answered
J

2

5

I'm writing an API using django-tastypie. I have two custom permisions issues that I'm hoping django-guardian can fix.

I have two user groups Clinicians and Patients. Clinicians should be able to access the objects belonging to only their Patients and Patients should only be able to access objects created by themselves.

My code is as follows:

class UserResource(ModelResource):
    class Meta:
        queryset = User.objects.all()
        resource_name = 'auth/user'
        excludes = ['email', 'password', 'is_superuser']


class BlogPostResource(ModelResource):
    author = fields.ToOneField(UserResource, 'author', full=True)

    class Meta:
        queryset = BlogPost.objects.all()
        resource_name = 'posts'
        allowed_methods = ["get", "post"]
        # Add it here.
        authentication = BasicAuthentication()
        authorization = DjangoAuthorization()
        filtering = {
            'author': ALL_WITH_RELATIONS,
        }

How can I used permissions to restrict access on this BlogPostResource?

Japanese answered 18/3, 2013 at 13:3 Comment(0)
J
3

I based my final solution off of the answer from @JamesO. The issue with his answer was it was written for an older version of django-tastypie before the Authorization class was rewritten. Here is my code for future reference:

from tastypie.authorization import Authorization
from django.contrib.auth.models import Group
from extendedusers.models import ExtendedUser


class CustomAuthorization(Authorization):
    def read_list(self, object_list, bundle):
        clinician_group = Group.objects.get(name='clinician')
        if bundle.request and hasattr(bundle.request, 'user'):
            if clinician_group in bundle.request.user.groups.all():
                patients = ExtendedUser.objects.filter(clinician_id=bundle.request.user.id)
                object_list = object_list.filter(author__id__in=patients)
            else:
                object_list = object_list.filter(author=bundle.request.user)
            return object_list
        else:
            return object_list.none()
Japanese answered 18/3, 2013 at 16:27 Comment(1)
for long term scalability, have a look at https://mcmap.net/q/2035368/-django-tastypie-record-level-authorizationPollerd
T
4

You could achieve this with a custom Authorization class, for example something like:

class CustomAuthorization(Authorization):
    def apply_limits(self, request, object_list):     
        ...
        clin_group = Group.objects.get(name='YOUR GROUP')
        if request and hasattr(request, 'user'):
            if clin_group in request.user.groups.all(): 
                 object_list = object_list.filter(user__in=request.user.patients.all()) # or however you stop clinician>patient relation
            else:
                 object_list = object_list.filter(user=request.user)
        return object_list 
Transducer answered 18/3, 2013 at 13:13 Comment(3)
Thanks, that is an approach that I had considered. The only thing is (which I forgot to mention in my question) that we've also got a normal django web interface to the content that needs the exact same permissions created and I don't want to fragment access policy code...Japanese
@Japanese - ah OK I see. You could create managers where you set the access control then call the manager in both your core app/admin and your tastypie app? keeping the code in one place.Transducer
managers allow you to user something like BlogPost.objects.patient_posts(clinician_user) see docs.djangoproject.com/en/dev/topics/db/managers/#managers might help!Transducer
J
3

I based my final solution off of the answer from @JamesO. The issue with his answer was it was written for an older version of django-tastypie before the Authorization class was rewritten. Here is my code for future reference:

from tastypie.authorization import Authorization
from django.contrib.auth.models import Group
from extendedusers.models import ExtendedUser


class CustomAuthorization(Authorization):
    def read_list(self, object_list, bundle):
        clinician_group = Group.objects.get(name='clinician')
        if bundle.request and hasattr(bundle.request, 'user'):
            if clinician_group in bundle.request.user.groups.all():
                patients = ExtendedUser.objects.filter(clinician_id=bundle.request.user.id)
                object_list = object_list.filter(author__id__in=patients)
            else:
                object_list = object_list.filter(author=bundle.request.user)
            return object_list
        else:
            return object_list.none()
Japanese answered 18/3, 2013 at 16:27 Comment(1)
for long term scalability, have a look at https://mcmap.net/q/2035368/-django-tastypie-record-level-authorizationPollerd

© 2022 - 2024 — McMap. All rights reserved.