From 10c156d98fee44444ed6d1366e615ddcdb2ee68a Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Mon, 10 Dec 2018 20:20:50 +0300 Subject: [PATCH 01/11] [#114] SMTP deps and config. --- config/config.exs | 2 ++ config/prod.exs | 41 ++++++++++++++++++++++++++++++++++++ lib/pleroma/emails/mailer.ex | 3 +++ mix.exs | 4 +++- mix.lock | 2 ++ 5 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 lib/pleroma/emails/mailer.ex diff --git a/config/config.exs b/config/config.exs index 1401b0a3d..410b3c618 100644 --- a/config/config.exs +++ b/config/config.exs @@ -101,6 +101,8 @@ finmoji_enabled: true, mrf_transparency: true +config :pleroma, Pleroma.Mailer, adapter: Swoosh.Adapters.Local + config :pleroma, :markup, # XXX - unfortunately, inline images must be enabled by default right now, because # of custom emoji. Issue #275 discusses defanging that somehow. diff --git a/config/prod.exs b/config/prod.exs index d0cfd1ac2..e281a4a03 100644 --- a/config/prod.exs +++ b/config/prod.exs @@ -17,6 +17,47 @@ http: [port: 4000], protocol: "http" +# Supported adapters: https://github.com/swoosh/swoosh#adapters +mailer_settings = + case String.downcase(System.get_env("PLEROMA_SWOOSH_ADAPTER") || "") do + "mailgun" -> + [ + adapter: Swoosh.Adapters.Mailgun, + api_key: System.get_env("PLEROMA_MAILGUN_API_KEY"), + domain: System.get_env("PLEROMA_MAILGUN_DOMAIN") + ] + + "mandrill" -> + [ + adapter: Swoosh.Adapters.Mandrill, + api_key: System.get_env("PLEROMA_MANDRILL_API_KEY") + ] + + "sendgrid" -> + [ + adapter: Swoosh.Adapters.Sendgrid, + api_key: System.get_env("PLEROMA_SENDGRID_API_KEY") + ] + + "smtp" -> + [ + adapter: Swoosh.Adapters.SMTP, + relay: System.get_env("PLEROMA_SMTP_RELAY"), + username: System.get_env("PLEROMA_SMTP_USERNAME"), + password: System.get_env("PLEROMA_SMTP_PASSWORD"), + port: System.get_env("PLEROMA_SMTP_PORT") || 1025, + ssl: true, + tls: :always, + auth: :always, + retries: 3 + ] + + _ -> + [adapter: Swoosh.Adapters.Local] + end + +config :pleroma, Pleroma.Mailer, mailer_settings + # Do not print debug messages in production config :logger, level: :info diff --git a/lib/pleroma/emails/mailer.ex b/lib/pleroma/emails/mailer.ex new file mode 100644 index 000000000..14ed32ea8 --- /dev/null +++ b/lib/pleroma/emails/mailer.ex @@ -0,0 +1,3 @@ +defmodule Pleroma.Mailer do + use Swoosh.Mailer, otp_app: :pleroma +end diff --git a/mix.exs b/mix.exs index b5bcd027f..0fb40e07b 100644 --- a/mix.exs +++ b/mix.exs @@ -75,7 +75,9 @@ defp deps do git: "https://github.com/msantos/crypt", ref: "1f2b58927ab57e72910191a7ebaeff984382a1d3"}, {:cors_plug, "~> 1.5"}, {:ex_doc, "> 0.18.3 and < 0.20.0", only: :dev, runtime: false}, - {:web_push_encryption, "~> 0.2.1"} + {:web_push_encryption, "~> 0.2.1"}, + {:swoosh, "~> 0.20"}, + {:gen_smtp, "~> 0.13"} ] end diff --git a/mix.lock b/mix.lock index ff8e9fdca..d9ae9a83b 100644 --- a/mix.lock +++ b/mix.lock @@ -20,6 +20,7 @@ "ex_aws_s3": {:hex, :ex_aws_s3, "2.0.1", "9e09366e77f25d3d88c5393824e613344631be8db0d1839faca49686e99b6704", [:mix], [{:ex_aws, "~> 2.0", [hex: :ex_aws, repo: "hexpm", optional: false]}, {:sweet_xml, ">= 0.0.0", [hex: :sweet_xml, repo: "hexpm", optional: true]}], "hexpm"}, "ex_doc": {:hex, :ex_doc, "0.19.1", "519bb9c19526ca51d326c060cb1778d4a9056b190086a8c6c115828eaccea6cf", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.7", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm"}, "ex_machina": {:hex, :ex_machina, "2.2.0", "fec496331e04fc2db2a1a24fe317c12c0c4a50d2beb8ebb3531ed1f0d84be0ed", [:mix], [{:ecto, "~> 2.1", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm"}, + "gen_smtp": {:hex, :gen_smtp, "0.13.0", "11f08504c4bdd831dc520b8f84a1dce5ce624474a797394e7aafd3c29f5dcd25", [:rebar3], [], "hexpm"}, "gettext": {:hex, :gettext, "0.15.0", "40a2b8ce33a80ced7727e36768499fc9286881c43ebafccae6bab731e2b2b8ce", [:mix], [], "hexpm"}, "hackney": {:hex, :hackney, "1.13.0", "24edc8cd2b28e1c652593833862435c80661834f6c9344e84b6a2255e7aeef03", [:rebar3], [{:certifi, "2.3.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "5.1.2", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.1", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"}, "html_sanitize_ex": {:hex, :html_sanitize_ex, "1.3.0", "f005ad692b717691203f940c686208aa3d8ffd9dd4bb3699240096a51fa9564e", [:mix], [{:mochiweb, "~> 2.15", [hex: :mochiweb, repo: "hexpm", optional: false]}], "hexpm"}, @@ -49,6 +50,7 @@ "postgrex": {:hex, :postgrex, "0.13.5", "3d931aba29363e1443da167a4b12f06dcd171103c424de15e5f3fc2ba3e6d9c5", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 1.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm"}, "ranch": {:hex, :ranch, "1.3.2", "e4965a144dc9fbe70e5c077c65e73c57165416a901bd02ea899cfd95aa890986", [:rebar3], [], "hexpm"}, "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.1", "28a4d65b7f59893bc2c7de786dec1e1555bd742d336043fe644ae956c3497fbe", [:make, :rebar], [], "hexpm"}, + "swoosh": {:hex, :swoosh, "0.20.0", "9a6c13822c9815993c03b6f8fccc370fcffb3c158d9754f67b1fdee6b3a5d928", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.12", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 1.1", [hex: :mime, repo: "hexpm", optional: false]}, {:plug, "~> 1.4", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm"}, "tesla": {:hex, :tesla, "1.2.1", "864783cc27f71dd8c8969163704752476cec0f3a51eb3b06393b3971dc9733ff", [:mix], [{:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, 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", [hex: :mime, repo: "hexpm", optional: false]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}], "hexpm"}, "trailing_format_plug": {:hex, :trailing_format_plug, "0.0.7", "64b877f912cf7273bed03379936df39894149e35137ac9509117e59866e10e45", [:mix], [{:plug, "> 0.12.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"}, "tzdata": {:hex, :tzdata, "0.5.17", "50793e3d85af49736701da1a040c415c97dc1caf6464112fd9bd18f425d3053b", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"}, From 12905ce1ad39106251e401fec81b871799708cc4 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Tue, 11 Dec 2018 14:59:25 +0300 Subject: [PATCH 02/11] [#114] Added /dev/mailbox dev-only route (emails preview). Added mailer config examples. --- config/config.md | 25 ++++++++++++++++++++++++ config/prod.exs | 41 --------------------------------------- lib/pleroma/web/router.ex | 18 +++++++++++++++++ 3 files changed, 43 insertions(+), 41 deletions(-) diff --git a/config/config.md b/config/config.md index dbbfa9194..2e7f6244c 100644 --- a/config/config.md +++ b/config/config.md @@ -30,6 +30,31 @@ This filter replaces the filename (not the path) of an upload. For complete obfu * `text`: Text to replace filenames in links. If empty, `{random}.extension` will be used. +## Pleroma.Mailer +* `adapter`: one of the mail adapters listed in [Swoosh readme](https://github.com/swoosh/swoosh#adapters), or `Swoosh.Adapters.Local` for in-memory mailbox. +* `api_key` / `password` and / or other adapter-specific settings, per the above documentation. + +An example for Sendgrid adapter: + +``` +config :pleroma, Pleroma.Mailer, + adapter: Swoosh.Adapters.Sendgrid, + api_key: "YOUR_API_KEY" +``` + +An example for SMTP adapter: +``` +config :pleroma, Pleroma.Mailer, + adapter: Swoosh.Adapters.SMTP, + relay: "smtp.gmail.com", + username: "YOUR_USERNAME@gmail.com", + password: "YOUR_SMTP_PASSWORD", + port: 465, + ssl: true, + tls: :always, + auth: :always +``` + ## :uri_schemes * `valid_schemes`: List of the scheme part that is considered valid to be an URL diff --git a/config/prod.exs b/config/prod.exs index e281a4a03..d0cfd1ac2 100644 --- a/config/prod.exs +++ b/config/prod.exs @@ -17,47 +17,6 @@ http: [port: 4000], protocol: "http" -# Supported adapters: https://github.com/swoosh/swoosh#adapters -mailer_settings = - case String.downcase(System.get_env("PLEROMA_SWOOSH_ADAPTER") || "") do - "mailgun" -> - [ - adapter: Swoosh.Adapters.Mailgun, - api_key: System.get_env("PLEROMA_MAILGUN_API_KEY"), - domain: System.get_env("PLEROMA_MAILGUN_DOMAIN") - ] - - "mandrill" -> - [ - adapter: Swoosh.Adapters.Mandrill, - api_key: System.get_env("PLEROMA_MANDRILL_API_KEY") - ] - - "sendgrid" -> - [ - adapter: Swoosh.Adapters.Sendgrid, - api_key: System.get_env("PLEROMA_SENDGRID_API_KEY") - ] - - "smtp" -> - [ - adapter: Swoosh.Adapters.SMTP, - relay: System.get_env("PLEROMA_SMTP_RELAY"), - username: System.get_env("PLEROMA_SMTP_USERNAME"), - password: System.get_env("PLEROMA_SMTP_PASSWORD"), - port: System.get_env("PLEROMA_SMTP_PORT") || 1025, - ssl: true, - tls: :always, - auth: :always, - retries: 3 - ] - - _ -> - [adapter: Swoosh.Adapters.Local] - end - -config :pleroma, Pleroma.Mailer, mailer_settings - # Do not print debug messages in production config :logger, level: :info diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 9c06fac4f..19b8750fc 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -85,6 +85,15 @@ defmodule Pleroma.Web.Router do plug(:accepts, ["html", "json"]) end + pipeline :mailbox_preview do + plug(:accepts, ["html"]) + + plug(:put_secure_browser_headers, %{ + "content-security-policy" => + "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline' 'unsafe-eval'" + }) + end + scope "/api/pleroma", Pleroma.Web.TwitterAPI do pipe_through(:pleroma_api) get("/password_reset/:token", UtilController, :show_password_reset) @@ -268,6 +277,7 @@ defmodule Pleroma.Web.Router do get("/statusnet/conversation/:id", TwitterAPI.Controller, :fetch_conversation) post("/account/register", TwitterAPI.Controller, :register) + post("/account/reset_password", TwitterAPI.Controller, :reset_password) get("/search", TwitterAPI.Controller, :search) get("/statusnet/tags/timeline/:tag", TwitterAPI.Controller, :public_and_external_timeline) @@ -424,6 +434,14 @@ defmodule Pleroma.Web.Router do get("/:sig/:url/:filename", MediaProxyController, :remote) end + if Mix.env() == :dev do + scope "/dev" do + pipe_through([:mailbox_preview]) + + forward("/mailbox", Plug.Swoosh.MailboxPreview, base_path: "/dev/mailbox") + end + end + scope "/", Fallback do get("/registration/:token", RedirectController, :registration_page) get("/*path", RedirectController, :redirector) From f5afb11032913fe523bd688954b4923f357c7802 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Tue, 11 Dec 2018 20:17:49 +0300 Subject: [PATCH 03/11] [#114] Initial implementation of user password reset emails (user-initiated). --- lib/pleroma/emails/user_email.ex | 37 +++++++++++++++++++ lib/pleroma/web/router.ex | 2 +- .../web/twitter_api/twitter_api_controller.ex | 19 ++++++++++ 3 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 lib/pleroma/emails/user_email.ex diff --git a/lib/pleroma/emails/user_email.ex b/lib/pleroma/emails/user_email.ex new file mode 100644 index 000000000..46d8b9c2d --- /dev/null +++ b/lib/pleroma/emails/user_email.ex @@ -0,0 +1,37 @@ +defmodule Pleroma.UserEmail do + @moduledoc "User emails" + + import Swoosh.Email + + alias Pleroma.Web.{Endpoint, Router} + + defp instance_config, do: Pleroma.Config.get(:instance) + + defp instance_name, do: instance_config()[:name] + + defp from do + {instance_name(), instance_config()[:email]} + end + + def password_reset_email(user, password_reset_token) when is_binary(password_reset_token) do + password_reset_url = + Router.Helpers.util_url( + Endpoint, + :show_password_reset, + password_reset_token + ) + + html_body = """ +

Reset your password at #{instance_name()}

+

Someone has requested password change for your account at #{instance_name()}.

+

If it was you, visit the following link to proceed: reset password.

+

If it was someone else, nothing to worry about: your data is secure and your password has not been changed.

+ """ + + new() + |> to({user.name, user.email}) + |> from(from()) + |> subject("Password reset") + |> html_body(html_body) + end +end diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 19b8750fc..6253a28db 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -277,7 +277,7 @@ defmodule Pleroma.Web.Router do get("/statusnet/conversation/:id", TwitterAPI.Controller, :fetch_conversation) post("/account/register", TwitterAPI.Controller, :register) - post("/account/reset_password", TwitterAPI.Controller, :reset_password) + post("/account/password_reset", TwitterAPI.Controller, :password_reset) get("/search", TwitterAPI.Controller, :search) get("/statusnet/tags/timeline/:tag", TwitterAPI.Controller, :public_and_external_timeline) diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex index 786849aa3..8837db566 100644 --- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex +++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex @@ -1,5 +1,9 @@ defmodule Pleroma.Web.TwitterAPI.Controller do use Pleroma.Web, :controller + + import Pleroma.Web.ControllerHelper, only: [json_response: 3] + + alias Pleroma.Formatter alias Pleroma.Web.TwitterAPI.{TwitterAPI, UserView, ActivityView, NotificationView} alias Pleroma.Web.CommonAPI alias Pleroma.{Repo, Activity, Object, User, Notification} @@ -322,6 +326,21 @@ def register(conn, params) do end end + def password_reset(conn, params) do + nickname_or_email = params["email"] || params["nickname"] + + with is_binary(nickname_or_email), + %User{local: true} = user <- User.get_by_nickname_or_email(nickname_or_email) do + {:ok, token_record} = Pleroma.PasswordResetToken.create_token(user) + + user + |> Pleroma.UserEmail.password_reset_email(token_record.token) + |> Pleroma.Mailer.deliver() + + json_response(conn, :no_content, "") + end + end + def update_avatar(%{assigns: %{user: user}} = conn, params) do {:ok, object} = ActivityPub.upload(params, type: :avatar) change = Changeset.change(user, %{avatar: object.data}) From 4e7d98922ec621a5c9bc6638a40c09890f0f17a4 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Wed, 12 Dec 2018 16:28:00 +0300 Subject: [PATCH 04/11] [#114] Added tests for "POST /api/account/password_reset". --- config/test.exs | 2 + .../twitter_api_controller_test.exs | 43 ++++++++++++++++++- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/config/test.exs b/config/test.exs index ca10a616c..5c6acfead 100644 --- a/config/test.exs +++ b/config/test.exs @@ -11,6 +11,8 @@ config :pleroma, Pleroma.Uploaders.Local, uploads: "test/uploads" +config :pleroma, Pleroma.Mailer, adapter: Swoosh.Adapters.Test + # Configure your database config :pleroma, Pleroma.Repo, adapter: Ecto.Adapters.Postgres, diff --git a/test/web/twitter_api/twitter_api_controller_test.exs b/test/web/twitter_api/twitter_api_controller_test.exs index a30d415a7..c16c0cdc0 100644 --- a/test/web/twitter_api/twitter_api_controller_test.exs +++ b/test/web/twitter_api/twitter_api_controller_test.exs @@ -9,6 +9,7 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do alias Pleroma.Web.CommonAPI alias Pleroma.Web.TwitterAPI.TwitterAPI alias Comeonin.Pbkdf2 + alias Ecto.Changeset import Pleroma.Factory @@ -270,7 +271,7 @@ test "with credentials", %{conn: conn, user: current_user} do since_id = List.last(activities).id current_user = - Ecto.Changeset.change(current_user, following: [User.ap_followers(user)]) + Changeset.change(current_user, following: [User.ap_followers(user)]) |> Repo.update!() conn = @@ -832,6 +833,46 @@ test "it returns errors on a problem", %{conn: conn} do end end + describe "POST /api/account/password_reset, with valid parameters" do + setup %{conn: conn} do + user = insert(:user) + conn = post(conn, "/api/account/password_reset?email=#{user.email}") + %{conn: conn, user: user} + end + + test "it returns 204", %{conn: conn} do + assert json_response(conn, :no_content) + end + + test "it creates a PasswordResetToken record for user", %{user: user} do + token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id) + assert token_record + end + + test "it sends an email to user", %{user: user} do + token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id) + + Swoosh.TestAssertions.assert_email_sent( + Pleroma.UserEmail.password_reset_email(user, token_record.token) + ) + end + end + + describe "POST /api/account/password_reset, with invalid parameters" do + setup [:valid_user] + + test "it returns 500 when user is not found", %{conn: conn, user: user} do + conn = post(conn, "/api/account/password_reset?email=nonexisting_#{user.email}") + assert json_response(conn, :internal_server_error) + end + + test "it returns 500 when user is not local", %{conn: conn, user: user} do + {:ok, user} = Repo.update(Changeset.change(user, local: false)) + conn = post(conn, "/api/account/password_reset?email=#{user.email}") + assert json_response(conn, :internal_server_error) + end + end + describe "GET /api/externalprofile/show" do test "it returns the user", %{conn: conn} do user = insert(:user) From e3a21bcd45dd1f27a2f6a74041d86c64ad8256e9 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Wed, 12 Dec 2018 17:14:31 +0300 Subject: [PATCH 05/11] [#114] Addressed warnings. Fix of `with` statement clause in `password_reset`. --- lib/pleroma/web/twitter_api/twitter_api_controller.ex | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex index 8837db566..479c92381 100644 --- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex +++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex @@ -3,7 +3,6 @@ defmodule Pleroma.Web.TwitterAPI.Controller do import Pleroma.Web.ControllerHelper, only: [json_response: 3] - alias Pleroma.Formatter alias Pleroma.Web.TwitterAPI.{TwitterAPI, UserView, ActivityView, NotificationView} alias Pleroma.Web.CommonAPI alias Pleroma.{Repo, Activity, Object, User, Notification} @@ -329,7 +328,7 @@ def register(conn, params) do def password_reset(conn, params) do nickname_or_email = params["email"] || params["nickname"] - with is_binary(nickname_or_email), + with true <- is_binary(nickname_or_email), %User{local: true} = user <- User.get_by_nickname_or_email(nickname_or_email) do {:ok, token_record} = Pleroma.PasswordResetToken.create_token(user) From bfff2399ff2d7b479b066c6f92bf9331f80bb914 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Wed, 12 Dec 2018 20:15:43 +0300 Subject: [PATCH 06/11] [#114] Routes and config for `confirm_email` and `email_invite` (Twitter API). --- lib/pleroma/web/router.ex | 3 +++ lib/pleroma/web/twitter_api/controllers/util_controller.ex | 3 +++ lib/pleroma/web/twitter_api/twitter_api_controller.ex | 4 ++++ 3 files changed, 10 insertions(+) diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 6253a28db..6a2e0d6c4 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -278,6 +278,7 @@ defmodule Pleroma.Web.Router do post("/account/register", TwitterAPI.Controller, :register) post("/account/password_reset", TwitterAPI.Controller, :password_reset) + get("/account/confirm_email", TwitterAPI.Controller, :confirm_email) get("/search", TwitterAPI.Controller, :search) get("/statusnet/tags/timeline/:tag", TwitterAPI.Controller, :public_and_external_timeline) @@ -312,6 +313,8 @@ defmodule Pleroma.Web.Router do post("/account/update_profile_banner", TwitterAPI.Controller, :update_banner) post("/qvitter/update_background_image", TwitterAPI.Controller, :update_background) + post("/email_invite", TwitterAPI.Controller, :email_invite) + get("/statuses/home_timeline", TwitterAPI.Controller, :friends_timeline) get("/statuses/friends_timeline", TwitterAPI.Controller, :friends_timeline) get("/statuses/mentions", TwitterAPI.Controller, :mentions_timeline) diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex index b1e4c77e8..cacfbbf91 100644 --- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex @@ -166,6 +166,9 @@ def config(conn, _params) do textlimit: to_string(Keyword.get(instance, :limit)), closed: if(Keyword.get(instance, :registrations_open), do: "0", else: "1"), private: if(Keyword.get(instance, :public, true), do: "0", else: "1"), + accountActivationRequired: + if(Keyword.get(instance, :account_activation_required, false), do: "1", else: "0"), + invitesEnabled: if(Keyword.get(instance, :invites_enabled, true), do: "1", else: "0"), vapidPublicKey: vapid_public_key } diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex index 479c92381..911dd6a48 100644 --- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex +++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex @@ -340,6 +340,10 @@ def password_reset(conn, params) do end end + def confirm_email(_conn, _params), do: :noop + + def email_invite(_conn, _params), do: :noop + def update_avatar(%{assigns: %{user: user}} = conn, params) do {:ok, object} = ActivityPub.upload(params, type: :avatar) change = Changeset.change(user, %{avatar: object.data}) From 908943352fe2d81c34323a5571ad5c1d391969e1 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Thu, 13 Dec 2018 13:17:49 +0300 Subject: [PATCH 07/11] [#114] Refactored `password_reset` (moved to TwitterAPI). Added homepage links to password reset result pages. --- .../util/password_reset_failed.html.eex | 1 + .../util/password_reset_success.html.eex | 1 + lib/pleroma/web/twitter_api/twitter_api.ex | 19 +++++++++++++++++++ .../web/twitter_api/twitter_api_controller.ex | 9 +-------- 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/lib/pleroma/web/templates/twitter_api/util/password_reset_failed.html.eex b/lib/pleroma/web/templates/twitter_api/util/password_reset_failed.html.eex index 58a3736fd..df037c01e 100644 --- a/lib/pleroma/web/templates/twitter_api/util/password_reset_failed.html.eex +++ b/lib/pleroma/web/templates/twitter_api/util/password_reset_failed.html.eex @@ -1 +1,2 @@

Password reset failed

+

Homepage

diff --git a/lib/pleroma/web/templates/twitter_api/util/password_reset_success.html.eex b/lib/pleroma/web/templates/twitter_api/util/password_reset_success.html.eex index c7dfcb6dd..f30ba3274 100644 --- a/lib/pleroma/web/templates/twitter_api/util/password_reset_success.html.eex +++ b/lib/pleroma/web/templates/twitter_api/util/password_reset_success.html.eex @@ -1 +1,2 @@

Password changed!

+

Homepage

diff --git a/lib/pleroma/web/twitter_api/twitter_api.ex b/lib/pleroma/web/twitter_api/twitter_api.ex index 79ea48d86..1e764f24a 100644 --- a/lib/pleroma/web/twitter_api/twitter_api.ex +++ b/lib/pleroma/web/twitter_api/twitter_api.ex @@ -167,6 +167,25 @@ def register_user(params) do end end + def password_reset(nickname_or_email) do + with true <- is_binary(nickname_or_email), + %User{local: true} = user <- User.get_by_nickname_or_email(nickname_or_email), + {:ok, token_record} <- Pleroma.PasswordResetToken.create_token(user) do + user + |> Pleroma.UserEmail.password_reset_email(token_record.token) + |> Pleroma.Mailer.deliver() + else + false -> + {:error, "bad user identifier"} + + %User{local: false} -> + {:error, "remote user"} + + nil -> + {:error, "unknown user"} + end + end + def get_by_id_or_nickname(id_or_nickname) do if !is_integer(id_or_nickname) && :error == Integer.parse(id_or_nickname) do Repo.get_by(User, nickname: id_or_nickname) diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex index 911dd6a48..a45fdcf51 100644 --- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex +++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex @@ -328,14 +328,7 @@ def register(conn, params) do def password_reset(conn, params) do nickname_or_email = params["email"] || params["nickname"] - with true <- is_binary(nickname_or_email), - %User{local: true} = user <- User.get_by_nickname_or_email(nickname_or_email) do - {:ok, token_record} = Pleroma.PasswordResetToken.create_token(user) - - user - |> Pleroma.UserEmail.password_reset_email(token_record.token) - |> Pleroma.Mailer.deliver() - + with {:ok, _} <- TwitterAPI.password_reset(nickname_or_email) do json_response(conn, :no_content, "") end end From 00744c6b03d043defcf87696f539d65e41ad6a62 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Thu, 13 Dec 2018 14:30:48 +0300 Subject: [PATCH 08/11] [#114] Initial implementation of user email invitations. --- lib/pleroma/emails/user_email.ex | 30 +++++++++++++++-- .../controllers/util_controller.ex | 2 +- .../web/twitter_api/twitter_api_controller.ex | 8 ++++- .../twitter_api_controller_test.exs | 32 +++++++++++++++++++ 4 files changed, 67 insertions(+), 5 deletions(-) diff --git a/lib/pleroma/emails/user_email.ex b/lib/pleroma/emails/user_email.ex index 46d8b9c2d..47dcd42e0 100644 --- a/lib/pleroma/emails/user_email.ex +++ b/lib/pleroma/emails/user_email.ex @@ -9,10 +9,13 @@ defp instance_config, do: Pleroma.Config.get(:instance) defp instance_name, do: instance_config()[:name] - defp from do + defp sender do {instance_name(), instance_config()[:email]} end + defp recipient(email, nil), do: email + defp recipient(email, name), do: {name, email} + def password_reset_email(user, password_reset_token) when is_binary(password_reset_token) do password_reset_url = Router.Helpers.util_url( @@ -29,9 +32,30 @@ def password_reset_email(user, password_reset_token) when is_binary(password_res """ new() - |> to({user.name, user.email}) - |> from(from()) + |> to(recipient(user.email, user.name)) + |> from(sender()) |> subject("Password reset") |> html_body(html_body) end + + def user_invitation_email(user, to_email, to_name \\ nil) do + registration_url = + Router.Helpers.redirect_url( + Endpoint, + :registration_page, + "" + ) + + html_body = """ +

You are invited to #{instance_name()}

+

#{user.name} invites you to join #{instance_name()}, an instance of Pleroma federated social networking platform.

+

Click the following link to register: accept invitation.

+ """ + + new() + |> to(recipient(to_email, to_name)) + |> from(sender()) + |> subject("Invitation to #{instance_name()}") + |> html_body(html_body) + end end diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex index cacfbbf91..cbde45e52 100644 --- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex @@ -168,7 +168,7 @@ def config(conn, _params) do private: if(Keyword.get(instance, :public, true), do: "0", else: "1"), accountActivationRequired: if(Keyword.get(instance, :account_activation_required, false), do: "1", else: "0"), - invitesEnabled: if(Keyword.get(instance, :invites_enabled, true), do: "1", else: "0"), + invitesEnabled: if(Keyword.get(instance, :invites_enabled, false), do: "1", else: "0"), vapidPublicKey: vapid_public_key } diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex index a45fdcf51..d51d71299 100644 --- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex +++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex @@ -335,7 +335,13 @@ def password_reset(conn, params) do def confirm_email(_conn, _params), do: :noop - def email_invite(_conn, _params), do: :noop + def email_invite(%{assigns: %{user: user}} = conn, %{"email" => email} = params) do + with true <- Pleroma.Config.get([:instance, :invites_enabled]), + email <- Pleroma.UserEmail.user_invitation_email(user, email, params["name"]), + {:ok, _} <- Pleroma.Mailer.deliver(email) do + json_response(conn, :no_content, "") + end + end def update_avatar(%{assigns: %{user: user}} = conn, params) do {:ok, object} = ActivityPub.upload(params, type: :avatar) diff --git a/test/web/twitter_api/twitter_api_controller_test.exs b/test/web/twitter_api/twitter_api_controller_test.exs index c16c0cdc0..cbb5f7796 100644 --- a/test/web/twitter_api/twitter_api_controller_test.exs +++ b/test/web/twitter_api/twitter_api_controller_test.exs @@ -873,6 +873,38 @@ test "it returns 500 when user is not local", %{conn: conn, user: user} do end end + describe "POST /api/email_invite, with valid parameters" do + setup [:valid_user] + + setup do + invites_enabled = Pleroma.Config.get([:instance, :invites_enabled]) + Pleroma.Config.put([:instance, :invites_enabled], true) + + on_exit(fn -> + Pleroma.Config.put([:instance, :invites_enabled], invites_enabled) + :ok + end) + + :ok + end + + test "it returns 204", %{conn: conn, user: user} do + recipient_email = "foo@bar.com" + recipient_name = "J. D." + + conn = + conn + |> assign(:user, user) + |> post("/api/email_invite?email=#{recipient_email}&name=#{recipient_name}") + + assert json_response(conn, :no_content) + + Swoosh.TestAssertions.assert_email_sent( + Pleroma.UserEmail.user_invitation_email(user, recipient_email, recipient_name) + ) + end + end + describe "GET /api/externalprofile/show" do test "it returns the user", %{conn: conn} do user = insert(:user) From 18b9467d1a63766123f625a964303f6b4d28a39f Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Thu, 13 Dec 2018 16:22:42 +0300 Subject: [PATCH 09/11] [#114] Removed `email_invite` implementation (to be addressed separately). --- .../web/twitter_api/twitter_api_controller.ex | 8 +---- .../twitter_api_controller_test.exs | 32 ------------------- 2 files changed, 1 insertion(+), 39 deletions(-) diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex index d51d71299..a45fdcf51 100644 --- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex +++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex @@ -335,13 +335,7 @@ def password_reset(conn, params) do def confirm_email(_conn, _params), do: :noop - def email_invite(%{assigns: %{user: user}} = conn, %{"email" => email} = params) do - with true <- Pleroma.Config.get([:instance, :invites_enabled]), - email <- Pleroma.UserEmail.user_invitation_email(user, email, params["name"]), - {:ok, _} <- Pleroma.Mailer.deliver(email) do - json_response(conn, :no_content, "") - end - end + def email_invite(_conn, _params), do: :noop def update_avatar(%{assigns: %{user: user}} = conn, params) do {:ok, object} = ActivityPub.upload(params, type: :avatar) diff --git a/test/web/twitter_api/twitter_api_controller_test.exs b/test/web/twitter_api/twitter_api_controller_test.exs index cbb5f7796..c16c0cdc0 100644 --- a/test/web/twitter_api/twitter_api_controller_test.exs +++ b/test/web/twitter_api/twitter_api_controller_test.exs @@ -873,38 +873,6 @@ test "it returns 500 when user is not local", %{conn: conn, user: user} do end end - describe "POST /api/email_invite, with valid parameters" do - setup [:valid_user] - - setup do - invites_enabled = Pleroma.Config.get([:instance, :invites_enabled]) - Pleroma.Config.put([:instance, :invites_enabled], true) - - on_exit(fn -> - Pleroma.Config.put([:instance, :invites_enabled], invites_enabled) - :ok - end) - - :ok - end - - test "it returns 204", %{conn: conn, user: user} do - recipient_email = "foo@bar.com" - recipient_name = "J. D." - - conn = - conn - |> assign(:user, user) - |> post("/api/email_invite?email=#{recipient_email}&name=#{recipient_name}") - - assert json_response(conn, :no_content) - - Swoosh.TestAssertions.assert_email_sent( - Pleroma.UserEmail.user_invitation_email(user, recipient_email, recipient_name) - ) - end - end - describe "GET /api/externalprofile/show" do test "it returns the user", %{conn: conn} do user = insert(:user) From 9e689de06302df8b1d281c41f7774d43a0db3758 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Thu, 13 Dec 2018 16:22:42 +0300 Subject: [PATCH 10/11] [#114] Removed `email_invite` implementation (to be addressed separately). --- lib/pleroma/emails/user_email.ex | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/lib/pleroma/emails/user_email.ex b/lib/pleroma/emails/user_email.ex index 47dcd42e0..9cdf002f3 100644 --- a/lib/pleroma/emails/user_email.ex +++ b/lib/pleroma/emails/user_email.ex @@ -37,25 +37,4 @@ def password_reset_email(user, password_reset_token) when is_binary(password_res |> subject("Password reset") |> html_body(html_body) end - - def user_invitation_email(user, to_email, to_name \\ nil) do - registration_url = - Router.Helpers.redirect_url( - Endpoint, - :registration_page, - "" - ) - - html_body = """ -

You are invited to #{instance_name()}

-

#{user.name} invites you to join #{instance_name()}, an instance of Pleroma federated social networking platform.

-

Click the following link to register: accept invitation.

- """ - - new() - |> to(recipient(to_email, to_name)) - |> from(sender()) - |> subject("Invitation to #{instance_name()}") - |> html_body(html_body) - end end From f81213910fb32505b92fc19270cc483e00862d0c Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Fri, 14 Dec 2018 12:09:55 +0300 Subject: [PATCH 11/11] [#114] Addressed MR comments. Removed functionality to be extracted to other MRs. --- config/config.exs | 2 -- config/dev.exs | 2 ++ lib/pleroma/web/router.ex | 3 --- lib/pleroma/web/twitter_api/controllers/util_controller.ex | 3 --- lib/pleroma/web/twitter_api/twitter_api_controller.ex | 4 ---- 5 files changed, 2 insertions(+), 12 deletions(-) diff --git a/config/config.exs b/config/config.exs index 410b3c618..1401b0a3d 100644 --- a/config/config.exs +++ b/config/config.exs @@ -101,8 +101,6 @@ finmoji_enabled: true, mrf_transparency: true -config :pleroma, Pleroma.Mailer, adapter: Swoosh.Adapters.Local - config :pleroma, :markup, # XXX - unfortunately, inline images must be enabled by default right now, because # of custom emoji. Issue #275 discusses defanging that somehow. diff --git a/config/dev.exs b/config/dev.exs index 166be721a..080a2f8db 100644 --- a/config/dev.exs +++ b/config/dev.exs @@ -17,6 +17,8 @@ check_origin: false, watchers: [] +config :pleroma, Pleroma.Mailer, adapter: Swoosh.Adapters.Local + # ## SSL Support # # In order to use HTTPS in development, a self-signed diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 6a2e0d6c4..6253a28db 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -278,7 +278,6 @@ defmodule Pleroma.Web.Router do post("/account/register", TwitterAPI.Controller, :register) post("/account/password_reset", TwitterAPI.Controller, :password_reset) - get("/account/confirm_email", TwitterAPI.Controller, :confirm_email) get("/search", TwitterAPI.Controller, :search) get("/statusnet/tags/timeline/:tag", TwitterAPI.Controller, :public_and_external_timeline) @@ -313,8 +312,6 @@ defmodule Pleroma.Web.Router do post("/account/update_profile_banner", TwitterAPI.Controller, :update_banner) post("/qvitter/update_background_image", TwitterAPI.Controller, :update_background) - post("/email_invite", TwitterAPI.Controller, :email_invite) - get("/statuses/home_timeline", TwitterAPI.Controller, :friends_timeline) get("/statuses/friends_timeline", TwitterAPI.Controller, :friends_timeline) get("/statuses/mentions", TwitterAPI.Controller, :mentions_timeline) diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex index cbde45e52..b1e4c77e8 100644 --- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex @@ -166,9 +166,6 @@ def config(conn, _params) do textlimit: to_string(Keyword.get(instance, :limit)), closed: if(Keyword.get(instance, :registrations_open), do: "0", else: "1"), private: if(Keyword.get(instance, :public, true), do: "0", else: "1"), - accountActivationRequired: - if(Keyword.get(instance, :account_activation_required, false), do: "1", else: "0"), - invitesEnabled: if(Keyword.get(instance, :invites_enabled, false), do: "1", else: "0"), vapidPublicKey: vapid_public_key } diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex index a45fdcf51..38eff8191 100644 --- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex +++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex @@ -333,10 +333,6 @@ def password_reset(conn, params) do end end - def confirm_email(_conn, _params), do: :noop - - def email_invite(_conn, _params), do: :noop - def update_avatar(%{assigns: %{user: user}} = conn, params) do {:ok, object} = ActivityPub.upload(params, type: :avatar) change = Changeset.change(user, %{avatar: object.data})