From d291a83cc935c4927bc3fcc9926d376acf6f7922 Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Tue, 16 Nov 2021 11:38:17 +0100 Subject: [PATCH] Warn when registering with email containing uppercase characters Closes #884 and #803 Signed-off-by: Thomas Citharel --- js/src/i18n/en_US.json | 3 +- js/src/i18n/fr_FR.json | 3 +- js/src/types/apollo.ts | 5 ++ js/src/views/User/Register.vue | 96 ++++++++++++++++++++++------ lib/graphql/resolvers/user.ex | 32 ++++++++-- test/graphql/resolvers/user_test.exs | 15 +++++ 6 files changed, 125 insertions(+), 29 deletions(-) create mode 100644 js/src/types/apollo.ts diff --git a/js/src/i18n/en_US.json b/js/src/i18n/en_US.json index 8c2f768f0..e34bb1e44 100644 --- a/js/src/i18n/en_US.json +++ b/js/src/i18n/en_US.json @@ -1251,5 +1251,6 @@ "Approve member": "Approve member", "Reject member": "Reject member", "The membership request from {profile} was rejected": "The membership request from {profile} was rejected", - "The member was approved": "The member was approved" + "The member was approved": "The member was approved", + "Emails usually don't contain capitals, make sure you haven't made a typo.": "Emails usually don't contain capitals, make sure you haven't made a typo." } diff --git a/js/src/i18n/fr_FR.json b/js/src/i18n/fr_FR.json index 3d475c107..72ec50fa8 100644 --- a/js/src/i18n/fr_FR.json +++ b/js/src/i18n/fr_FR.json @@ -1355,5 +1355,6 @@ "Approve member": "Approuver le ou la membre", "Reject member": "Rejeter le ou la membre", "The membership request from {profile} was rejected": "La demande d'adhésion de {profile} a été rejetée", - "The member was approved": "Le ou la membre a été approuvée" + "The member was approved": "Le ou la membre a été approuvée", + "Emails usually don't contain capitals, make sure you haven't made a typo.": "Les emails ne contiennent d'ordinaire pas de capitales, assurez-vous de n'avoir pas fait de faute de frappe." } diff --git a/js/src/types/apollo.ts b/js/src/types/apollo.ts new file mode 100644 index 000000000..244b56005 --- /dev/null +++ b/js/src/types/apollo.ts @@ -0,0 +1,5 @@ +import { GraphQLError } from "graphql/error/GraphQLError"; + +export class AbsintheGraphQLError extends GraphQLError { + readonly field: string | undefined; +} diff --git a/js/src/views/User/Register.vue b/js/src/views/User/Register.vue index a1638a75d..bb7710963 100644 --- a/js/src/views/User/Register.vue +++ b/js/src/views/User/Register.vue @@ -83,8 +83,8 @@
- -
- {{ - error - }} -
@@ -191,13 +185,18 @@ @@ -302,4 +353,7 @@ p.create-account { margin: 1rem auto 2rem; } } +::v-deep .help.is-warning { + color: #755033; +} diff --git a/lib/graphql/resolvers/user.ex b/lib/graphql/resolvers/user.ex index 430bc6b48..afbd0909f 100644 --- a/lib/graphql/resolvers/user.ex +++ b/lib/graphql/resolvers/user.ex @@ -145,13 +145,17 @@ defmodule Mobilizon.GraphQL.Resolvers.User do """ @spec create_user(any, %{email: String.t()}, any) :: {:ok, User.t()} | {:error, String.t()} def create_user(_parent, %{email: email} = args, _resolution) do - with :registration_ok <- check_registration_config(email), + with {:ok, email} <- lowercase_domain(email), + :registration_ok <- check_registration_config(email), :not_deny_listed <- check_registration_denylist(email), - {:ok, %User{} = user} <- Users.register(args), + {:ok, %User{} = user} <- Users.register(%{args | email: email}), %Bamboo.Email{} <- Email.User.send_confirmation_email(user, Map.get(args, :locale, "en")) do {:ok, user} else + {:error, :invalid_email} -> + {:error, dgettext("errors", "Your email seems to be using an invalid format")} + :registration_closed -> {:error, dgettext("errors", "Registrations are not open")} @@ -190,24 +194,40 @@ defmodule Mobilizon.GraphQL.Resolvers.User do # Remove everything behind the + email = String.replace(email, ~r/(\+.*)(?=\@)/, "") - if email_in_list(email, Config.instance_registrations_denylist()), + if email_in_list?(email, Config.instance_registrations_denylist()), do: :deny_listed, else: :not_deny_listed end @spec check_allow_listed_email(String.t()) :: :registration_ok | :not_allowlisted defp check_allow_listed_email(email) do - if email_in_list(email, Config.instance_registrations_allowlist()), + if email_in_list?(email, Config.instance_registrations_allowlist()), do: :registration_ok, else: :not_allowlisted end - defp email_in_list(email, list) do - [_, domain] = String.split(email, "@", parts: 2, trim: true) + @spec email_in_list?(String.t(), list(String.t())) :: boolean() + defp email_in_list?(email, list) do + [_, domain] = split_email(email) domain in list or email in list end + # Domains should always be lower-case, so let's force that + @spec lowercase_domain(String.t()) :: {:ok, String.t()} | {:error, :invalid_email} + defp lowercase_domain(email) do + case split_email(email) do + [user_part, domain_part] -> + {:ok, "#{user_part}@#{String.downcase(domain_part)}"} + + _ -> + {:error, :invalid_email} + end + end + + @spec split_email(String.t()) :: list(String.t()) + defp split_email(email), do: String.split(email, "@", parts: 2, trim: true) + @doc """ Validate an user, get its actor and a token """ diff --git a/test/graphql/resolvers/user_test.exs b/test/graphql/resolvers/user_test.exs index 293fcb212..0f77eae21 100644 --- a/test/graphql/resolvers/user_test.exs +++ b/test/graphql/resolvers/user_test.exs @@ -454,6 +454,21 @@ defmodule Mobilizon.GraphQL.Resolvers.UserTest do Config.put([:instance, :registration_email_denylist], []) end + test "create_user/3 lowers domain part of email", + %{ + conn: conn + } do + res = + conn + |> AbsintheHelpers.graphql_query( + query: @create_user_mutation, + variables: Map.put(@user_creation, :email, "test+alias@DEMO.tld") + ) + + assert res["errors"] == nil + assert res["data"]["createUser"]["email"] == "test+alias@demo.tld" + end + test "register_person/3 doesn't register a profile from an unknown email", %{conn: conn} do conn |> put_req_header("accept-language", "fr")