Merge branch 'fix/signup-without-email' into 'develop'
[akkoma] / lib / pleroma / web / oauth / oauth_controller.ex
index 1b1394787fdcf1be51e30aecf194e759f20abc3c..46688db7e8358cf85e4a78765812addffab8f76a 100644 (file)
@@ -1,5 +1,5 @@
 # Pleroma: A lightweight social networking server
-# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
 # SPDX-License-Identifier: AGPL-3.0-only
 
 defmodule Pleroma.Web.OAuth.OAuthController do
@@ -14,10 +14,10 @@ defmodule Pleroma.Web.OAuth.OAuthController do
   alias Pleroma.Web.ControllerHelper
   alias Pleroma.Web.OAuth.App
   alias Pleroma.Web.OAuth.Authorization
+  alias Pleroma.Web.OAuth.Scopes
   alias Pleroma.Web.OAuth.Token
   alias Pleroma.Web.OAuth.Token.Strategy.RefreshToken
   alias Pleroma.Web.OAuth.Token.Strategy.Revoke, as: RevokeToken
-  alias Pleroma.Web.OAuth.Scopes
 
   require Logger
 
@@ -37,7 +37,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
     authorize(conn, Map.merge(params, auth_attrs))
   end
 
-  def authorize(%Plug.Conn{assigns: %{token: %Token{}}} = conn, params) do
+  def authorize(%Plug.Conn{assigns: %{token: %Token{}}} = conn, %{"force_login" => _} = params) do
     if ControllerHelper.truthy_param?(params["force_login"]) do
       do_authorize(conn, params)
     else
@@ -45,6 +45,22 @@ defmodule Pleroma.Web.OAuth.OAuthController do
     end
   end
 
+  # Note: the token is set in oauth_plug, but the token and client do not always go together.
+  # For example, MastodonFE's token is set if user requests with another client,
+  # after user already authorized to MastodonFE.
+  # So we have to check client and token.
+  def authorize(
+        %Plug.Conn{assigns: %{token: %Token{} = token}} = conn,
+        %{"client_id" => client_id} = params
+      ) do
+    with %Token{} = t <- Repo.get_by(Token, token: token.token) |> Repo.preload(:app),
+         ^client_id <- t.app.client_id do
+      handle_existing_authorization(conn, params)
+    else
+      _ -> do_authorize(conn, params)
+    end
+  end
+
   def authorize(%Plug.Conn{} = conn, params), do: do_authorize(conn, params)
 
   defp do_authorize(%Plug.Conn{} = conn, params) do
@@ -151,17 +167,37 @@ defmodule Pleroma.Web.OAuth.OAuthController do
 
   defp handle_create_authorization_error(
          %Plug.Conn{} = conn,
-         {:auth_active, false},
+         {:account_status, :confirmation_pending},
          %{"authorization" => _} = params
        ) do
-    # Per https://github.com/tootsuite/mastodon/blob/
-    #   51e154f5e87968d6bb115e053689767ab33e80cd/app/controllers/api/base_controller.rb#L76
     conn
     |> put_flash(:error, dgettext("errors", "Your login is missing a confirmed e-mail address"))
     |> put_status(:forbidden)
     |> authorize(params)
   end
 
+  defp handle_create_authorization_error(
+         %Plug.Conn{} = conn,
+         {:account_status, :password_reset_pending},
+         %{"authorization" => _} = params
+       ) do
+    conn
+    |> put_flash(:error, dgettext("errors", "Password reset is required"))
+    |> put_status(:forbidden)
+    |> authorize(params)
+  end
+
+  defp handle_create_authorization_error(
+         %Plug.Conn{} = conn,
+         {:account_status, :deactivated},
+         %{"authorization" => _} = params
+       ) do
+    conn
+    |> put_flash(:error, dgettext("errors", "Your account is currently disabled"))
+    |> put_status(:forbidden)
+    |> authorize(params)
+  end
+
   defp handle_create_authorization_error(%Plug.Conn{} = conn, error, %{"authorization" => _}) do
     Authenticator.handle_error(conn, error)
   end
