Skip to content

Commit

Permalink
Merge pull request #123 from ahopkins/dev
Browse files Browse the repository at this point in the history
Update version 1.1.3
  • Loading branch information
ahopkins committed Aug 6, 2018
2 parents 2ea96d4 + 2c7ab02 commit bfd4263
Show file tree
Hide file tree
Showing 39 changed files with 960 additions and 237 deletions.
4 changes: 4 additions & 0 deletions .pyup.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# autogenerated pyup.io config file
# see https://pyup.io/docs/configuration/ for all available options

update: insecure
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
language: python

python:
# - "3.7-dev"
- "3.6"
- "3.5"
# - "pypy"
Expand Down
2 changes: 1 addition & 1 deletion docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
# The short X.Y version.
version = u"1.1"
# The full version, including alpha/beta/rc tags.
release = u"1.1.2"
release = u"1.1.3"

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
Expand Down
16 changes: 16 additions & 0 deletions docs/source/pages/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,22 @@ 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.1.3 - 2018-08-06
++++++++++++++++++++++++++

| **Changed**
| - Exception handling to consistently have a ``exception`` and ``reasons`` key
| - ``reasons`` in exception handling to be consistently formatted
| - ``400`` responses for ``debug`` turned off, and ``401`` when turned on
|
| **Fixed**
| - `#110 <https://github.com/ahopkins/sanic-jwt/issues/110>`_. Preflight methods now properly handled
| - `#114 <https://github.com/ahopkins/sanic-jwt/issues/114>`_. Proper use of ``utils.call`` to allow for sync and async ``retrieve_user`` functions
| - `#116 <https://github.com/ahopkins/sanic-jwt/issues/116>`_. Proper error reporting on malformed tokens
| - `#118 <https://github.com/ahopkins/sanic-jwt/issues/118>`_. Proper error reporting on expired token for ``/auth/me`` and ``/auth/refresh`` by applying ``@protected`` decorators
++++++++++++++++++++++++++
Version 1.1.2 - 2018-06-18
++++++++++++++++++++++++++
Expand Down
7 changes: 6 additions & 1 deletion docs/source/pages/endpoints.rst
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,12 @@ What if we wanted a ``/register`` endpoint? It could easily be added like this:
('/register', Register),
)
Initialize(app, class_views=my_views)
# Please note that this Initialize instance is incomplete.
# It is missing handlers for: authenticate, store_refresh_token, and
# retrieve_refresh_token.
# It is meant as illustrative purposes on how you might approach this.
# See https://github.com/ahopkins/sanic-jwt/issues/111 for more information.
Initialize(app, class_views=my_views, ...)
You hook up your custom endpoints at :doc:`initialization<initialization>` by providing ``Initialize`` with a ``class_views`` argument naming your endpoint and its path.

Expand Down
2 changes: 1 addition & 1 deletion example/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ chardet==3.0.4
codacy-coverage==1.3.10
codecov==2.0.15
coverage==4.5
cryptography==2.1.4
cryptography==2.3
freezegun==0.3.9
httptools==0.0.9
idna==2.6
Expand Down
2 changes: 1 addition & 1 deletion sanic_jwt/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
__version__ = "1.1.2"
__version__ = "1.1.3"
__author__ = "Adam Hopkins"
__credits__ = "Richard Kuesters"

Expand Down
84 changes: 62 additions & 22 deletions sanic_jwt/authentication.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from datetime import datetime, timedelta
import warnings
import jwt
from .exceptions import SanicJWTException

from . import exceptions, utils

Expand All @@ -16,6 +17,7 @@ def __init__(self, app, config):
self.app = app
self.claims = ["exp"]
self.config = config
self._reasons = []

