Merge remote-tracking branch 'remotes/origin/develop' into auth-improvements
authorIvan Tashkinov <ivantashkinov@gmail.com>
Sat, 21 Nov 2020 16:47:46 +0000 (19:47 +0300)
committerIvan Tashkinov <ivantashkinov@gmail.com>
Sat, 21 Nov 2020 16:47:46 +0000 (19:47 +0300)
21 files changed:
docs/dev.md
lib/pleroma/helpers/auth_helper.ex [new file with mode: 0644]
lib/pleroma/web/o_auth/o_auth_controller.ex
lib/pleroma/web/o_auth/token.ex
lib/pleroma/web/plugs/admin_secret_authentication_plug.ex
lib/pleroma/web/plugs/authentication_plug.ex
lib/pleroma/web/plugs/basic_auth_decoder_plug.ex
lib/pleroma/web/plugs/ensure_user_key_plug.ex
lib/pleroma/web/plugs/legacy_authentication_plug.ex [deleted file]
lib/pleroma/web/plugs/o_auth_plug.ex
lib/pleroma/web/plugs/session_authentication_plug.ex [deleted file]
lib/pleroma/web/plugs/set_user_session_id_plug.ex
lib/pleroma/web/plugs/user_enabled_plug.ex
lib/pleroma/web/plugs/user_fetcher_plug.ex
lib/pleroma/web/router.ex
test/pleroma/web/plugs/admin_secret_authentication_plug_test.exs
test/pleroma/web/plugs/authentication_plug_test.exs
test/pleroma/web/plugs/legacy_authentication_plug_test.exs [deleted file]
test/pleroma/web/plugs/o_auth_plug_test.exs
test/pleroma/web/plugs/session_authentication_plug_test.exs [deleted file]
test/pleroma/web/plugs/set_user_session_id_plug_test.exs

index aa89a941fdfe3e011150a7115f47e372fbc12a2f..765380a58e3d504b0c13fcfc9b5723f480188d63 100644 (file)
@@ -14,9 +14,9 @@ This document contains notes and guidelines for Pleroma developers.
 
   For `:api` pipeline routes, it'll be verified whether `OAuthScopesPlug` was called or explicitly skipped, and if it was not then auth information will be dropped for request. Then `EnsurePublicOrAuthenticatedPlug` will be called to ensure that either the instance is not private or user is authenticated (unless explicitly skipped). Such automated checks help to prevent human errors and result in higher security / privacy for users.
 
-## [HTTP Basic Authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization)
+## Non-OAuth authentication
 
-* With HTTP Basic Auth, OAuth scopes check is _not_ performed for any action (since password is provided during the auth, requester is able to obtain a token with full permissions anyways). `Pleroma.Web.Plugs.AuthenticationPlug` and `Pleroma.Web.Plugs.LegacyAuthenticationPlug` both call `Pleroma.Web.Plugs.OAuthScopesPlug.skip_plug(conn)` when password is provided.
+* With non-OAuth authentication ([HTTP Basic Authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization) or HTTP header- or params-provided auth), OAuth scopes check is _not_ performed for any action (since password is provided during the auth, requester is able to obtain a token with full permissions anyways); auth plugs invoke `Pleroma.Helpers.AuthHelper.skip_oauth(conn)` in this case.
 
 ## Auth-related configuration, OAuth consumer mode etc.
 
diff --git a/lib/pleroma/helpers/auth_helper.ex b/lib/pleroma/helpers/auth_helper.ex
new file mode 100644 (file)
index 0000000..878fec3
--- /dev/null
@@ -0,0 +1,25 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Helpers.AuthHelper do
+  alias Pleroma.Web.Plugs.OAuthScopesPlug
+
+  import Plug.Conn
+
+  @doc """
+  Skips OAuth permissions (scopes) checks, assigns nil `:token`.
+  Intended to be used with explicit authentication and only when OAuth token cannot be determined.
+  """
+  def skip_oauth(conn) do
+    conn
+    |> assign(:token, nil)
+    |> OAuthScopesPlug.skip_plug()
+  end
+
+  def drop_auth_info(conn) do
+    conn
+    |> assign(:user, nil)
+    |> assign(:token, nil)
+  end
+end
index d2f9d1cebf379c941df3dfffe46d6b2f19215ac9..83a25907d7f2b5bf2ee77ebc4d995591626960d6 100644 (file)
@@ -363,7 +363,15 @@ defmodule Pleroma.Web.OAuth.OAuthController do
 
   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
