Skip to content

Commit

Permalink
Merge pull request #20 from yunojuno/django2
Browse files Browse the repository at this point in the history
Drop support for Python2 and Django < 1.10
  • Loading branch information
hugorodgerbrown authored Feb 28, 2018
2 parents 9b7712a + 8168a55 commit e987002
Show file tree
Hide file tree
Showing 30 changed files with 92 additions and 111 deletions.
1 change: 0 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
language:
python
python:
- "2.7"
- "3.6"
addons:
postgresql: "9.4"
Expand Down
5 changes: 5 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ Django app that uses JWT to manage one-time and expiring tokens to protect URLs.

This app currently requires the use of PostgreSQL.

Compatibility
=============

This library is now Python3 and Django1.11 and above only. If you are on Python2 then you will have to refer to the python2 branch.

Background
==========

Expand Down
1 change: 0 additions & 1 deletion request_token/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
# -*- coding: utf-8 -*-
default_app_config = 'request_token.apps.RequestTokenAppConfig'
1 change: 0 additions & 1 deletion request_token/admin.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
import json

from django.contrib import admin
Expand Down
1 change: 0 additions & 1 deletion request_token/apps.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from django.apps import AppConfig
from django.core.exceptions import ImproperlyConfigured
from django.template import loader, TemplateDoesNotExist
Expand Down
15 changes: 0 additions & 15 deletions request_token/compat.py

This file was deleted.

1 change: 0 additions & 1 deletion request_token/decorators.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
import functools
import logging

Expand Down
1 change: 0 additions & 1 deletion request_token/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# -*- coding: utf8 -*-
"""
Local exceptions related to tokens inherit from the PyJWT base
InvalidTokenError.
Expand Down
13 changes: 8 additions & 5 deletions request_token/middleware.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
# -*- coding: utf-8 -*-
import logging

from django.http import HttpResponseForbidden, HttpResponseNotAllowed
from django.template import loader

from jwt.exceptions import InvalidTokenError

from .compat import MiddlewareMixin
from .models import RequestToken
from .settings import JWT_QUERYSTRING_ARG, FOUR03_TEMPLATE
from .utils import decode

logger = logging.getLogger(__name__)


class RequestTokenMiddleware(MiddlewareMixin):
class RequestTokenMiddleware:

"""
Extract and verify request tokens from incoming GET requests.
Expand All @@ -24,7 +22,10 @@ class RequestTokenMiddleware(MiddlewareMixin):
"""

def process_request(self, request):
def __init__(self, get_response):
self.get_response = get_response

def __call__(self, request):
"""Verify JWT request querystring arg.
If a token is found (using JWT_QUERYSTRING_ARG), then it is decoded,
Expand Down Expand Up @@ -54,7 +55,7 @@ def process_request(self, request):
token = request.GET.get(JWT_QUERYSTRING_ARG)

if token is None:
return
return self.get_response(request)

if request.method != 'GET':
return HttpResponseNotAllowed(['GET'])
Expand All @@ -72,6 +73,8 @@ def process_request(self, request):
request.token = None
logger.exception("RequestToken cannot be decoded: %s", token)

return self.get_response(request)

def process_exception(self, request, exception):
"""Handle all InvalidTokenErrors."""
if isinstance(exception, InvalidTokenError):
Expand Down
6 changes: 3 additions & 3 deletions request_token/migrations/0001_initial.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class Migration(migrations.Migration):
('issued_at', models.DateTimeField(help_text='Time the token was created, set in the initial save.', null=True, blank=True)),
('max_uses', models.IntegerField(default=1, help_text='Cap on the number of times the token can be used, defaults to 1 (single use).')),
('used_to_date', models.IntegerField(default=0, help_text='Denormalised count of the number times the token has been used.')),
('user', models.ForeignKey(blank=True, to=settings.AUTH_USER_MODEL, help_text='Intended recipient of the JWT.', null=True)),
('user', models.ForeignKey(blank=True, to=settings.AUTH_USER_MODEL, help_text='Intended recipient of the JWT.', null=True, on_delete=models.deletion.CASCADE)),
],
),
migrations.CreateModel(
Expand All @@ -33,8 +33,8 @@ class Migration(migrations.Migration):
('user_agent', models.TextField(help_text='User-agent of client used to make the request.', blank=True)),
('client_ip', models.CharField(help_text='Client IP of device used to make the request.', max_length=15)),
('timestamp', models.DateTimeField(help_text='Time the request was logged.')),
('token', models.ForeignKey(help_text='The RequestToken that was used.', to='request_token.RequestToken')),
('user', models.ForeignKey(blank=True, to=settings.AUTH_USER_MODEL, help_text='The user who made the request (None if anonymous).', null=True)),
('token', models.ForeignKey(help_text='The RequestToken that was used.', to='request_token.RequestToken', on_delete=models.deletion.CASCADE)),
('user', models.ForeignKey(blank=True, to=settings.AUTH_USER_MODEL, help_text='The user who made the request (None if anonymous).', null=True, on_delete=models.deletion.CASCADE)),
],
),
]
2 changes: 1 addition & 1 deletion request_token/migrations/0003_auto_20151229_1105.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,6 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='requesttoken',
name='user',
field=models.ForeignKey(blank=True, to=settings.AUTH_USER_MODEL, help_text='Intended recipient of the JWT (can be used by anyone if not set).', null=True),
field=models.ForeignKey(blank=True, to=settings.AUTH_USER_MODEL, help_text='Intended recipient of the JWT (can be used by anyone if not set).', null=True, on_delete=models.deletion.CASCADE),
),
]
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