@@ -202,46 +238,14 @@ defmodule Pleroma.Web.OAuth.OAuthController do
       ) do
     with {:ok, %User{} = user} <- Authenticator.get_user(conn),
          {:ok, app} <- Token.Utils.fetch_app(conn),
-         {:auth_active, true} <- {:auth_active, User.auth_active?(user)},
-         {:user_active, true} <- {:user_active, !user.deactivated},
-         {:password_reset_pending, false} <-
-           {:password_reset_pending, user.password_reset_pending},
+         {:account_status, :active} <- {:account_status, User.account_status(user)},
          {:ok, scopes} <- validate_scopes(app, params),
          {:ok, auth} <- Authorization.create_authorization(app, user, scopes),
          {:ok, token} <- Token.exchange_token(app, auth) do
       json(conn, Token.Response.build(user, token))
     else
-      {:auth_active, false} ->
-        # Per https://github.com/tootsuite/mastodon/blob/
-        #   51e154f5e87968d6bb115e053689767ab33e80cd/app/controllers/api/base_controller.rb#L76
-        render_error(
-          conn,
-          :forbidden,
-          "Your login is missing a confirmed e-mail address",
-          %{},
-          "missing_confirmed_email"
-        )
-
-      {:user_active, false} ->
-        render_error(
-          conn,
-          :forbidden,
-          "Your account is currently disabled",
-          %{},
-          "account_is_disabled"
-        )
-
-      {:password_reset_pending, true} ->
-        render_error(
-          conn,
-          :forbidden,
-          "Password reset is required",
-          %{},
-          "password_reset_required"
-        )
-
-      _error ->
-        render_invalid_credentials_error(conn)
+      error ->
+        handle_token_exchange_error(conn, error)
     end
   end
 
@@ -270,6 +274,43 @@ defmodule Pleroma.Web.OAuth.OAuthController do
   # Bad request
   def token_exchange(%Plug.Conn{} = conn, params), do: bad_request(conn, params)
 
+  defp handle_token_exchange_error(%Plug.Conn{} = conn, {:account_status, :deactivated}) do
+    render_error(
+      conn,
+      :forbidden,
+      "Your account is currently disabled",
+      %{},
+      "account_is_disabled"
+    )
+  end
+
+  defp handle_token_exchange_error(
+         %Plug.Conn{} = conn,
+         {:account_status, :password_reset_pending}
+       ) do
+    render_error(
+      conn,
+      :forbidden,
+      "Password reset is required",
+      %{},
+      "password_reset_required"
+    )
+  end
+
+  defp handle_token_exchange_error(%Plug.Conn{} = conn, {:account_status, :confirmation_pending}) do
+    render_error(
+      conn,
+      :forbidden,
+      "Your login is missing a confirmed e-mail address",
+      %{},
+      "missing_confirmed_email"
+    )
+  end
+
+  defp handle_token_exchange_error(%Plug.Conn{} = conn, _error) do
+    render_invalid_credentials_error(conn)
+  end
+
   def token_revoke(%Plug.Conn{} = conn, %{"token" => _token} = params) do
     with {:ok, app} <- Token.Utils.fetch_app(conn),
          {:ok, _token} <- RevokeToken.revoke(app, params) do
@@ -456,7 +497,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
          %App{} = app <- Repo.get_by(App, client_id: client_id),
          true <- redirect_uri in String.split(app.redirect_uris),
          {:ok, scopes} <- validate_scopes(app, auth_attrs),
-         {:auth_active, true} <- {:auth_active, User.auth_active?(user)} do
+         {:account_status, :active} <- {:account_status, User.account_status(user)} do
       Authorization.create_authorization(app, user, scopes)
     end
   end
@@ -473,7 +514,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
 
   @spec validate_scopes(App.t(), map()) ::
           {:ok, list()} | {:error, :missing_scopes | :unsupported_scopes}
-  defp validate_scopes(app, params) do
+  defp validate_scopes(%App{} = app, params) do
     params
     |> Scopes.fetch_scopes(app.scopes)
     |> Scopes.validate(app.scopes)