+         {:ok, %Token{} = oauth_token} <- RevokeToken.revoke(app, params) do
+      conn =
+        with session_token = get_session(conn, :oauth_token),
+             %Token{token: ^session_token} <- oauth_token do
+          delete_session(conn, :oauth_token)
+        else
+          _ -> conn
+        end
+
       json(conn, %{})
     else
       _error ->
index de37998f24578b60f28aee2c441d31840629273a..9170a7ec7daaf4fba673656f62d4e63d3da75a51 100644 (file)
@@ -27,6 +27,14 @@ defmodule Pleroma.Web.OAuth.Token do
     timestamps()
   end
 
+  @doc "Gets token by unique access token"
+  @spec get_by_token(String.t()) :: {:ok, t()} | {:error, :not_found}
+  def get_by_token(token) do
+    token
+    |> Query.get_by_token()
+    |> Repo.find_resource()
+  end
+
   @doc "Gets token for app by access token"
   @spec get_by_token(App.t(), String.t()) :: {:ok, t()} | {:error, :not_found}
   def get_by_token(%App{id: app_id} = _app, token) do
index d7d4e4092b4ba51631262de2b8b168acb49f5d0e..ff49801f4cca8692160b90e15e835fe32a658e30 100644 (file)
@@ -5,8 +5,8 @@
 defmodule Pleroma.Web.Plugs.AdminSecretAuthenticationPlug do
   import Plug.Conn
 
+  alias Pleroma.Helpers.AuthHelper
   alias Pleroma.User
-  alias Pleroma.Web.Plugs.OAuthScopesPlug
   alias Pleroma.Web.Plugs.RateLimiter
 
   def init(options) do
@@ -51,7 +51,7 @@ defmodule Pleroma.Web.Plugs.AdminSecretAuthenticationPlug do
   defp assign_admin_user(conn) do
     conn
     |> assign(:user, %User{is_admin: true})
-    |> OAuthScopesPlug.skip_plug()
+    |> AuthHelper.skip_oauth()
   end
 
   defp handle_bad_token(conn) do
index e2a8b1b69366062b8571f861ed17db1e56fd403c..a7b8a9bfe4ee5546b9e1244397751b171f0a5f94 100644 (file)
@@ -3,6 +3,9 @@
 # SPDX-License-Identifier: AGPL-3.0-only
 
 defmodule Pleroma.Web.Plugs.AuthenticationPlug do
+  @moduledoc "Password authentication plug."
+
+  alias Pleroma.Helpers.AuthHelper
   alias Pleroma.User
 
   import Plug.Conn
@@ -11,6 +14,30 @@ defmodule Pleroma.Web.Plugs.AuthenticationPlug do
 
   def init(options), do: options
 
+  def call(%{assigns: %{user: %User{}}} = conn, _), do: conn
+
+  def call(
+        %{
+          assigns: %{
+            auth_user: %{password_hash: password_hash} = auth_user,
+            auth_credentials: %{password: password}
+          }
+        } = conn,
+        _
+      ) do
+    if checkpw(password, password_hash) do
+      {:ok, auth_user} = maybe_update_password(auth_user, password)
+
+      conn
+      |> assign(:user, auth_user)
+      |> AuthHelper.skip_oauth()
+    else
+      conn
+    end
+  end
+
+  def call(conn, _), do: conn
+
   def checkpw(password, "$6" <> _ = password_hash) do
     :crypt.crypt(password, password_hash) == password_hash
   end
@@ -40,40 +67,6 @@ defmodule Pleroma.Web.Plugs.AuthenticationPlug do
   def maybe_update_password(user, _), do: {:ok, user}
 
   defp do_update_password(user, password) do
-    user
-    |> User.password_update_changeset(%{
-      "password" => password,
-      "password_confirmation" => password
-    })
-    |> Pleroma.Repo.update()
+    User.reset_password(user, %{password: password, password_confirmation: password})
   end
-
-  def call(%{assigns: %{user: %User{}}} = conn, _), do: conn
-
-  def call(
-        %{
-          assigns: %{
-            auth_user: %{password_hash: password_hash} = auth_user,
-            auth_credentials: %{password: password}
-          }
-        } = conn,
-        _
-      ) do
-    if checkpw(password, password_hash) do
-      {:ok, auth_user} = maybe_update_password(auth_user, password)
-
-      conn
-      |> assign(:user, auth_user)
-      |> Pleroma.Web.Plugs.OAuthScopesPlug.skip_plug()
-    else
-      conn
-    end
-  end
-
-  def call(%{assigns: %{auth_credentials: %{password: _}}} = conn, _) do
-    Pbkdf2.no_user_verify()
-    conn
-  end
-
-  def call(conn, _), do: conn
 end
