Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F85710268
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
8 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/lib/pleroma/captcha/captcha.ex b/lib/pleroma/captcha/captcha.ex
index c2765a5b8..cf75c3adc 100644
--- a/lib/pleroma/captcha/captcha.ex
+++ b/lib/pleroma/captcha/captcha.ex
@@ -1,114 +1,115 @@
# 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.Captcha do
import Pleroma.Web.Gettext
alias Calendar.DateTime
alias Plug.Crypto.KeyGenerator
alias Plug.Crypto.MessageEncryptor
use GenServer
@doc false
def start_link(_) do
GenServer.start_link(__MODULE__, [], name: __MODULE__)
end
@doc false
def init(_) do
{:ok, nil}
end
@doc """
Ask the configured captcha service for a new captcha
"""
def new do
GenServer.call(__MODULE__, :new)
end
@doc """
Ask the configured captcha service to validate the captcha
"""
def validate(token, captcha, answer_data) do
GenServer.call(__MODULE__, {:validate, token, captcha, answer_data})
end
@doc false
def handle_call(:new, _from, state) do
enabled = Pleroma.Config.get([__MODULE__, :enabled])
if !enabled do
{:reply, %{type: :none}, state}
else
new_captcha = method().new()
secret_key_base = Pleroma.Config.get!([Pleroma.Web.Endpoint, :secret_key_base])
# This make salt a little different for two keys
token = new_captcha[:token]
secret = KeyGenerator.generate(secret_key_base, token <> "_encrypt")
sign_secret = KeyGenerator.generate(secret_key_base, token <> "_sign")
- # Basicallty copy what Phoenix.Token does here, add the time to
+ # Basically copy what Phoenix.Token does here, add the time to
# the actual data and make it a binary to then encrypt it
encrypted_captcha_answer =
%{
at: DateTime.now_utc(),
answer_data: new_captcha[:answer_data]
}
|> :erlang.term_to_binary()
|> MessageEncryptor.encrypt(secret, sign_secret)
{
:reply,
- # Repalce the answer with the encrypted answer
+ # Replace the answer with the encrypted answer
%{new_captcha | answer_data: encrypted_captcha_answer},
state
}
end
end
@doc false
def handle_call({:validate, token, captcha, answer_data}, _from, state) do
secret_key_base = Pleroma.Config.get!([Pleroma.Web.Endpoint, :secret_key_base])
secret = KeyGenerator.generate(secret_key_base, token <> "_encrypt")
sign_secret = KeyGenerator.generate(secret_key_base, token <> "_sign")
# If the time found is less than (current_time-seconds_valid) then the time has already passed
# Later we check that the time found is more than the presumed invalidatation time, that means
# that the data is still valid and the captcha can be checked
seconds_valid = Pleroma.Config.get!([Pleroma.Captcha, :seconds_valid])
valid_if_after = DateTime.subtract!(DateTime.now_utc(), seconds_valid)
result =
- with {:ok, data} <- MessageEncryptor.decrypt(answer_data, secret, sign_secret),
+ with false <- is_nil(answer_data),
+ {:ok, data} <- MessageEncryptor.decrypt(answer_data, secret, sign_secret),
%{at: at, answer_data: answer_md5} <- :erlang.binary_to_term(data) do
try do
if DateTime.before?(at, valid_if_after),
do: throw({:error, dgettext("errors", "CAPTCHA expired")})
if not is_nil(Cachex.get!(:used_captcha_cache, token)),
do: throw({:error, dgettext("errors", "CAPTCHA already used")})
res = method().validate(token, captcha, answer_md5)
# Throw if an error occurs
if res != :ok, do: throw(res)
# Mark this captcha as used
{:ok, _} =
Cachex.put(:used_captcha_cache, token, true, ttl: :timer.seconds(seconds_valid))
:ok
catch
:throw, e -> e
end
else
_ -> {:error, dgettext("errors", "Invalid answer data")}
end
{:reply, result, state}
end
defp method, do: Pleroma.Config.get!([__MODULE__, :method])
end
diff --git a/test/captcha_test.exs b/test/captcha_test.exs
index 393c8219e..5e29b48b0 100644
--- a/test/captcha_test.exs
+++ b/test/captcha_test.exs
@@ -1,64 +1,116 @@
# 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.CaptchaTest do
- use ExUnit.Case
+ use Pleroma.DataCase
import Tesla.Mock
+ alias Pleroma.Captcha
alias Pleroma.Captcha.Kocaptcha
alias Pleroma.Captcha.Native
@ets_options [:ordered_set, :private, :named_table, {:read_concurrency, true}]
+ clear_config([Pleroma.Captcha, :enabled])
+
describe "Kocaptcha" do
setup do
ets_name = Kocaptcha.Ets
^ets_name = :ets.new(ets_name, @ets_options)
mock(fn
%{method: :get, url: "https://captcha.kotobank.ch/new"} ->
json(%{
md5: "63615261b77f5354fb8c4e4986477555",
token: "afa1815e14e29355e6c8f6b143a39fa2",
url: "/captchas/afa1815e14e29355e6c8f6b143a39fa2.png"
})
end)
:ok
end
test "new and validate" do
new = Kocaptcha.new()
- assert new[:type] == :kocaptcha
- assert new[:token] == "afa1815e14e29355e6c8f6b143a39fa2"
- assert new[:url] ==
- "https://captcha.kotobank.ch/captchas/afa1815e14e29355e6c8f6b143a39fa2.png"
+ token = "afa1815e14e29355e6c8f6b143a39fa2"
+ url = "https://captcha.kotobank.ch/captchas/afa1815e14e29355e6c8f6b143a39fa2.png"
+
+ assert %{
+ answer_data: answer,
+ token: ^token,
+ url: ^url,
+ type: :kocaptcha
+ } = new
- assert Kocaptcha.validate(
- new[:token],
- "7oEy8c",
- new[:answer_data]
- ) == :ok
+ assert Kocaptcha.validate(token, "7oEy8c", answer) == :ok
end
end
describe "Native" do
test "new and validate" do
new = Native.new()
assert %{
answer_data: answer,
token: token,
type: :native,
url: "data:image/png;base64," <> _
} = new
assert is_binary(answer)
assert :ok = Native.validate(token, answer, answer)
assert {:error, "Invalid CAPTCHA"} == Native.validate(token, answer, answer <> "foobar")
end
end
+
+ describe "Captcha Wrapper" do
+ test "validate" do
+ Pleroma.Config.put([Pleroma.Captcha, :enabled], true)
+
+ new = Captcha.new()
+
+ assert %{
+ answer_data: answer,
+ token: token
+ } = new
+
+ assert is_binary(answer)
+ assert :ok = Captcha.validate(token, "63615261b77f5354fb8c4e4986477555", answer)
+ end
+
+ test "doesn't validate invalid answer" do
+ Pleroma.Config.put([Pleroma.Captcha, :enabled], true)
+
+ new = Captcha.new()
+
+ assert %{
+ answer_data: answer,
+ token: token
+ } = new
+
+ assert is_binary(answer)
+
+ assert {:error, "Invalid answer data"} =
+ Captcha.validate(token, "63615261b77f5354fb8c4e4986477555", answer <> "foobar")
+ end
+
+ test "nil answer_data" do
+ Pleroma.Config.put([Pleroma.Captcha, :enabled], true)
+
+ new = Captcha.new()
+
+ assert %{
+ answer_data: answer,
+ token: token
+ } = new
+
+ assert is_binary(answer)
+
+ assert {:error, "Invalid answer data"} =
+ Captcha.validate(token, "63615261b77f5354fb8c4e4986477555", nil)
+ end
+ end
end
diff --git a/test/support/captcha_mock.ex b/test/support/captcha_mock.ex
index 65ca6b3bd..6dae94edf 100644
--- a/test/support/captcha_mock.ex
+++ b/test/support/captcha_mock.ex
@@ -1,14 +1,23 @@
# 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.Captcha.Mock do
alias Pleroma.Captcha.Service
@behaviour Service
@impl Service
- def new, do: %{type: :mock}
+ def new,
+ do: %{
+ type: :mock,
+ token: "afa1815e14e29355e6c8f6b143a39fa2",
+ answer_data: "63615261b77f5354fb8c4e4986477555",
+ url: "https://example.org/captcha.png"
+ }
@impl Service
- def validate(_token, _captcha, _data), do: :ok
+ def validate(_token, captcha, captcha) when not is_nil(captcha), do: :ok
+
+ def validate(_token, captcha, answer),
+ do: {:error, "Invalid CAPTCHA captcha: #{inspect(captcha)} ; answer: #{inspect(answer)}"}
end
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Fri, Sep 18, 11:14 PM (5 h, 51 s)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1768429
Default Alt Text
(8 KB)
Attached To
Mode
rPUBE pleroma-upstream
Attached
Detach File
Event Timeline
Log In to Comment