Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 <[email protected]>"]
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