Tuesday, 7 December 2021

Patch request not patching - 403 returned - django rest framework

I'm trying to test an API endpoint with a patch request to ensure it works.

I'm using APILiveServerTestCase but can't seem to get the permissions required to patch the item. I created one user (adminuser) who is a superadmin with access to everything and all permissions.

My test case looks like this:

class FutureVehicleURLTest(APILiveServerTestCase):
    def setUp(self):
        # Setup users and some vehicle data we can query against
        management.call_command("create_users_and_vehicle_data", verbosity=0)
        self.user = UserFactory()
        self.admin_user = User.objects.get(username="adminuser")
        self.future_vehicle = f.FutureVehicleFactory(
            user=self.user,
            last_updated_by=self.user,
        )
        self.vehicle = f.VehicleFactory(
            user=self.user,
            created_by=self.user,
            modified_by=self.user,
        )
        self.url = reverse("FutureVehicles-list")
        self.full_url = self.live_server_url + self.url
        time = str(datetime.now())
        self.form_data = {
            "signature": "TT",
            "purchasing": True,
            "confirmed_at": time,
        }

I've tried this test a number of different ways - all giving the same result (403).

I have setup the python debugger in the test, and I have tried actually going to http://localhost:xxxxx/admin/ in the browser and logging in manually with any user but the page just refreshes when I click to login and I never get 'logged in' to see the admin. I'm not sure if that's because it doesn't completely work from within a debugger like that or not.

My test looks like this (using the Requests library):

    def test_patch_request_updates_object(self):
        data_dict = {
            "signature": "TT",
            "purchasing": "true",
            "confirmed_at": datetime.now().strftime("%m/%d/%Y, %H:%M:%S"),
        }
        url = self.full_url + str(self.future_vehicle.id) + "/"
        client = requests.Session()
        client.auth = HTTPBasicAuth(self.admin_user.username, "test")
        client.headers.update({"x-test": "true"})
        response = client.get(self.live_server_url + "/admin/")
        csrftoken = response.cookies["csrftoken"]
        # interact with the api
        response = client.patch(
            url,
            data=json.dumps(data_dict),
            cookies=response.cookies,
            headers={
                "X-Requested-With": "XMLHttpRequest",
                "X-CSRFTOKEN": csrftoken,
            },
        )
        # RESPONSE GIVES 403 PERMISSION DENIED
        fte_future_vehicle = FutureVehicle.objects.filter(
            id=self.future_vehicle.id
        ).first()
        # THIS ERRORS WITH '' not equal to 'TT'
        self.assertEqual(fte_future_vehicle.signature, "TT")

I have tried it very similarly to the documentation using APIRequestFactory and forcing authentication:

    def test_patch_request_updates_object(self):
        data_dict = {
            "signature": "TT",
            "purchasing": "true",
            "confirmed_at": datetime.now().strftime("%m/%d/%Y, %H:%M:%S"),
        }
        url = self.full_url + str(self.future_vehicle.id) + "/"
        api_req_factory = APIRequestFactory()
        view = FutureVehicleViewSet.as_view({"patch": "partial_update"})
        api_request = api_req_factory.patch(
            url, json.dumps(data_dict), content_type="application/json"
        )
        force_authenticate(api_request, self.admin_user)
        response = view(api_request, pk=self.future_assignment.id)
        fte_future_assignment = FutureVehicle.objects.filter(
            id=self.future_assignment.id
        ).first()
        self.assertEqual(fte_future_assignment.signature, "TT")

If I enter the debugger to look at the responses, it's always a 403.

The viewset itself is very simple:

class FutureVehicleViewSet(ModelViewSet):
    serializer_class = FutureVehicleSerializer

    def get_queryset(self):
        queryset = FutureVehicle.exclude_denied.all()
        user_id = self.request.query_params.get("user_id", None)
        if user_id:
            queryset = queryset.filter(user_id=user_id)
        return queryset

The serializer is just as basic as it gets - it's just the FutureVehicle model and all fields.

I just can't figure out why my user won't login - or if maybe I'm doing something wrong in my attempts to patch?

I'm pretty new to Django Rest Framework in general, so any guidances is helpful!

Edit to add - my DRF Settings look like this:

REST_FRAMEWORK = {
    "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.LimitOffsetPagination",
    "DATETIME_FORMAT": "%m/%d/%Y - %I:%M:%S %p",
    "DATE_INPUT_FORMATS": ["%Y-%m-%d"],
    "DEFAULT_AUTHENTICATION_CLASSES": [
        # Enabling this it will require Django Session (Including CSRF)
        "rest_framework.authentication.SessionAuthentication"
    ],
    "DEFAULT_PERMISSION_CLASSES": [
        # Globally only allow IsAuthenticated users access to API Endpoints
        "rest_framework.permissions.IsAuthenticated"
    ],
}

I'm certain adminuser is the user we wish to login - if I go into the debugger and check the users, they exist as a user. During creation, any user created has a password set to 'test'.



from Patch request not patching - 403 returned - django rest framework

No comments:

Post a Comment