Skip to content

Commit

Permalink
Merge pull request #248 from maykinmedia/feature/register-necessary-f…
Browse files Browse the repository at this point in the history
…ields

Feature/register necessary fields
  • Loading branch information
alextreme authored Jun 8, 2022
2 parents a1964ea + 985ce46 commit f04f510
Show file tree
Hide file tree
Showing 14 changed files with 312 additions and 76 deletions.
26 changes: 25 additions & 1 deletion src/open_inwoner/accounts/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,30 @@ class Meta:
)


class NecessaryUserForm(forms.ModelForm):
invite = forms.ModelChoiceField(
queryset=Invite.objects.all(),
to_field_name="key",
widget=forms.HiddenInput(),
required=False,
)

class Meta:
model = User
fields = (
"first_name",
"last_name",
"email",
"invite",
)

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

self.fields["first_name"].required = True
self.fields["last_name"].required = True


class ThemesForm(forms.ModelForm):
class Meta:
model = User
Expand All @@ -76,7 +100,7 @@ class ContactForm(forms.ModelForm):
def __init__(self, user, create, *args, **kwargs):
self.user = user
self.create = create
return super().__init__(*args, **kwargs)
super().__init__(*args, **kwargs)

class Meta:
model = Contact
Expand Down
5 changes: 4 additions & 1 deletion src/open_inwoner/accounts/managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

from digid_eherkenning.managers import BaseDigidManager, BaseeHerkenningManager

from open_inwoner.utils.hash import generate_email_from_string

from .choices import LoginTypeChoices


Expand All @@ -14,8 +16,9 @@ def get_by_bsn(self, bsn):
return self.get_queryset().get(bsn=bsn)

def digid_create(self, bsn, **kwargs):
email = generate_email_from_string(bsn)
return super().create(
email="user-{}@bsn.com".format(bsn),
email=email,
login_type=LoginTypeChoices.digid,
bsn=bsn,
)
Expand Down
26 changes: 26 additions & 0 deletions src/open_inwoner/accounts/middleware.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from django.http import HttpResponseRedirect
from django.urls import reverse


class NecessaryFieldsMiddleware:
"""
Redirect the user to a view to fill in necessary fields
"""

def __init__(self, get_response):
self.get_response = get_response

def __call__(self, request):
user = request.user
if user.is_authenticated:
necessary_fields_url = reverse("accounts:registration_necessary")

# If the user is currently not editing their information, but it is required
# redirect to that view.
if (
not request.path.startswith((necessary_fields_url, reverse("logout")))
and request.user.require_necessary_fields()
):
return HttpResponseRedirect(necessary_fields_url)

return self.get_response(request)
30 changes: 30 additions & 0 deletions src/open_inwoner/accounts/migrations/0043_change_digid_email.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from django.db import migrations
from open_inwoner.utils.hash import generate_email_from_string
from ..choices import LoginTypeChoices


def change_bsn_email_to_hash(apps, _):
User = apps.get_model("accounts", "User")

for user in User.objects.filter(login_type=LoginTypeChoices.digid).all():
if user.email == f"user-{user.bsn}@bsn.com":
user.email = generate_email_from_string(user.bsn)
user.save()


def change_hash_email_to_bsn(apps, _):
User = apps.get_model("accounts", "User")

for user in User.objects.filter(login_type=LoginTypeChoices.digid).all():
if user.email == generate_email_from_string(user.bsn):
user.email = f"user-{user.bsn}@bsn.com"
user.save()


class Migration(migrations.Migration):

dependencies = [("accounts", "0042_alter_invite_invitee_email")]

operations = [
migrations.RunPython(change_bsn_email_to_hash, change_hash_email_to_bsn)
]
8 changes: 8 additions & 0 deletions src/open_inwoner/accounts/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,14 @@ def get_interests(self) -> str:

return ", ".join(list(self.selected_themes.values_list("name", flat=True)))

def require_necessary_fields(self) -> bool:
"""returns whether user needs to fill in necessary fields"""
return (
self.login_type == LoginTypeChoices.digid
and not self.first_name
and not self.last_name
)