import django.contrib.postgres.fields.jsonb
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):
Expand All @@ -22,6 +21,6 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='requesttokenlog',
name='token',
field=models.ForeignKey(help_text='The RequestToken that was used.', on_delete=django.db.models.deletion.CASCADE, related_name='logs', to='request_token.RequestToken'),
field=models.ForeignKey(help_text='The RequestToken that was used.', on_delete=models.deletion.CASCADE, related_name='logs', to='request_token.RequestToken'),
),
]
3 changes: 1 addition & 2 deletions request_token/migrations/0010_auto_20170521_1944.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):
Expand All @@ -17,6 +16,6 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='requesttoken',
name='user',
field=models.ForeignKey(blank=True, help_text='Intended recipient of the JWT (can be used by anyone if not set).', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='request_tokens', to=settings.AUTH_USER_MODEL),
field=models.ForeignKey(blank=True, help_text='Intended recipient of the JWT (can be used by anyone if not set).', null=True, on_delete=models.deletion.CASCADE, related_name='request_tokens', to=settings.AUTH_USER_MODEL),
),
]
16 changes: 11 additions & 5 deletions request_token/models.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""request_token models."""
from __future__ import unicode_literals

Expand Down Expand Up @@ -86,6 +85,7 @@ class RequestToken(models.Model):
settings.AUTH_USER_MODEL,
related_name="request_tokens",
blank=True, null=True,
on_delete=models.CASCADE,
help_text="Intended recipient of the JWT (can be used by anyone if not set)."
)
scope = models.CharField(
Expand Down Expand Up @@ -238,7 +238,8 @@ def validate_max_uses(self):

def _auth_is_anonymous(self, request):
"""Authenticate anonymous requests."""
assert request.user.is_anonymous(), 'User is authenticated.'
if request.user.is_authenticated:
raise InvalidAudienceError('Token requires anonymous user.')

if self.login_mode == RequestToken.LOGIN_MODE_NONE:
pass
Expand All @@ -265,7 +266,8 @@ def _auth_is_anonymous(self, request):

def _auth_is_authenticated(self, request):
"""Authenticate requests with existing users."""
assert request.user.is_authenticated(), 'User is anonymous.'
if request.user.is_anonymous:
raise InvalidAudienceError('Token requires authenticated user.')

if self.login_mode == RequestToken.LOGIN_MODE_NONE:
return request
Expand All @@ -285,7 +287,7 @@ def authenticate(self, request):
has a user assigned, then this will be added to the request.
"""
if request.user.is_anonymous():
if request.user.is_anonymous:
return self._auth_is_anonymous(request)
else:
return self._auth_is_authenticated(request)
Expand All @@ -312,7 +314,7 @@ def rmg(key, default=None):

log = RequestTokenLog(
token=self,
user=None if request.user.is_anonymous() else request.user,
user=None if request.user.is_anonymous else request.user,
user_agent=rmg('HTTP_USER_AGENT', 'unknown'),
client_ip=parse_xff(rmg('HTTP_X_FORWARDED_FOR')) or rmg('REMOTE_ADDR', None),
status_code=response.status_code
Expand Down Expand Up @@ -355,11 +357,13 @@ class RequestTokenLog(models.Model):
RequestToken,
related_name='logs',
help_text="The RequestToken that was used.",
on_delete=models.CASCADE,
db_index=True
)
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
blank=True, null=True,
on_delete=models.CASCADE,
help_text="The user who made the request (None if anonymous)."
)
user_agent = models.TextField(
Expand Down Expand Up @@ -421,12 +425,14 @@ class RequestTokenErrorLog(models.Model):
token = models.ForeignKey(
RequestToken,
related_name='errors',
on_delete=models.CASCADE,
help_text="The RequestToken that was used.",
db_index=True
)
log = models.OneToOneField(
RequestTokenLog,
related_name='error',
on_delete=models.CASCADE,
help_text="The token use against which the error occurred.",
db_index=True
)
Expand Down
1 change: 0 additions & 1 deletion request_token/settings.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from os import getenv

from django.conf import settings
Expand Down
3 changes: 1 addition & 2 deletions request_token/tests/test_admin.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
# -*- coding: utf-8 -*-
import datetime
from unittest import mock

from jwt.exceptions import MissingRequiredClaimError

from django.test import TestCase
from django.utils.timezone import now as tz_now

from ..admin import pretty_print, RequestTokenAdmin
from ..compat import mock
from ..models import RequestToken


Expand Down
4 changes: 2 additions & 2 deletions request_token/tests/test_apps.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# -*- coding: utf-8 -*-
from unittest import mock

from django.template import TemplateDoesNotExist
from django.test import TestCase

from ..apps import check_template, ImproperlyConfigured
from ..compat import mock


class AppTests(TestCase):
Expand Down
5 changes: 2 additions & 3 deletions request_token/tests/test_decorators.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from django.contrib.auth.models import AnonymousUser
from django.http import HttpResponse, HttpRequest
from django.test import TestCase, RequestFactory
Expand Down Expand Up @@ -41,14 +40,14 @@ class DecoratorTests(TestCase):

def setUp(self):
self.factory = RequestFactory()
self.middleware = RequestTokenMiddleware()
self.middleware = RequestTokenMiddleware(get_response=lambda r: r)

def _request(self, path, token, user):
path = path + "?%s=%s" % (JWT_QUERYSTRING_ARG, token) if token else path
request = self.factory.get(path)
request.session = MockSession()
request.user = user
self.middleware.process_request(request)
self.middleware(request)
return request

def test_no_token(self):
Expand Down
Loading

0 comments on commit e987002

Please sign in to comment.