From 6062885df6178c09544b6a0b5b731a554786397e Mon Sep 17 00:00:00 2001 From: Ekaterina Vaartis Date: Sun, 16 Dec 2018 22:04:43 +0300 Subject: [PATCH] Add a configurable auto-cleanup for captchas --- config/config.exs | 1 + config/config.md | 1 + lib/pleroma/captcha/captcha.ex | 15 ++++++++++++++- lib/pleroma/captcha/captcha_service.ex | 5 +++++ lib/pleroma/captcha/kocaptcha.ex | 11 ++++++++++- 5 files changed, 31 insertions(+), 2 deletions(-) diff --git a/config/config.exs b/config/config.exs index 5149e9c41..21cdc0537 100644 --- a/config/config.exs +++ b/config/config.exs @@ -12,6 +12,7 @@ config :pleroma, Pleroma.Captcha, enabled: false, + minutes_retained: 5, method: Pleroma.Captcha.Kocaptcha config :pleroma, Pleroma.Captcha.Kocaptcha, endpoint: "http://localhost:9093" diff --git a/config/config.md b/config/config.md index e8b5e52cb..65f0866fd 100644 --- a/config/config.md +++ b/config/config.md @@ -167,6 +167,7 @@ Web Push Notifications configuration. You can use the mix task `mix web_push.gen ## Pleroma.Captcha * `enabled`: Whether the captcha should be shown on registration * `method`: The method/service to use for captcha +* `minutes_retained`: The time in minutes for which the captcha is valid (stored in the cache) ### Pleroma.Captcha.Kocaptcha Kocaptcha is a very simple captcha service with a single API endpoint, diff --git a/lib/pleroma/captcha/captcha.ex b/lib/pleroma/captcha/captcha.ex index df33406ee..2dcbc4717 100644 --- a/lib/pleroma/captcha/captcha.ex +++ b/lib/pleroma/captcha/captcha.ex @@ -38,7 +38,13 @@ def handle_call(:new, _from, state) do if !enabled do {:reply, %{type: :none}, state} else - {:reply, method().new(), state} + new_captcha = method().new() + + minutes_retained = Pleroma.Config.get!([__MODULE__, :minutes_retained]) + # Wait several minutes and if the captcha is still there, delete it + Process.send_after(self(), {:cleanup, new_captcha.token}, 1000 * 60 * minutes_retained) + + {:reply, new_captcha, state} end end @@ -47,5 +53,12 @@ def handle_call({:validate, token, captcha}, _from, state) do {:reply, method().validate(token, captcha), state} end + @doc false + def handle_info({:cleanup, token}, state) do + method().cleanup(token) + + {:noreply, state} + end + defp method, do: Pleroma.Config.get!([__MODULE__, :method]) end diff --git a/lib/pleroma/captcha/captcha_service.ex b/lib/pleroma/captcha/captcha_service.ex index 907a73ad0..fe5a6bf66 100644 --- a/lib/pleroma/captcha/captcha_service.ex +++ b/lib/pleroma/captcha/captcha_service.ex @@ -20,4 +20,9 @@ defmodule Pleroma.Captcha.Service do `true` if captcha is valid, `false` if not """ @callback validate(token :: String.t(), captcha :: String.t()) :: boolean + + @doc """ + This function is called periodically to clean up old captchas + """ + @callback cleanup(token :: String.t()) :: :ok end diff --git a/lib/pleroma/captcha/kocaptcha.ex b/lib/pleroma/captcha/kocaptcha.ex index 4ecd1a81f..9891d4031 100644 --- a/lib/pleroma/captcha/kocaptcha.ex +++ b/lib/pleroma/captcha/kocaptcha.ex @@ -29,11 +29,20 @@ def validate(token, captcha) do [{^token, saved_md5}] <- :ets.lookup(@ets, token), true <- :crypto.hash(:md5, captcha) |> Base.encode16() == String.upcase(saved_md5) do # Clear the saved value - :ets.delete(@ets, token) + cleanup(token) true else _ -> false end end + + @impl Service + def cleanup(token) do + # Only delete the entry if it exists in the table, because ets:delete raises an exception if it does not + case :ets.lookup(@ets, token) do + [{^token, _}] -> :ets.delete(@ets, token) + _ -> true + end + end end