index 4dadfb0004526355f704f60ee7c4962b94225f3e..97529aedbae6f0d919f91e0333b0a57cf09b46a3 100644 (file)
@@ -3,6 +3,12 @@
 # SPDX-License-Identifier: AGPL-3.0-only
 
 defmodule Pleroma.Web.Plugs.BasicAuthDecoderPlug do
+  @moduledoc """
+  Decodes HTTP Basic Auth information and assigns `:auth_credentials`.
+
+  NOTE: no checks are performed at this step, auth_credentials/username could be easily faked.
+  """
+
   import Plug.Conn
 
   def init(options) do
index 70d3091f0268623735b1675d5a498e4108961ea9..31608dbbf2e0b6259b7f7cb6f7b5a43b6d6fd8ba 100644 (file)
@@ -5,6 +5,8 @@
 defmodule Pleroma.Web.Plugs.EnsureUserKeyPlug do
   import Plug.Conn
 
+  @moduledoc "Ensures `conn.assigns.user` is initialized."
+
   def init(opts) do
     opts
   end
@@ -12,7 +14,6 @@ defmodule Pleroma.Web.Plugs.EnsureUserKeyPlug do
   def call(%{assigns: %{user: _}} = conn, _), do: conn
 
   def call(conn, _) do
-    conn
-    |> assign(:user, nil)
+    assign(conn, :user, nil)
   end
 end
diff --git a/lib/pleroma/web/plugs/legacy_authentication_plug.ex b/lib/pleroma/web/plugs/legacy_authentication_plug.ex
deleted file mode 100644 (file)
index 2a54d0b..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.Plugs.LegacyAuthenticationPlug do
-  import Plug.Conn
-
-  alias Pleroma.User
-
-  def init(options) do
-    options
-  end
-
-  def call(%{assigns: %{user: %User{}}} = conn, _), do: conn
-
-  def call(
-        %{
-          assigns: %{
-            auth_user: %{password_hash: "$6$" <> _ = password_hash} = auth_user,
-            auth_credentials: %{password: password}
-          }
-        } = conn,
-        _
-      ) do
-    with ^password_hash <- :crypt.crypt(password, password_hash),
-         {:ok, user} <-
-           User.reset_password(auth_user, %{password: password, password_confirmation: password}) do
-      conn
-      |> assign(:auth_user, user)
-      |> assign(:user, user)
-      |> Pleroma.Web.Plugs.OAuthScopesPlug.skip_plug()
-    else
-      _ ->
-        conn
-    end
-  end
-
-  def call(conn, _) do
-    conn
-  end
-end
index c7b58d90fa7dd20d28959c1664b782a38c9d74fe..a3b7d42f749dba5687f799d5d72f87536092361f 100644 (file)
@@ -3,6 +3,8 @@
 # SPDX-License-Identifier: AGPL-3.0-only
 
 defmodule Pleroma.Web.Plugs.OAuthPlug do
+  @moduledoc "Performs OAuth authentication by token from params / headers / cookies."
+
   import Plug.Conn
   import Ecto.Query
 
@@ -17,45 +19,26 @@ defmodule Pleroma.Web.Plugs.OAuthPlug do
 
   def call(%{assigns: %{user: %User{}}} = conn, _), do: conn
 
-  def call(%{params: %{"access_token" => access_token}} = conn, _) do
-    with {:ok, user, token_record} <- fetch_user_and_token(access_token) do
-      conn
-      |> assign(:token, token_record)
-      |> assign(:user, user)
-    else
-      _ ->
-        # token found, but maybe only with app
-        with {:ok, app, token_record} <- fetch_app_and_token(access_token) do
-          conn
-          |> assign(:token, token_record)
-          |> assign(:app, app)
-        else
-          _ -> conn
-        end
-    end
-  end
-
   def call(conn, _) do
-    case fetch_token_str(conn) do
-      {:ok, token} ->
-        with {:ok, user, token_record} <- fetch_user_and_token(token) do
-          conn
-          |> assign(:token, token_record)
-          |> assign(:user, user)
-        else
-          _ ->
-            # token found, but maybe only with app
-            with {:ok, app, token_record} <- fetch_app_and_token(token) do
-              conn
-              |> assign(:token, token_record)
-              |> assign(:app, app)
-            else
-              _ -> conn
-            end
-        end
-
-      _ ->
+    with {:ok, token_str} <- fetch_token_str(conn) do
+      with {:ok, user, user_token} <- fetch_user_and_token(token_str),
+           false <- Token.is_expired?(user_token) do
         conn
