Skip to content

Commit

Permalink
Fix issue with JSON arrays (#51)
Browse files Browse the repository at this point in the history
  • Loading branch information
hugorodgerbrown committed Apr 18, 2021
1 parent 3f55204 commit 5f8fdbb
Show file tree
Hide file tree
Showing 6 changed files with 46 additions and 16 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/tox.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ jobs:
strategy:
matrix:
python: [3.8,3.9]
django: [31,main]
django: [31,32,main]

env:
TOXENV: py${{ matrix.python }}-django${{ matrix.django }}
Expand Down
8 changes: 4 additions & 4 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
repos:
# python import sorting - will amend files
- repo: https://github.com/pre-commit/mirrors-isort
rev: v5.4.2
rev: v5.8.0
hooks:
- id: isort
language_version: python3.8

# python code formatting - will amend files
- repo: https://github.com/ambv/black
rev: 19.10b0
rev: 20.8b1
hooks:
- id: black
language_version: python3.8

# Flake8 includes pyflakes, pycodestyle, mccabe, pydocstyle, bandit
- repo: https://gitlab.com/pycqa/flake8
rev: 3.8.3
rev: 3.9.1
hooks:
- id: flake8
language_version: python3.8
Expand All @@ -24,7 +24,7 @@ repos:

# python static type checking
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v0.782
rev: v0.812
hooks:
- id: mypy
language_version: python3.8
Expand Down
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "django-request-token"
version = "0.14"
version = "0.14.1"
description = "JWT-backed Django app for managing querystring tokens."
license = "MIT"
authors = ["YunoJuno <code@yunojuno.com>"]
Expand All @@ -14,6 +14,7 @@ classifiers = [
"Framework :: Django :: 2.2",
"Framework :: Django :: 3.0",
"Framework :: Django :: 3.1",
"Framework :: Django :: 3.2",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.7",
Expand Down
15 changes: 13 additions & 2 deletions request_token/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import json
import logging
from typing import Callable
from typing import Callable, Optional

from django.core.exceptions import ImproperlyConfigured, PermissionDenied
from django.http.request import HttpRequest
Expand All @@ -28,6 +28,17 @@ class RequestTokenMiddleware:
def __init__(self, get_response: Callable):
self.get_response = get_response

def extract_ajax_token(self, request: HttpRequest) -> Optional[str]:
"""Extract token from AJAX request."""
try:
payload = json.loads(request.body)
except json.decoder.JSONDecodeError:
return None
try:
return payload.get(JWT_QUERYSTRING_ARG)
except AttributeError:
return None

def __call__(self, request: HttpRequest) -> HttpResponse: # noqa: C901
"""
Verify JWT request querystring arg.
Expand Down Expand Up @@ -62,7 +73,7 @@ def __call__(self, request: HttpRequest) -> HttpResponse: # noqa: C901
token = request.GET.get(JWT_QUERYSTRING_ARG)
if not token and request.method == "POST":
if request.META.get("CONTENT_TYPE") == "application/json":
token = json.loads(request.body).get(JWT_QUERYSTRING_ARG)
token = self.extract_ajax_token(request)
if not token:
token = request.POST.get(JWT_QUERYSTRING_ARG)
else:
Expand Down
31 changes: 24 additions & 7 deletions tests/test_middleware.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import json
from typing import Any
from unittest import mock

from django.contrib.auth import get_user_model
Expand All @@ -14,7 +15,6 @@


class MockSession(object):

"""Fake Session model used to support `session_key` property."""

@property
Expand All @@ -23,29 +23,29 @@ def session_key(self):


class MiddlewareTests(TestCase):

"""RequestTokenMiddleware tests."""

def setUp(self):
self.user = get_user_model().objects.create_user("zoidberg")
self.factory = RequestFactory()
self.middleware = RequestTokenMiddleware(get_response=lambda r: HttpResponse())
self.token = RequestToken.objects.create_token(scope="foo")
self.default_payload = {JWT_QUERYSTRING_ARG: self.token.jwt()}

def get_request(self):
request = self.factory.get("/?%s=%s" % (JWT_QUERYSTRING_ARG, self.token.jwt()))
request = self.factory.get(f"/?{JWT_QUERYSTRING_ARG}={self.token.jwt()}")
request.user = self.user
request.session = MockSession()
return request

def post_request(self):
request = self.factory.post("/", {JWT_QUERYSTRING_ARG: self.token.jwt()})
request = self.factory.post("/", self.default_payload)
request.user = self.user
request.session = MockSession()
return request

def post_request_with_JSON(self):
data = json.dumps({JWT_QUERYSTRING_ARG: self.token.jwt()})
def post_request_with_JSON(self, payload: Any):
data = json.dumps(payload)
request = self.factory.post("/", data, "application/json")
request.user = self.user
request.session = MockSession()
Expand Down Expand Up @@ -80,10 +80,16 @@ def test_process_POST_request_with_valid_token(self):
self.assertEqual(request.token, self.token)

def test_process_POST_request_with_valid_token_with_json(self):
request = self.post_request_with_JSON()
request = self.post_request_with_JSON(self.default_payload)
self.middleware(request)
self.assertEqual(request.token, self.token)

def test_process_AJAX_request_with_array(self):
"""Test for issue #50."""
request = self.post_request_with_JSON([1])
self.middleware(request)
self.assertFalse(hasattr(request, "token"))

def test_process_request_not_allowed(self):
# PUT requests won't decode the token
request = self.factory.put("/?rt=foo")
Expand Down Expand Up @@ -132,3 +138,14 @@ def test_process_exception(self, mock_log):
# round it out with a non-token error
response = self.middleware.process_exception(request, Exception("foo"))
self.assertIsNone(response)

def test_extract_json_token__array(self):
"""Test for issue #51."""
request = self.post_request_with_JSON(["foo"])
middleware = RequestTokenMiddleware(lambda r: HttpResponse())
self.assertIsNone(middleware.extract_ajax_token(request))

def test_extract_json_token(self):
request = self.post_request_with_JSON(self.default_payload)
middleware = RequestTokenMiddleware(lambda r: HttpResponse())
self.assertEqual(middleware.extract_ajax_token(request), self.token.jwt())
3 changes: 2 additions & 1 deletion tox.ini
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tox]
isolated_build = True
envlist = fmt, lint, mypy, py{3.7,3.8,3.9}-django{22,30,31,main}
envlist = fmt, lint, mypy, py{3.7,3.8,3.9}-django{22,30,31,32,main}

[testenv]
deps =
Expand All @@ -12,6 +12,7 @@ deps =
django22: Django>=2.2,<2.3
django30: Django>=3.0,<3.1
django31: Django>=3.1,<3.2
django32: Django>=3.2,<3.3
djangomain: https://github.com/django/django/archive/main.tar.gz

commands =
Expand Down

0 comments on commit 5f8fdbb

Please sign in to comment.