From e66b96376fa6ad640a82a40573e14c24c1f8d77a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Wed, 9 Sep 2015 09:15:34 +0200 Subject: [PATCH] Add filter by email domain on register --- CHANGELOG.md | 1 + settings/common.py | 2 + settings/local.py.example | 4 + taiga/base/api/fields.py | 10 ++- taiga/users/api.py | 2 + tests/integration/test_auth_api.py | 14 ++++ tests/integration/test_memberships.py | 103 ++++++++++++++++++++++++++ tests/integration/test_users.py | 29 ++++++++ 8 files changed, 164 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 293cb3d0..1b601d3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ - Import/Export: - Gzip export/import support. - Export performance improvements. +- Add filter by email domain registration and invitation by setting. ### Misc - [API] Improve performance of some calls over list. diff --git a/settings/common.py b/settings/common.py index 3ee3b3f3..66fbb67f 100644 --- a/settings/common.py +++ b/settings/common.py @@ -441,6 +441,8 @@ APP_EXTRA_EXPOSE_HEADERS = [ DEFAULT_PROJECT_TEMPLATE = "scrum" PUBLIC_REGISTER_ENABLED = False +# None or [] values in USER_EMAIL_ALLOWED_DOMAINS means allow any domain +USER_EMAIL_ALLOWED_DOMAINS = None SEARCHES_MAX_RESULTS = 150 diff --git a/settings/local.py.example b/settings/local.py.example index 4ae5a8ab..7e0c44a8 100644 --- a/settings/local.py.example +++ b/settings/local.py.example @@ -105,3 +105,7 @@ DATABASES = { # To use celery in memory #CELERY_ENABLED = True #CELERY_ALWAYS_EAGER = True + +# LIMIT ALLOWED DOMAINS FOR REGISTER AND INVITE +# None or [] values in USER_EMAIL_ALLOWED_DOMAINS means allow any domain +# USER_EMAIL_ALLOWED_DOMAINS = None diff --git a/taiga/base/api/fields.py b/taiga/base/api/fields.py index fc4035c2..c8f55a60 100644 --- a/taiga/base/api/fields.py +++ b/taiga/base/api/fields.py @@ -612,6 +612,14 @@ class ChoiceField(WritableField): return value +def validate_user_email_allowed_domains(value): + domain_name = value.split("@")[1] + print("EMAIL VALIDATE DOMAIN") + + if settings.USER_EMAIL_ALLOWED_DOMAINS and domain_name not in settings.USER_EMAIL_ALLOWED_DOMAINS: + raise ValidationError(_("You email domain is not allowed")) + + class EmailField(CharField): type_name = "EmailField" type_label = "email" @@ -620,7 +628,7 @@ class EmailField(CharField): default_error_messages = { "invalid": _("Enter a valid email address."), } - default_validators = [validators.validate_email] + default_validators = [validators.validate_email, validate_user_email_allowed_domains] def from_native(self, value): ret = super(EmailField, self).from_native(value) diff --git a/taiga/users/api.py b/taiga/users/api.py index 00d5d279..a9f1c957 100644 --- a/taiga/users/api.py +++ b/taiga/users/api.py @@ -33,6 +33,7 @@ from taiga.base.decorators import list_route from taiga.base.decorators import detail_route from taiga.base.api import ModelCrudViewSet from taiga.base.api.mixins import BlockedByProjectMixin +from taiga.base.api.fields import validate_user_email_allowed_domains from taiga.base.api.utils import get_object_or_404 from taiga.base.filters import MembersFilterBackend from taiga.base.mails import mail_builder @@ -112,6 +113,7 @@ class UsersViewSet(ModelCrudViewSet): try: validate_email(new_email) + validate_user_email_allowed_domains(new_email) except ValidationError: valid_new_email = False diff --git a/tests/integration/test_auth_api.py b/tests/integration/test_auth_api.py index 5781c58a..0504881b 100644 --- a/tests/integration/test_auth_api.py +++ b/tests/integration/test_auth_api.py @@ -48,6 +48,20 @@ def test_respond_400_when_public_registration_is_disabled(client, register_form, assert response.status_code == 400 +def test_respond_400_when_the_email_domain_isnt_in_allowed_domains(client, register_form, settings): + settings.PUBLIC_REGISTER_ENABLED = True + settings.USER_EMAIL_ALLOWED_DOMAINS = ['other-domain.com'] + response = client.post(reverse("auth-register"), register_form) + assert response.status_code == 400 + + +def test_respond_201_when_the_email_domain_is_in_allowed_domains(client, settings, register_form): + settings.PUBLIC_REGISTER_ENABLED = True + settings.USER_EMAIL_ALLOWED_DOMAINS = ['email.com'] + response = client.post(reverse("auth-register"), register_form) + assert response.status_code == 201 + + def test_respond_201_with_invitation_without_public_registration(client, register_form, settings): settings.PUBLIC_REGISTER_ENABLED = False user = factories.UserFactory() diff --git a/tests/integration/test_memberships.py b/tests/integration/test_memberships.py index d5a8a5c3..c6b2ca5e 100644 --- a/tests/integration/test_memberships.py +++ b/tests/integration/test_memberships.py @@ -72,6 +72,79 @@ def test_api_create_bulk_members(client): assert response.data[1]["email"] == joseph.email +def test_api_create_bulk_members_with_allowed_domain(client): + project = f.ProjectFactory() + john = f.UserFactory.create() + joseph = f.UserFactory.create() + tester = f.RoleFactory(project=project, name="Tester") + gamer = f.RoleFactory(project=project, name="Gamer") + f.MembershipFactory(project=project, user=project.owner, is_admin=True) + + url = reverse("memberships-bulk-create") + + data = { + "project_id": project.id, + "bulk_memberships": [ + {"role_id": tester.pk, "email": "test1@email.com"}, + {"role_id": gamer.pk, "email": "test2@email.com"}, + ] + } + client.login(project.owner) + response = client.json.post(url, json.dumps(data)) + + assert response.status_code == 200 + assert response.data[0]["email"] == "test1@email.com" + assert response.data[1]["email"] == "test2@email.com" + + +def test_api_create_bulk_members_with_allowed_and_unallowed_domain(client, settings): + project = f.ProjectFactory() + settings.USER_EMAIL_ALLOWED_DOMAINS = ['email.com'] + tester = f.RoleFactory(project=project, name="Tester") + gamer = f.RoleFactory(project=project, name="Gamer") + f.MembershipFactory(project=project, user=project.owner, is_admin=True) + + url = reverse("memberships-bulk-create") + + data = { + "project_id": project.id, + "bulk_memberships": [ + {"role_id": tester.pk, "email": "test@invalid-domain.com"}, + {"role_id": gamer.pk, "email": "test@email.com"}, + ] + } + client.login(project.owner) + response = client.json.post(url, json.dumps(data)) + + assert response.status_code == 400 + assert "email" in response.data["bulk_memberships"][0] + assert "email" not in response.data["bulk_memberships"][1] + + +def test_api_create_bulk_members_with_unallowed_domains(client, settings): + project = f.ProjectFactory() + settings.USER_EMAIL_ALLOWED_DOMAINS = ['email.com'] + tester = f.RoleFactory(project=project, name="Tester") + gamer = f.RoleFactory(project=project, name="Gamer") + f.MembershipFactory(project=project, user=project.owner, is_admin=True) + + url = reverse("memberships-bulk-create") + + data = { + "project_id": project.id, + "bulk_memberships": [ + {"role_id": tester.pk, "email": "test1@invalid-domain.com"}, + {"role_id": gamer.pk, "email": "test2@invalid-domain.com"}, + ] + } + client.login(project.owner) + response = client.json.post(url, json.dumps(data)) + + assert response.status_code == 400 + assert "email" in response.data["bulk_memberships"][0] + assert "email" in response.data["bulk_memberships"][1] + + def test_api_create_bulk_members_without_enough_memberships_private_project_slots_one_project(client): user = f.UserFactory.create(max_memberships_private_projects=3) project = f.ProjectFactory(owner=user, is_private=True) @@ -314,6 +387,36 @@ def test_api_create_membership(client): assert response.data["user_email"] == user.email +def test_api_create_membership_with_unallowed_domain(client, settings): + settings.USER_EMAIL_ALLOWED_DOMAINS = ['email.com'] + + membership = f.MembershipFactory(is_admin=True) + role = f.RoleFactory.create(project=membership.project) + + client.login(membership.user) + url = reverse("memberships-list") + data = {"role": role.pk, "project": role.project.pk, "email": "test@invalid-email.com"} + response = client.json.post(url, json.dumps(data)) + + assert response.status_code == 400 + assert "email" in response.data + + +def test_api_create_membership_with_allowed_domain(client, settings): + settings.USER_EMAIL_ALLOWED_DOMAINS = ['email.com'] + + membership = f.MembershipFactory(is_admin=True) + role = f.RoleFactory.create(project=membership.project) + + client.login(membership.user) + url = reverse("memberships-list") + data = {"role": role.pk, "project": role.project.pk, "email": "test@email.com"} + response = client.json.post(url, json.dumps(data)) + + assert response.status_code == 201 + assert response.data["email"] == "test@email.com" + + def test_api_create_membership_without_enough_memberships_private_project_slots_one_projects(client): user = f.UserFactory.create(max_memberships_private_projects=1) project = f.ProjectFactory(owner=user, is_private=True) diff --git a/tests/integration/test_users.py b/tests/integration/test_users.py index f4bf2b51..79f78d11 100644 --- a/tests/integration/test_users.py +++ b/tests/integration/test_users.py @@ -99,6 +99,35 @@ def test_update_user_with_invalid_email(client): assert response.data['_error_message'] == 'Not valid email' +def test_update_user_with_unallowed_domain_email(client, settings): + settings.USER_EMAIL_ALLOWED_DOMAINS = ['email.com'] + user = f.UserFactory.create(email="my@email.com") + url = reverse('users-detail', kwargs={"pk": user.pk}) + data = {"email": "my@invalid-email.com"} + + client.login(user) + response = client.patch(url, json.dumps(data), content_type="application/json") + + assert response.status_code == 400 + assert response.data['_error_message'] == 'Not valid email' + + +def test_update_user_with_allowed_domain_email(client, settings): + settings.USER_EMAIL_ALLOWED_DOMAINS = ['email.com'] + + user = f.UserFactory.create(email="old@email.com") + url = reverse('users-detail', kwargs={"pk": user.pk}) + data = {"email": "new@email.com"} + + client.login(user) + response = client.patch(url, json.dumps(data), content_type="application/json") + + assert response.status_code == 200 + user = models.User.objects.get(pk=user.id) + assert user.email_token is not None + assert user.new_email == "new@email.com" + + def test_update_user_with_valid_email(client): user = f.UserFactory.create(email="old@email.com") url = reverse('users-detail', kwargs={"pk": user.pk})