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
11 changes: 7 additions & 4 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
name: Tests
permissions:
contents: read
pull-requests: write

on: [push]

jobs:
build:

runs-on: ubuntu-24.04
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.9, "3.10", "3.11", "3.12", "3.13"]
python-version: ["3.11", "3.12", "3.13", "3.14"]

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
uses: actions/setup-python@v7
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
# supported CodeQL languages.
#
name: "CodeQL"
permissions:
contents: read
pull-requests: write

on:
push:
Expand Down
4 changes: 2 additions & 2 deletions atlassian_jwt_auth/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
from atlassian_jwt_auth.verifier import JWTAuthVerifier

__all__ = [
"get_permitted_algorithm_names",
"HTTPSPublicKeyRetriever",
"JWTAuthVerifier",
"KeyIdentifier",
"create_signer",
"create_signer_from_file_private_key_repository",
"JWTAuthVerifier",
"get_permitted_algorithm_names",
]
5 changes: 1 addition & 4 deletions atlassian_jwt_auth/algorithms.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
from typing import List


def get_permitted_algorithm_names() -> List[str]:
def get_permitted_algorithm_names() -> list[str]:
"""returns permitted algorithm names."""
return [
"RS256",
Expand Down
7 changes: 3 additions & 4 deletions atlassian_jwt_auth/auth.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
from __future__ import absolute_import

from typing import Any, Iterable, Union
from collections.abc import Iterable
from typing import Any, Union

import atlassian_jwt_auth
from atlassian_jwt_auth import KeyIdentifier
from atlassian_jwt_auth.signer import JWTAuthSigner


class BaseJWTAuth(object):
class BaseJWTAuth:
"""Adds a JWT bearer token to the request per the ASAP specification"""

def __init__(
Expand Down
2 changes: 1 addition & 1 deletion atlassian_jwt_auth/contrib/aiohttp/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from .verifier import JWTAuthVerifier

__all__ = [
"JWTAuth",
"HTTPSPublicKeyRetriever",
"JWTAuth",
"JWTAuthVerifier",
]
3 changes: 2 additions & 1 deletion atlassian_jwt_auth/contrib/aiohttp/auth.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from typing import Any, Iterable, Union
from collections.abc import Iterable
from typing import Any, Union

from aiohttp import BasicAuth

Expand Down
9 changes: 5 additions & 4 deletions atlassian_jwt_auth/contrib/aiohttp/key.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import asyncio
import urllib.parse
from asyncio import AbstractEventLoop
from typing import Any, Awaitable, Dict, Optional
from collections.abc import Awaitable
from typing import Any, Optional

import aiohttp

Expand Down Expand Up @@ -31,8 +32,8 @@ def _get_session(self) -> aiohttp.ClientSession: # type: ignore[override]
return HTTPSPublicKeyRetriever._class_session

def _convert_proxies_to_proxy_arg(
self, url: str, requests_kwargs: Dict[Any, Any]
) -> Dict[str, Any]:
self, url: str, requests_kwargs: dict[Any, Any]
) -> dict[str, Any]:
"""returns a modified requests_kwargs dict that contains proxy
information in a form that aiohttp accepts
(it wants proxy information instead of a dict of proxies).
Expand All @@ -46,7 +47,7 @@ def _convert_proxies_to_proxy_arg(
return requests_kwargs

async def _retrieve(
self, url: str, requests_kwargs: Dict[Any, Any]
self, url: str, requests_kwargs: dict[Any, Any]
) -> Awaitable[str]:
requests_kwargs = self._convert_proxies_to_proxy_arg(url, requests_kwargs)
try:
Expand Down
5 changes: 3 additions & 2 deletions atlassian_jwt_auth/contrib/aiohttp/verifier.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import asyncio
from typing import Any, Dict, Iterable, Union
from collections.abc import Iterable
from typing import Any, Union

import jwt

Expand All @@ -14,7 +15,7 @@ async def verify_jwt( # type: ignore[override]
audience: Union[str, Iterable[str]],
leeway: int = 0,
**requests_kwargs: Any,
) -> Dict[Any, Any]:
) -> dict[Any, Any]:
"""Verify if the token is correct

Returns:
Expand Down
5 changes: 2 additions & 3 deletions atlassian_jwt_auth/contrib/requests.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from __future__ import absolute_import

from typing import Any, Iterable, Union
from collections.abc import Iterable
from typing import Any, Union

import requests
from requests.auth import AuthBase
Expand Down
4 changes: 2 additions & 2 deletions atlassian_jwt_auth/contrib/tests/aiohttp/test_auth.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import unittest
from typing import Any, Type
from typing import Any

from atlassian_jwt_auth.auth import BaseJWTAuth
from atlassian_jwt_auth.contrib.aiohttp.auth import JWTAuth, create_jwt_auth
Expand All @@ -10,7 +10,7 @@
class BaseAuthTest(test_requests.BaseRequestsTest):
"""tests for the contrib.aiohttp.JWTAuth class"""

auth_cls: Type[JWTAuth] = JWTAuth
auth_cls: type[JWTAuth] = JWTAuth

def _get_auth_header(self, auth) -> bytes:
return auth.encode().encode("latin1")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def _get_session(self) -> Mock:
return session


class BaseHTTPSPublicKeyRetrieverTestMixin(object):
class BaseHTTPSPublicKeyRetrieverTestMixin:
"""Tests for aiohttp.HTTPSPublicKeyRetriever class for RS256 algorithm"""

def setUp(self):
Expand Down
2 changes: 1 addition & 1 deletion atlassian_jwt_auth/contrib/tests/test_requests.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from atlassian_jwt_auth.tests import utils


class BaseRequestsTest(object):
class BaseRequestsTest:
"""tests for the contrib.requests.JWTAuth class"""

auth_cls: Any = JWTAuth
Expand Down
8 changes: 4 additions & 4 deletions atlassian_jwt_auth/contrib/tests/utils.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
from typing import Any, Dict, Type
from typing import Any

import atlassian_jwt_auth
from atlassian_jwt_auth import JWTAuthVerifier
from atlassian_jwt_auth.key import BasePublicKeyRetriever


def get_static_retriever_class(keys: Dict[str, Any]) -> Type[BasePublicKeyRetriever]:
def get_static_retriever_class(keys: dict[str, Any]) -> type[BasePublicKeyRetriever]:
class StaticPublicKeyRetriever(BasePublicKeyRetriever):
"""Retrieves a key from a static dict of public keys
(for use in tests only)"""

def __init__(self, *args: Any, **kwargs: Any) -> None:
self.keys: Dict[str, Any] = keys
self.keys: dict[str, Any] = keys

def retrieve(self, key_identifier, **requests_kwargs) -> Any:
return self.keys[key_identifier.key_id]

return StaticPublicKeyRetriever


def static_verifier(keys: Dict[str, Any]) -> JWTAuthVerifier:
def static_verifier(keys: dict[str, Any]) -> JWTAuthVerifier:
return atlassian_jwt_auth.JWTAuthVerifier(get_static_retriever_class(keys)())
10 changes: 4 additions & 6 deletions atlassian_jwt_auth/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from typing import Any


class _WrappedException(object):
class _WrappedException:
"""Allow wrapping exceptions in a new class while preserving the original
as an attribute.

Expand All @@ -18,10 +18,10 @@ def __init__(self, *args: Any, **kwargs: Any) -> None:
if isinstance(orig, Exception):
wrapped_args[0] = str(orig)
self.original_exception = getattr(orig, "original_exception", orig)
super(_WrappedException, self).__init__(*wrapped_args, **kwargs)
super().__init__(*wrapped_args, **kwargs)


class _WithStatus(object):
class _WithStatus:
"""Allow an optional status_code attribute on wrapped exceptions.

This should allow inspecting HTTP-related errors without having to know
Expand All @@ -30,7 +30,7 @@ class _WithStatus(object):

def __init__(self, *args: Any, **kwargs: Any) -> None:
status_code = kwargs.pop("status_code", None)
super(_WithStatus, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
self.status_code = status_code


Expand Down Expand Up @@ -64,5 +64,3 @@ class SubjectDoesNotMatchIssuerException(ASAPAuthenticationException):

class NoTokenProvidedError(ASAPAuthenticationException):
"""Raise when no token is provided"""

pass
5 changes: 3 additions & 2 deletions atlassian_jwt_auth/frameworks/common/asap.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import logging
from typing import Any, Dict, Iterable, Optional
from collections.abc import Iterable
from typing import Any, Optional

from jwt.exceptions import InvalidIssuerError, InvalidTokenError

Expand Down Expand Up @@ -90,7 +91,7 @@ def _process_asap_token(


def _verify_issuers(
asap_claims: Dict[Any, Any], issuers: Optional[Iterable[str]] = None
asap_claims: dict[Any, Any], issuers: Optional[Iterable[str]] = None
) -> None:
"""Verify that the issuer in the claims is valid and is expected."""
claim_iss = asap_claims.get("iss")
Expand Down
9 changes: 5 additions & 4 deletions atlassian_jwt_auth/frameworks/common/backend.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import typing
from abc import ABCMeta, abstractmethod, abstractproperty
from functools import lru_cache
from typing import Any, Dict, Optional, Union
from typing import Any, Optional, Union

from atlassian_jwt_auth import HTTPSPublicKeyRetriever, JWTAuthVerifier

Expand Down Expand Up @@ -34,8 +35,8 @@ class Backend:

__metaclass__ = ABCMeta

default_headers_401 = {"WWW-Authenticate": "Bearer"}
default_settings = {
default_headers_401: typing.ClassVar = {"WWW-Authenticate": "Bearer"}
default_settings: typing.ClassVar = {
# The class to be instantiated to retrieve public keys
"ASAP_KEY_RETRIEVER_CLASS": HTTPSPublicKeyRetriever,
# The repository URL where the key retriever can fetch public keys
Expand Down Expand Up @@ -117,7 +118,7 @@ def get_verifier(self, settings: Optional[SettingsDict] = None) -> JWTAuthVerifi
def _get_verifier(self, settings: SettingsDict) -> JWTAuthVerifier:
return _get_verifier(settings)

def _process_settings(self, settings: Union[SettingsDict, Dict]) -> SettingsDict:
def _process_settings(self, settings: Union[SettingsDict, dict]) -> SettingsDict:
valid_issuers = settings.get("ASAP_VALID_ISSUERS")
if valid_issuers:
settings["ASAP_VALID_ISSUERS"] = set(valid_issuers)
Expand Down
5 changes: 3 additions & 2 deletions atlassian_jwt_auth/frameworks/common/decorators.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from collections.abc import Callable, Iterable
from functools import wraps
from typing import Any, Callable, Dict, Iterable, Optional
from typing import Any, Optional

from jwt.exceptions import InvalidIssuerError, InvalidTokenError

Expand Down Expand Up @@ -103,7 +104,7 @@ def restrict_asap_wrapper(request, *args, **kwargs) -> Any:


def _update_settings_from_kwargs(
settings: Dict[Any, Any],
settings: dict[Any, Any],
issuers: Optional[Iterable] = None,
required: bool = True,
subject_should_match_issuer: Optional[bool] = None,
Expand Down
6 changes: 3 additions & 3 deletions atlassian_jwt_auth/frameworks/django/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
from .middleware import OldStyleASAPMiddleware, asap_middleware

__all__ = [
"restrict_asap",
"with_asap",
"requires_asap",
"OldStyleASAPMiddleware",
"asap_middleware",
"requires_asap",
"restrict_asap",
"with_asap",
]
3 changes: 2 additions & 1 deletion atlassian_jwt_auth/frameworks/django/decorators.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from typing import Callable, Iterable, Optional
from collections.abc import Callable, Iterable
from typing import Optional

from ..common.backend import Backend
from ..common.decorators import _restrict_asap, _with_asap
Expand Down
5 changes: 3 additions & 2 deletions atlassian_jwt_auth/frameworks/django/middleware.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from typing import Any, Callable, Optional
from collections.abc import Callable
from typing import Any, Optional

from django.http import HttpRequest

Expand All @@ -24,7 +25,7 @@ def middleware(request: HttpRequest) -> Any:
return middleware


class OldStyleASAPMiddleware(object):
class OldStyleASAPMiddleware:
"""Middleware to enable ASAP for all requests (for legacy applications
using MIDDLEWARE_CLASSES)"""

Expand Down
8 changes: 4 additions & 4 deletions atlassian_jwt_auth/frameworks/django/tests/test_django.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
)


class DjangoAsapMixin(object):
class DjangoAsapMixin:
@classmethod
def setUpClass(cls):
os.environ.setdefault(
Expand All @@ -28,15 +28,15 @@ def setUpClass(cls):
)

django.setup()
super(DjangoAsapMixin, cls).setUpClass()
super().setUpClass()

@classmethod
def tearDownClass(cls):
super(DjangoAsapMixin, cls).tearDownClass()
super().tearDownClass()
del os.environ["DJANGO_SETTINGS_MODULE"]

def setUp(self):
super(DjangoAsapMixin, self).setUp()
super().setUp()
self._private_key_pem = self.get_new_private_key_in_pem_format()
self._public_key_pem = utils.get_public_key_pem_for_private_key_pem(
self._private_key_pem
Expand Down
2 changes: 1 addition & 1 deletion atlassian_jwt_auth/frameworks/flask/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
from .decorators import requires_asap, with_asap

__all__ = ["with_asap", "requires_asap"]
__all__ = ["requires_asap", "with_asap"]
4 changes: 2 additions & 2 deletions atlassian_jwt_auth/frameworks/flask/decorators.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from collections.abc import Callable
from typing import Iterable, Optional
from collections.abc import Callable, Iterable
from typing import Optional

from ..common.decorators import _with_asap
from .backend import FlaskBackend
Expand Down
2 changes: 1 addition & 1 deletion atlassian_jwt_auth/frameworks/wsgi/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
Request = namedtuple("Request", ["environ", "start_response"])


class ASAPMiddleware(object):
class ASAPMiddleware:
def __init__(self, handler: Any, settings: Any) -> None:
self._next = handler
self._backend = WSGIBackend(settings)
Expand Down
Loading