Merge branch 'stable' into stable-sync/2.1.2
[akkoma] / lib / pleroma / web / twitter_api / controllers / remote_follow_controller.ex
index 89da760da335699fad61a7b0815a69ca79d85a4f..072d889e2e54e7a9b5bdd105ce95e6290490ae0a 100644 (file)
@@ -8,10 +8,12 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowController do
   require Logger
 
   alias Pleroma.Activity
+  alias Pleroma.MFA
   alias Pleroma.Object.Fetcher
   alias Pleroma.Plugs.OAuthScopesPlug
   alias Pleroma.User
   alias Pleroma.Web.Auth.Authenticator
+  alias Pleroma.Web.Auth.TOTPAuthenticator
   alias Pleroma.Web.CommonAPI
 
   @status_types ["Article", "Event", "Note", "Video", "Page", "Question"]
@@ -68,6 +70,8 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowController do
 
   # POST  /ostatus_subscribe
   #
+  # adds a remote account in followers if user already is signed in.
+  #
   def do_follow(%{assigns: %{user: %User{} = user}} = conn, %{"user" => %{"id" => id}}) do
     with {:fetch_user, %User{} = followee} <- {:fetch_user, User.get_cached_by_id(id)},
          {:ok, _, _, _} <- CommonAPI.follow(user, followee) do
@@ -78,9 +82,33 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowController do
     end
   end
 
+  # POST  /ostatus_subscribe
+  #
+  # step 1.
+  # checks login\password and displays step 2 form of MFA if need.
+  #
   def do_follow(conn, %{"authorization" => %{"name" => _, "password" => _, "id" => id}}) do
-    with {:fetch_user, %User{} = followee} <- {:fetch_user, User.get_cached_by_id(id)},
+    with {_, %User{} = followee} <- {:fetch_user, User.get_cached_by_id(id)},
          {_, {:ok, user}, _} <- {:auth, Authenticator.get_user(conn), followee},
+         {_, _, _, false} <- {:mfa_required, followee, user, MFA.require?(user)},
+         {:ok, _, _, _} <- CommonAPI.follow(user, followee) do
+      redirect(conn, to: "/users/#{followee.id}")
+    else
+      error ->
+        handle_follow_error(conn, error)
+    end
+  end
+
+  # POST  /ostatus_subscribe
+  #
+  # step 2
+  # checks TOTP code. otherwise displays form with errors
+  #
+  def do_follow(conn, %{"mfa" => %{"code" => code, "token" => token, "id" => id}}) do
+    with {_, %User{} = followee} <- {:fetch_user, User.get_cached_by_id(id)},
+         {_, _, {:ok, %{user: user}}} <- {:mfa_token, followee, MFA.Token.validate(token)},
+         {_, _, _, {:ok, _}} <-
+           {:verify_mfa_code, followee, token, TOTPAuthenticator.verify(code, user)},
          {:ok, _, _, _} <- CommonAPI.follow(user, followee) do
       redirect(conn, to: "/users/#{followee.id}")
     else
@@ -94,6 +122,23 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowController do
     render(conn, "followed.html", %{error: "Insufficient permissions: follow | write:follows."})
   end
 
+  defp handle_follow_error(conn, {:mfa_token, followee, _} = _) do
+    render(conn, "follow_login.html", %{error: "Wrong username or password", followee: followee})
+  end
+
+  defp handle_follow_error(conn, {:verify_mfa_code, followee, token, _} = _) do
+    render(conn, "follow_mfa.html", %{
+      error: "Wrong authentication code",
+      followee: followee,
+      mfa_token: token
+    })
+  end
+
+  defp handle_follow_error(conn, {:mfa_required, followee, user, _} = _) do
+    {:ok, %{token: token}} = MFA.Token.create(user)
+    render(conn, "follow_mfa.html", %{followee: followee, mfa_token: token, error: false})
+  end
+
   defp handle_follow_error(conn, {:auth, _, followee} = _) do
     render(conn, "follow_login.html", %{error: "Wrong username or password", followee: followee})
   end