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
8 changes: 0 additions & 8 deletions atlassian_jwt_auth/contrib/django/__init__.py

This file was deleted.

68 changes: 0 additions & 68 deletions atlassian_jwt_auth/contrib/django/decorators.py

This file was deleted.

66 changes: 0 additions & 66 deletions atlassian_jwt_auth/contrib/django/middleware.py

This file was deleted.

14 changes: 0 additions & 14 deletions atlassian_jwt_auth/contrib/flask_app/__init__.py

This file was deleted.

21 changes: 0 additions & 21 deletions atlassian_jwt_auth/contrib/flask_app/decorators.py

This file was deleted.

Empty file.
10 changes: 8 additions & 2 deletions atlassian_jwt_auth/frameworks/django/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
from .decorators import restrict_asap, with_asap
from .decorators import requires_asap, restrict_asap, with_asap
from .middleware import OldStyleASAPMiddleware, asap_middleware

__all__ = ["restrict_asap", "with_asap", "OldStyleASAPMiddleware", "asap_middleware"]
__all__ = [
"restrict_asap",
"with_asap",
"requires_asap",
"OldStyleASAPMiddleware",
"asap_middleware",
]
17 changes: 17 additions & 0 deletions atlassian_jwt_auth/frameworks/django/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,20 @@ def restrict_asap(
required,
subject_should_match_issuer=None,
)


def requires_asap(
issuers: Optional[Iterable[str]] = None,
subject_should_match_issuer: Optional[bool] = None,
func: Optional[Callable] = None,
) -> Callable:
"""Decorator for Django endpoints to require ASAP

:param list issuers: *required The 'iss' claims that this endpoint is from.
"""
return with_asap(
func=func,
required=True,
issuers=issuers,
subject_should_match_issuer=subject_should_match_issuer,
)
12 changes: 0 additions & 12 deletions atlassian_jwt_auth/frameworks/django/tests/test_django.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,18 +196,6 @@ def test_request_decorated_issuer_is_allowed(self):
retriever_key="whitelist/key01",
)

# TODO: modify JWTAuthSigner to allow non-issuer subjects and update the
# decorated subject test cases
def test_request_non_decorated_subject_is_rejected(self):
self.check_response(
"restricted_subject",
"Forbidden",
403,
issuer="whitelist",
key_id="whitelist/key01",
retriever_key="whitelist/key01",
)

def test_request_using_settings_only_is_allowed(self):
self.check_response("unneeded", "two")

Expand Down
5 changes: 0 additions & 5 deletions atlassian_jwt_auth/frameworks/django/tests/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,4 @@
views.restricted_issuer_view,
name="restricted_issuer",
),
path(
"asap/restricted_subject",
views.restricted_subject_view,
name="restricted_subject",
),
]
8 changes: 1 addition & 7 deletions atlassian_jwt_auth/frameworks/django/tests/views.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from django.http import HttpResponse

from atlassian_jwt_auth.contrib.django.decorators import requires_asap, validate_asap
from atlassian_jwt_auth.frameworks.django import restrict_asap, with_asap
from atlassian_jwt_auth.frameworks.django import requires_asap, restrict_asap, with_asap


@with_asap(issuers=["client-app"])
Expand Down Expand Up @@ -52,8 +51,3 @@ def unneeded_view(request):
@restrict_asap(issuers=["whitelist"])
def restricted_issuer_view(request):
return HttpResponse("three")


@validate_asap(subjects=["client-app"])
def restricted_subject_view(request):
return HttpResponse("four")
4 changes: 2 additions & 2 deletions atlassian_jwt_auth/frameworks/flask/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
from .decorators import with_asap
from .decorators import requires_asap, with_asap

__all__ = ["with_asap"]
__all__ = ["with_asap", "requires_asap"]
18 changes: 18 additions & 0 deletions atlassian_jwt_auth/frameworks/flask/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,21 @@ def with_asap(
return _with_asap(
func, FlaskBackend(), issuers, required, subject_should_match_issuer
)


def requires_asap(
f: Callable,
issuers: Optional[Iterable[str]] = None,
subject_should_match_issuer: Optional[bool] = None,
) -> Callable:
"""
Wrapper for Flask endpoints to make them require asap authentication to
access.
"""

return with_asap(
func=f,
required=True,
issuers=issuers,
subject_should_match_issuer=subject_should_match_issuer,
)
3 changes: 1 addition & 2 deletions atlassian_jwt_auth/frameworks/flask/tests/test_flask.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@

from flask import Flask

from atlassian_jwt_auth.contrib.flask_app import requires_asap
from atlassian_jwt_auth.contrib.tests.utils import get_static_retriever_class
from atlassian_jwt_auth.frameworks.flask import with_asap
from atlassian_jwt_auth.frameworks.flask import requires_asap, with_asap
from atlassian_jwt_auth.tests import utils
from atlassian_jwt_auth.tests.utils import (
create_token,
Expand Down
4 changes: 3 additions & 1 deletion atlassian_jwt_auth/signer.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import jwt
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
from jwt.types import Options

from atlassian_jwt_auth import algorithms, key
from atlassian_jwt_auth.key import BasePrivateKeyRetriever, KeyIdentifier
Expand Down Expand Up @@ -116,8 +117,9 @@ def can_reuse_token(self, existing_token, claims) -> bool:
"""
if existing_token is None:
return False

existing_claims = jwt.decode(
existing_token, options={"verify_signature": False}
existing_token, options=Options(verify_signature=False)
)
existing_lifetime = int(existing_claims["exp"]) - int(existing_claims["iat"])
this_lifetime = (claims["exp"] - claims["iat"]).total_seconds()
Expand Down
13 changes: 7 additions & 6 deletions atlassian_jwt_auth/verifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicKey
from jwt import PyJWK
from jwt.exceptions import InvalidAlgorithmError
from jwt.types import Options

from atlassian_jwt_auth import KeyIdentifier, algorithms, exceptions, key
from atlassian_jwt_auth.key import BasePublicKeyRetriever
Expand Down Expand Up @@ -96,12 +97,12 @@ def _decode_jwt(
leeway: int = 0,
) -> Dict[Any, Any]:
"""Decode JWT and check if it's valid"""
options = {
"verify_signature": True,
"require": ["exp", "iat"],
"require_exp": True,
"require_iat": True,
}
options: Options = Options(
verify_signature=True,
require=["exp", "iat"],
verify_exp=True,
verify_iat=True,
)

claims = jwt.decode(
a_jwt,
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
PyJWT>=2.4.0,<3.0.0
PyJWT>=2.11.0,<3.0.0
PyJWT[crypto]>=2.4.0,<3.0.0
requests>=2.8.1,<3.0.0
CacheControl
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from setuptools import setup

setup(
setup_requires=["pbr<7.0.0"],
setup_requires=["pbr<8.0.0"],
pbr=True,
platforms=["any"],
zip_safe=False,
Expand Down
Loading