misc: unserialize attribute in registration view (#45710)

Registration view pass attribute values in JSON tokens, so we need to
get a JSON compatible serialization of attribute values for the token
using their serialization function.
This commit is contained in:
Benjamin Dauvergne 2020-08-13 15:43:49 +02:00
parent d1b30f213c
commit 079102d851
4 changed files with 61 additions and 16 deletions

View File

@ -355,11 +355,15 @@ def get_form_field(kind, **kwargs):
return defn['field_class'](**kwargs)
def identity(x):
return x
def get_kind(kind):
d = get_attribute_kinds()[kind]
d.setdefault('default', None)
d.setdefault('serialize', lambda x: x)
d.setdefault('deserialize', lambda x: x)
d.setdefault('serialize', identity)
d.setdefault('deserialize', identity)
rest_field_kwargs = d.setdefault('rest_framework_field_kwargs', {})
if 'rest_framework_field_class' not in d:
d['rest_framework_field_class'] = serializers.CharField

View File

@ -53,6 +53,12 @@ def get_attributes_map():
return mapping
def iter_attributes():
for key, value in get_attributes_map().items():
if isinstance(key, str):
yield value
class Attributes(object):
def __init__(self, owner, verified=None):
self.__dict__['owner'] = owner

View File

@ -17,7 +17,6 @@
import collections
from email.utils import parseaddr
import logging
import random
import re
from ratelimit.utils import is_ratelimited
@ -28,8 +27,7 @@ from django.template.loader import render_to_string
from django.views.generic.edit import UpdateView, FormView
from django.views.generic import TemplateView
from django.views.generic.base import View
from django.contrib.auth import SESSION_KEY
from django import http, shortcuts
from django import shortcuts
from django.core import signing
from django.core.exceptions import ValidationError
from django.contrib import messages
@ -42,7 +40,6 @@ from django.contrib.auth.views import PasswordChangeView as DjPasswordChangeView
from django.http import (HttpResponseRedirect, HttpResponseForbidden, HttpResponse)
from django.views.decorators.csrf import csrf_exempt, ensure_csrf_cookie
from django.views.decorators.cache import never_cache
from django.views.decorators.debug import sensitive_post_parameters
from django.contrib.auth.decorators import login_required
from django.db.models.fields import FieldDoesNotExist
from django.db.models.query import Q
@ -54,9 +51,10 @@ from django.forms import CharField
from django.http import HttpResponseBadRequest
from django.template import loader
from authentic2.custom_user.models import iter_attributes
from authentic2.compat.misc import default_token_generator
from . import (utils, app_settings, decorators, constants,
models, cbv, hooks, validators)
models, cbv, hooks, validators, attribute_kinds)
from .utils import switch_user
from .a2_rbac.utils import get_default_ou
from .a2_rbac.models import OrganizationalUnit as OU
@ -1082,7 +1080,14 @@ class RegistrationCompletionView(CreateView):
and ('email' not in self.token or self.request.POST['email'] != self.token['email'])
and not self.token.get('skip_email_check')):
# If an email is submitted it must be validated or be the same as in the token
data = form.cleaned_data
data = form.cleaned_data.copy()
# handle complex attributes
for attribute in iter_attributes():
kind = attribute.get_kind()
if kind['serialize'] == attribute_kinds.identity:
continue
data[attribute.name] = kind['serialize'](data[attribute.name])
data['no_password'] = self.token.get('no_password', False)
utils.send_registration_mail(
self.request,

View File

@ -15,8 +15,6 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import re
from django.contrib.auth import get_user_model, REDIRECT_FIELD_NAME
from django.urls import reverse
from django.utils.http import urlquote
@ -28,6 +26,9 @@ from authentic2.validators import EmailValidator
from .utils import get_link_from_mail
User = get_user_model()
def test_registration(app, db, settings, mailoutbox, external_redirect):
next_url, good_next_url = external_redirect
@ -37,7 +38,6 @@ def test_registration(app, db, settings, mailoutbox, external_redirect):
# disable existing attributes
models.Attribute.objects.update(disabled=True)
User = get_user_model()
url = utils.make_url('registration_register', params={REDIRECT_FIELD_NAME: next_url})
response = app.get(url)
response.form.set('email', 'testbot@entrouvert.com')
@ -109,7 +109,6 @@ def test_registration_realm(app, db, settings, mailoutbox):
# disable existing attributes
models.Attribute.objects.update(disabled=True)
User = get_user_model()
next_url = 'http://relying-party.org/'
url = utils.make_url('registration_register', params={REDIRECT_FIELD_NAME: next_url})
@ -494,7 +493,6 @@ def test_email_is_unique_multiple_objects_returned(app, db, settings, mailoutbox
settings.A2_REGISTRATION_EMAIL_IS_UNIQUE = True
# Create two user objects
User = get_user_model()
User.objects.create(email='testbot@entrouvert.com')
User.objects.create(email='testbot@entrouvert.com')
@ -518,7 +516,6 @@ def test_username_is_unique_multiple_objects_returned(app, db, settings, mailout
settings.A2_REQUIRED_FIELDS = ['username', 'first_name', 'last_name']
# Create two user objects
User = get_user_model()
User.objects.create(username='testbot', email='testbot1@entrouvert.com')
User.objects.create(username='testbot', email='testbot2@entrouvert.com')
@ -550,7 +547,6 @@ def test_registration_redirect(app, db, settings, mailoutbox, external_redirect)
# disable existing attributes
models.Attribute.objects.update(disabled=True)
User = get_user_model()
url = utils.make_url('registration_register', params={REDIRECT_FIELD_NAME: next_url})
response = app.get(url)
response.form.set('email', 'testbot@entrouvert.com')
@ -674,8 +670,42 @@ def test_registration_with_email_suggestions(app, db, settings):
assert "email_domains_suggestions.js" in response.text
assert "field-live-hint" in response.text
settings.A2_SUGGESTED_EMAIL_DOMAINS = []
response = app.get(url)
assert "email_domains_suggestions.js" not in response.text
assert "field-live-hint" not in response.text
def test_registration_no_email_full_profile_no_password(app, db, rf, mailoutbox):
models.Attribute.objects.create(
kind='birthdate',
name='birthdate',
label='birthdate',
required=True)
data = {
'email': 'john.doe@example.com',
'first_name': 'John',
'last_name': 'Doe',
'confirm_data': 'required',
'no_password': True,
'valid_email': False,
'franceconnect': True,
'authentication_method': 'france-connect',
}
activation_url = utils.build_activation_url(
rf.post('/accounts/register/'),
next_url='/',
**data)
response = app.get(activation_url)
response.form.set('first_name', data['first_name'])
response.form.set('last_name', data['last_name'])
response.form.set('birthdate', '1981-01-01')
response.form.set('email', 'john.doe2@example.com')
response = response.form.submit().follow()
link = get_link_from_mail(mailoutbox[0])
response = app.get(link)
assert response.location == '/'
assert User.objects.count() == 1