[#699] add worker to clean expired oauth tokens
This commit is contained in:
parent
714d8d4ef9
commit
620908a2db
8 changed files with 132 additions and 21 deletions
|
@ -41,6 +41,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- OAuth: added support for refresh tokens
|
- OAuth: added support for refresh tokens
|
||||||
- Emoji packs and emoji pack manager
|
- Emoji packs and emoji pack manager
|
||||||
- Object pruning (`mix pleroma.database prune_objects`)
|
- Object pruning (`mix pleroma.database prune_objects`)
|
||||||
|
- OAuth: added job to clean expired access tokens
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- **Breaking:** Configuration: move from Pleroma.Mailer to Pleroma.Emails.Mailer
|
- **Breaking:** Configuration: move from Pleroma.Mailer to Pleroma.Emails.Mailer
|
||||||
|
|
|
@ -481,7 +481,9 @@
|
||||||
|
|
||||||
config :pleroma, :oauth2,
|
config :pleroma, :oauth2,
|
||||||
token_expires_in: 600,
|
token_expires_in: 600,
|
||||||
issue_new_refresh_token: true
|
issue_new_refresh_token: true,
|
||||||
|
clean_expired_tokens: false,
|
||||||
|
clean_expired_tokens_interval: 86_400_000
|
||||||
|
|
||||||
config :pleroma, :database, rum_enabled: false
|
config :pleroma, :database, rum_enabled: false
|
||||||
|
|
||||||
|
|
|
@ -550,6 +550,8 @@ Configure OAuth 2 provider capabilities:
|
||||||
|
|
||||||
* `token_expires_in` - The lifetime in seconds of the access token.
|
* `token_expires_in` - The lifetime in seconds of the access token.
|
||||||
* `issue_new_refresh_token` - Keeps old refresh token or generate new refresh token when to obtain an access token.
|
* `issue_new_refresh_token` - Keeps old refresh token or generate new refresh token when to obtain an access token.
|
||||||
|
* `clean_expired_tokens` - Enable a background job to clean expired oauth tokens. Defaults to `false`.
|
||||||
|
* `clean_expired_tokens_interval` - Interval to run the job to clean expired tokens. Defaults to `86_400_000` (24 hours).
|
||||||
|
|
||||||
## :emoji
|
## :emoji
|
||||||
* `shortcode_globs`: Location of custom emoji files. `*` can be used as a wildcard. Example `["/emoji/custom/**/*.png"]`
|
* `shortcode_globs`: Location of custom emoji files. `*` can be used as a wildcard. Example `["/emoji/custom/**/*.png"]`
|
||||||
|
|
|
@ -110,6 +110,7 @@ def start(_type, _args) do
|
||||||
hackney_pool_children() ++
|
hackney_pool_children() ++
|
||||||
[
|
[
|
||||||
worker(Pleroma.Web.Federator.RetryQueue, []),
|
worker(Pleroma.Web.Federator.RetryQueue, []),
|
||||||
|
worker(Pleroma.Web.OAuth.Token.CleanWorker, []),
|
||||||
worker(Pleroma.Stats, []),
|
worker(Pleroma.Stats, []),
|
||||||
worker(Task, [&Pleroma.Web.Push.init/0], restart: :temporary, id: :web_push_init),
|
worker(Task, [&Pleroma.Web.Push.init/0], restart: :temporary, id: :web_push_init),
|
||||||
worker(Task, [&Pleroma.Web.Federator.init/0], restart: :temporary, id: :federator_init)
|
worker(Task, [&Pleroma.Web.Federator.init/0], restart: :temporary, id: :federator_init)
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
defmodule Pleroma.Web.OAuth.Token do
|
defmodule Pleroma.Web.OAuth.Token do
|
||||||
use Ecto.Schema
|
use Ecto.Schema
|
||||||
|
|
||||||
import Ecto.Query
|
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
|
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
|
@ -13,6 +12,7 @@ defmodule Pleroma.Web.OAuth.Token do
|
||||||
alias Pleroma.Web.OAuth.App
|
alias Pleroma.Web.OAuth.App
|
||||||
alias Pleroma.Web.OAuth.Authorization
|
alias Pleroma.Web.OAuth.Authorization
|
||||||
alias Pleroma.Web.OAuth.Token
|
alias Pleroma.Web.OAuth.Token
|
||||||
|
alias Pleroma.Web.OAuth.Token.Query
|
||||||
|
|
||||||
@expires_in Pleroma.Config.get([:oauth2, :token_expires_in], 600)
|
@expires_in Pleroma.Config.get([:oauth2, :token_expires_in], 600)
|
||||||
@type t :: %__MODULE__{}
|
@type t :: %__MODULE__{}
|
||||||
|
@ -31,17 +31,17 @@ defmodule Pleroma.Web.OAuth.Token do
|
||||||
@doc "Gets token for app by access token"
|
@doc "Gets token for app by access token"
|
||||||
@spec get_by_token(App.t(), String.t()) :: {:ok, t()} | {:error, :not_found}
|
@spec get_by_token(App.t(), String.t()) :: {:ok, t()} | {:error, :not_found}
|
||||||
def get_by_token(%App{id: app_id} = _app, token) do
|
def get_by_token(%App{id: app_id} = _app, token) do
|
||||||
from(t in __MODULE__, where: t.app_id == ^app_id and t.token == ^token)
|
Query.get_by_app(app_id)
|
||||||
|
|> Query.get_by_token(token)
|
||||||
|> Repo.find_resource()
|
|> Repo.find_resource()
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc "Gets token for app by refresh token"
|
@doc "Gets token for app by refresh token"
|
||||||
@spec get_by_refresh_token(App.t(), String.t()) :: {:ok, t()} | {:error, :not_found}
|
@spec get_by_refresh_token(App.t(), String.t()) :: {:ok, t()} | {:error, :not_found}
|
||||||
def get_by_refresh_token(%App{id: app_id} = _app, token) do
|
def get_by_refresh_token(%App{id: app_id} = _app, token) do
|
||||||
from(t in __MODULE__,
|
Query.get_by_app(app_id)
|
||||||
where: t.app_id == ^app_id and t.refresh_token == ^token,
|
|> Query.get_by_refresh_token(token)
|
||||||
preload: [:user]
|
|> Query.preload([:user])
|
||||||
)
|
|
||||||
|> Repo.find_resource()
|
|> Repo.find_resource()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -97,29 +97,25 @@ def create_token(%App{} = app, %User{} = user, attrs \\ %{}) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def delete_user_tokens(%User{id: user_id}) do
|
def delete_user_tokens(%User{id: user_id}) do
|
||||||
from(
|
Query.get_by_user(user_id)
|
||||||
t in Token,
|
|
||||||
where: t.user_id == ^user_id
|
|
||||||
)
|
|
||||||
|> Repo.delete_all()
|
|> Repo.delete_all()
|
||||||
end
|
end
|
||||||
|
|
||||||
def delete_user_token(%User{id: user_id}, token_id) do
|
def delete_user_token(%User{id: user_id}, token_id) do
|
||||||
from(
|
Query.get_by_user(user_id)
|
||||||
t in Token,
|
|> Query.get_by_id(token_id)
|
||||||
where: t.user_id == ^user_id,
|
|> Repo.delete_all()
|
||||||
where: t.id == ^token_id
|
end
|
||||||
)
|
|
||||||
|
def delete_expired_tokens do
|
||||||
|
Query.get_expired_tokens()
|
||||||
|> Repo.delete_all()
|
|> Repo.delete_all()
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_user_tokens(%User{id: user_id}) do
|
def get_user_tokens(%User{id: user_id}) do
|
||||||
from(
|
Query.get_by_user(user_id)
|
||||||
t in Token,
|
|> Query.preload([:app])
|
||||||
where: t.user_id == ^user_id
|
|
||||||
)
|
|
||||||
|> Repo.all()
|
|> Repo.all()
|
||||||
|> Repo.preload(:app)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def is_expired?(%__MODULE__{valid_until: valid_until}) do
|
def is_expired?(%__MODULE__{valid_until: valid_until}) do
|
||||||
|
|
41
lib/pleroma/web/oauth/token/clean_worker.ex
Normal file
41
lib/pleroma/web/oauth/token/clean_worker.ex
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.OAuth.Token.CleanWorker do
|
||||||
|
@moduledoc """
|
||||||
|
The module represents functions to clean an expired oauth tokens.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# 10 seconds
|
||||||
|
@start_interval 10_000
|
||||||
|
@interval Pleroma.Config.get(
|
||||||
|
# 24 hours
|
||||||
|
[:oauth2, :clean_expired_tokens_interval],
|
||||||
|
86_400_000
|
||||||
|
)
|
||||||
|
@queue :background
|
||||||
|
|
||||||
|
alias Pleroma.Web.OAuth.Token
|
||||||
|
|
||||||
|
def start_link, do: GenServer.start_link(__MODULE__, nil)
|
||||||
|
|
||||||
|
def init(_) do
|
||||||
|
if Pleroma.Config.get([:oauth2, :clean_expired_tokens], false) do
|
||||||
|
Process.send_after(self(), :perform, @start_interval)
|
||||||
|
{:ok, nil}
|
||||||
|
else
|
||||||
|
:ignore
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc false
|
||||||
|
def handle_info(:perform, state) do
|
||||||
|
Process.send_after(self(), :perform, @interval)
|
||||||
|
PleromaJobQueue.enqueue(@queue, __MODULE__, [:clean])
|
||||||
|
{:noreply, state}
|
||||||
|
end
|
||||||
|
|
||||||
|
# Job Worker Callbacks
|
||||||
|
def perform(:clean), do: Token.delete_expired_tokens()
|
||||||
|
end
|
55
lib/pleroma/web/oauth/token/query.ex
Normal file
55
lib/pleroma/web/oauth/token/query.ex
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.OAuth.Token.Query do
|
||||||
|
@moduledoc """
|
||||||
|
Contains queries for OAuth Token.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import Ecto.Query, only: [from: 2]
|
||||||
|
|
||||||
|
@type query :: Ecto.Queryable.t() | Token.t()
|
||||||
|
|
||||||
|
alias Pleroma.Web.OAuth.Token
|
||||||
|
|
||||||
|
@spec get_by_refresh_token(query, String.t()) :: query
|
||||||
|
def get_by_refresh_token(query \\ Token, refresh_token) do
|
||||||
|
from(q in query, where: q.refresh_token == ^refresh_token)
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec get_by_token(query, String.t()) :: query
|
||||||
|
def get_by_token(query \\ Token, token) do
|
||||||
|
from(q in query, where: q.token == ^token)
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec get_by_app(query, String.t()) :: query
|
||||||
|
def get_by_app(query \\ Token, app_id) do
|
||||||
|
from(q in query, where: q.app_id == ^app_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec get_by_id(query, String.t()) :: query
|
||||||
|
def get_by_id(query \\ Token, id) 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)
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec preload(query, any) :: query
|
||||||
|
def preload(query \\ Token, assoc_preload \\ [])
|
||||||
|
|
||||||
|
def preload(query, assoc_preload) when is_list(assoc_preload) do
|
||||||
|
from(q in query, preload: ^assoc_preload)
|
||||||
|
end
|
||||||
|
|
||||||
|
def preload(query, _assoc_preload), do: query
|
||||||
|
end
|
|
@ -69,4 +69,17 @@ test "deletes all tokens of a user" do
|
||||||
|
|
||||||
assert tokens == 2
|
assert tokens == 2
|
||||||
end
|
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
|
end
|
||||||
|
|
Loading…
Reference in a new issue