diff --git a/CHANGELOG.rst b/CHANGELOG.rst index e12f336fd5b173aff9b272f1ff9ca0ce3ae2a689..709b59e44a5b5bb9170d57d593fe1ea7930f612d 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -9,6 +9,11 @@ and this project adheres to `Semantic Versioning`_. Unreleased ---------- +Added +~~~~~ + +* [OAuth] Allow apps to fill in their own claim data matching their scopes + `2.2.1_ – 2021-12-02 -------------------- diff --git a/aleksis/core/apps.py b/aleksis/core/apps.py index 2af102477a08571dfe5f3968eefcc8b3e805e0ff..520a5fc435fe5cd8ef55435d3c680f978250abb0 100644 --- a/aleksis/core/apps.py +++ b/aleksis/core/apps.py @@ -9,6 +9,7 @@ from django.utils.translation import gettext as _ from dynamic_preferences.registries import preference_models from health_check.plugins import plugin_dir +from oauthlib.common import Request as OauthlibRequest from .registries import ( group_preferences_registry, @@ -156,3 +157,49 @@ class CoreConfig(AppConfig): "groups": _("Groups"), } return scopes + + @classmethod + def get_additional_claims(cls, scopes: list[str], request: OauthlibRequest) -> dict[str, Any]: + django_request = HttpRequest() + django_request.META = request.headers + + claims = { + "preferred_username": request.user.username, + } + + if "profile" in scopes: + if has_person(request.user): + claims["given_name"] = request.user.person.first_name + claims["family_name"] = request.user.person.last_name + claims["profile"] = django_request.build_absolute_uri( + request.user.person.get_absolute_url() + ) + if request.user.person.photo: + claims["picture"] = django_request.build_absolute_uri( + request.user.person.photo.url + ) + else: + claims["given_name"] = request.user.first_name + claims["family_name"] = request.user.last_name + + if "email" in scopes: + if has_person(request.user): + claims["email"] = request.user.person.email + else: + claims["email"] = request.user.email + + if "address" in scopes and has_person(request.user): + claims["address"] = { + "street_address": request.user.person.street + + " " + + request.user.person.housenumber, + "locality": request.user.person.place, + "postal_code": request.user.person.postal_code, + } + + if "groups" in scopes and has_person(request.user): + claims["groups"] = list( + request.user.person.member_of.values_list("name", flat=True).all() + ) + + return claims diff --git a/aleksis/core/util/apps.py b/aleksis/core/util/apps.py index 38b5d27946ad4542694298571a6bd4f36da325ae..5b3898dd835506ade932ebccf30e4cf0de15486e 100644 --- a/aleksis/core/util/apps.py +++ b/aleksis/core/util/apps.py @@ -8,6 +8,7 @@ from django.http import HttpRequest from dynamic_preferences.signals import preference_updated from license_expression import Licensing +from oauthlib.common import Request as OauthlibRequest from spdx_license_list import LICENSES from .core_helpers import copyright_years @@ -244,6 +245,11 @@ class AppConfig(django.apps.AppConfig): """Return a list of all OAuth scopes to always include for this request and application.""" return [] + @classmethod + def get_additional_claims(cls, scopes: list[str], request: OauthlibRequest) -> dict[str, Any]: + """Get claim data for requested scopes.""" + return {} + def _maintain_default_data(self): from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType diff --git a/aleksis/core/util/auth_helpers.py b/aleksis/core/util/auth_helpers.py index e0cfcc778a55563e91ecf4d0d1027227e01a99b3..056b5156da68233d3b3ee2378ccffdff95cfbcd6 100644 --- a/aleksis/core/util/auth_helpers.py +++ b/aleksis/core/util/auth_helpers.py @@ -1,6 +1,6 @@ """Helpers/overrides for django-allauth.""" -from typing import Optional +from typing import Any, Optional from django.conf import settings from django.http import HttpRequest @@ -16,7 +16,6 @@ from oauth2_provider.views.mixins import ( from oauthlib.common import Request as OauthlibRequest from .apps import AppConfig -from .core_helpers import get_site_preferences, has_person class OurSocialAccountAdapter(DefaultSocialAccountAdapter): @@ -43,52 +42,16 @@ class OurAccountAdapter(DefaultAccountAdapter): class CustomOAuth2Validator(OAuth2Validator): - def get_additional_claims(self, request): - django_request = HttpRequest() - django_request.META = request.headers - + def get_additional_claims(self, request: OauthlibRequest) -> dict[str, Any]: + # Pull together scopes from request and from access token scopes = request.scopes.copy() if request.access_token: scopes += request.access_token.scope.split(" ") - claims = { - "preferred_username": request.user.username, - } - - if "profile" in scopes: - if has_person(request.user): - claims["given_name"] = request.user.person.first_name - claims["family_name"] = request.user.person.last_name - claims["profile"] = django_request.build_absolute_uri( - request.user.person.get_absolute_url() - ) - if request.user.person.photo: - claims["picture"] = django_request.build_absolute_uri( - request.user.person.photo.url - ) - else: - claims["given_name"] = request.user.first_name - claims["family_name"] = request.user.last_name - - if "email" in scopes: - if has_person(request.user): - claims["email"] = request.user.person.email - else: - claims["email"] = request.user.email - - if "address" in scopes and has_person(request.user): - claims["address"] = { - "street_address": request.user.person.street - + " " - + request.user.person.housenumber, - "locality": request.user.person.place, - "postal_code": request.user.person.postal_code, - } - - if "groups" in scopes and has_person(request.user): - claims["groups"] = list( - request.user.person.member_of.values_list("name", flat=True).all() - ) + claims = {} + # Pull together claim data from all apps + for app in AppConfig.__subclasses__(): + claims.update(app.get_additional_claims(scopes, request)) return claims