[mailman3] Add patch for django_mailman3
Till https://src.fedoraproject.org/rpms/python-django-mailman3/pull-request/2 is merged let's apply the patch directly. Signed-off-by: Michal Konecny <mkonecny@redhat.com>
This commit is contained in:
parent
4f35e96a31
commit
d0e06d1ab0
2 changed files with 430 additions and 0 deletions
|
@ -0,0 +1,422 @@
|
|||
diff --git a/django_mailman3/lib/auth/fedora/provider.py b/django_mailman3/lib/auth/fedora/provider.py
|
||||
index b371fbb..966dda0 100644
|
||||
--- a/django_mailman3/lib/auth/fedora/provider.py
|
||||
+++ b/django_mailman3/lib/auth/fedora/provider.py
|
||||
@@ -18,59 +18,71 @@
|
||||
#
|
||||
# Author: Aurelien Bompard <abompard@fedoraproject.org>
|
||||
#
|
||||
+import logging
|
||||
|
||||
-from urllib.parse import urlparse
|
||||
-
|
||||
-from django.urls import reverse
|
||||
-from django.utils.http import urlencode
|
||||
+from django.conf import settings as django_settings
|
||||
|
||||
from allauth.account.models import EmailAddress
|
||||
from allauth.socialaccount import providers
|
||||
-from allauth.socialaccount.providers.openid.provider import (
|
||||
- OpenIDAccount, OpenIDProvider)
|
||||
-from allauth.socialaccount.providers.openid.utils import (
|
||||
- get_email_from_response)
|
||||
+from allauth.socialaccount.providers.base import ProviderAccount
|
||||
+from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider
|
||||
|
||||
|
||||
-def extract_username(url):
|
||||
- return urlparse(url).netloc.split('.')[0]
|
||||
+_log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
-class FedoraAccount(OpenIDAccount):
|
||||
+class FedoraAccount(ProviderAccount):
|
||||
|
||||
def get_brand(self):
|
||||
return dict(id='fedora', name='Fedora')
|
||||
|
||||
def to_str(self):
|
||||
- return extract_username(self.account.uid)
|
||||
+ return self.account.extra_data.get("preferred_username")
|
||||
|
||||
|
||||
-class FedoraProvider(OpenIDProvider):
|
||||
+class FedoraProvider(OAuth2Provider):
|
||||
id = 'fedora'
|
||||
name = 'Fedora'
|
||||
account_class = FedoraAccount
|
||||
- endpoint = 'https://id.fedoraproject.org'
|
||||
- login_view = 'fedora_login'
|
||||
|
||||
- def get_login_url(self, request, **kwargs):
|
||||
- url = reverse(self.login_view)
|
||||
- if kwargs:
|
||||
- url += '?' + urlencode(kwargs)
|
||||
+ @property
|
||||
+ def settings(self):
|
||||
+ if not hasattr(self, "_settings"):
|
||||
+ self._settings = django_settings.SOCIALACCOUNT_PROVIDERS.get(
|
||||
+ self.id
|
||||
+ )
|
||||
+ return self._settings
|
||||
+
|
||||
+ @property
|
||||
+ def server_url(self):
|
||||
+ url = self.settings["server_url"]
|
||||
+ return self.wk_server_url(url)
|
||||
+
|
||||
+ def wk_server_url(self, url):
|
||||
+ well_known_uri = "/.well-known/openid-configuration"
|
||||
+ if not url.endswith(well_known_uri):
|
||||
+ url += well_known_uri
|
||||
return url
|
||||
|
||||
- def extract_username(self, data):
|
||||
- """
|
||||
- https://fedoraproject.org/wiki/OpenID
|
||||
- For fedoraproject.org, the identity_url looks like:
|
||||
+ @property
|
||||
+ def token_auth_method(self):
|
||||
+ return self.settings.get("token_auth_method")
|
||||
|
||||
- https://username.id.fedoraproject.org
|
||||
- """
|
||||
- return extract_username(data.identity_url)
|
||||
+ def get_default_scope(self):
|
||||
+ return ["openid", "profile", "email"]
|
||||
+
|
||||
+ def extract_uid(self, data):
|
||||
+ return str(data["sub"])
|
||||
|
||||
def extract_common_fields(self, data):
|
||||
- fields = super(FedoraProvider, self).extract_common_fields(data)
|
||||
- fields['username'] = self.extract_username(data)
|
||||
- return fields
|
||||
+ return dict(
|
||||
+ email=data.get("email"),
|
||||
+ username=data.get("preferred_username"),
|
||||
+ name=data.get("name"),
|
||||
+ user_id=data.get("user_id"),
|
||||
+ picture=data.get("picture"),
|
||||
+ zoneinfo=data.get("zoneinfo"),
|
||||
+ )
|
||||
|
||||
def extract_email_addresses(self, data):
|
||||
"""
|
||||
@@ -81,7 +93,7 @@ class FedoraProvider(OpenIDProvider):
|
||||
primary=True)]
|
||||
"""
|
||||
ret = []
|
||||
- primary_email = get_email_from_response(data)
|
||||
+ primary_email = data.get("email")
|
||||
if primary_email:
|
||||
# It would be added by cleanup_email_addresses(), but we add it
|
||||
# here to mark it as verified.
|
||||
@@ -89,7 +101,7 @@ class FedoraProvider(OpenIDProvider):
|
||||
email=primary_email, verified=True, primary=True))
|
||||
# Add the email alias provided by the Fedora project.
|
||||
ret.append(EmailAddress(
|
||||
- email='%s@fedoraproject.org' % self.extract_username(data),
|
||||
+ email='%s@fedoraproject.org' % self.extract_uid(data),
|
||||
verified=True, primary=False))
|
||||
return ret
|
||||
|
||||
diff --git a/django_mailman3/lib/auth/fedora/urls.py b/django_mailman3/lib/auth/fedora/urls.py
|
||||
index ca371a8..54dd656 100644
|
||||
--- a/django_mailman3/lib/auth/fedora/urls.py
|
||||
+++ b/django_mailman3/lib/auth/fedora/urls.py
|
||||
@@ -1,5 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
-# Copyright (C) 2012-2023 by the Free Software Foundation, Inc.
|
||||
+# Copyright (C) 2012-2024 by the Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is part of Django-Mailman.
|
||||
#
|
||||
@@ -18,16 +18,9 @@
|
||||
#
|
||||
# Author: Aurelien Bompard <abompard@fedoraproject.org>
|
||||
#
|
||||
+from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns
|
||||
|
||||
+from .provider import FedoraProvider
|
||||
|
||||
-from django.urls import re_path
|
||||
|
||||
-from . import views
|
||||
-
|
||||
-
|
||||
-urlpatterns = [
|
||||
- re_path('^fedora/login/$', views.LoginView.as_view(),
|
||||
- name="fedora_login"),
|
||||
- re_path('^fedora/callback/$', views.CallbackView.as_view(),
|
||||
- name='fedora_callback'),
|
||||
-]
|
||||
+urlpatterns = default_urlpatterns(FedoraProvider)
|
||||
diff --git a/django_mailman3/lib/auth/fedora/views.py b/django_mailman3/lib/auth/fedora/views.py
|
||||
index 505f9f7..4b1779e 100644
|
||||
--- a/django_mailman3/lib/auth/fedora/views.py
|
||||
+++ b/django_mailman3/lib/auth/fedora/views.py
|
||||
@@ -18,107 +18,60 @@
|
||||
#
|
||||
# Author: Aurelien Bompard <abompard@fedoraproject.org>
|
||||
#
|
||||
-
|
||||
-from django.http import HttpResponseRedirect
|
||||
-from django.shortcuts import render
|
||||
-from django.urls import reverse
|
||||
-from django.utils.decorators import method_decorator
|
||||
-from django.views.decorators.csrf import csrf_exempt
|
||||
-from django.views.generic import View
|
||||
-
|
||||
-from allauth.socialaccount import providers
|
||||
-from allauth.socialaccount.app_settings import QUERY_EMAIL
|
||||
-from allauth.socialaccount.helpers import (
|
||||
- complete_social_login, render_authentication_error)
|
||||
-from allauth.socialaccount.models import SocialLogin
|
||||
-from allauth.socialaccount.providers.base import AuthError
|
||||
-from allauth.socialaccount.providers.openid.forms import LoginForm
|
||||
-from allauth.socialaccount.providers.openid.utils import (
|
||||
- AXAttributes, SRegFields)
|
||||
-from allauth.socialaccount.providers.openid.views import _openid_consumer
|
||||
-from openid.consumer import consumer
|
||||
-from openid.consumer.discover import DiscoveryFailure
|
||||
-from openid.extensions.ax import AttrInfo, FetchRequest
|
||||
-from openid.extensions.sreg import SRegRequest
|
||||
+import requests
|
||||
+from allauth.socialaccount.providers.oauth2.views import (
|
||||
+ OAuth2Adapter, OAuth2CallbackView, OAuth2LoginView)
|
||||
|
||||
from .provider import FedoraProvider
|
||||
|
||||
|
||||
-class LoginView(View):
|
||||
+class FedoraAdapter(OAuth2Adapter):
|
||||
+ provider_id = FedoraProvider.id
|
||||
+
|
||||
+ @property
|
||||
+ def openid_config(self):
|
||||
+ if not hasattr(self, "_openid_config"):
|
||||
+ server_url = self.get_provider().server_url
|
||||
+ resp = requests.get(server_url)
|
||||
+ resp.raise_for_status()
|
||||
+ self._openid_config = resp.json()
|
||||
+ return self._openid_config
|
||||
|
||||
- form_class = LoginForm
|
||||
- template_name = 'openid/login.html'
|
||||
- provider = FedoraProvider
|
||||
- callback_view = 'fedora_callback'
|
||||
+ @property
|
||||
+ def basic_auth(self):
|
||||
+ token_auth_method = self.get_provider().settings.get(
|
||||
+ "token_auth_method"
|
||||
+ )
|
||||
+ if token_auth_method:
|
||||
+ return token_auth_method == "client_secret_basic"
|
||||
+ return "client_secret_basic" in self.openid_config.get(
|
||||
+ "token_endpoint_auth_methods_supported", []
|
||||
+ )
|
||||
|
||||
- def get(self, request, *args, **kwargs):
|
||||
- if 'openid' in request.GET or self.provider.endpoint:
|
||||
- return self.post(request, *args, **kwargs)
|
||||
- form = LoginForm(initial={'next': request.GET.get('next'),
|
||||
- 'process': request.GET.get('process')})
|
||||
- return render(request, self.template_name, {'form': form})
|
||||
+ @property
|
||||
+ def access_token_url(self):
|
||||
+ return self.openid_config["token_endpoint"]
|
||||
|
||||
- def post(self, request, *args, **kwargs):
|
||||
- data = dict(list(request.GET.items()) + list(request.POST.items()))
|
||||
- if self.provider.endpoint:
|
||||
- data['openid'] = self.provider.endpoint
|
||||
- form = LoginForm(data)
|
||||
- if form.is_valid():
|
||||
- client = _openid_consumer(
|
||||
- request, self.provider, self.provider.endpoint)
|
||||
- try:
|
||||
- auth_request = client.begin(form.cleaned_data['openid'])
|
||||
- if QUERY_EMAIL:
|
||||
- sreg = SRegRequest()
|
||||
- for name in SRegFields:
|
||||
- sreg.requestField(field_name=name,
|
||||
- required=True)
|
||||
- auth_request.addExtension(sreg)
|
||||
- ax = FetchRequest()
|
||||
- for name in AXAttributes:
|
||||
- ax.add(AttrInfo(name,
|
||||
- required=True))
|
||||
- auth_request.addExtension(ax)
|
||||
- callback_url = reverse(self.callback_view)
|
||||
- SocialLogin.stash_state(request)
|
||||
- redirect_url = auth_request.redirectURL(
|
||||
- request.build_absolute_uri('/'),
|
||||
- request.build_absolute_uri(callback_url))
|
||||
- return HttpResponseRedirect(redirect_url)
|
||||
- # UnicodeDecodeError:
|
||||
- # see https://github.com/necaris/python3-openid/issues/1
|
||||
- except (UnicodeDecodeError, DiscoveryFailure) as e:
|
||||
- if request.method == 'POST':
|
||||
- form._errors["openid"] = form.error_class([e])
|
||||
- else:
|
||||
- return render_authentication_error(
|
||||
- request, self.provider.id, exception=e)
|
||||
- return render(request, self.template_name, {'form': form})
|
||||
+ @property
|
||||
+ def authorize_url(self):
|
||||
+ return self.openid_config["authorization_endpoint"]
|
||||
|
||||
+ @property
|
||||
+ def profile_url(self):
|
||||
+ return self.openid_config["userinfo_endpoint"]
|
||||
|
||||
-class CallbackView(View):
|
||||
+ def complete_login(self, request, app, token, response):
|
||||
+ response = (
|
||||
+ requests.get(self.profile_url, headers={
|
||||
+ "Authorization": "Bearer " + str(token)
|
||||
+ })
|
||||
+ )
|
||||
+ response.raise_for_status()
|
||||
+ extra_data = response.json()
|
||||
+ return self.get_provider().sociallogin_from_response(
|
||||
+ request, extra_data
|
||||
+ )
|
||||
|
||||
- provider = FedoraProvider
|
||||
|
||||
- @method_decorator(csrf_exempt)
|
||||
- def dispatch(self, request, *args, **kwargs):
|
||||
- client = _openid_consumer(request)
|
||||
- response = client.complete(
|
||||
- dict(list(request.GET.items()) + list(request.POST.items())),
|
||||
- request.build_absolute_uri(request.path))
|
||||
- if response.status == consumer.SUCCESS:
|
||||
- login = providers.registry \
|
||||
- .by_id(self.provider.id) \
|
||||
- .sociallogin_from_response(request, response)
|
||||
- login.state = SocialLogin.unstash_state(request)
|
||||
- ret = complete_social_login(request, login)
|
||||
- else:
|
||||
- if response.status == consumer.CANCEL:
|
||||
- error = AuthError.CANCELLED
|
||||
- else:
|
||||
- error = AuthError.UNKNOWN
|
||||
- ret = render_authentication_error(
|
||||
- request,
|
||||
- self.provider.id,
|
||||
- error=error)
|
||||
- return ret
|
||||
+oauth2_login = OAuth2LoginView.adapter_view(FedoraAdapter)
|
||||
+oauth2_callback = OAuth2CallbackView.adapter_view(FedoraAdapter)
|
||||
diff --git a/django_mailman3/tests/test_lib_auth_fedora_provider.py b/django_mailman3/tests/test_lib_auth_fedora_provider.py
|
||||
index 29c5508..851dce4 100644
|
||||
--- a/django_mailman3/tests/test_lib_auth_fedora_provider.py
|
||||
+++ b/django_mailman3/tests/test_lib_auth_fedora_provider.py
|
||||
@@ -15,14 +15,13 @@
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# Django-Mailman3. If not, see <http://www.gnu.org/licenses/>.
|
||||
+from unittest.mock import patch
|
||||
|
||||
-
|
||||
-from unittest.mock import Mock, patch
|
||||
-
|
||||
-from django.test import RequestFactory, TestCase
|
||||
+from django.test import RequestFactory
|
||||
from django.urls import reverse
|
||||
|
||||
-from openid.consumer import consumer
|
||||
+from allauth.socialaccount.tests import setup_app
|
||||
+from allauth.tests import TestCase
|
||||
|
||||
from django_mailman3.lib.auth.fedora.provider import (
|
||||
FedoraAccount, FedoraProvider)
|
||||
@@ -45,39 +44,61 @@ class TestFedoraProvider(TestCase):
|
||||
"""
|
||||
Test FedoraProvider openid authentication.
|
||||
"""
|
||||
+ provider_id = FedoraProvider.id
|
||||
|
||||
- @patch('allauth.socialaccount.providers.openid.views._openid_consumer')
|
||||
- def setUp(self, consumer_mock):
|
||||
- self.factory = RequestFactory()
|
||||
- client = Mock()
|
||||
- complete = Mock()
|
||||
- consumer_mock.return_value = client
|
||||
- client.complete = complete
|
||||
- self.complete_response = Mock()
|
||||
- complete.return_value = self.complete_response
|
||||
- self.complete_response.status = consumer.SUCCESS
|
||||
- self.complete_response.identity_url = 'http://bob.id.fedoraproject.org'
|
||||
+ def setUp(self):
|
||||
+ self.app = setup_app(self.provider_id)
|
||||
+ self.app.provider_id = self.provider_id
|
||||
+ self.app.provider = "fedora"
|
||||
+ self.app.save()
|
||||
+ self.request = RequestFactory().get("/")
|
||||
+ self.provider = self.app.get_provider(self.request)
|
||||
|
||||
def test_get_login_url(self):
|
||||
- req = self.factory.get('/')
|
||||
- login_url = FedoraProvider(req).get_login_url(req)
|
||||
+ req = self.request
|
||||
+ login_url = self.provider.get_login_url(req)
|
||||
self.assertEqual(login_url, reverse('fedora_login'))
|
||||
- login_url = FedoraProvider(req).get_login_url(req, query1='value1')
|
||||
+ login_url = self.provider.get_login_url(req, query1='value1')
|
||||
new_url = reverse('fedora_login') + '?query1=value1'
|
||||
self.assertEqual(login_url, new_url)
|
||||
|
||||
- def test_extract_username(self):
|
||||
- req = self.factory.get('/')
|
||||
- username = FedoraProvider(req).extract_username(self.complete_response)
|
||||
- self.assertEqual(username, 'bob')
|
||||
-
|
||||
def test_extract_email_addresses(self):
|
||||
- with patch('django_mailman3.lib.auth.fedora.provider'
|
||||
- '.get_email_from_response') as email_mock:
|
||||
- email_mock.return_value = 'testuser@example.com'
|
||||
- req = self.factory.get('/')
|
||||
- emails = FedoraProvider(req).extract_email_addresses(
|
||||
- self.complete_response)
|
||||
+ emails = self.provider.extract_email_addresses(
|
||||
+ {"email": "testuser@example.com", "sub": "bob"})
|
||||
self.assertEqual(len(emails), 2)
|
||||
self.assertEqual(sorted([x.email for x in emails]),
|
||||
['bob@fedoraproject.org', 'testuser@example.com'])
|
||||
+
|
||||
+ def test_server_url(self):
|
||||
+ mock_sp_settings = {
|
||||
+ "server_url": "https://id.fedoraproject.org"
|
||||
+ }
|
||||
+ with patch(
|
||||
+ "django_mailman3.lib.auth.fedora.provider.django_settings"
|
||||
+ ) as mock_settings:
|
||||
+ mock_settings.SOCIALACCOUNT_PROVIDERS.get.return_value = (
|
||||
+ mock_sp_settings
|
||||
+ )
|
||||
+ server_url = self.provider.server_url
|
||||
+
|
||||
+ self.assertEqual(
|
||||
+ server_url,
|
||||
+ "https://id.fedoraproject.org/.well-known/openid-configuration"
|
||||
+ )
|
||||
+
|
||||
+ def test_token_auth_method(self):
|
||||
+ mock_sp_settings = {
|
||||
+ "token_auth_method": "basic_auth_token"
|
||||
+ }
|
||||
+ with patch(
|
||||
+ "django_mailman3.lib.auth.fedora.provider.django_settings"
|
||||
+ ) as mock_settings:
|
||||
+ mock_settings.SOCIALACCOUNT_PROVIDERS.get.return_value = (
|
||||
+ mock_sp_settings
|
||||
+ )
|
||||
+ token_auth_method = self.provider.token_auth_method
|
||||
+
|
||||
+ self.assertEqual(
|
||||
+ token_auth_method,
|
||||
+ "basic_auth_token"
|
||||
+ )
|
|
@ -26,6 +26,14 @@
|
|||
- packages
|
||||
- mailman
|
||||
|
||||
# This is needed till https://src.fedoraproject.org/rpms/python-django-mailman3/pull-request/2
|
||||
# is available
|
||||
- name: Apply django_mailman3 patch
|
||||
ansible.posix.patch:
|
||||
src: django_mailman3_patch/django-mailman3-fedora-oidc.patch
|
||||
basedir: /usr/lib/python3.9/site-packages/django_mailman3/
|
||||
strip: 1
|
||||
|
||||
- name: Set the mailman conffile
|
||||
ansible.builtin.template:
|
||||
src: mailman.cfg.j2
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue