From 110b73f7d8632c197945b4524578e003913cf21f Mon Sep 17 00:00:00 2001 From: Dulmandakh Date: Wed, 14 Aug 2024 16:09:42 +0800 Subject: [PATCH 01/14] compat with LoginRequiredMiddleware and login_not_required --- oauth2_provider/compat.py | 8 ++++++++ oauth2_provider/views/base.py | 3 +++ oauth2_provider/views/oidc.py | 8 +++++--- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/oauth2_provider/compat.py b/oauth2_provider/compat.py index 0c83cb37a..5c9c2025a 100644 --- a/oauth2_provider/compat.py +++ b/oauth2_provider/compat.py @@ -2,3 +2,11 @@ The `compat` module provides support for backwards compatibility with older versions of Django and Python. """ +try: + # Django 5.1 introduced LoginRequiredMiddleware, and login_not_required decorator + from django.contrib.auth.decorators import login_not_required +except ImportError: + def login_not_required(view_func): + return view_func + +__all__ = ['login_not_required'] diff --git a/oauth2_provider/views/base.py b/oauth2_provider/views/base.py index 52cb151d5..69598b16c 100644 --- a/oauth2_provider/views/base.py +++ b/oauth2_provider/views/base.py @@ -13,6 +13,7 @@ from django.views.decorators.debug import sensitive_post_parameters from django.views.generic import FormView, View +from ..compat import login_not_required from ..exceptions import OAuthToolkitError from ..forms import AllowForm from ..http import OAuth2ResponseRedirect @@ -274,6 +275,7 @@ def handle_no_permission(self): @method_decorator(csrf_exempt, name="dispatch") +@method_decorator(login_not_required, name="dispatch") class TokenView(OAuthLibMixin, View): """ Implements an endpoint to provide access tokens @@ -301,6 +303,7 @@ def post(self, request, *args, **kwargs): @method_decorator(csrf_exempt, name="dispatch") +@method_decorator(login_not_required, name="dispatch") class RevokeTokenView(OAuthLibMixin, View): """ Implements an endpoint to revoke access or refresh tokens diff --git a/oauth2_provider/views/oidc.py b/oauth2_provider/views/oidc.py index c9d10c25e..22f5da6b5 100644 --- a/oauth2_provider/views/oidc.py +++ b/oauth2_provider/views/oidc.py @@ -14,6 +14,7 @@ from jwcrypto.jwt import JWTExpired from oauthlib.common import add_params_to_uri +from ..compat import login_not_required from ..exceptions import ( ClientIdMissmatch, InvalidIDTokenError, @@ -38,7 +39,7 @@ Application = get_application_model() - +@method_decorator(login_not_required, name="dispatch") class ConnectDiscoveryInfoView(OIDCOnlyMixin, View): """ View used to show oidc provider configuration information per @@ -105,7 +106,7 @@ def get(self, request, *args, **kwargs): response["Access-Control-Allow-Origin"] = "*" return response - +@method_decorator(login_not_required, name="dispatch") class JwksInfoView(OIDCOnlyMixin, View): """ View used to show oidc json web key set document @@ -134,6 +135,7 @@ def get(self, request, *args, **kwargs): @method_decorator(csrf_exempt, name="dispatch") +@method_decorator(login_not_required, name="dispatch") class UserInfoView(OIDCOnlyMixin, OAuthLibMixin, View): """ View used to show Claims about the authenticated End-User @@ -210,7 +212,7 @@ def _validate_claims(request, claims): return True - +@method_decorator(login_not_required, name="dispatch") class RPInitiatedLogoutView(OIDCLogoutOnlyMixin, FormView): template_name = "oauth2_provider/logout_confirm.html" form_class = ConfirmLogoutForm From 3454b90646d8c3c72a3099f9f2e4667a9cb0cc06 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 14 Aug 2024 08:11:00 +0000 Subject: [PATCH 02/14] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- oauth2_provider/compat.py | 5 ++++- oauth2_provider/views/oidc.py | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/oauth2_provider/compat.py b/oauth2_provider/compat.py index 5c9c2025a..846e32d0e 100644 --- a/oauth2_provider/compat.py +++ b/oauth2_provider/compat.py @@ -2,11 +2,14 @@ The `compat` module provides support for backwards compatibility with older versions of Django and Python. """ + try: # Django 5.1 introduced LoginRequiredMiddleware, and login_not_required decorator from django.contrib.auth.decorators import login_not_required except ImportError: + def login_not_required(view_func): return view_func -__all__ = ['login_not_required'] + +__all__ = ["login_not_required"] diff --git a/oauth2_provider/views/oidc.py b/oauth2_provider/views/oidc.py index 22f5da6b5..c746c30ce 100644 --- a/oauth2_provider/views/oidc.py +++ b/oauth2_provider/views/oidc.py @@ -39,6 +39,7 @@ Application = get_application_model() + @method_decorator(login_not_required, name="dispatch") class ConnectDiscoveryInfoView(OIDCOnlyMixin, View): """ @@ -106,6 +107,7 @@ def get(self, request, *args, **kwargs): response["Access-Control-Allow-Origin"] = "*" return response + @method_decorator(login_not_required, name="dispatch") class JwksInfoView(OIDCOnlyMixin, View): """ @@ -212,6 +214,7 @@ def _validate_claims(request, claims): return True + @method_decorator(login_not_required, name="dispatch") class RPInitiatedLogoutView(OIDCLogoutOnlyMixin, FormView): template_name = "oauth2_provider/logout_confirm.html" From 0970bf9335585018c5c0a00f01c3427fd58f87e7 Mon Sep 17 00:00:00 2001 From: Dulmandakh Date: Wed, 14 Aug 2024 16:16:34 +0800 Subject: [PATCH 03/14] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 99be61e48..d3883f3ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [unreleased] ### Added * Add migration to include `token_checksum` field in AbstractAccessToken model. +* Added compatibility with LoginRequiredMiddleware introduced in Django 5.1 * #1404 Add a new setting `REFRESH_TOKEN_REUSE_PROTECTION` ### Changed * Update token to TextField from CharField with 255 character limit and SHA-256 checksum in AbstractAccessToken model. Removing the 255 character limit enables supporting JWT tokens with additional claims From f97199b8b9c9f955ab9722b869a7304b58bd0ad5 Mon Sep 17 00:00:00 2001 From: Dulmandakh Date: Wed, 14 Aug 2024 16:22:02 +0800 Subject: [PATCH 04/14] update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d3883f3ea..2105a9bf5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [unreleased] ### Added * Add migration to include `token_checksum` field in AbstractAccessToken model. -* Added compatibility with LoginRequiredMiddleware introduced in Django 5.1 +* Added compatibility with `LoginRequiredMiddleware` introduced in Django 5.1 * #1404 Add a new setting `REFRESH_TOKEN_REUSE_PROTECTION` ### Changed * Update token to TextField from CharField with 255 character limit and SHA-256 checksum in AbstractAccessToken model. Removing the 255 character limit enables supporting JWT tokens with additional claims From 79e327f529d4e1af6ff8ae148b2951cd8600453a Mon Sep 17 00:00:00 2001 From: Dulmandakh Date: Thu, 15 Aug 2024 09:46:28 +0800 Subject: [PATCH 05/14] django_login_required_middleware fixture --- oauth2_provider/compat.py | 1 - oauth2_provider/views/introspect.py | 6 ++++-- tests/conftest.py | 7 +++++++ tox.ini | 5 ++++- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/oauth2_provider/compat.py b/oauth2_provider/compat.py index 846e32d0e..fb48fbba4 100644 --- a/oauth2_provider/compat.py +++ b/oauth2_provider/compat.py @@ -7,7 +7,6 @@ # Django 5.1 introduced LoginRequiredMiddleware, and login_not_required decorator from django.contrib.auth.decorators import login_not_required except ImportError: - def login_not_required(view_func): return view_func diff --git a/oauth2_provider/views/introspect.py b/oauth2_provider/views/introspect.py index 05a77909f..5474c3a7e 100644 --- a/oauth2_provider/views/introspect.py +++ b/oauth2_provider/views/introspect.py @@ -6,11 +6,13 @@ from django.utils.decorators import method_decorator from django.views.decorators.csrf import csrf_exempt -from oauth2_provider.models import get_access_token_model -from oauth2_provider.views.generic import ClientProtectedScopedResourceView +from ..compat import login_not_required +from ..models import get_access_token_model +from ..views.generic import ClientProtectedScopedResourceView @method_decorator(csrf_exempt, name="dispatch") +@method_decorator(login_not_required, name="dispatch") class IntrospectTokenView(ClientProtectedScopedResourceView): """ Implements an endpoint for token introspection based diff --git a/tests/conftest.py b/tests/conftest.py index eff48f7fb..d4cf9edfe 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -4,6 +4,7 @@ from urllib.parse import parse_qs, urlparse import pytest +from django import VERSION from django.conf import settings as test_settings from django.contrib.auth import get_user_model from django.urls import reverse @@ -294,3 +295,9 @@ def oidc_non_confidential_tokens(oauth2_settings, public_application, test_user, "openid", "http://other.org", ) + +@pytest.fixture(autouse=True) +def django_login_required_middleware(settings): + # Django 5.1 introduced LoginRequiredMiddleware + if VERSION[0] >= 5 and VERSION[1] >= 1: + settings.MIDDLEWARE = [*settings.MIDDLEWARE, "django.contrib.auth.middleware.LoginRequiredMiddleware"] diff --git a/tox.ini b/tox.ini index ba97bd113..0ce8da212 100644 --- a/tox.ini +++ b/tox.ini @@ -10,6 +10,7 @@ envlist = py{38,39,310,311}-dj41, py{38,39,310,311,312}-dj42, py{310,311,312}-dj50, + py{310,311,312}-dj51, py{310,311,312}-djmain, [gh-actions] @@ -28,6 +29,7 @@ DJANGO = 4.1: dj41 4.2: dj42 5.0: dj50 + 5.1: dj51 main: djmain [pytest] @@ -55,7 +57,8 @@ deps = dj40: Django>=4.0.0,<4.1 dj41: Django>=4.1,<4.2 dj42: Django>=4.2,<4.3 - dj50: Django>=5.0b1,<5.1 + dj50: Django>=5.0,<5.1 + dj51: Django>=5.1,<5.2 djmain: https://github.com/django/django/archive/main.tar.gz djangorestframework oauthlib>=3.1.0 From 6696fb6b73a08b4b28d77d23f6d593a0ca9ca520 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 15 Aug 2024 01:46:50 +0000 Subject: [PATCH 06/14] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- oauth2_provider/compat.py | 1 + tests/conftest.py | 1 + 2 files changed, 2 insertions(+) diff --git a/oauth2_provider/compat.py b/oauth2_provider/compat.py index fb48fbba4..846e32d0e 100644 --- a/oauth2_provider/compat.py +++ b/oauth2_provider/compat.py @@ -7,6 +7,7 @@ # Django 5.1 introduced LoginRequiredMiddleware, and login_not_required decorator from django.contrib.auth.decorators import login_not_required except ImportError: + def login_not_required(view_func): return view_func diff --git a/tests/conftest.py b/tests/conftest.py index d4cf9edfe..afc6d29fd 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -296,6 +296,7 @@ def oidc_non_confidential_tokens(oauth2_settings, public_application, test_user, "http://other.org", ) + @pytest.fixture(autouse=True) def django_login_required_middleware(settings): # Django 5.1 introduced LoginRequiredMiddleware From 2877e8eb1b89a89951f9539d00b6b16cd8a59b4d Mon Sep 17 00:00:00 2001 From: Dulmandakh Date: Thu, 15 Aug 2024 09:48:22 +0800 Subject: [PATCH 07/14] sss --- .github/workflows/test.yml | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 627aacf97..85c873972 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,8 +10,8 @@ jobs: fail-fast: false matrix: python-version: - - '3.8' - - '3.9' + # - '3.8' + # - '3.9' - '3.10' - '3.11' - '3.12' @@ -21,19 +21,20 @@ jobs: - '4.1' - '4.2' - '5.0' + - '5.1' - 'main' exclude: # https://docs.djangoproject.com/en/dev/faq/install/#what-python-version-can-i-use-with-django # < Python 3.10 is not supported by Django 5.0+ - - python-version: '3.8' - django-version: '5.0' - - python-version: '3.9' - django-version: '5.0' - - python-version: '3.8' - django-version: 'main' - - python-version: '3.9' - django-version: 'main' + # - python-version: '3.8' + # django-version: '5.0' + # - python-version: '3.9' + # django-version: '5.0' + # - python-version: '3.8' + # django-version: 'main' + # - python-version: '3.9' + # django-version: 'main' # Python 3.12 is not supported by Django < 5.0 - python-version: '3.12' From 03bd0cdd7e464533f6e7f97c2b26c0ac6229ac16 Mon Sep 17 00:00:00 2001 From: Dulmandakh Date: Thu, 15 Aug 2024 09:49:51 +0800 Subject: [PATCH 08/14] dd --- .github/workflows/test.yml | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 85c873972..f4a8be177 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -16,14 +16,14 @@ jobs: - '3.11' - '3.12' django-version: - - '3.2' - - '4.0' - - '4.1' + # - '3.2' + # - '4.0' + # - '4.1' - '4.2' - '5.0' - '5.1' - 'main' - exclude: + # exclude: # https://docs.djangoproject.com/en/dev/faq/install/#what-python-version-can-i-use-with-django # < Python 3.10 is not supported by Django 5.0+ @@ -36,15 +36,15 @@ jobs: # - python-version: '3.9' # django-version: 'main' - # Python 3.12 is not supported by Django < 5.0 - - python-version: '3.12' - django-version: '3.2' - - python-version: '3.12' - django-version: '4.0' - - python-version: '3.12' - django-version: '4.1' - - python-version: '3.12' - django-version: '4.2' + # # Python 3.12 is not supported by Django < 5.0 + # - python-version: '3.12' + # django-version: '3.2' + # - python-version: '3.12' + # django-version: '4.0' + # - python-version: '3.12' + # django-version: '4.1' + # - python-version: '3.12' + # django-version: '4.2' steps: - uses: actions/checkout@v4 From 0e20893757915fa37a5e9f2661d783499776f015 Mon Sep 17 00:00:00 2001 From: Dulmandakh Date: Thu, 15 Aug 2024 10:19:14 +0800 Subject: [PATCH 09/14] exclude rest framework from login required middleware --- tests/conftest.py | 5 ++++- tests/test_rest_framework.py | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/conftest.py b/tests/conftest.py index afc6d29fd..ef6898b05 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -298,7 +298,10 @@ def oidc_non_confidential_tokens(oauth2_settings, public_application, test_user, @pytest.fixture(autouse=True) -def django_login_required_middleware(settings): +def django_login_required_middleware(settings, request): + if 'nologinrequiredmiddleware' in request.keywords: + return + # Django 5.1 introduced LoginRequiredMiddleware if VERSION[0] >= 5 and VERSION[1] >= 1: settings.MIDDLEWARE = [*settings.MIDDLEWARE, "django.contrib.auth.middleware.LoginRequiredMiddleware"] diff --git a/tests/test_rest_framework.py b/tests/test_rest_framework.py index 632c62e26..84b4ad7d9 100644 --- a/tests/test_rest_framework.py +++ b/tests/test_rest_framework.py @@ -127,6 +127,7 @@ class AuthenticationNoneOAuth2View(MockView): @override_settings(ROOT_URLCONF=__name__) +@pytest.mark.nologinrequiredmiddleware @pytest.mark.usefixtures("oauth2_settings") @pytest.mark.oauth2_settings(presets.REST_FRAMEWORK_SCOPES) class TestOAuth2Authentication(TestCase): From 20a7772700b453ba83f30a5d5b1fc44abd05af74 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 15 Aug 2024 02:19:35 +0000 Subject: [PATCH 10/14] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/conftest.py b/tests/conftest.py index ef6898b05..2510025ce 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -299,7 +299,7 @@ def oidc_non_confidential_tokens(oauth2_settings, public_application, test_user, @pytest.fixture(autouse=True) def django_login_required_middleware(settings, request): - if 'nologinrequiredmiddleware' in request.keywords: + if "nologinrequiredmiddleware" in request.keywords: return # Django 5.1 introduced LoginRequiredMiddleware From 15d381fdc3fac6b22fc669bc25f82a162bc2a1d5 Mon Sep 17 00:00:00 2001 From: Dulmandakh Date: Thu, 15 Aug 2024 10:27:52 +0800 Subject: [PATCH 11/14] pytest markers --- pyproject.toml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 884f7aec4..4b7305f95 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,3 +15,9 @@ skip = '.git,package-lock.json,locale,AUTHORS,tox.ini' check-hidden = true ignore-regex = '.*pragma: codespell-ignore.*' ignore-words-list = 'assertIn' + +[tool.pytest.ini_options] +markers = [ + "nologinrequiredmiddleware", + "oauth2_settings", +] From b687f1b7f7191290752a1357bb90930915fd128d Mon Sep 17 00:00:00 2001 From: Dulmandakh Date: Thu, 15 Aug 2024 11:37:36 +0800 Subject: [PATCH 12/14] all tests pass --- oauth2_provider/views/base.py | 3 ++- tests/test_introspection_auth.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/oauth2_provider/views/base.py b/oauth2_provider/views/base.py index 69598b16c..f1d9db73d 100644 --- a/oauth2_provider/views/base.py +++ b/oauth2_provider/views/base.py @@ -26,7 +26,8 @@ log = logging.getLogger("oauth2_provider") - +# login_not_required decorator to bypass LoginRequiredMiddleware +@method_decorator(login_not_required, name="dispatch") class BaseAuthorizationView(LoginRequiredMixin, OAuthLibMixin, View): """ Implements a generic endpoint to handle *Authorization Requests* as in :rfc:`4.1.1`. The view diff --git a/tests/test_introspection_auth.py b/tests/test_introspection_auth.py index 100ef064e..d96a013e3 100644 --- a/tests/test_introspection_auth.py +++ b/tests/test_introspection_auth.py @@ -11,6 +11,7 @@ from django.utils import timezone from oauthlib.common import Request +from oauth2_provider.compat import login_not_required from oauth2_provider.models import get_access_token_model, get_application_model from oauth2_provider.oauth2_validators import OAuth2Validator from oauth2_provider.settings import oauth2_settings @@ -93,7 +94,7 @@ def mocked_introspect_request_short_living_token(url, data, *args, **kwargs): urlpatterns = [ path("oauth2/", include("oauth2_provider.urls")), - path("oauth2-test-resource/", ScopeResourceView.as_view()), + path("oauth2-test-resource/", login_not_required(ScopeResourceView.as_view())), ] From 443f49c33d7ac65860e4864b58a399986669e1d9 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 15 Aug 2024 03:37:53 +0000 Subject: [PATCH 13/14] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- oauth2_provider/views/base.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/oauth2_provider/views/base.py b/oauth2_provider/views/base.py index f1d9db73d..d2644f35f 100644 --- a/oauth2_provider/views/base.py +++ b/oauth2_provider/views/base.py @@ -26,7 +26,8 @@ log = logging.getLogger("oauth2_provider") -# login_not_required decorator to bypass LoginRequiredMiddleware + +# login_not_required decorator to bypass LoginRequiredMiddleware @method_decorator(login_not_required, name="dispatch") class BaseAuthorizationView(LoginRequiredMixin, OAuthLibMixin, View): """ From 53f2888862c622faf977ed8ce00b922352c3706d Mon Sep 17 00:00:00 2001 From: Dulmandakh Date: Thu, 15 Aug 2024 11:45:44 +0800 Subject: [PATCH 14/14] fix coverage --- pyproject.toml | 6 ------ tox.ini | 1 + 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 4b7305f95..884f7aec4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,9 +15,3 @@ skip = '.git,package-lock.json,locale,AUTHORS,tox.ini' check-hidden = true ignore-regex = '.*pragma: codespell-ignore.*' ignore-words-list = 'assertIn' - -[tool.pytest.ini_options] -markers = [ - "nologinrequiredmiddleware", - "oauth2_settings", -] diff --git a/tox.ini b/tox.ini index 0ce8da212..cbfd99bd7 100644 --- a/tox.ini +++ b/tox.ini @@ -41,6 +41,7 @@ addopts = -s markers = oauth2_settings: Custom OAuth2 settings to use - use with oauth2_settings fixture + nologinrequiredmiddleware [testenv] commands =