diff --git a/config/config.exs b/config/config.exs index 78bf04ddc..d2a0f9214 100644 --- a/config/config.exs +++ b/config/config.exs @@ -113,17 +113,22 @@ config :mobilizon, :media_proxy, config :mobilizon, Mobilizon.Web.Email.Mailer, adapter: Swoosh.Adapters.SMTP, relay: "localhost", - # usually 25, 465 or 587 - port: 25, username: "", password: "", # can be `:always` or `:never` auth: :if_available, # can be `true` - ssl: false, + # ssl: false, # can be `:always` or `:never` tls: :if_available, allowed_tls_versions: [:tlsv1, :"tlsv1.1", :"tlsv1.2"], + tls_options: [ + verify: :verify_peer, + versions: [:"tlsv1.2", :"tlsv1.3"], + cacerts: :public_key.cacerts_get(), + server_name_indication: "localhost", + depth: 99 + ], retries: 1, # can be `true` no_mx_lookups: false diff --git a/config/docker.exs b/config/docker.exs index ec238770b..994c20011 100644 --- a/config/docker.exs +++ b/config/docker.exs @@ -50,7 +50,14 @@ config :mobilizon, Mobilizon.Web.Email.Mailer, username: System.get_env("MOBILIZON_SMTP_USERNAME", nil), password: System.get_env("MOBILIZON_SMTP_PASSWORD", nil), tls: :if_available, - allowed_tls_versions: [:tlsv1, :"tlsv1.1", :"tlsv1.2"], + allowed_tls_versions: [:"tlsv1.2", :"tlsv1.3"], + tls_options: [ + verify: :verify_peer, + versions: [:"tlsv1.2", :"tlsv1.3"], + cacerts: :public_key.cacerts_get(), + server_name_indication: System.get_env("MOBILIZON_SMTP_SERVER", "localhost"), + depth: 99 + ], ssl: System.get_env("MOBILIZON_SMTP_SSL", "false"), retries: 1, no_mx_lookups: false, diff --git a/lib/graphql/resolvers/user.ex b/lib/graphql/resolvers/user.ex index 73d85e0ee..e08f413ce 100644 --- a/lib/graphql/resolvers/user.ex +++ b/lib/graphql/resolvers/user.ex @@ -302,6 +302,9 @@ defmodule Mobilizon.GraphQL.Resolvers.User do {:error, :invalid_email} -> {:error, dgettext("errors", "This email doesn't seem to be valid")} + {:error, :failed_sending_mail} -> + {:error, dgettext("errors", "Couldn't send an email. Internal error.")} + {:error, :email_too_soon} -> {:error, dgettext( diff --git a/lib/web/email/mailer.ex b/lib/web/email/mailer.ex index 2f78477a2..90632214b 100644 --- a/lib/web/email/mailer.ex +++ b/lib/web/email/mailer.ex @@ -4,10 +4,18 @@ defmodule Mobilizon.Web.Email.Mailer do """ use Swoosh.Mailer, otp_app: :mobilizon alias Mobilizon.Service.ErrorReporting.Sentry + require Logger @spec send_email(Swoosh.Email.t()) :: {:ok, term} | {:error, term} def send_email(email) do - Mobilizon.Web.Email.Mailer.deliver(email) + Logger.debug( + "Mailer options #{inspect(Keyword.drop(Application.get_env(:mobilizon, Mobilizon.Web.Email.Mailer), [:tls_options]))}" + ) + + Logger.debug("Sending mail, #{inspect(email)}") + res = Mobilizon.Web.Email.Mailer.deliver(email) + Logger.debug("Return from sending mail #{inspect(res)}") + res rescue error -> Sentry.capture_exception(error, diff --git a/lib/web/email/user.ex b/lib/web/email/user.ex index d0278d7da..6967f055b 100644 --- a/lib/web/email/user.ex +++ b/lib/web/email/user.ex @@ -91,13 +91,19 @@ defmodule Mobilizon.Web.Email.User do Users.update_user(user, %{ "confirmation_sent_at" => DateTime.utc_now() |> DateTime.truncate(:second) }) do - send_confirmation_email(user, locale) - Logger.info("Sent confirmation email again to #{user.email}") - {:ok, user.email} + case send_confirmation_email(user, locale) do + {:ok, _} -> + Logger.info("Sent confirmation email again to #{user.email}") + {:ok, user.email} + + {:error, err} -> + Logger.error("Failed sending email to #{user.email}. #{inspect(err)}") + {:error, :failed_sending_mail} + end end end - @spec send_confirmation_email(User.t(), String.t()) :: Swoosh.Email.t() + @spec send_confirmation_email(User.t(), String.t()) :: {:ok, term} | {:error, term} def send_confirmation_email(%User{} = user, locale \\ "en") do user |> Email.User.confirmation_email(locale) diff --git a/mix.exs b/mix.exs index f2a45055c..842262899 100644 --- a/mix.exs +++ b/mix.exs @@ -211,7 +211,7 @@ defmodule Mobilizon.Mixfile do {:nimble_csv, "~> 1.1"}, {:export, "~> 0.1.0"}, {:erlport, "~> 0.11.0"}, - {:tz_world, "~> 1.0"}, + {:tz_world, "1.3.0"}, {:tzdata, "~> 1.1"}, {:codepagex, "~> 0.1.6"}, {:vite_phx, "~> 0.2"}, diff --git a/mix.lock b/mix.lock index e9804a273..84bca8c37 100644 --- a/mix.lock +++ b/mix.lock @@ -139,7 +139,7 @@ "telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"}, "tesla": {:hex, :tesla, "1.7.0", "a62dda2f80d4f8a925eb7b8c5b78c461e0eb996672719fe1a63b26321a5f8b4e", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:finch, "~> 0.13", [hex: :finch, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, "~> 1.3", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "4.4.0", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:msgpax, "~> 2.3", [hex: :msgpax, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "2e64f01ebfdb026209b47bc651a0e65203fcff4ae79c11efb73c4852b00dc313"}, "timex": {:hex, :timex, "3.7.11", "bb95cb4eb1d06e27346325de506bcc6c30f9c6dea40d1ebe390b262fad1862d1", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.20", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 1.1", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "8b9024f7efbabaf9bd7aa04f65cf8dcd7c9818ca5737677c7b76acbc6a94d1aa"}, - "tz_world": {:hex, :tz_world, "1.3.1", "dedb8373fce594098909ff36d37f5e5e30e47cb40ef846d1dfc91eb39f7ebaaf", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:certifi, "~> 2.5", [hex: :certifi, repo: "hexpm", optional: true]}, {:geo, "~> 1.0 or ~> 2.0 or ~> 3.3", [hex: :geo, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "901ed2b4a4430ecab3765244da4a19e6f19141867c2ab3753924919b87ed2224"}, + "tz_world": {:hex, :tz_world, "1.3.0", "a4d70486c7934b710f8b3b4374d62ebdd75e3d2b8914771ef6c62c3635a6088f", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:certifi, "~> 2.5", [hex: :certifi, repo: "hexpm", optional: true]}, {:geo, "~> 1.0 or ~> 2.0 or ~> 3.3", [hex: :geo, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "78b565aa0899b48ce34686319119dfdadff07a255ec43fd9ed6e7d60cc8d1390"}, "tzdata": {:hex, :tzdata, "1.1.1", "20c8043476dfda8504952d00adac41c6eda23912278add38edc140ae0c5bcc46", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "a69cec8352eafcd2e198dea28a34113b60fdc6cb57eb5ad65c10292a6ba89787"}, "ueberauth": {:hex, :ueberauth, "0.10.5", "806adb703df87e55b5615cf365e809f84c20c68aa8c08ff8a416a5a6644c4b02", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "3efd1f31d490a125c7ed453b926f7c31d78b97b8a854c755f5c40064bf3ac9e1"}, "ueberauth_cas": {:hex, :ueberauth_cas, "2.3.1", "df45a1f2c5df8bc80191cbca4baeeed808d697702ec5ebe5bd5d5a264481752f", [:mix], [{:httpoison, "~> 1.8", [hex: :httpoison, repo: "hexpm", optional: false]}, {:sweet_xml, "~> 0.7", [hex: :sweet_xml, repo: "hexpm", optional: false]}, {:ueberauth, "~> 0.6", [hex: :ueberauth, repo: "hexpm", optional: false]}], "hexpm", "5068ae2b9e217c2f05aa9a67483a6531e21ba0be9a6f6c8749bb7fd1599be321"}, diff --git a/test/graphql/resolvers/user_test.exs b/test/graphql/resolvers/user_test.exs index 544e6fb5f..e3e1822ae 100644 --- a/test/graphql/resolvers/user_test.exs +++ b/test/graphql/resolvers/user_test.exs @@ -121,6 +121,12 @@ defmodule Mobilizon.GraphQL.Resolvers.UserTest do } """ + @resend_registration_email_mutation """ + mutation ResendConfirmationEmail($email: String!) { + resendConfirmationEmail(email: $email) + } + """ + @send_reset_password_mutation """ mutation SendResetPassword($email: String!) { sendResetPassword(email: $email) @@ -706,22 +712,16 @@ defmodule Mobilizon.GraphQL.Resolvers.UserTest do describe "Resolver: Resend confirmation emails" do test "test resend_confirmation_email/3 with valid email resends an validation email", - context do + %{conn: conn} do {:ok, %User{} = user} = Users.register(%{email: "toto@tata.tld", password: "p4ssw0rd"}) - mutation = """ - mutation { - resendConfirmationEmail( - email: "#{user.email}" - ) - } - """ - res = - context.conn - |> post("/api", AbsintheHelpers.mutation_skeleton(mutation)) + AbsintheHelpers.graphql_query(conn, + query: @resend_registration_email_mutation, + variables: %{email: user.email} + ) - assert hd(json_response(res, 200)["errors"])["message"] == + assert hd(res["errors"])["message"] == "You requested again a confirmation email too soon. Please try again in a few minutes" # Hammer time ! @@ -730,28 +730,24 @@ defmodule Mobilizon.GraphQL.Resolvers.UserTest do }) res = - context.conn - |> post("/api", AbsintheHelpers.mutation_skeleton(mutation)) + AbsintheHelpers.graphql_query(conn, + query: @resend_registration_email_mutation, + variables: %{email: user.email} + ) - assert json_response(res, 200)["data"]["resendConfirmationEmail"] == user.email + assert res["data"]["resendConfirmationEmail"] == user.email assert_email_sent(to: user.email) end - test "test resend_confirmation_email/3 with invalid email resends an validation email", - context do - mutation = """ - mutation { - resendConfirmationEmail( - email: "oh@no.com" - ) - } - """ - + test "test resend_confirmation_email/3 with invalid email does not resend an validation email", + %{conn: conn} do res = - context.conn - |> post("/api", AbsintheHelpers.mutation_skeleton(mutation)) + AbsintheHelpers.graphql_query(conn, + query: @resend_registration_email_mutation, + variables: %{email: "oh@no.com"} + ) - assert hd(json_response(res, 200)["errors"])["message"] == + assert hd(res["errors"])["message"] == "No user to validate with this email was found" end end