aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEkaterina Vaartis <vaartis@cock.li>2018-12-15 01:31:19 +0300
committerEkaterina Vaartis <vaartis@cock.li>2018-12-15 03:12:44 +0300
commita2399c1c7c17ee1c8e85ae0b6095405c7cb9f6f1 (patch)
treedfeb13506c7cd497fc0eb68d187a72793eeaa612
parente74f384b685edff5e4fac9da788a7516dd83fe94 (diff)
downloadpleroma-a2399c1c7c17ee1c8e85ae0b6095405c7cb9f6f1.tar.gz
Add base CAPTCHA support (currently only kocaptcha)
-rw-r--r--config/config.exs7
-rw-r--r--lib/pleroma/application.ex1
-rw-r--r--lib/pleroma/captcha.ex68
-rw-r--r--lib/pleroma/web/router.ex1
-rw-r--r--lib/pleroma/web/twitter_api/controllers/util_controller.ex4
-rw-r--r--lib/pleroma/web/twitter_api/twitter_api.ex47
6 files changed, 109 insertions, 19 deletions
diff --git a/config/config.exs b/config/config.exs
index 1401b0a3d..df4c618a7 100644
--- a/config/config.exs
+++ b/config/config.exs
@@ -10,6 +10,13 @@ config :pleroma, ecto_repos: [Pleroma.Repo]
config :pleroma, Pleroma.Repo, types: Pleroma.PostgresTypes
+config :pleroma, Pleroma.Captcha,
+ method: Pleroma.Captcha.Kocaptcha
+
+# Kocaptcha is a very simple captcha service, the source code is here: https://github.com/koto-bank/kocaptcha
+config :pleroma, Pleroma.Captcha.Kocaptcha,
+ endpoint: "http://localhost:9093"
+
# Upload configuration
config :pleroma, Pleroma.Upload,
uploader: Pleroma.Uploaders.Local,
diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex
index 8705395a4..e15991957 100644
--- a/lib/pleroma/application.ex
+++ b/lib/pleroma/application.ex
@@ -24,6 +24,7 @@ defmodule Pleroma.Application do
# Start the Ecto repository
supervisor(Pleroma.Repo, []),
worker(Pleroma.Emoji, []),
+ worker(Pleroma.Captcha, []),
worker(
Cachex,
[
diff --git a/lib/pleroma/captcha.ex b/lib/pleroma/captcha.ex
new file mode 100644
index 000000000..31f3bc797
--- /dev/null
+++ b/lib/pleroma/captcha.ex
@@ -0,0 +1,68 @@
+defmodule Pleroma.Captcha do
+ use GenServer
+
+ @ets __MODULE__.Ets
+ @ets_options [:ordered_set, :private, :named_table, {:read_concurrency, true}]
+
+
+ @doc false
+ def start_link() do
+ GenServer.start_link(__MODULE__, [], name: __MODULE__)
+ end
+
+
+ @doc false
+ def init(_) do
+ @ets = :ets.new(@ets, @ets_options)
+
+ {:ok, nil}
+ end
+
+ def new() do
+ GenServer.call(__MODULE__, :new)
+ end
+
+ def validate(token, captcha) do
+ GenServer.call(__MODULE__, {:validate, token, captcha})
+ end
+
+ @doc false
+ def handle_call(:new, _from, state) do
+ method = Pleroma.Config.get!([__MODULE__, :method])
+
+ case method do
+ __MODULE__.Kocaptcha ->
+ endpoint = Pleroma.Config.get!([method, :endpoint])
+ case HTTPoison.get(endpoint <> "/new") do
+ {:error, _} ->
+ %{error: "Kocaptcha service unavailable"}
+ {:ok, res} ->
+ json_resp = Poison.decode!(res.body)
+
+ token = json_resp["token"]
+
+ true = :ets.insert(@ets, {token, json_resp["md5"]})
+
+ {
+ :reply,
+ %{type: :kocaptcha, token: token, url: endpoint <> json_resp["url"]},
+ state
+ }
+ end
+ end
+ end
+
+ @doc false
+ def handle_call({:validate, token, captcha}, _from, state) do
+ with false <- is_nil(captcha),
+ [{^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)
+
+ {:reply, true, state}
+ else
+ e -> IO.inspect(e); {:reply, false, state}
+ end
+ end
+end
diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex
index daff3362c..60342cfb4 100644
--- a/lib/pleroma/web/router.ex
+++ b/lib/pleroma/web/router.ex
@@ -99,6 +99,7 @@ defmodule Pleroma.Web.Router do
get("/password_reset/:token", UtilController, :show_password_reset)
post("/password_reset", UtilController, :password_reset)
get("/emoji", UtilController, :emoji)
+ get("/captcha", UtilController, :captcha)
end
scope "/api/pleroma/admin", Pleroma.Web.AdminAPI do
diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex
index 2f2b69623..38653f0b8 100644
--- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex
+++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex
@@ -284,4 +284,8 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
json(conn, %{error: msg})
end
end
+
+ def captcha(conn, _params) do
+ json(conn, Pleroma.Captcha.new())
+ end
end
diff --git a/lib/pleroma/web/twitter_api/twitter_api.ex b/lib/pleroma/web/twitter_api/twitter_api.ex
index 1e764f24a..c9e8fbcbb 100644
--- a/lib/pleroma/web/twitter_api/twitter_api.ex
+++ b/lib/pleroma/web/twitter_api/twitter_api.ex
@@ -132,38 +132,47 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
bio: User.parse_bio(params["bio"]),
email: params["email"],
password: params["password"],
- password_confirmation: params["confirm"]
+ password_confirmation: params["confirm"],
+ captcha_solution: params["captcha_solution"],
+ captcha_token: params["captcha_token"]
}
- registrations_open = Pleroma.Config.get([:instance, :registrations_open])
+ # Captcha invalid
+ if not Pleroma.Captcha.validate(params[:captcha_token], params[:captcha_solution]) do
+ # I have no idea how this error handling works
+ {:error, %{error: Jason.encode!(%{captcha: ["Invalid CAPTCHA"]})}}
+ else
+ registrations_open = Pleroma.Config.get([:instance, :registrations_open])
- # no need to query DB if registration is open
- token =
- unless registrations_open || is_nil(tokenString) do
+ # no need to query DB if registration is open
+ token =
+ unless registrations_open || is_nil(tokenString) do
Repo.get_by(UserInviteToken, %{token: tokenString})
end
- cond do
- registrations_open || (!is_nil(token) && !token.used) ->
- changeset = User.register_changeset(%User{info: %{}}, params)
+ cond do
+ registrations_open || (!is_nil(token) && !token.used) ->
+ changeset = User.register_changeset(%User{info: %{}}, params)
- with {:ok, user} <- Repo.insert(changeset) do
- !registrations_open && UserInviteToken.mark_as_used(token.token)
- {:ok, user}
- else
- {:error, changeset} ->
- errors =
+ with {:ok, user} <- Repo.insert(changeset) do
+ !registrations_open && UserInviteToken.mark_as_used(token.token)
+ {:ok, user}
+ else
+ {:error, changeset} ->
+ errors =
Ecto.Changeset.traverse_errors(changeset, fn {msg, _opts} -> msg end)
|> Jason.encode!()
{:error, %{error: errors}}
- end
+ end
+
- !registrations_open && is_nil(token) ->
- {:error, "Invalid token"}
+ !registrations_open && is_nil(token) ->
+ {:error, "Invalid token"}
- !registrations_open && token.used ->
- {:error, "Expired token"}
+ !registrations_open && token.used ->
+ {:error, "Expired token"}
+ end
end
end