schedule expired oauth tokens deletion with Oban
authorAlexander Strizhakov <alex.strizhakov@gmail.com>
Tue, 25 Aug 2020 16:17:51 +0000 (19:17 +0300)
committerAlexander Strizhakov <alex.strizhakov@gmail.com>
Thu, 10 Sep 2020 13:01:19 +0000 (16:01 +0300)
13 files changed:
config/config.exs
config/description.exs
lib/pleroma/web/oauth/token.ex
lib/pleroma/web/oauth/token/clean_worker.ex
lib/pleroma/web/oauth/token/query.ex
lib/pleroma/web/oauth/token/strategy/refresh_token.ex
lib/pleroma/workers/cron/clear_oauth_token_worker.ex [deleted file]
lib/pleroma/workers/purge_expired_token.ex [new file with mode: 0644]
test/plugs/oauth_plug_test.exs
test/web/oauth/token_test.exs
test/web/twitter_api/password_controller_test.exs
test/workers/cron/clear_oauth_token_worker_test.exs [deleted file]
test/workers/purge_expired_oauth_token_test.exs [new file with mode: 0644]

index 1a2b312b500e5b236620a9e24771e49cf0be79ef..fa4c96b79ddb43ac9a8b8cd29daaf162d07b2918 100644 (file)
@@ -530,6 +530,7 @@ config :pleroma, Oban,
   log: false,
   queues: [
     activity_expiration: 10,
+    oauth_token_expiration: 1,
     federator_incoming: 50,
     federator_outgoing: 50,
     web_push: 50,
@@ -543,7 +544,6 @@ config :pleroma, Oban,
   ],
   plugins: [Oban.Plugins.Pruner],
   crontab: [
-    {"0 0 * * *", Pleroma.Workers.Cron.ClearOauthTokenWorker},
     {"* * * * *", Pleroma.Workers.Cron.PurgeExpiredActivitiesWorker},
     {"0 0 * * 0", Pleroma.Workers.Cron.DigestEmailsWorker},
     {"0 0 * * *", Pleroma.Workers.Cron.NewUsersDigestWorker}
index eac97ad64c5a864d93bce5e40db65cc27b73c01e..4c4deed30a55d595194db566e6f63ea0e8d5aeea 100644 (file)
@@ -2290,7 +2290,6 @@ config :pleroma, :config_description, [
         type: {:list, :tuple},
         description: "Settings for cron background jobs",
         suggestions: [
-          {"0 0 * * *", Pleroma.Workers.Cron.ClearOauthTokenWorker},
           {"* * * * *", Pleroma.Workers.Cron.PurgeExpiredActivitiesWorker},
           {"0 0 * * 0", Pleroma.Workers.Cron.DigestEmailsWorker},
           {"0 0 * * *", Pleroma.Workers.Cron.NewUsersDigestWorker}
index 08bb7326dad90583161ea51cdb4dcd12626fc057..4d00fcb1cbe5b8762a9f4dc73165ba564945b2e5 100644 (file)
@@ -50,7 +50,7 @@ defmodule Pleroma.Web.OAuth.Token do
          true <- auth.app_id == app.id do
       user = if auth.user_id, do: User.get_cached_by_id(auth.user_id), else: %User{}
 
-      create_token(
+      create(
         app,
         user,
         %{scopes: auth.scopes}
@@ -83,8 +83,21 @@ defmodule Pleroma.Web.OAuth.Token do
     |> validate_required([:valid_until])
   end
 
-  @spec create_token(App.t(), User.t(), map()) :: {:ok, Token} | {:error, Changeset.t()}
-  def create_token(%App{} = app, %User{} = user, attrs \\ %{}) do
+  @spec create(App.t(), User.t(), map()) :: {:ok, Token} | {:error, Changeset.t()}
+  def create(%App{} = app, %User{} = user, attrs \\ %{}) do
+    with {:ok, token} <- do_create(app, user, attrs) do
+      if Pleroma.Config.get([:oauth2, :clean_expired_tokens]) do
+        Pleroma.Workers.PurgeExpiredOAuthToken.enqueue(%{
+          token_id: token.id,
+          valid_until: DateTime.from_naive!(token.valid_until, "Etc/UTC")
+        })
+      end
+
+      {:ok, token}
+    end
+  end
+
+  defp do_create(app, user, attrs) do
     %__MODULE__{user_id: user.id, app_id: app.id}
     |> cast(%{scopes: attrs[:scopes] || app.scopes}, [:scopes])
     |> validate_required([:scopes, :app_id])
@@ -105,11 +118,6 @@ defmodule Pleroma.Web.OAuth.Token do
     |> Repo.delete_all()
   end
 
-  def delete_expired_tokens do
-    Query.get_expired_tokens()
-    |> Repo.delete_all()
-  end
-
   def get_user_tokens(%User{id: user_id}) do
     Query.get_by_user(user_id)
     |> Query.preload([:app])
index e3aa4eb7e539a2e4b097eb8db6830793de5389f9..2f51bdb75e8c7fb33cb31c861cf7d8e033db52ba 100644 (file)
@@ -12,7 +12,6 @@ defmodule Pleroma.Web.OAuth.Token.CleanWorker do
   @one_day 86_400_000
 
   alias Pleroma.MFA
-  alias Pleroma.Web.OAuth
   alias Pleroma.Workers.BackgroundWorker
 
   def start_link(_), do: GenServer.start_link(__MODULE__, %{})
@@ -32,7 +31,6 @@ defmodule Pleroma.Web.OAuth.Token.CleanWorker do
   end
 
   def perform(:clean) do
-    OAuth.Token.delete_expired_tokens()
     MFA.Token.delete_expired_tokens()
   end
 end
index 93d6e26ed80f7c4469346596e20c4cccac0776e6..fd6d9b112509da33c741efbc271a58d6368ba166 100644 (file)
@@ -33,12 +33,6 @@ defmodule Pleroma.Web.OAuth.Token.Query do
     from(q in query, where: q.id == ^id)
   end
 
-  @spec get_expired_tokens(query, DateTime.t() | nil) :: query
-  def get_expired_tokens(query \\ Token, date \\ nil) do
-    expired_date = date || Timex.now()
-    from(q in query, where: fragment("?", q.valid_until) < ^expired_date)
-  end
-
   @spec get_by_user(query, String.t()) :: query
   def get_by_user(query \\ Token, user_id) do
     from(q in query, where: q.user_id == ^user_id)
index debc29b0b08d1f71279bb428810441d2d410b8e5..625b0fde20040e0fa029f3f2589038d010cd345f 100644 (file)
@@ -46,7 +46,7 @@ defmodule Pleroma.Web.OAuth.Token.Strategy.RefreshToken do
   defp create_access_token({:error, error}, _), do: {:error, error}
 
   defp create_access_token({:ok, token}, %{app: app, user: user} = token_params) do
-    Token.create_token(app, user, add_refresh_token(token_params, token.refresh_token))
+    Token.create(app, user, add_refresh_token(token_params, token.refresh_token))
   end
 
   defp add_refresh_token(params, token) do
diff --git a/lib/pleroma/workers/cron/clear_oauth_token_worker.ex b/lib/pleroma/workers/cron/clear_oauth_token_worker.ex
deleted file mode 100644 (file)
index 276f47e..0000000
+++ /dev/null
@@ -1,23 +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.Workers.Cron.ClearOauthTokenWorker do
-  @moduledoc """
-  The worker to cleanup expired oAuth tokens.
-  """
-
-  use Oban.Worker, queue: "background"
-
-  alias Pleroma.Config
-  alias Pleroma.Web.OAuth.Token
-
-  @impl Oban.Worker
-  def perform(_job) do
-    if Config.get([:oauth2, :clean_expired_tokens], false) do
-      Token.delete_expired_tokens()
-    end
-
-    :ok
-  end
-end
diff --git a/lib/pleroma/workers/purge_expired_token.ex b/lib/pleroma/workers/purge_expired_token.ex
new file mode 100644 (file)
index 0000000..6068e43
--- /dev/null
@@ -0,0 +1,28 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Workers.PurgeExpiredOAuthToken do
+  @moduledoc """
+  Worker which purges expired OAuth tokens
+  """
+
+  use Oban.Worker, queue: :oauth_token_expiration, max_attempts: 1
+
+  @spec enqueue(%{token_id: integer(), valid_until: DateTime.t()}) ::
+          {:ok, Oban.Job.t()} | {:error, Ecto.Changeset.t()}
+  def enqueue(args) do
+    {scheduled_at, args} = Map.pop(args, :valid_until)
+
+    args
+    |> __MODULE__.new(scheduled_at: scheduled_at)
+    |> Oban.insert()
+  end
+
+  @impl true
+  def perform(%Oban.Job{args: %{"token_id" => id}}) do
+    Pleroma.Web.OAuth.Token
+    |> Pleroma.Repo.get(id)
+    |> Pleroma.Repo.delete()
+  end
+end
index f74c068cdfac9dde01b30dcaf540db8389c7b061..9d39d31533b0f8053f926e23e087789e5e63a0cf 100644 (file)
@@ -16,7 +16,7 @@ defmodule Pleroma.Plugs.OAuthPlugTest do
 
   setup %{conn: conn} do
     user = insert(:user)
-    {:ok, %{token: token}} = Pleroma.Web.OAuth.Token.create_token(insert(:oauth_app), user)
+    {:ok, %{token: token}} = Pleroma.Web.OAuth.Token.create(insert(:oauth_app), user)
     %{user: user, token: token, conn: conn}
   end
 
index 40d71eb59771228236e9ef2442ba61a2a976f835..c88b9cc9892cfab28c36f78b49e1cd73a00713e9 100644 (file)
@@ -69,17 +69,4 @@ defmodule Pleroma.Web.OAuth.TokenTest do
 
     assert tokens == 2
   end
-
-  test "deletes expired tokens" do
-    insert(:oauth_token, valid_until: Timex.shift(Timex.now(), days: -3))
-    insert(:oauth_token, valid_until: Timex.shift(Timex.now(), days: -3))
-    t3 = insert(:oauth_token)
-    t4 = insert(:oauth_token, valid_until: Timex.shift(Timex.now(), minutes: 10))
-    {tokens, _} = Token.delete_expired_tokens()
-    assert tokens == 2
-    available_tokens = Pleroma.Repo.all(Token)
-
-    token_ids = available_tokens |> Enum.map(& &1.id)
-    assert token_ids == [t3.id, t4.id]
-  end
 end
index 231a46c67474dbd6f0b50a8714228c0c7e6c9069..a5e9e2178d84d95bdde9cefb534c3d51533c7fd2 100644 (file)
@@ -37,7 +37,7 @@ defmodule Pleroma.Web.TwitterAPI.PasswordControllerTest do
     test "it returns HTTP 200", %{conn: conn} do
       user = insert(:user)
       {:ok, token} = PasswordResetToken.create_token(user)
-      {:ok, _access_token} = Token.create_token(insert(:oauth_app), user, %{})
+      {:ok, _access_token} = Token.create(insert(:oauth_app), user, %{})
 
       params = %{
         "password" => "test",
@@ -62,7 +62,7 @@ defmodule Pleroma.Web.TwitterAPI.PasswordControllerTest do
       user = insert(:user, password_reset_pending: true)
 
       {:ok, token} = PasswordResetToken.create_token(user)
-      {:ok, _access_token} = Token.create_token(insert(:oauth_app), user, %{})
+      {:ok, _access_token} = Token.create(insert(:oauth_app), user, %{})
 
       params = %{
         "password" => "test",
diff --git a/test/workers/cron/clear_oauth_token_worker_test.exs b/test/workers/cron/clear_oauth_token_worker_test.exs
deleted file mode 100644 (file)
index 67836f3..0000000
+++ /dev/null
@@ -1,22 +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.Workers.Cron.ClearOauthTokenWorkerTest do
-  use Pleroma.DataCase
-
-  import Pleroma.Factory
-  alias Pleroma.Workers.Cron.ClearOauthTokenWorker
-
-  setup do: clear_config([:oauth2, :clean_expired_tokens])
-
-  test "deletes expired tokens" do
-    insert(:oauth_token,
-      valid_until: NaiveDateTime.add(NaiveDateTime.utc_now(), -60 * 10)
-    )
-
-    Pleroma.Config.put([:oauth2, :clean_expired_tokens], true)
-    ClearOauthTokenWorker.perform(%Oban.Job{})
-    assert Pleroma.Repo.all(Pleroma.Web.OAuth.Token) == []
-  end
-end
diff --git a/test/workers/purge_expired_oauth_token_test.exs b/test/workers/purge_expired_oauth_token_test.exs
new file mode 100644 (file)
index 0000000..3bd650d
--- /dev/null
@@ -0,0 +1,27 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Workers.PurgeExpiredOAuthTokenTest do
+  use Pleroma.DataCase, async: true
+  use Oban.Testing, repo: Pleroma.Repo
+
+  import Pleroma.Factory
+
+  setup do: clear_config([:oauth2, :clean_expired_tokens], true)
+
+  test "purges expired token" do
+    user = insert(:user)
+    app = insert(:oauth_app)
+
+    {:ok, %{id: id}} = Pleroma.Web.OAuth.Token.create(app, user)
+
+    assert_enqueued(
+      worker: Pleroma.Workers.PurgeExpiredOAuthToken,
+      args: %{token_id: id}
+    )
+
+    assert {:ok, %{id: ^id}} =
+             perform_job(Pleroma.Workers.PurgeExpiredOAuthToken, %{token_id: id})
+  end
+end