diff --git a/kobo/apps/accounts/forms.py b/kobo/apps/accounts/forms.py index 603ef5c797..c6da409da8 100644 --- a/kobo/apps/accounts/forms.py +++ b/kobo/apps/accounts/forms.py @@ -11,15 +11,14 @@ from allauth.socialaccount.forms import SignupForm as BaseSocialSignupForm from django import forms from django.contrib.auth import get_user_model -from django.conf import settings from django.utils.safestring import mark_safe -from django.utils.translation import gettext, gettext_lazy as t +from django.utils.translation import gettext +from django.utils.translation import gettext_lazy as t from hub.models.sitewide_message import SitewideMessage from hub.utils.i18n import I18nUtils from kobo.static_lists import COUNTRIES, USER_METADATA_DEFAULT_LABELS - # Only these fields can be controlled by constance.config.USER_METADATA_FIELDS CONFIGURABLE_METADATA_FIELDS = ( 'name', @@ -131,8 +130,8 @@ def __init__(self, *args, **kwargs): privacy_policy_link = gettext('Privacy Policy') self.fields['terms_of_service'].label = mark_safe( t('I agree with the ##terms_of_service## and ##privacy_policy##') - .replace("##terms_of_service##", terms_of_service_link) - .replace("##privacy_policy##", privacy_policy_link) + .replace('##terms_of_service##', terms_of_service_link) + .replace('##privacy_policy##', privacy_policy_link) ) # Remove upstream placeholders @@ -260,10 +259,16 @@ class SocialSignupForm(KoboSignupMixin, BaseSocialSignupForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - if settings.UNSAFE_SSO_REGISTRATION_EMAIL_DISABLE: - self.fields['email'].widget.attrs['readonly'] = True + self.fields['email'].widget.attrs['readonly'] = True self.label_suffix = '' + def clean_email(self): + # do not allow any other email besides the one retrieved from the SSO server + email = super().clean_email() + if email != self.initial['email']: + raise forms.ValidationError(t('Email must match SSO server email')) + return email + class SignupForm(KoboSignupMixin, BaseSignupForm): field_order = [ diff --git a/kobo/apps/accounts/tests/test_forms.py b/kobo/apps/accounts/tests/test_forms.py index 8c74ac8286..9966d05e8b 100644 --- a/kobo/apps/accounts/tests/test_forms.py +++ b/kobo/apps/accounts/tests/test_forms.py @@ -2,15 +2,15 @@ from constance.test import override_config from django.conf import settings from django.contrib.auth import get_user_model -from django.test import TestCase, override_settings, Client +from django.test import Client, TestCase from django.urls import reverse from django.utils import translation from django.utils.timezone import now -from hub.models.sitewide_message import SitewideMessage from model_bakery import baker from pyquery import PyQuery from rest_framework import status +from hub.models.sitewide_message import SitewideMessage from kobo.apps.accounts.forms import SignupForm, SocialSignupForm from kpi.utils.json import LazyJSONSerializable @@ -18,28 +18,27 @@ class AccountFormsTestCase(TestCase): @classmethod def setUpTestData(cls): - cls.user = baker.make(settings.AUTH_USER_MODEL) - cls.sociallogin = baker.make( - "socialaccount.SocialAccount", user=cls.user - ) + cls.user = baker.make(settings.AUTH_USER_MODEL, email='email@email.com') + cls.sociallogin = baker.make('socialaccount.SocialAccount', user=cls.user) def setUp(self): self.client = Client() self.url = reverse('account_signup') - @override_settings(UNSAFE_SSO_REGISTRATION_EMAIL_DISABLE=True) - def test_social_signup_form_not_email_disabled(self): - form = SocialSignupForm(sociallogin=self.sociallogin) - pq = PyQuery(str(form)) - assert (email_input := pq("[name=email]")) - assert "readonly" in email_input[0].attrib - - @override_settings(UNSAFE_SSO_REGISTRATION_EMAIL_DISABLE=False) - def test_social_signup_form_not_email_not_disabled(self): + def test_social_signup_form_email_disabled(self): form = SocialSignupForm(sociallogin=self.sociallogin) pq = PyQuery(str(form)) - assert (email_input := pq("[name=email]")) - assert "readonly" not in email_input[0].attrib + assert (email_input := pq('[name=email]')) + assert 'readonly' in email_input[0].attrib + + def test_social_signup_email_must_match(self): + # replace something@email.com with something+bad@email.com + bad_email = self.user.email.replace('@', '+bad@') + form = SocialSignupForm( + sociallogin=self.sociallogin, + data={'email': bad_email, 'name': 'name', 'username': 'uname'}, + ) + self.assertEquals(form.errors['email'], ['Email must match SSO server email']) def test_only_configurable_fields_can_be_removed(self): with override_config(USER_METADATA_FIELDS='{}'): @@ -300,7 +299,7 @@ def _organization_field_skip_logic(self, form_type, **kwargs): """ basic_data = { 'username': 'foo', - 'email': 'double@foo.bar', + 'email': kwargs.get('email', 'double@foo.bar'), 'password1': 'tooxox', 'password2': 'tooxox', } @@ -320,6 +319,7 @@ def _organization_field_skip_logic(self, form_type, **kwargs): data = basic_data.copy() data['organization_type'] = 'government' form = form_type(data, **kwargs.get('form_kwargs', {})) + # No other organization fields should be required assert form.is_valid() @@ -419,5 +419,7 @@ def test_organization_field_skip_logic_signup_form(self): def test_organization_field_skip_logic_sso_form(self): self._organization_field_skip_logic( - SocialSignupForm, form_kwargs={'sociallogin': self.sociallogin} + SocialSignupForm, + form_kwargs={'sociallogin': self.sociallogin}, + email=self.sociallogin.user.email, ) diff --git a/kobo/settings/base.py b/kobo/settings/base.py index 873985faf7..ab681b29a3 100644 --- a/kobo/settings/base.py +++ b/kobo/settings/base.py @@ -1338,11 +1338,7 @@ def dj_stripe_request_callback_method(): SOCIALACCOUNT_FORMS = { 'signup': 'kobo.apps.accounts.forms.SocialSignupForm', } -# For SSO, the signup form is prepopulated with the account email -# If set True, the email field in the SSO signup form will be readonly -UNSAFE_SSO_REGISTRATION_EMAIL_DISABLE = env.bool( - 'UNSAFE_SSO_REGISTRATION_EMAIL_DISABLE', False -) + WEBPACK_LOADER = { 'DEFAULT': {