async def _get_user_id(self, user, *, asdict=False):
"""
Expand Down Expand Up @@ -50,7 +52,7 @@ async def add_claims(self, payload, *args, **kwargs):

for option in ["iss", "iat", "nbf", "aud"]:
setting = "claim_{}".format(option.lower())
if setting in self.config:
if setting in self.config: # noqa
attr = self.config.get(setting)
if attr:
self.claims.append(option)
Expand Down Expand Up @@ -92,7 +94,7 @@ def destructure_scopes(self, scopes, *args, **kwargs): # noqa
return scopes

async def retrieve_user(self, *args, **kwargs):
raise exceptions.MeEndpointNotSetup() # noqa
raise exceptions.MeEndpointNotSetup # noqa


class Authentication(BaseAuthentication):
Expand All @@ -115,7 +117,12 @@ def _check_authentication(self, request, request_args, request_kwargs):
)
except Exception as e:
logger.debug(e.args)
raise exceptions.Unauthorized()
if self.config.debug():
raise e

args = e.args if isinstance(e, SanicJWTException) else []

raise exceptions.Unauthorized(*args)

return is_valid, status, reasons

Expand All @@ -131,14 +138,14 @@ def _decode(self, token, verify=True):
for claim in self.claims:
if claim != "exp":
setting = "claim_{}".format(claim.lower())
if setting in self.config:
if setting in self.config: # noqa
value = self.config.get(setting)
kwargs.update({claim_label[claim]: value})

kwargs["leeway"] = int(self.config.leeway())
if "claim_aud" in self.config:
if "claim_aud" in self.config: # noqa
kwargs["audience"] = self.config.claim_aud()
if "claim_iss" in self.config:
if "claim_iss" in self.config: # noqa
kwargs["issuer"] = self.config.claim_iss()

decoded = jwt.decode(
Expand Down Expand Up @@ -291,6 +298,7 @@ def _verify(
request,
return_payload=False,
verify=True,
raise_missing=False,
request_args=None,
request_kwargs=None,
*args,
Expand All @@ -299,23 +307,56 @@ def _verify(
"""
Verify that a request object is authenticated.
"""
token = self._get_token(request)
is_valid = True
reason = None

try:
payload = self._decode(token, verify=verify)
token = self._get_token(request)
is_valid = True
reason = None
except (
jwt.exceptions.ExpiredSignatureError,
jwt.exceptions.InvalidIssuerError,
jwt.exceptions.ImmatureSignatureError,
jwt.exceptions.InvalidIssuedAtError,
jwt.exceptions.InvalidAudienceError,
exceptions.MissingAuthorizationCookie,
exceptions.MissingAuthorizationQueryArg,
exceptions.MissingAuthorizationHeader,
) as e:
token = None
is_valid = False
reason = list(e.args)
status = e.status_code if self.config.debug() else 401

if raise_missing:
if not self.config.debug():
e.status_code = 401
raise e

if token:
try:
payload = self._decode(token, verify=verify)
except (
jwt.exceptions.ExpiredSignatureError,
jwt.exceptions.InvalidIssuerError,
jwt.exceptions.ImmatureSignatureError,
jwt.exceptions.InvalidIssuedAtError,
jwt.exceptions.InvalidAudienceError,
) as e:
# Make sure that the reasons all end with '.' for consistency
reason = [
x if x.endswith('.') else '{}.'.format(x)
for x in list(e.args)
]
payload = None
status = 403
is_valid = False
except jwt.exceptions.DecodeError as e:
self._reasons = e.args
# Make sure that the reasons all end with '.' for consistency
reason = [
x if x.endswith('.') else '{}.'.format(x)
for x in list(e.args)
] if self.config.debug() else "Auth required."
logger.debug(e.args)
is_valid = False
payload = None
status = 400 if self.config.debug() else 401
else:
payload = None
status = 403

if return_payload:
return payload
Expand All @@ -338,6 +379,9 @@ def extract_scopes(self, request):
Extract scopes from a request object.
"""
payload = self.extract_payload(request)
if not payload:
return None

scopes_attribute = self.config.scopes_name()
return payload.get(scopes_attribute, None)

Expand Down Expand Up @@ -394,11 +438,7 @@ def is_authenticated(self, request):
Checks a request object to determine if that request contains a valid,
and authenticated JWT.
"""
try:
is_valid, *_ = self._verify(request)
except Exception as e:
logger.debug(e.args)
is_valid = False
is_valid, *_ = self._verify(request)

return is_valid

Expand Down
10 changes: 5 additions & 5 deletions sanic_jwt/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ def __call__(self, **kwargs):
if self._get_from_config is not None:
args = []

if self._inject_request and is_cached("_request"):
if self._inject_request and is_cached("_request"): # noqa
args.append(get_cached("_request"))
val = self._get_from_config.__call__(*args)
to_cache(self._item_name, val)
Expand Down Expand Up @@ -162,7 +162,7 @@ def __new__(cls, *args, **kwargs):
_aliases = copy.deepcopy(aliases)
_config_keys = []

if args and isinstance(args[0], dict):
if args and isinstance(args[0], dict): # noqa
_args = cls.extract_presets(args[0])
for key, value in _args.items():
if key in _defaults or key in _aliases:
Expand Down Expand Up @@ -231,7 +231,7 @@ def __new__(cls, *args, **kwargs):
def get(self, item):
"""Helper method to avoid calling getattr
"""
if item in self:
if item in self: # noqa
item = getattr(self, item)
return item()

Expand Down Expand Up @@ -267,12 +267,12 @@ def _merge(self, key, value):
self._merge(alias, value)
elif key in self.config_aliases_keys:
correct_key = None
for v in self.config_aliases.values():
for v in self.config_aliases.values(): # noqa
if key == v:
correct_key = key
break

if hasattr(self, correct_key):
if hasattr(self, correct_key): # noqa
getattr(self, correct_key).update(value)
else:
_warn_key(key)
Expand Down

0 comments on commit bfd4263

Please sign in to comment.