+        |> assign(:token, user_token)
+        |> assign(:user, user)
+      else
+        _ ->
+          with {:ok, app, app_token} <- fetch_app_and_token(token_str),
+               false <- Token.is_expired?(app_token) do
+            conn
+            |> assign(:token, app_token)
+            |> assign(:app, app)
+          else
+            _ -> conn
+          end
+      end
+    else
+      _ -> conn
     end
   end
 
@@ -70,7 +53,6 @@ defmodule Pleroma.Web.Plugs.OAuthPlug do
         preload: [user: user]
       )
 
-    # credo:disable-for-next-line Credo.Check.Readability.MaxLineLength
     with %Token{user: user} = token_record <- Repo.one(query) do
       {:ok, user, token_record}
     end
@@ -86,29 +68,23 @@ defmodule Pleroma.Web.Plugs.OAuthPlug do
     end
   end
 
-  # Gets token from session by :oauth_token key
+  # Gets token string from conn (in params / headers / session)
   #
-  @spec fetch_token_from_session(Plug.Conn.t()) :: :no_token_found | {:ok, String.t()}
-  defp fetch_token_from_session(conn) do
-    case get_session(conn, :oauth_token) do
-      nil -> :no_token_found
-      token -> {:ok, token}
-    end
+  @spec fetch_token_str(Plug.Conn.t() | list(String.t())) :: :no_token_found | {:ok, String.t()}
+  defp fetch_token_str(%Plug.Conn{params: %{"access_token" => access_token}} = _conn) do
+    {:ok, access_token}
   end
 
-  # Gets token from headers
-  #
-  @spec fetch_token_str(Plug.Conn.t()) :: :no_token_found | {:ok, String.t()}
   defp fetch_token_str(%Plug.Conn{} = conn) do
     headers = get_req_header(conn, "authorization")
 
-    with :no_token_found <- fetch_token_str(headers),
-         do: fetch_token_from_session(conn)
+    with {:ok, token} <- fetch_token_str(headers) do
+      {:ok, token}
+    else
+      _ -> fetch_token_from_session(conn)
+    end
   end
 
-  @spec fetch_token_str(Keyword.t()) :: :no_token_found | {:ok, String.t()}
-  defp fetch_token_str([]), do: :no_token_found
-
   defp fetch_token_str([token | tail]) do
     trimmed_token = String.trim(token)
 
@@ -117,4 +93,14 @@ defmodule Pleroma.Web.Plugs.OAuthPlug do
       _ -> fetch_token_str(tail)
     end
   end
+
+  defp fetch_token_str([]), do: :no_token_found
+
+  @spec fetch_token_from_session(Plug.Conn.t()) :: :no_token_found | {:ok, String.t()}
+  defp fetch_token_from_session(conn) do
+    case get_session(conn, :oauth_token) do
+      nil -> :no_token_found
+      token -> {:ok, token}
+    end
+  end
 end
diff --git a/lib/pleroma/web/plugs/session_authentication_plug.ex b/lib/pleroma/web/plugs/session_authentication_plug.ex
deleted file mode 100644 (file)
index 6e176d5..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.Plugs.SessionAuthenticationPlug do
-  import Plug.Conn
-
-  def init(options) do
-    options
-  end
-
-  def call(conn, _) do
-    with saved_user_id <- get_session(conn, :user_id),
-         %{auth_user: %{id: ^saved_user_id}} <- conn.assigns do
-      conn
-      |> assign(:user, conn.assigns.auth_user)
-    else
-      _ -> conn
-    end
-  end
-end
index e520159e4aaa5211e254e47c9e610a18bc80e307..d2338c03f681c073fde571bf350a556e8a19800f 100644 (file)
@@ -4,15 +4,15 @@
 
 defmodule Pleroma.Web.Plugs.SetUserSessionIdPlug do
   import Plug.Conn
-  alias Pleroma.User
+
+  alias Pleroma.Web.OAuth.Token
 
   def init(opts) do
     opts
   end
 
-  def call(%{assigns: %{user: %User{id: id}}} = conn, _) do
-    conn
-    |> put_session(:user_id, id)
+  def call(%{assigns: %{token: %Token{} = oauth_token}} = conn, _) do
+    put_session(conn, :oauth_token, oauth_token.token)
   end
 
   def call(conn, _), do: conn
