From e53679698424a7d58c308c21d466b07e34e8c3e9 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Wed, 11 Dec 2019 22:29:31 +0700 Subject: [PATCH 1/3] Add native captcha and enable it by default. --- CHANGELOG.md | 2 ++ config/config.exs | 6 ++---- docs/configuration/cheatsheet.md | 6 ++++++ lib/pleroma/captcha/native.ex | 35 ++++++++++++++++++++++++++++++++ mix.exs | 3 +++ mix.lock | 1 + test/captcha_test.exs | 18 ++++++++++++++++ 7 files changed, 67 insertions(+), 4 deletions(-) create mode 100644 lib/pleroma/captcha/native.ex diff --git a/CHANGELOG.md b/CHANGELOG.md index 37b8b0be7..c133cd9ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -84,6 +84,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - ActivityPub: Configurable `type` field of the actors. - Mastodon API: `/api/v1/accounts/:id` has `source/pleroma/actor_type` field. - Mastodon API: `/api/v1/update_credentials` accepts `actor_type` field. +- Captcha: Support native provider +- Captcha: Enable by default ### Fixed diff --git a/config/config.exs b/config/config.exs index 6ed800056..370ddd855 100644 --- a/config/config.exs +++ b/config/config.exs @@ -66,9 +66,9 @@ jobs: scheduled_jobs config :pleroma, Pleroma.Captcha, - enabled: false, + enabled: true, seconds_valid: 60, - method: Pleroma.Captcha.Kocaptcha + method: Pleroma.Captcha.Native config :pleroma, :hackney_pools, federation: [ @@ -84,8 +84,6 @@ timeout: 300_000 ] -config :pleroma, Pleroma.Captcha.Kocaptcha, endpoint: "https://captcha.kotobank.ch" - # Upload configuration config :pleroma, Pleroma.Upload, uploader: Pleroma.Uploaders.Local, diff --git a/docs/configuration/cheatsheet.md b/docs/configuration/cheatsheet.md index ef2711e3c..ce2a14210 100644 --- a/docs/configuration/cheatsheet.md +++ b/docs/configuration/cheatsheet.md @@ -379,13 +379,19 @@ For each pool, the options are: ## Captcha ### Pleroma.Captcha + * `enabled`: Whether the captcha should be shown on registration. * `method`: The method/service to use for captcha. * `seconds_valid`: The time in seconds for which the captcha is valid. ### Captcha providers +#### Pleroma.Captcha.Native + +A built-in captcha provider. Enabled by default. + #### Pleroma.Captcha.Kocaptcha + Kocaptcha is a very simple captcha service with a single API endpoint, the source code is here: https://github.com/koto-bank/kocaptcha. The default endpoint `https://captcha.kotobank.ch` is hosted by the developer. diff --git a/lib/pleroma/captcha/native.ex b/lib/pleroma/captcha/native.ex new file mode 100644 index 000000000..5306fe1aa --- /dev/null +++ b/lib/pleroma/captcha/native.ex @@ -0,0 +1,35 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Captcha.Native do + import Pleroma.Web.Gettext + alias Pleroma.Captcha.Service + @behaviour Service + + @impl Service + def new do + case Captcha.get() do + {:timeout} -> + %{error: dgettext("errors", "Captcha timeout")} + + {:ok, answer_data, img_binary} -> + %{ + type: :native, + token: token(), + url: "data:image/png;base64," <> Base.encode64(img_binary), + answer_data: answer_data + } + end + end + + @impl Service + def validate(_token, captcha, captcha) when not is_nil(captcha), do: :ok + def validate(_token, _captcha, _answer), do: {:error, dgettext("errors", "Invalid CAPTCHA")} + + defp token do + 10 + |> :crypto.strong_rand_bytes() + |> Base.url_encode64(padding: false) + end +end diff --git a/mix.exs b/mix.exs index 7c8e52a67..f1a62f6e3 100644 --- a/mix.exs +++ b/mix.exs @@ -163,6 +163,9 @@ defp deps do {:remote_ip, git: "https://git.pleroma.social/pleroma/remote_ip.git", ref: "825dc00aaba5a1b7c4202a532b696b595dd3bcb3"}, + {:captcha, + git: "https://git.pleroma.social/pleroma/elixir-libraries/elixir-captcha.git", + ref: "c3c795c55f6b49d79d6ac70a0f91e525099fc3e2"}, {:mox, "~> 0.5", only: :test} ] ++ oauth_deps() end diff --git a/mix.lock b/mix.lock index b22918156..401bcdb6e 100644 --- a/mix.lock +++ b/mix.lock @@ -8,6 +8,7 @@ "bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm"}, "cachex": {:hex, :cachex, "3.0.3", "4e2d3e05814a5738f5ff3903151d5c25636d72a3527251b753f501ad9c657967", [:mix], [{:eternal, "~> 1.2", [hex: :eternal, repo: "hexpm", optional: false]}, {:unsafe, "~> 1.0", [hex: :unsafe, repo: "hexpm", optional: false]}], "hexpm"}, "calendar": {:hex, :calendar, "0.17.6", "ec291cb2e4ba499c2e8c0ef5f4ace974e2f9d02ae9e807e711a9b0c7850b9aee", [:mix], [{:tzdata, "~> 0.5.20 or ~> 0.1.201603 or ~> 1.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm"}, + "captcha": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/elixir-captcha.git", "c3c795c55f6b49d79d6ac70a0f91e525099fc3e2", [ref: "c3c795c55f6b49d79d6ac70a0f91e525099fc3e2"]}, "certifi": {:hex, :certifi, "2.5.1", "867ce347f7c7d78563450a18a6a28a8090331e77fa02380b4a21962a65d36ee5", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm"}, "combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm"}, "comeonin": {:hex, :comeonin, "4.1.2", "3eb5620fd8e35508991664b4c2b04dd41e52f1620b36957be837c1d7784b7592", [:mix], [{:argon2_elixir, "~> 1.2", [hex: :argon2_elixir, repo: "hexpm", optional: true]}, {:bcrypt_elixir, "~> 0.12.1 or ~> 1.0", [hex: :bcrypt_elixir, repo: "hexpm", optional: true]}, {:pbkdf2_elixir, "~> 0.12", [hex: :pbkdf2_elixir, repo: "hexpm", optional: true]}], "hexpm"}, diff --git a/test/captcha_test.exs b/test/captcha_test.exs index 9f395d6b4..393c8219e 100644 --- a/test/captcha_test.exs +++ b/test/captcha_test.exs @@ -8,6 +8,7 @@ defmodule Pleroma.CaptchaTest do import Tesla.Mock alias Pleroma.Captcha.Kocaptcha + alias Pleroma.Captcha.Native @ets_options [:ordered_set, :private, :named_table, {:read_concurrency, true}] @@ -43,4 +44,21 @@ test "new and validate" do ) == :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 end From 470a3a678dfb4a7b9c2fe29c6b7ea03cee35ee82 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Thu, 12 Dec 2019 18:04:52 +0700 Subject: [PATCH 2/3] Add Kocaptcha endpoint to the test config --- config/test.exs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config/test.exs b/config/test.exs index d52ced612..b48b89c8f 100644 --- a/config/test.exs +++ b/config/test.exs @@ -95,6 +95,8 @@ config :pleroma, Pleroma.ReverseProxy.Client, Pleroma.ReverseProxy.ClientMock +config :pleroma, Pleroma.Captcha.Kocaptcha, endpoint: "https://captcha.kotobank.ch" + if File.exists?("./config/test.secret.exs") do import_config "test.secret.exs" else From bcd16676a78065a1b9549189ed3d594b4c62584c Mon Sep 17 00:00:00 2001 From: rinpatch Date: Fri, 13 Dec 2019 14:27:10 +0300 Subject: [PATCH 3/3] Publisher: check out a connection for inserting publish_one jobs Related to #1474, federation of one post on my istance creates in best-case 360 jobs, so if they for some reason take a while to insert, it will exhaust the connection pool. This fixes it by checking out one dedicated connection for inserting them. --- lib/pleroma/web/activity_pub/publisher.ex | 49 +++++++++++++---------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/lib/pleroma/web/activity_pub/publisher.ex b/lib/pleroma/web/activity_pub/publisher.ex index 4ea37fc7b..4073d3d63 100644 --- a/lib/pleroma/web/activity_pub/publisher.ex +++ b/lib/pleroma/web/activity_pub/publisher.ex @@ -9,6 +9,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do alias Pleroma.HTTP alias Pleroma.Instances alias Pleroma.Object + alias Pleroma.Repo alias Pleroma.User alias Pleroma.Web.ActivityPub.Relay alias Pleroma.Web.ActivityPub.Transmogrifier @@ -188,31 +189,35 @@ def publish(%User{} = actor, %{data: %{"bcc" => bcc}} = activity) recipients = recipients(actor, activity) - recipients - |> Enum.filter(&User.ap_enabled?/1) - |> Enum.map(fn %{source_data: data} -> data["inbox"] end) - |> Enum.filter(fn inbox -> should_federate?(inbox, public) end) - |> Instances.filter_reachable() - |> Enum.each(fn {inbox, unreachable_since} -> - %User{ap_id: ap_id} = - Enum.find(recipients, fn %{source_data: data} -> data["inbox"] == inbox end) + inboxes = + recipients + |> Enum.filter(&User.ap_enabled?/1) + |> Enum.map(fn %{source_data: data} -> data["inbox"] end) + |> Enum.filter(fn inbox -> should_federate?(inbox, public) end) + |> Instances.filter_reachable() - # Get all the recipients on the same host and add them to cc. Otherwise, a remote - # instance would only accept a first message for the first recipient and ignore the rest. - cc = get_cc_ap_ids(ap_id, recipients) + Repo.checkout(fn -> + Enum.each(inboxes, fn {inbox, unreachable_since} -> + %User{ap_id: ap_id} = + Enum.find(recipients, fn %{source_data: data} -> data["inbox"] == inbox end) - json = - data - |> Map.put("cc", cc) - |> Jason.encode!() + # Get all the recipients on the same host and add them to cc. Otherwise, a remote + # instance would only accept a first message for the first recipient and ignore the rest. + cc = get_cc_ap_ids(ap_id, recipients) - Pleroma.Web.Federator.Publisher.enqueue_one(__MODULE__, %{ - inbox: inbox, - json: json, - actor_id: actor.id, - id: activity.data["id"], - unreachable_since: unreachable_since - }) + json = + data + |> Map.put("cc", cc) + |> Jason.encode!() + + Pleroma.Web.Federator.Publisher.enqueue_one(__MODULE__, %{ + inbox: inbox, + json: json, + actor_id: actor.id, + id: activity.data["id"], + unreachable_since: unreachable_since + }) + end) end) end