class Contact(models.Model):
uuid = models.UUIDField(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{% extends 'master.html' %}
{% load i18n static form_tags card_tags grid_tags %}

{% block content %}

{% render_grid %}
{% render_column span=9 %}
{% render_card tinted=True %}
<h1 class="h1">{% trans "Registratie voltooien" %}</h1><br>
{% form form_object=form method="POST" id="necessary-form" %}
{% endrender_card %}
{% endrender_column %}
{% endrender_grid %}

{% endblock content %}
140 changes: 107 additions & 33 deletions src/open_inwoner/accounts/tests/test_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

from open_inwoner.configurations.models import SiteConfiguration

from ..choices import LoginTypeChoices
from ..models import User
from .factories import ContactFactory, InviteFactory, UserFactory

Expand Down Expand Up @@ -129,35 +130,6 @@ def test_registration_active_user(self):
self.assertEqual(get_response.status_code, 302)
self.assertEqual(get_response.url, reverse("django_registration_complete"))

def test_registration_active_user_with_invite(self):
"""
the user should be redirected to the registration complete page
and the invite should be updated
"""

user = UserFactory.create()
contact = ContactFactory.create(email=user.email, contact_user=None)
invite = InviteFactory.create(contact=contact, invitee=None)
self.assertEqual(user.contacts.count(), 0)

get_response = self.app.get(f"{self.url}?invite={invite.key}", user=user)

self.assertEqual(get_response.status_code, 302)
self.assertEqual(get_response.url, reverse("django_registration_complete"))

contact.refresh_from_db()
invite.refresh_from_db()
self.assertEqual(contact.contact_user, user)
self.assertEqual(invite.invitee, user)

# reverse contact was created
self.assertEqual(user.contacts.count(), 1)
reverse_contact = user.contacts.get()
self.assertEqual(reverse_contact.contact_user, contact.created_by)
self.assertEqual(reverse_contact.email, contact.created_by.email)
self.assertEqual(reverse_contact.first_name, contact.created_by.first_name)
self.assertEqual(reverse_contact.last_name, contact.created_by.last_name)


class TestRegistrationDigid(WebTest):
url = reverse_lazy("django_registration_register")
Expand All @@ -172,23 +144,125 @@ def test_registration_page_only_digid(self):
self.assertIsNotNone(digid_tag)
self.assertEqual(
digid_tag.attrs["href"],
furl(reverse("digid:login")).add({"next": str(self.url)}).url,
furl(reverse("digid:login"))
.add({"next": reverse("accounts:registration_necessary")})
.url,
)

def test_registration_page_only_digid_with_invite(self):
invite = InviteFactory.create()
url = f"{self.url}?invite={invite.key}"

get_response = self.app.get(url)
get_response = self.app.get(f"{self.url}?invite={invite.key}")

self.assertEqual(get_response.status_code, 200)
self.assertIsNone(get_response.html.find(id="registration-form"))

digid_tag = get_response.html.find("a", title="Registreren met DigiD")
self.assertIsNotNone(digid_tag)
necessary_url = (
furl(reverse("accounts:registration_necessary"))
.add({"invite": invite.key})
.url
)
self.assertEqual(
digid_tag.attrs["href"], furl(reverse("digid:login")).add({"next": url}).url
digid_tag.attrs["href"],
furl(reverse("digid:login")).add({"next": necessary_url}).url,
)


class TestRegistrationNecessary(WebTest):
url = reverse_lazy("accounts:registration_necessary")

def test_any_page_for_digid_user_redirect_to_necessary_fields(self):
user = UserFactory(
first_name="",
last_name="",
login_type=LoginTypeChoices.digid,
)
urls = [
reverse("root"),
reverse("pdc:category_list"),
reverse("accounts:my_profile"),
reverse("accounts:inbox"),
reverse("accounts:my_cases"),
reverse("plans:plan_list"),
reverse("general_faq"),
]

for url in urls:
with self.subTest(url=url):
response = self.app.get(url, user=user)

self.assertRedirects(
response, reverse("accounts:registration_necessary")
)

def test_submit_without_invite(self):
user = UserFactory(
first_name="",
last_name="",
login_type=LoginTypeChoices.digid,
)
self.assertTrue(user.require_necessary_fields())

get_response = self.app.get(self.url, user=user)
form = get_response.forms["necessary-form"]

form["email"] = "[email protected]"
form["first_name"] = "John"
form["last_name"] = "Smith"

response = form.submit()

self.assertEqual(response.status_code, 302)
self.assertEqual(response.url, reverse("django_registration_complete"))

user.refresh_from_db()

self.assertFalse(user.require_necessary_fields())
self.assertEqual(user.email, "[email protected]")
self.assertEqual(user.first_name, "John")
self.assertEqual(user.last_name, "Smith")

def test_submit_with_invite(self):
user = UserFactory(
first_name="",
last_name="",
login_type=LoginTypeChoices.digid,
)
contact = ContactFactory.create(contact_user=None)
invite = InviteFactory.create(contact=contact, invitee=None)

get_response = self.app.get(f"{self.url}?invite={invite.key}", user=user)
form = get_response.forms["necessary-form"]

# assert initials are retrieved from invite.contact
self.assertEqual(form["email"].value, contact.email)
self.assertEqual(form["first_name"].value, contact.first_name)
self.assertEqual(form["last_name"].value, contact.last_name)

response = form.submit()

self.assertEqual(response.status_code, 302)
self.assertEqual(response.url, reverse("django_registration_complete"))

user.refresh_from_db()
contact.refresh_from_db()
invite.refresh_from_db()

self.assertEqual(user.first_name, contact.first_name)
self.assertEqual(user.last_name, contact.last_name)
self.assertEqual(user.email, contact.email)
self.assertEqual(contact.contact_user, user)
self.assertEqual(invite.invitee, user)

# reverse contact checks
self.assertEqual(user.contacts.count(), 1)
reverse_contact = user.contacts.get()
self.assertEqual(reverse_contact.contact_user, contact.created_by)
self.assertEqual(reverse_contact.email, contact.created_by.email)
self.assertEqual(reverse_contact.first_name, contact.created_by.first_name)
self.assertEqual(reverse_contact.last_name, contact.created_by.last_name)


class TestLoginLogoutFunctionality(WebTest):
Expand Down
6 changes: 6 additions & 0 deletions src/open_inwoner/accounts/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
MyCategoriesView,
MyProfileExportView,
MyProfileView,
NecessaryFieldsUserView,
)

app_name = "accounts"
Expand Down Expand Up @@ -86,5 +87,10 @@
path("edit/", EditProfileView.as_view(), name="edit_profile"),
path("invite/<str:key>/accept/", InviteAcceptView.as_view(), name="invite_accept"),
path("export/", MyProfileExportView.as_view(), name="profile_export"),
path(
"register/necessary/",
NecessaryFieldsUserView.as_view(),
name="registration_necessary",
),
path("", MyProfileView.as_view(), name="my_profile"),
]
2 changes: 1 addition & 1 deletion src/open_inwoner/accounts/views/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,4 @@
MyProfileExportView,
MyProfileView,
)
from .registration import CustomRegistrationView
from .registration import CustomRegistrationView, NecessaryFieldsUserView
Loading

0 comments on commit f04f510

Please sign in to comment.