index fa28ee48b7179c0ffa661529f5392e30c4591091..291d1f568009753751cd6fd04d14827d2f4301e1 100644 (file)
@@ -3,7 +3,7 @@
 # SPDX-License-Identifier: AGPL-3.0-only
 
 defmodule Pleroma.Web.Plugs.UserEnabledPlug do
-  import Plug.Conn
+  alias Pleroma.Helpers.AuthHelper
   alias Pleroma.User
 
   def init(options) do
@@ -12,8 +12,11 @@ defmodule Pleroma.Web.Plugs.UserEnabledPlug do
 
   def call(%{assigns: %{user: %User{} = user}} = conn, _) do
     case User.account_status(user) do
-      :active -> conn
-      _ -> assign(conn, :user, nil)
+      :active ->
+        conn
+
+      _ ->
+        AuthHelper.drop_auth_info(conn)
     end
   end
 
index 4039600daff9ecae0d135f2ff798ef62441956ea..89e16b49f0b278a92b57483ee248354ee49c94d1 100644 (file)
@@ -3,6 +3,12 @@
 # SPDX-License-Identifier: AGPL-3.0-only
 
 defmodule Pleroma.Web.Plugs.UserFetcherPlug do
+  @moduledoc """
+  Assigns `:auth_user` basing on `:auth_credentials`.
+
+  NOTE: no checks are performed at this step, auth_credentials/username could be easily faked.
+  """
+
   alias Pleroma.User
   import Plug.Conn
 
index 75a88537758f239bede91e85420e12221dade4fa..3a3e63db60a1eeb40caae34687e968ec87c31594 100644 (file)
@@ -34,6 +34,7 @@ defmodule Pleroma.Web.Router do
     plug(:fetch_session)
     plug(Pleroma.Web.Plugs.OAuthPlug)
     plug(Pleroma.Web.Plugs.UserEnabledPlug)
+    plug(Pleroma.Web.Plugs.EnsureUserKeyPlug)
   end
 
   pipeline :expect_authentication do
@@ -48,8 +49,6 @@ defmodule Pleroma.Web.Router do
     plug(Pleroma.Web.Plugs.OAuthPlug)
     plug(Pleroma.Web.Plugs.BasicAuthDecoderPlug)
     plug(Pleroma.Web.Plugs.UserFetcherPlug)
-    plug(Pleroma.Web.Plugs.SessionAuthenticationPlug)
-    plug(Pleroma.Web.Plugs.LegacyAuthenticationPlug)
     plug(Pleroma.Web.Plugs.AuthenticationPlug)
   end
 
@@ -323,18 +322,24 @@ defmodule Pleroma.Web.Router do
   scope "/oauth", Pleroma.Web.OAuth do
     scope [] do
       pipe_through(:oauth)
+
       get("/authorize", OAuthController, :authorize)
+      post("/authorize", OAuthController, :create_authorization)
     end
 
-    post("/authorize", OAuthController, :create_authorization)
     post("/token", OAuthController, :token_exchange)
-    post("/revoke", OAuthController, :token_revoke)
     get("/registration_details", OAuthController, :registration_details)
 
     post("/mfa/challenge", MFAController, :challenge)
     post("/mfa/verify", MFAController, :verify, as: :mfa_verify)
     get("/mfa", MFAController, :show)
 
+    scope [] do
+      pipe_through(:fetch_session)
+
+      post("/revoke", OAuthController, :token_revoke)
+    end
+
     scope [] do
       pipe_through(:browser)
 
index 33394722a5e39da6d63581834067391042ab2ab8..23498badfe59e9c73dd4ef276ab53e026afe20ed 100644 (file)
@@ -49,6 +49,7 @@ defmodule Pleroma.Web.Plugs.AdminSecretAuthenticationPlugTest do
         |> AdminSecretAuthenticationPlug.call(%{})
 
       assert conn.assigns[:user].is_admin
+      assert conn.assigns[:token] == nil
       assert PlugHelper.plug_skipped?(conn, OAuthScopesPlug)
     end
 
@@ -69,6 +70,7 @@ defmodule Pleroma.Web.Plugs.AdminSecretAuthenticationPlugTest do
         |> AdminSecretAuthenticationPlug.call(%{})
 
       assert conn.assigns[:user].is_admin
+      assert conn.assigns[:token] == nil
       assert PlugHelper.plug_skipped?(conn, OAuthScopesPlug)
     end
   end
