diff --git a/config/config.exs b/config/config.exs index 7cc6adde34..2d2b62f378 100644 --- a/config/config.exs +++ b/config/config.exs @@ -872,7 +872,7 @@ config :pleroma, ConcurrentLimiter, [ {Pleroma.Web.RichMedia.Helpers, [max_running: 5, max_waiting: 5]}, {Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy, [max_running: 5, max_waiting: 5]}, - {Pleroma.Webhook.Notify, [max_running: 5, max_waiting: :infinity]} + {Pleroma.Webhook.Notify, [max_running: 5, max_waiting: 200]} ] import_config "soapbox.exs" diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 0f29ac8a59..014d8543ad 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -8,7 +8,6 @@ defmodule Pleroma.User do import Ecto.Changeset import Ecto.Query import Ecto, only: [assoc: 2] - import Pleroma.Webhook.Notify, only: [trigger_webhooks: 2] alias Ecto.Multi alias Pleroma.Activity @@ -37,7 +36,7 @@ defmodule Pleroma.User do alias Pleroma.Web.Endpoint alias Pleroma.Web.OAuth alias Pleroma.Web.RelMe - alias Pleroma.Webhook + alias Pleroma.Webhook.Notify alias Pleroma.Workers.BackgroundWorker require Logger @@ -861,8 +860,8 @@ defp autofollowing_users(user) do @doc "Inserts provided changeset, performs post-registration actions (confirmation email sending etc.)" def register(%Ecto.Changeset{} = changeset) do with {:ok, user} <- Repo.insert(changeset) do + Notify.trigger_webhooks(user, :"account.created") post_register_action(user) - trigger_webhooks(user, :"account.created") end end diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 963818327b..39687a550e 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -24,7 +24,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do alias Pleroma.Web.ActivityPub.Transmogrifier alias Pleroma.Web.Streamer alias Pleroma.Web.WebFinger - alias Pleroma.Webhook alias Pleroma.Workers.BackgroundWorker alias Pleroma.Workers.PollWorker diff --git a/lib/pleroma/web/admin_api/controllers/webhook_controller.ex b/lib/pleroma/web/admin_api/controllers/webhook_controller.ex index 054905cb4d..8a6b0de7ac 100644 --- a/lib/pleroma/web/admin_api/controllers/webhook_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/webhook_controller.ex @@ -14,7 +14,7 @@ defmodule Pleroma.Web.AdminAPI.WebhookController do plug( OAuthScopesPlug, %{scopes: ["admin:write"]} - when action in [:update, :create, :enable, :disable, :rotate_secret] + when action in [:update, :create, :delete, :enable, :disable, :rotate_secret] ) plug(OAuthScopesPlug, %{scopes: ["admin:read"]} when action in [:index, :show]) @@ -40,17 +40,14 @@ def show(conn, %{id: id}) do end def create(%{body_params: params} = conn, _) do - with {:ok, webhook} <- Webhook.create(params) do + with webhook <- Webhook.create(params) do render(conn, "show.json", webhook: webhook) - # else - # nil -> {:error, :not_found} end end def update(%{body_params: params} = conn, %{id: id}) do with %Webhook{} = webhook <- Webhook.get(id), - changeset <- Webhook.update(webhook, params), - {:ok, webhook} <- Repo.update(changeset) do + webhook <- Webhook.update(webhook, params) do render(conn, "show.json", webhook: webhook) end end diff --git a/lib/pleroma/webhook/notify.ex b/lib/pleroma/webhook/notify.ex index d1b189ef0c..ec84b89ef5 100644 --- a/lib/pleroma/webhook/notify.ex +++ b/lib/pleroma/webhook/notify.ex @@ -29,7 +29,7 @@ def trigger_webhooks(%User{} = user, :"account.created" = type) do end) end - def report_created(%Webhook{} = webhook, %Activity{} = report) do + defp report_created(%Webhook{} = webhook, %Activity{} = report) do object = View.render( Pleroma.Web.MastodonAPI.Admin.ReportView, @@ -40,7 +40,7 @@ def report_created(%Webhook{} = webhook, %Activity{} = report) do deliver(webhook, object, :"report.created") end - def account_created(%Webhook{} = webhook, %User{} = user) do + defp account_created(%Webhook{} = webhook, %User{} = user) do object = View.render( Pleroma.Web.MastodonAPI.Admin.AccountView, diff --git a/test/pleroma/user_test.exs b/test/pleroma/user_test.exs index 7364493d31..2f561c46ec 100644 --- a/test/pleroma/user_test.exs +++ b/test/pleroma/user_test.exs @@ -12,12 +12,14 @@ defmodule Pleroma.UserTest do alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.CommonAPI + alias Pleroma.Webhook.Notify use Pleroma.DataCase use Oban.Testing, repo: Pleroma.Repo import Pleroma.Factory import ExUnit.CaptureLog + import Mock import Swoosh.TestAssertions setup_all do @@ -692,6 +694,14 @@ test "it sets 'accepts_email_list'" do assert user.accepts_email_list end + + test_with_mock "triggers webhooks", Notify, trigger_webhooks: fn _, _ -> nil end do + cng = User.register_changeset(%User{}, @full_user_data) + + {:ok, registered_user} = User.register(cng) + + assert_called(Notify.trigger_webhooks(registered_user, :"account.created")) + end end describe "user registration, with :account_activation_required" do diff --git a/test/pleroma/web/activity_pub/activity_pub_test.exs b/test/pleroma/web/activity_pub/activity_pub_test.exs index 3064ffc810..070fbf7930 100644 --- a/test/pleroma/web/activity_pub/activity_pub_test.exs +++ b/test/pleroma/web/activity_pub/activity_pub_test.exs @@ -16,6 +16,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.AdminAPI.AccountView alias Pleroma.Web.CommonAPI + alias Pleroma.Webhook.Notify import ExUnit.CaptureLog import Mock @@ -1640,6 +1641,29 @@ test "it can create a Flag activity", assert Repo.aggregate(Object, :count, :id) == 2 assert Repo.aggregate(Notification, :count, :id) == 0 end + + test_with_mock "triggers webhooks", + %{ + reporter: reporter, + context: context, + target_account: target_account, + reported_activity: reported_activity, + content: content + }, + Notify, + [:passthrough], + trigger_webhooks: fn _, _ -> nil end do + {:ok, activity} = + ActivityPub.flag(%{ + actor: reporter, + context: context, + account: target_account, + statuses: [reported_activity], + content: content + }) + + assert_called(Notify.trigger_webhooks(activity, :"report.created")) + end end test "fetch_activities/2 returns activities addressed to a list " do diff --git a/test/pleroma/web/admin_api/controllers/webhook_controller_test.exs b/test/pleroma/web/admin_api/controllers/webhook_controller_test.exs new file mode 100644 index 0000000000..6a1586ff1d --- /dev/null +++ b/test/pleroma/web/admin_api/controllers/webhook_controller_test.exs @@ -0,0 +1,84 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.AdminAPI.WebhookControllerTest do + use Pleroma.Web.ConnCase, async: true + + import Pleroma.Factory + + alias Pleroma.Webhook + + setup do + admin = insert(:user, is_admin: true) + token = insert(:oauth_admin_token, user: admin) + + conn = + build_conn() + |> assign(:user, admin) + |> assign(:token, token) + + {:ok, %{admin: admin, token: token, conn: conn}} + end + + describe "GET /api/pleroma/admin/webhook" do + test "lists existing webhooks", %{conn: conn} do + Webhook.create(%{url: "https://example.com/webhook1", events: [:"report.created"]}) + Webhook.create(%{url: "https://example.com/webhook2", events: [:"account.created"]}) + + response = + conn + |> get("/api/pleroma/admin/webhooks") + |> json_response_and_validate_schema(:ok) + + assert length(response) == 2 + end + end + + describe "POST /api/pleroma/admin/webhooks" do + test "creates a webhook", %{conn: conn} do + %{"id" => id} = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/webhooks", %{ + url: "http://example.com/webhook", + events: ["account.created"] + }) + |> json_response_and_validate_schema(:ok) + + assert %{url: "http://example.com/webhook", events: [:"account.created"]} = Webhook.get(id) + end + end + + describe "PATCH /api/pleroma/admin/webhooks" do + test "edits a webhook", %{conn: conn} do + %{id: id} = + Webhook.create(%{url: "https://example.com/webhook1", events: [:"report.created"]}) + + conn + |> put_req_header("content-type", "application/json") + |> patch("/api/pleroma/admin/webhooks/#{id}", %{ + events: ["report.created", "account.created"] + }) + |> json_response_and_validate_schema(:ok) + + assert %{events: [:"report.created", :"account.created"]} = Webhook.get(id) + end + end + + describe "DELETE /api/pleroma/admin/webhooks" do + test "deletes a webhook", %{conn: conn} do + %{id: id} = + Webhook.create(%{url: "https://example.com/webhook1", events: [:"report.created"]}) + + conn + |> put_req_header("content-type", "application/json") + |> delete("/api/pleroma/admin/webhooks/#{id}") + |> json_response_and_validate_schema(:ok) + + assert [] = + Webhook + |> Pleroma.Repo.all() + end + end +end diff --git a/test/pleroma/webhook/notify_test.ex b/test/pleroma/webhook/notify_test.ex new file mode 100644 index 0000000000..8aa9de08c6 --- /dev/null +++ b/test/pleroma/webhook/notify_test.ex @@ -0,0 +1,29 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Webhook.NotifyTest do + use Pleroma.DataCase, async: true + + alias Pleroma.Webhook + alias Pleroma.Webhook.Notify + + import Pleroma.Factory + + test "notifies have a valid signature" do + activity = insert(:report_activity) + + %{secret: secret} = + webhook = Webhook.create(%{url: "https://example.com/webhook", events: [:"report.created"]}) + + Tesla.Mock.mock(fn %{url: "https://example.com/webhook", body: body, headers: headers} = _ -> + {"X-Hub-Signature", "sha256=" <> signature} = + Enum.find(headers, fn {key, _} -> key == "X-Hub-Signature" end) + + assert signature == :crypto.mac(:hmac, :sha256, secret, body) |> Base.encode16() + %Tesla.Env{status: 200, body: ""} + end) + + Notify.report_created(webhook, activity) + end +end