diff options
| author | Ekaterina Vaartis <vaartis@cock.li> | 2018-12-15 01:31:19 +0300 | 
|---|---|---|
| committer | Ekaterina Vaartis <vaartis@cock.li> | 2018-12-15 03:12:44 +0300 | 
| commit | a2399c1c7c17ee1c8e85ae0b6095405c7cb9f6f1 (patch) | |
| tree | dfeb13506c7cd497fc0eb68d187a72793eeaa612 | |
| parent | e74f384b685edff5e4fac9da788a7516dd83fe94 (diff) | |
| download | pleroma-a2399c1c7c17ee1c8e85ae0b6095405c7cb9f6f1.tar.gz pleroma-a2399c1c7c17ee1c8e85ae0b6095405c7cb9f6f1.zip | |
Add base CAPTCHA support (currently only kocaptcha)
| -rw-r--r-- | config/config.exs | 7 | ||||
| -rw-r--r-- | lib/pleroma/application.ex | 1 | ||||
| -rw-r--r-- | lib/pleroma/captcha.ex | 68 | ||||
| -rw-r--r-- | lib/pleroma/web/router.ex | 1 | ||||
| -rw-r--r-- | lib/pleroma/web/twitter_api/controllers/util_controller.ex | 4 | ||||
| -rw-r--r-- | lib/pleroma/web/twitter_api/twitter_api.ex | 47 | 
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 | 