index af39352e261a39bcf5a349e678352e49d5d5cebd..3dedd38b279cb162e763a54d471d1b9946b7fc1f 100644 (file)
@@ -48,6 +48,7 @@ defmodule Pleroma.Web.Plugs.AuthenticationPlugTest do
       |> AuthenticationPlug.call(%{})
 
     assert conn.assigns.user == conn.assigns.auth_user
+    assert conn.assigns.token == nil
     assert PlugHelper.plug_skipped?(conn, OAuthScopesPlug)
   end
 
@@ -62,6 +63,7 @@ defmodule Pleroma.Web.Plugs.AuthenticationPlugTest do
       |> AuthenticationPlug.call(%{})
 
     assert conn.assigns.user.id == conn.assigns.auth_user.id
+    assert conn.assigns.token == nil
     assert PlugHelper.plug_skipped?(conn, OAuthScopesPlug)
 
     user = User.get_by_id(user.id)
@@ -83,6 +85,7 @@ defmodule Pleroma.Web.Plugs.AuthenticationPlugTest do
       |> AuthenticationPlug.call(%{})
 
     assert conn.assigns.user.id == conn.assigns.auth_user.id
+    assert conn.assigns.token == nil
     assert PlugHelper.plug_skipped?(conn, OAuthScopesPlug)
 
     user = User.get_by_id(user.id)
diff --git a/test/pleroma/web/plugs/legacy_authentication_plug_test.exs b/test/pleroma/web/plugs/legacy_authentication_plug_test.exs
deleted file mode 100644 (file)
index 2016a31..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.Plugs.LegacyAuthenticationPlugTest do
-  use Pleroma.Web.ConnCase
-
-  import Pleroma.Factory
-
-  alias Pleroma.User
-  alias Pleroma.Web.Plugs.LegacyAuthenticationPlug
-  alias Pleroma.Web.Plugs.OAuthScopesPlug
-  alias Pleroma.Web.Plugs.PlugHelper
-
-  setup do
-    user =
-      insert(:user,
-        password: "password",
-        password_hash:
-          "$6$9psBWV8gxkGOZWBz$PmfCycChoxeJ3GgGzwvhlgacb9mUoZ.KUXNCssekER4SJ7bOK53uXrHNb2e4i8yPFgSKyzaW9CcmrDXWIEMtD1"
-      )
-
-    %{user: user}
-  end
-
-  test "it does nothing if a user is assigned", %{conn: conn, user: user} do
-    conn =
-      conn
-      |> assign(:auth_credentials, %{username: "dude", password: "password"})
-      |> assign(:auth_user, user)
-      |> assign(:user, %User{})
-
-    ret_conn =
-      conn
-      |> LegacyAuthenticationPlug.call(%{})
-
-    assert ret_conn == conn
-  end
-
-  @tag :skip_on_mac
-  test "if `auth_user` is present and password is correct, " <>
-         "it authenticates the user, resets the password, marks OAuthScopesPlug as skipped",
-       %{
-         conn: conn,
-         user: user
-       } do
-    conn =
-      conn
-      |> assign(:auth_credentials, %{username: "dude", password: "password"})
-      |> assign(:auth_user, user)
-
-    conn = LegacyAuthenticationPlug.call(conn, %{})
-
-    assert conn.assigns.user.id == user.id
-    assert PlugHelper.plug_skipped?(conn, OAuthScopesPlug)
-  end
-
-  @tag :skip_on_mac
-  test "it does nothing if the password is wrong", %{
-    conn: conn,
-    user: user
-  } do
-    conn =
-      conn
-      |> assign(:auth_credentials, %{username: "dude", password: "wrong_password"})
-      |> assign(:auth_user, user)
-
-    ret_conn =
-      conn
-      |> LegacyAuthenticationPlug.call(%{})
-
-    assert conn == ret_conn
-  end
-
-  test "with no credentials or user it does nothing", %{conn: conn} do
-    ret_conn =
-      conn
-      |> LegacyAuthenticationPlug.call(%{})
-
-    assert ret_conn == conn
-  end
-end
index b9d722f7624c43de5992b6102c3a45ce220cef71..ad2aa5d1b098fed9473334b8feced2ddee887b42 100644 (file)
@@ -5,43 +5,48 @@
 defmodule Pleroma.Web.Plugs.OAuthPlugTest do
   use Pleroma.Web.ConnCase, async: true
 
