Skip to content

Commit

Permalink
Merge pull request #80 from ahopkins/dev
Browse files Browse the repository at this point in the history
Version 1.0.1 [2018-02-27]
  • Loading branch information
ahopkins committed Feb 27, 2018
2 parents a18672a + a6eea73 commit 9a5fd66
Show file tree
Hide file tree
Showing 9 changed files with 129 additions and 73 deletions.
2 changes: 1 addition & 1 deletion docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
# The short X.Y version.
version = u'1.0'
# The full version, including alpha/beta/rc tags.
release = u'1.0.0'
release = u'1.0.1'

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
Expand Down
14 changes: 14 additions & 0 deletions docs/source/pages/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,20 @@ Changelog

The format is based on `Keep a Changelog <http://keepachangelog.com/en/1.0.0/>`_ and this project adheres to `Semantic Versioning <http://semver.org/spec/v2.0.0.html>`_.


++++++++++++++++++++++++++
Version 1.0.1 [2018-02-27]
++++++++++++++++++++++++++

| **Added**
| - ``OPTIONS`` handler method for ``BaseEndpoint``
|
| **Fixed**
| - Some tests for claims that were not using UTC timestamps
| - Consistency of docs with ``class_views``
|
++++++++++++++++++++++++++
Version 1.0.0 [2018-02-25]
++++++++++++++++++++++++++
Expand Down
2 changes: 1 addition & 1 deletion docs/source/pages/initialization.rst
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ If your user should **not** be authenticated, then you should :doc:`raise an exc
**Purpose**: It is a handler to retrieve a user object from your application. It is used to return the user object in the ``/auth/me`` `endpoint <endpoints>`_. It should return:

- a ``dict``, **or**
- an instance with a ``to_dict`` or ``__dict__`` method.
- an instance with a ``to_dict`` method.

**Example**:

Expand Down
7 changes: 2 additions & 5 deletions sanic_jwt/endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ def __init__(self, responses, *args, **kwargs):
super().__init__(*args, **kwargs)
self.responses = responses


class AuthenticateEndpoint(BaseEndpoint):
async def options(self, request, *args, **kwargs):
return text('', status=204)


class AuthenticateEndpoint(BaseEndpoint):
async def post(self, request, *args, **kwargs):
config = self.config
user = await utils.call(
Expand Down Expand Up @@ -99,9 +99,6 @@ async def get(self, request, *args, **kwargs):


class RefreshEndpoint(BaseEndpoint):
async def options(self, request, *args, **kwargs):
return text('', status=204)

async def post(self, request, *args, **kwargs):
# TODO:
# - Add more exceptions
Expand Down
116 changes: 59 additions & 57 deletions sanic_jwt/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,58 +10,58 @@ class SanicJWTException(SanicException):
@add_status_code(401)
class AuthenticationFailed(SanicJWTException):

def __init__(self, message='Authentication failed.'):
super().__init__(message)
def __init__(self, message='Authentication failed.', **kwargs):
super().__init__(message, **kwargs)


@add_status_code(400)
class MissingAuthorizationHeader(SanicJWTException):

def __init__(self, message='Authorization header not present.'):
super().__init__(message)
def __init__(self, message='Authorization header not present.', **kwargs):
super().__init__(message, **kwargs)


@add_status_code(400)
class MissingAuthorizationCookie(SanicJWTException):

def __init__(self, message='Authorization cookie not present.'):
super().__init__(message)
def __init__(self, message='Authorization cookie not present.', **kwargs):
super().__init__(message, **kwargs)


@add_status_code(400)
class InvalidAuthorizationHeader(SanicJWTException):

def __init__(self, message='Authorization header is invalid.'):
super().__init__(message)
def __init__(self, message='Authorization header is invalid.', **kwargs):
super().__init__(message, **kwargs)


@add_status_code(500)
class AuthenticateNotImplemented(SanicJWTException):

def __init__(
self,
message='Sanic JWT initialized without providing an authenticate '
'method.'
):
super().__init__(message)
self,
message='Sanic JWT initialized without providing an authenticate '
'method.',
**kwargs):
super().__init__(message, **kwargs)


@add_status_code(500)
class RefreshTokenNotImplemented(SanicJWTException):

def __init__(self, message="Refresh tokens have not been enabled."):
super().__init__(message)
def __init__(self, message="Refresh tokens have not been enabled.", **kwargs):
super().__init__(message, **kwargs)


@add_status_code(500)
class ScopesNotImplemented(SanicJWTException):

def __init__(
self,
message='Scopes have not been enabled. Initialize with '
'add_scopes_to_payload to provide scoping.'
):
super().__init__(message)
self,
message='Scopes have not been enabled. Initialize with '
'add_scopes_to_payload to provide scoping.',
**kwargs):
super().__init__(message, **kwargs)


@add_status_code(500)
Expand All @@ -71,91 +71,93 @@ def __init__(
self,
message='One or more claims have been registered, but your '
'SANIC_JWT_HANDLER_PAYLOAD_EXTEND does not supply them. ',
missing=None):
missing=None,
**kwargs):
if missing:
message += str(missing)
super().__init__(message)
super().__init__(message, **kwargs)


@add_status_code(500)
class MeEndpointNotSetup(SanicJWTException):

def __init__(
self,
message='/me endpoint has not been setup. Pass retrieve_user if '
'you with to proceeed.'
):
super().__init__(message)
self,
message='/me endpoint has not been setup. Pass retrieve_user if '
'you with to proceeed.',
**kwargs):
super().__init__(message, **kwargs)


@add_status_code(500)
class InvalidRetrieveUserObject(SanicJWTException):

def __init__(
self,
message='The retrieve_user method should return either a dict or '
'an object with a to_dict method.'
):
super().__init__(message)
self,
message='The retrieve_user method should return either a dict or '
'an object with a to_dict method.',
**kwargs):
super().__init__(message, **kwargs)


@add_status_code(500)
class InitializationFailure(SanicJWTException):

def __init__(
self,
message='Sanic JWT was not initialized properly. It must be '
'instantiated on a sanic.Sanic or sanic.Blueprint '
'instance.'
):
super().__init__(message)
self,
message='Sanic JWT was not initialized properly. It must be '
'instantiated on a sanic.Sanic or sanic.Blueprint '
'instance.',
**kwargs):
super().__init__(message, **kwargs)


class Unauthorized(SanicJWTException, SanicUnauthorized):

def __init__(self, message="Auth required."):
super().__init__(message, scheme="Bearer")
def __init__(self, message="Auth required.", **kwargs):
super().__init__(message, scheme="Bearer", **kwargs)


class InvalidClassViewsFormat(SanicJWTException):

def __init__(
self,
message='class_views should follow this format (\'<SOME ROUTE>\', '
'ClassInheritedFromHTTPMethodView)'
):
super().__init__(message)
self,
message='class_views should follow this format (\'<SOME ROUTE>\', '
'ClassInheritedFromHTTPMethodView)',
**kwargs):
super().__init__(message, **kwargs)


class InvalidConfiguration(SanicJWTException):

def __init__(self, message=""):
message = 'An invalid setting was passed to the Sanic JWT ' \
def __init__(self, message="", **kwargs):
message = 'An invalid setting was , **kwargspassed to the Sanic JWT ' \
'configuration: ' + str(message)
super().__init__(message)


class InvalidPayload(SanicJWTException):

def __init__(self, message=""):
message = 'Payload must be a dictionary with a key mapped to ' \
def __init__(self, message="", **kwargs):
message = 'Payload must be a dicti, **kwargsonary with a key mapped to ' \
'SANIC_JWT_USER_ID'
super().__init__(message)


class RequiredKeysNotFound(SanicJWTException):

def __init__(
self,
message='You must provide both (valid) SANIC_JWT_PUBLIC_KEY and '
'SANIC_JWT_PRIVATE_KEY when using asymmetric '
'cryptographic algorithms like RS*, EC* or PS*'
):
super().__init__(message)
self,
message='You must provide both (valid) SANIC_JWT_PUBLIC_KEY and '
'SANIC_JWT_PRIVATE_KEY when using asymmetric '
'cryptographic algorithms like RS*, EC* or PS*',
**kwargs):
super().__init__(message, **kwargs)


class ProvidedPathNotFound(SanicJWTException):

def __init__(self,
message='The Path object given is not a valid file'):
super().__init__(message)
message='The Path object given is not a valid file',
**kwargs):
super().__init__(message, **kwargs)
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
# Versions should comply with PEP440. For a discussion on single-sourcing
# the version across setup.py and the project code, see
# https://packaging.python.org/en/latest/single_source_version.html
version='1.0.0',
version='1.0.1',
description='JWT oauth flow for Sanic',

# The project's main homepage.
Expand Down
16 changes: 8 additions & 8 deletions tests/test_claims.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ def test_unexpired(self, app):

assert 'exp' in payload

exp = datetime.fromtimestamp(exp)
exp = datetime.utcfromtimestamp(exp)

assert isinstance(exp, datetime)
assert datetime.now() < exp
assert datetime.utcnow() < exp

_, response = sanic_app.test_client.get(
'/protected',
Expand All @@ -45,11 +45,11 @@ def test_expired(self, app):

assert 'exp' in payload

exp = datetime.fromtimestamp(exp)
exp = datetime.utcfromtimestamp(exp)

with freeze_time(datetime.now() + timedelta(seconds=(60 * 35))):
with freeze_time(datetime.utcnow() + timedelta(seconds=(60 * 35))):
assert isinstance(exp, datetime)
assert datetime.now() > exp
assert datetime.utcnow() > exp

_, response = sanic_app.test_client.get(
'/protected',
Expand All @@ -71,7 +71,7 @@ def test_exp_configuration(self, app_with_extended_exp):
sanic_jwt.config.access_token_name, None)
payload = jwt.decode(access_token, sanic_jwt.config.secret, verify=False)
exp = payload.get('exp', None)
exp = datetime.fromtimestamp(exp)
exp = datetime.utcfromtimestamp(exp)

with freeze_time(datetime.utcnow() + timedelta(seconds=(60 * 35))):
assert isinstance(exp, datetime)
Expand All @@ -95,7 +95,7 @@ def test_leeway_configuration(self, app_with_leeway):
sanic_jwt.config.access_token_name, None)
payload = jwt.decode(access_token, sanic_jwt.config.secret, verify=False)
exp = payload.get('exp', None)
exp = datetime.fromtimestamp(exp)
exp = datetime.utcfromtimestamp(exp)

with freeze_time(datetime.utcnow() + timedelta(seconds=(60 * 35 + 1))):
_, response = sanic_app.test_client.get(
Expand Down Expand Up @@ -124,7 +124,7 @@ def test_nbf(self, app_with_nbf):
sanic_jwt.config.access_token_name, None)
payload = jwt.decode(access_token, sanic_jwt.config.secret, verify=False)
exp = payload.get('exp', None)
exp = datetime.fromtimestamp(exp)
exp = datetime.utcfromtimestamp(exp)

assert 'nbf' in payload
assert isinstance(payload.get('nbf'), int)
Expand Down
30 changes: 30 additions & 0 deletions tests/test_preflight.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
def test_preflight_authenticate(app):
sanic_app, _ = app
_, response = sanic_app.test_client.options('/auth')

assert response.status == 204
assert not response.body


def test_preflight_retrieve_user(app):
sanic_app, _ = app
_, response = sanic_app.test_client.options('/auth/me')

assert response.status == 204
assert not response.body


def test_preflight_verify(app):
sanic_app, _ = app
_, response = sanic_app.test_client.options('/auth/verify')

assert response.status == 204
assert not response.body


def test_preflight_refresh(app):
sanic_app, _ = app
_, response = sanic_app.test_client.options('/auth/refresh')

assert response.status == 204
assert not response.body
13 changes: 13 additions & 0 deletions tests/test_sanic_abort.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from sanic.exceptions import abort


def test_sanic_abort_401(app):
sanic_app, _ = app

@sanic_app.route("/abort")
async def abort_request(request):
abort(401)

_, response = sanic_app.test_client.get('/abort')

assert response.status == 401

0 comments on commit 9a5fd66

Please sign in to comment.