2

I'm setting up a Django REST API that involves families, parents, and children. I want to setup permissions so that Admins can send a GET request to the /api/parents/ endpoint and see a list of all Parents, but User's GET requests only return the list of Parent objects that they created, or "own".

I'm building an app with a React.js frontend and a Django REST backend. When a User Signs Up, they are issued a JSON Web Token using the open source simplejwt package that django has. The application involves parents and children in families. When the User tries to create a Parent object, they need to be able to retrieve the unique hyperlinked URL to their personally created Parent object in order to create a linked Child object for their children and associate it only to their Parent object.

Right now, when I make a POST request to the endpoint for the Parent router, Anyone can create a Parent object as long as they are logged in. Only GET requests from admins succeed(with the attached JWT as a Bearer in Authorization in the request). Admin GET requests succeed with a list of all Parent objects in the database. If the User who created the Parent object sends a GET request with their personal JWT, it returns an empty list.

I want to implement permissions so that admins can GET, POST, PUT, and DELETE. Other than that, only the User that authenticates with their JWT and creates (and "owns") and object should be able to view it.

For example, if I create an account as the user: newuser01 and I create 3 parent objects, my GET request to the endpoint for /api/parents/ should return JSON of the 3 Parent objects that I created or "own".

If there are 10 total Parent objects in the database, a GET request to the same endpoint from the admin should return all 10.

What I've tried:

These result in the following issues:

  1. Either you must be logged in and provide credential (the JWT) to make any requests to the API, but only admins can POST and create new Parents, and GET a list of Parents back. Normal Users get an issue that they do not have permission to perform the request action for all GET, POST, etc.

  2. The other issue that arises using the last attempted option of creating my own custom permissions is that any logged in User can POST and create new Parents, but only admins can see a list of Parents. A GET from a normal USER with the Bearer JWT returns an empty list.

From settings.py:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django.contrib.sites',
    'api',
    'rest_framework',
    'rest_framework.authtoken',
    'rest_auth',
    'rest_auth.registration',
    'allauth',
    'allauth.account',
    'allauth.socialaccount',
    'allauth.socialaccount.providers.facebook',
    'corsheaders',
    'phonenumber_field',
    'guardian',
]

AUTHENTICATION_BACKENDS = (
    'django.contrib.auth.backends.ModelBackend', # this is default
    'guardian.backends.ObjectPermissionBackend',
)

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.DjangoObjectPermissions',
    ),
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_simplejwt.authentication.JWTAuthentication',
        'rest_framework.authentication.SessionAuthentication',
        #'rest_framework.authentication.TokenAuthentication',
        'rest_framework.authentication.BasicAuthentication',
    ),
}

From models.py:

class Parent(models.Model):

    first_name = models.CharField(max_length=100)
    last_name = models.CharField(max_length=100)

    def __str__(self):
        return "{}".format(self.first_name + ' ' + self.last_name)

From serializers.py:

class ParentSerializer(serializers.HyperlinkedModelSerializer):

    class Meta:
        model = Parent
        fields = ('url', 'first_name', 'last_name')

From views.py:

class ParentViewSet(viewsets.ModelViewSet):

    queryset = Parent.objects.all()
    serializer_class = ParentSerializer

    permission_classes=[CustomObjectsPermissions]

From permissions.py (both options I've tried included):

class CustomObjectPermissions(permissions.DjangoObjectPermissions):

    perms_map = {
        'GET': ['%(app_label)s.view_%(model_name)s'],
        'OPTIONS': ['%(app_label)s.view_%(model_name)s'],
        'HEAD': ['%(app_label)s.view_%(model_name)s'],
        'POST': ['%(app_label)s.add_%(model_name)s'],
        'PUT': ['%(app_label)s.change_%(model_name)s'],
        'PATCH': ['%(app_label)s.change_%(model_name)s'],
        'DELETE': ['%(app_label)s.delete_%(model_name)s'],
    }

class MyUserPermissions(permissions.BasePermission):

    def has_permission(self, request, view):
        return request.user 

    def has_object_permission(self, request, view, obj):
        return obj == request.user

Expectation: New User sends a POST to /api/parents/ and creates a Parent object. Then the User can send a GET to /api/parents/ and see a list of only the Parents they have created. Admins can send a GET to /api/parents/ and see all Parents that any User have created.

Result as of now:

Either, Users aren't permitted to do anything, but admins can do everything.

Or, Users can create a Parent object, but Users only see an empty list when sending a GET request.

Derek Jackson
  • 31
  • 1
  • 2

0 Answers0