+  alias Pleroma.Web.OAuth.Token
+  alias Pleroma.Web.OAuth.Token.Strategy.Revoke
   alias Pleroma.Web.Plugs.OAuthPlug
-  import Pleroma.Factory
+  alias Plug.Session
 
-  @session_opts [
-    store: :cookie,
-    key: "_test",
-    signing_salt: "cooldude"
-  ]
+  import Pleroma.Factory
 
   setup %{conn: conn} do
     user = insert(:user)
-    {:ok, %{token: token}} = Pleroma.Web.OAuth.Token.create(insert(:oauth_app), user)
-    %{user: user, token: token, conn: conn}
+    {:ok, oauth_token} = Token.create(insert(:oauth_app), user)
+    %{user: user, token: oauth_token, conn: conn}
+  end
+
+  test "it does nothing if a user is assigned", %{conn: conn} do
+    conn = assign(conn, :user, %Pleroma.User{})
+    ret_conn = OAuthPlug.call(conn, %{})
+
+    assert ret_conn == conn
   end
 
-  test "with valid token(uppercase), it assigns the user", %{conn: conn} = opts do
+  test "with valid token (uppercase) in auth header, it assigns the user", %{conn: conn} = opts do
     conn =
       conn
-      |> put_req_header("authorization", "BEARER #{opts[:token]}")
+      |> put_req_header("authorization", "BEARER #{opts[:token].token}")
       |> OAuthPlug.call(%{})
 
     assert conn.assigns[:user] == opts[:user]
   end
 
-  test "with valid token(downcase), it assigns the user", %{conn: conn} = opts do
+  test "with valid token (downcase) in auth header, it assigns the user", %{conn: conn} = opts do
     conn =
       conn
-      |> put_req_header("authorization", "bearer #{opts[:token]}")
+      |> put_req_header("authorization", "bearer #{opts[:token].token}")
       |> OAuthPlug.call(%{})
 
     assert conn.assigns[:user] == opts[:user]
   end
 
-  test "with valid token(downcase) in url parameters, it assigns the user", opts do
+  test "with valid token (downcase) in url parameters, it assigns the user", opts do
     conn =
       :get
-      |> build_conn("/?access_token=#{opts[:token]}")
+      |> build_conn("/?access_token=#{opts[:token].token}")
       |> put_req_header("content-type", "application/json")
       |> fetch_query_params()
       |> OAuthPlug.call(%{})
@@ -49,16 +54,16 @@ defmodule Pleroma.Web.Plugs.OAuthPlugTest do
     assert conn.assigns[:user] == opts[:user]
   end
 
-  test "with valid token(downcase) in body parameters, it assigns the user", opts do
+  test "with valid token (downcase) in body parameters, it assigns the user", opts do
     conn =
       :post
-      |> build_conn("/api/v1/statuses", access_token: opts[:token], status: "test")
+      |> build_conn("/api/v1/statuses", access_token: opts[:token].token, status: "test")
       |> OAuthPlug.call(%{})
 
     assert conn.assigns[:user] == opts[:user]
   end
 
-  test "with invalid token, it not assigns the user", %{conn: conn} do
+  test "with invalid token, it does not assign the user", %{conn: conn} do
     conn =
       conn
       |> put_req_header("authorization", "bearer TTTTT")
@@ -67,14 +72,56 @@ defmodule Pleroma.Web.Plugs.OAuthPlugTest do
     refute conn.assigns[:user]
   end
 
-  test "when token is missed but token in session, it assigns the user", %{conn: conn} = opts do
-    conn =
-      conn
-      |> Plug.Session.call(Plug.Session.init(@session_opts))
-      |> fetch_session()
-      |> put_session(:oauth_token, opts[:token])
-      |> OAuthPlug.call(%{})
-
-    assert conn.assigns[:user] == opts[:user]
+  describe "with :oauth_token in session, " do
+    setup %{token: oauth_token, conn: conn} do
+      session_opts = [
+        store: :cookie,
+        key: "_test",
+        signing_salt: "cooldude"
+      ]
+
+      conn =
+        conn
+        |> Session.call(Session.init(session_opts))
+        |> fetch_session()
+        |> put_session(:oauth_token, oauth_token.token)
+
+      %{conn: conn}
+    end
+
+    test "if session-stored token matches a valid OAuth token, assigns :user and :token", %{
+      conn: conn,
+      user: user,
+      token: oauth_token
+    } do
+      conn = OAuthPlug.call(conn, %{})
+
+      assert conn.assigns.user && conn.assigns.user.id == user.id
+      assert conn.assigns.token && conn.assigns.token.id == oauth_token.id
+    end
+
+    test "if session-stored token matches an expired OAuth token, does nothing", %{
+      conn: conn,
+      token: oauth_token
+    } do
+      expired_valid_until = NaiveDateTime.add(NaiveDateTime.utc_now(), -3600 * 24, :second)
+
+      oauth_token
+      |> Ecto.Changeset.change(valid_until: expired_valid_until)
+      |> Pleroma.Repo.update()
+
+      ret_conn = OAuthPlug.call(conn, %{})
+      assert ret_conn == conn
+    end
+
+    test "if session-stored token matches a revoked OAuth token, does nothing", %{
+      conn: conn,
+      token: oauth_token
+    } do
+      Revoke.revoke(oauth_token)
+
+      ret_conn = OAuthPlug.call(conn, %{})
+      assert ret_conn == conn
+    end
   end
 end
