From 915a195c6d74e717cf6459950177ed6f5abb532f Mon Sep 17 00:00:00 2001 From: Sergei Maertens Date: Wed, 7 Feb 2024 14:20:59 +0100 Subject: [PATCH 1/2] :boom: Drop support for older versions * Dropped support for python older than 3.10 * Drop support for mozilla-django-oidc older than 3.0 * Drop support for non-maintained Django versions * Updated CI actions to non-deprecated versions * Added typing marker to project --- .github/workflows/ci.yml | 23 +++++++++++++---------- .github/workflows/code_quality.yml | 6 +++--- CHANGELOG.rst | 16 ++++++++++++++++ MANIFEST.in | 1 + README.rst | 10 ++-------- mozilla_django_oidc_db/py.typed | 0 setup.cfg | 16 +++++----------- tox.ini | 11 +++++------ 8 files changed, 45 insertions(+), 38 deletions(-) create mode 100644 mozilla_django_oidc_db/py.typed diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a3d48ab..705c9f5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,14 +15,17 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python: ['3.8', '3.9', '3.10', '3.11'] - django: ['3.2', '4.1', '4.2'] - mozilla_django_oidc: ['2.0', '3.0'] + python: ['3.10', '3.11', '3.12'] + django: ['3.2', '4.2'] + mozilla_django_oidc: ['3.0', '4.0'] exclude: - - django: ['4.1', '4.2'] - mozilla_django_oidc: '2.0' - python: '3.11' django: '3.2' + - python: '3.12' + django: '3.2' + # support for django 4.2 was added in 4.0 + - django: '4.2' + mozilla_django_oidc: '3.0' name: Run the test suite (Python ${{ matrix.python }}, Django ${{ matrix.django }}, mozilla-django-oidc ${{ matrix.mozilla_django_oidc }}) @@ -37,8 +40,8 @@ jobs: options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 with: python-version: ${{ matrix.python }} @@ -65,10 +68,10 @@ jobs: if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 with: - python-version: '3.8' + python-version: '3.10' - name: Build sdist and wheel run: | diff --git a/.github/workflows/code_quality.yml b/.github/workflows/code_quality.yml index 98fcd07..1a50bb0 100644 --- a/.github/workflows/code_quality.yml +++ b/.github/workflows/code_quality.yml @@ -21,10 +21,10 @@ jobs: matrix: toxenv: [isort, black] steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 with: - python-version: '3.9' + python-version: '3.10' - name: Install dependencies run: pip install tox - run: tox diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 4c7c00a..0a7569a 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,6 +2,22 @@ Changelog ========= +0.15.0 (unreleased) +=================== + +**Breaking changes** + +* Dropped support for Django 4.1 +* Dropped support for Python 3.8 and 3.9 +* Dropped support for mozilla-django-oidc 2.0 + +**New features** + +* Confirmed support for mozilla-django-oidc 4.0 +* Confirmed support for Python 3.12 +* Added more typehints +* ... + 0.14.1 (2024-01-12) =================== diff --git a/MANIFEST.in b/MANIFEST.in index 9f57449..c10c339 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,5 +1,6 @@ include *.rst include LICENSE +include mozilla_django_oidc_db/py.typed recursive-include mozilla_django_oidc_db *.html recursive-include mozilla_django_oidc_db *.txt recursive-include mozilla_django_oidc_db *.po diff --git a/README.rst b/README.rst index 7c4c05e..a56863a 100644 --- a/README.rst +++ b/README.rst @@ -10,7 +10,6 @@ Welcome to mozilla_django_oidc_db's documentation! :Version: 0.14.1 :Source: https://github.com/maykinmedia/mozilla-django-oidc-db :Keywords: OIDC, django, database, authentication -:PythonVersion: 3.7 |build-status| |coverage| |black| @@ -44,13 +43,8 @@ Installation Requirements ------------ -* Python 3.7 or above -* setuptools 30.4.0 or above -* Django 3.2 or newer -* A database supporting ``models.JSONField`` -* If you're using Django 4.1 or newer, you need at least mozilla-django-oidc 3.0. - 2.0 is still supported with Django 3.2. - +* See the badges for the supported Python and Django versions +* A PostgreSQL database (we use ``django.contrib.postgres.fields.ArrayField``) Install ------- diff --git a/mozilla_django_oidc_db/py.typed b/mozilla_django_oidc_db/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/setup.cfg b/setup.cfg index ad974f0..bcda27d 100644 --- a/setup.cfg +++ b/setup.cfg @@ -14,28 +14,27 @@ classifiers = Development Status :: 4 - Beta Framework :: Django Framework :: Django :: 3.2 - Framework :: Django :: 4.1 Framework :: Django :: 4.2 Intended Audience :: Developers Operating System :: Unix Operating System :: MacOS Operating System :: Microsoft :: Windows - Programming Language :: Python :: 3.8 - Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 Programming Language :: Python :: 3.11 + Programming Language :: Python :: 3.12 Topic :: Software Development :: Libraries :: Python Modules [options] zip_safe = False include_package_data = True packages = find: +python_requires = >=3.10 install_requires = Django >=3.2 django-jsonform django-solo glom - mozilla-django-oidc >=2.0.0 + mozilla-django-oidc >=3.0.0 tests_require = psycopg2 pytest @@ -77,14 +76,9 @@ release = test=pytest [isort] +profile = black combine_as_imports = true -default_section = THIRDPARTY -include_trailing_comma = true -line_length = 88 -multi_line_output = 3 -skip = env,.tox,.history,.eggs -; skip_glob = -known_django=django +known_django = django known_first_party=mozilla_django_oidc_db sections=FUTURE,STDLIB,DJANGO,THIRDPARTY,FIRSTPARTY,LOCALFOLDER diff --git a/tox.ini b/tox.ini index 4b6d559..1910d63 100644 --- a/tox.ini +++ b/tox.ini @@ -1,7 +1,7 @@ [tox] envlist = - py{38,39,310}-django32-mozilla_django_oidc{20} - py{38,39,310,311}-django{41,42}-mozilla_django_oidc{30} + py310-django32-mozilla_django_oidc{30,40} + py{310,311,312}-django42-mozilla_django_oidc40 isort black ; docs @@ -13,25 +13,24 @@ python = 3.9: py39 3.10: py310 3.11: py311 + 3.12: py312 [gh-actions:env] DJANGO = 3.2: django32 - 4.1: django41 4.2: django42 MOZILLA_DJANGO_OIDC = - 2.0: mozilla_django_oidc20 3.0: mozilla_django_oidc30 + 4.0: mozilla_django_oidc40 [testenv] extras = tests coverage deps = django32: Django~=3.2.0 - django41: Django~=4.1.0 django42: Django~=4.2.0 - mozilla_django_oidc20: mozilla-django-oidc~=2.0.0 mozilla_django_oidc30: mozilla-django-oidc~=3.0.0 + mozilla_django_oidc40: mozilla-django-oidc~=4.0.0 passenv = PGUSER PGDATABASE From 3fb1b3cd49849be566326e1181b7f17178ddc24b Mon Sep 17 00:00:00 2001 From: Sergei Maertens Date: Wed, 7 Feb 2024 14:39:42 +0100 Subject: [PATCH 2/2] :label: Add more type annotations Made the solo config mixin a generic so that subclasses can provide more narrow type annotations. --- mozilla_django_oidc_db/backends.py | 33 +++++++++++++++++------------- mozilla_django_oidc_db/mixins.py | 22 +++++++++++++------- 2 files changed, 34 insertions(+), 21 deletions(-) diff --git a/mozilla_django_oidc_db/backends.py b/mozilla_django_oidc_db/backends.py index aeab936..5de8177 100644 --- a/mozilla_django_oidc_db/backends.py +++ b/mozilla_django_oidc_db/backends.py @@ -1,6 +1,6 @@ import fnmatch import logging -from typing import Any, Dict +from typing import Any, TypeVar, cast from django.contrib.auth import get_user_model from django.contrib.auth.models import Group @@ -12,14 +12,16 @@ ) from .mixins import GetAttributeMixin, SoloConfigMixin -from .models import UserInformationClaimsSources +from .models import OpenIDConnectConfig, UserInformationClaimsSources from .utils import obfuscate_claims logger = logging.getLogger(__name__) +T = TypeVar("T", bound=OpenIDConnectConfig) + class OIDCAuthenticationBackend( - GetAttributeMixin, SoloConfigMixin, _OIDCAuthenticationBackend + GetAttributeMixin, SoloConfigMixin[T], _OIDCAuthenticationBackend ): """ Modifies the default OIDCAuthenticationBackend to use a configurable claim @@ -80,7 +82,7 @@ def authenticate(self, *args, **kwargs): return super().authenticate(*args, **kwargs) - def get_user_instance_values(self, claims) -> Dict[str, Any]: + def get_user_instance_values(self, claims) -> dict[str, Any]: """ Map the names and values of the claims to the fields of the User model """ @@ -152,22 +154,25 @@ def update_user(self, user, claims): return user - def update_user_superuser_status(self, user, claims): + def update_user_superuser_status(self, user, claims) -> None: """ Assigns superuser status to the user if the user is a member of at least one specific group. Superuser status is explicitly removed if the user is not or no longer member of at least one of these groups. """ groups_claim = self.config.groups_claim - superuser_group_names = self.config.superuser_group_names - - if superuser_group_names: - claim_groups = glom(claims, groups_claim, default=[]) - if set(superuser_group_names) & set(claim_groups): - user.is_superuser = True - else: - user.is_superuser = False - user.save() + # can't do an isinstance check here + superuser_group_names = cast(list[str], self.config.superuser_group_names) + + if not superuser_group_names: + return + + claim_groups = glom(claims, groups_claim, default=[]) + if set(superuser_group_names) & set(claim_groups): + user.is_superuser = True + else: + user.is_superuser = False + user.save() def update_user_groups(self, user, claims): """ diff --git a/mozilla_django_oidc_db/mixins.py b/mozilla_django_oidc_db/mixins.py index e57af17..4ae1038 100644 --- a/mozilla_django_oidc_db/mixins.py +++ b/mozilla_django_oidc_db/mixins.py @@ -1,18 +1,26 @@ +from typing import ClassVar, Generic, TypeVar, cast + from mozilla_django_oidc.utils import import_from_settings -from .models import OpenIDConnectConfig +from .models import OpenIDConnectConfig, OpenIDConnectConfigBase + +T = TypeVar("T", bound=OpenIDConnectConfigBase) -class SoloConfigMixin: - config_class = OpenIDConnectConfig +class SoloConfigMixin(Generic[T]): + config_class: ClassVar[type[OpenIDConnectConfigBase]] = OpenIDConnectConfig + _solo_config: T @property - def config(self): + def config(self) -> T: if not hasattr(self, "_solo_config"): - self._solo_config = self.config_class.get_solo() + # django-solo and type checking is challenging, but a new release is on the + # way and should fix that :fingers_crossed: + config = self.config_class.get_solo() + self._solo_config = cast(T, config) return self._solo_config - def refresh_config(self): + def refresh_config(self) -> None: """ Refreshes the cached config on the instance, required for middleware since middleware is only instantiated once (during the Django startup phase) @@ -34,7 +42,7 @@ def get_settings(self, attr, *args): class GetAttributeMixin: - def __getattribute__(self, attr): + def __getattribute__(self, attr: str): """ Mixin used to avoid calls to the config model on __init__ and instead do these calls runtime