diff --git a/test/pleroma/web/plugs/session_authentication_plug_test.exs b/test/pleroma/web/plugs/session_authentication_plug_test.exs
deleted file mode 100644 (file)
index 2b4d5bc..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.Plugs.SessionAuthenticationPlugTest do
-  use Pleroma.Web.ConnCase, async: true
-
-  alias Pleroma.User
-  alias Pleroma.Web.Plugs.SessionAuthenticationPlug
-
-  setup %{conn: conn} do
-    session_opts = [
-      store: :cookie,
-      key: "_test",
-      signing_salt: "cooldude"
-    ]
-
-    conn =
-      conn
-      |> Plug.Session.call(Plug.Session.init(session_opts))
-      |> fetch_session
-      |> assign(:auth_user, %User{id: 1})
-
-    %{conn: conn}
-  end
-
-  test "it does nothing if a user is assigned", %{conn: conn} do
-    conn =
-      conn
-      |> assign(:user, %User{})
-
-    ret_conn =
-      conn
-      |> SessionAuthenticationPlug.call(%{})
-
-    assert ret_conn == conn
-  end
-
-  test "if the auth_user has the same id as the user_id in the session, it assigns the user", %{
-    conn: conn
-  } do
-    conn =
-      conn
-      |> put_session(:user_id, conn.assigns.auth_user.id)
-      |> SessionAuthenticationPlug.call(%{})
-
-    assert conn.assigns.user == conn.assigns.auth_user
-  end
-
-  test "if the auth_user has a different id as the user_id in the session, it does nothing", %{
-    conn: conn
-  } do
-    conn =
-      conn
-      |> put_session(:user_id, -1)
-
-    ret_conn =
-      conn
-      |> SessionAuthenticationPlug.call(%{})
-
-    assert ret_conn == conn
-  end
-end
index a89b5628fb0e3b0791e05215b50461b3857d6e49..a50e8010736596c2ece1cfa31a6148d247cca934 100644 (file)
@@ -5,7 +5,6 @@
 defmodule Pleroma.Web.Plugs.SetUserSessionIdPlugTest do
   use Pleroma.Web.ConnCase, async: true
 
-  alias Pleroma.User
   alias Pleroma.Web.Plugs.SetUserSessionIdPlug
 
   setup %{conn: conn} do
@@ -18,28 +17,26 @@ defmodule Pleroma.Web.Plugs.SetUserSessionIdPlugTest do
     conn =
       conn
       |> Plug.Session.call(Plug.Session.init(session_opts))
-      |> fetch_session
+      |> fetch_session()
 
     %{conn: conn}
   end
 
   test "doesn't do anything if the user isn't set", %{conn: conn} do
-    ret_conn =
-      conn
-      |> SetUserSessionIdPlug.call(%{})
+    ret_conn = SetUserSessionIdPlug.call(conn, %{})
 
     assert ret_conn == conn
   end
 
-  test "sets the user_id in the session to the user id of the user assign", %{conn: conn} do
-    Code.ensure_compiled(Pleroma.User)
+  test "sets :oauth_token in session to :token assign", %{conn: conn} do
+    %{user: user, token: oauth_token} = oauth_access(["read"])
 
-    conn =
+    ret_conn =
       conn
-      |> assign(:user, %User{id: 1})
+      |> assign(:user, user)
+      |> assign(:token, oauth_token)
       |> SetUserSessionIdPlug.call(%{})
 
-    id = get_session(conn, :user_id)
-    assert id == 1
+    assert get_session(ret_conn, :oauth_token) == oauth_token.token
   end
 end