diff --git a/lib/pleroma/web/admin_api/controllers/fallback_controller.ex b/lib/pleroma/web/admin_api/controllers/fallback_controller.ex index e72f45c219..d8fb54cadb 100644 --- a/lib/pleroma/web/admin_api/controllers/fallback_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/fallback_controller.ex @@ -11,6 +11,12 @@ def call(conn, {:error, :not_found}) do |> json(%{error: dgettext("errors", "Not found")}) end + def call(conn, {:error, :forbidden}) do + conn + |> put_status(:forbidden) + |> json(%{error: dgettext("errors", "Forbidden")}) + end + def call(conn, {:error, reason}) do conn |> put_status(:bad_request) diff --git a/lib/pleroma/web/admin_api/controllers/webhook_controller.ex b/lib/pleroma/web/admin_api/controllers/webhook_controller.ex index 8a6b0de7ac..dd9712a318 100644 --- a/lib/pleroma/web/admin_api/controllers/webhook_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/webhook_controller.ex @@ -46,42 +46,49 @@ def create(%{body_params: params} = conn, _) do end def update(%{body_params: params} = conn, %{id: id}) do - with %Webhook{} = webhook <- Webhook.get(id), + with %Webhook{internal: false} = webhook <- Webhook.get(id), webhook <- Webhook.update(webhook, params) do render(conn, "show.json", webhook: webhook) + else + %Webhook{internal: true} -> {:error, :forbidden} end end def delete(conn, %{id: id}) do - with %Webhook{} = webhook <- Webhook.get(id), + with %Webhook{internal: false} = webhook <- Webhook.get(id), {:ok, webhook} <- Webhook.delete(webhook) do render(conn, "show.json", webhook: webhook) + else + %Webhook{internal: true} -> {:error, :forbidden} end end def enable(conn, %{id: id}) do - with %Webhook{} = webhook <- Webhook.get(id), + with %Webhook{internal: false} = webhook <- Webhook.get(id), {:ok, webhook} <- Webhook.set_enabled(webhook, true) do render(conn, "show.json", webhook: webhook) else + %Webhook{internal: true} -> {:error, :forbidden} nil -> {:error, :not_found} end end def disable(conn, %{id: id}) do - with %Webhook{} = webhook <- Webhook.get(id), + with %Webhook{internal: false} = webhook <- Webhook.get(id), {:ok, webhook} <- Webhook.set_enabled(webhook, false) do render(conn, "show.json", webhook: webhook) else + %Webhook{internal: true} -> {:error, :forbidden} nil -> {:error, :not_found} end end def rotate_secret(conn, %{id: id}) do - with %Webhook{} = webhook <- Webhook.get(id), + with %Webhook{internal: false} = webhook <- Webhook.get(id), {:ok, webhook} <- Webhook.rotate_secret(webhook) do render(conn, "show.json", webhook: webhook) else + %Webhook{internal: true} -> {:error, :forbidden} nil -> {:error, :not_found} end end diff --git a/lib/pleroma/web/admin_api/views/webhook_view.ex b/lib/pleroma/web/admin_api/views/webhook_view.ex index 725183029d..552aba4f1e 100644 --- a/lib/pleroma/web/admin_api/views/webhook_view.ex +++ b/lib/pleroma/web/admin_api/views/webhook_view.ex @@ -18,6 +18,7 @@ def render("show.json", %{webhook: webhook}) do events: webhook.events, secret: webhook.secret, enabled: webhook.enabled, + internal: webhook.internal, created_at: Utils.to_masto_date(webhook.inserted_at), updated_at: Utils.to_masto_date(webhook.updated_at) } diff --git a/lib/pleroma/web/api_spec/operations/admin/webhook_operation.ex b/lib/pleroma/web/api_spec/operations/admin/webhook_operation.ex index 0c4e1797fb..3323228dbe 100644 --- a/lib/pleroma/web/api_spec/operations/admin/webhook_operation.ex +++ b/lib/pleroma/web/api_spec/operations/admin/webhook_operation.ex @@ -5,6 +5,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.WebhookOperation do alias OpenApiSpex.Operation alias OpenApiSpex.Schema + alias Pleroma.Web.ApiSpec.Schemas.ApiError import Pleroma.Web.ApiSpec.Helpers @@ -88,7 +89,8 @@ def update_operation do } ), responses: %{ - 200 => Operation.response("Webhook", "application/json", webhook()) + 200 => Operation.response("Webhook", "application/json", webhook()), + 403 => Operation.response("Forbidden", "application/json", ApiError) } } end @@ -101,7 +103,8 @@ def delete_operation do security: [%{"oAuth" => ["admin:write"]}], parameters: [id_param()], responses: %{ - 200 => Operation.response("Webhook", "application/json", webhook()) + 200 => Operation.response("Webhook", "application/json", webhook()), + 403 => Operation.response("Forbidden", "application/json", ApiError) } } end @@ -114,7 +117,8 @@ def enable_operation do security: [%{"oAuth" => ["admin:write"]}], parameters: [id_param()], responses: %{ - 200 => Operation.response("Webhook", "application/json", webhook()) + 200 => Operation.response("Webhook", "application/json", webhook()), + 403 => Operation.response("Forbidden", "application/json", ApiError) } } end @@ -127,7 +131,8 @@ def disable_operation do security: [%{"oAuth" => ["admin:write"]}], parameters: [id_param()], responses: %{ - 200 => Operation.response("Webhook", "application/json", webhook()) + 200 => Operation.response("Webhook", "application/json", webhook()), + 403 => Operation.response("Forbidden", "application/json", ApiError) } } end @@ -140,7 +145,8 @@ def rotate_secret_operation do security: [%{"oAuth" => ["admin:write"]}], parameters: [id_param()], responses: %{ - 200 => Operation.response("Webhook", "application/json", webhook()) + 200 => Operation.response("Webhook", "application/json", webhook()), + 403 => Operation.response("Forbidden", "application/json", ApiError) } } end @@ -156,6 +162,7 @@ defp webhook do events: event_type(), secret: %Schema{type: :string}, enabled: %Schema{type: :boolean}, + internal: %Schema{type: :boolean}, created_at: %Schema{type: :string, format: :"date-time"}, updated_at: %Schema{type: :string, format: :"date-time"} }, @@ -165,6 +172,7 @@ defp webhook do "events" => ["report.created"], "secret" => "D3D8CF4BC11FD9C41FD34DCC38D282E451C8BD34", "enabled" => true, + "internal" => false, "created_at" => "2022-06-24T16:19:38.523Z", "updated_at" => "2022-06-24T16:19:38.523Z" } diff --git a/lib/pleroma/webhook.ex b/lib/pleroma/webhook.ex index d257f9d8cc..f22c00f623 100644 --- a/lib/pleroma/webhook.ex +++ b/lib/pleroma/webhook.ex @@ -18,6 +18,7 @@ defmodule Pleroma.Webhook do field(:events, {:array, Ecto.Enum}, values: @event_types, null: false, default: []) field(:secret, :string, null: false, default: "") field(:enabled, :boolean, null: false, default: true) + field(:internal, :boolean, null: false, default: false) timestamps() end @@ -32,7 +33,7 @@ def get_by_type(type) do def changeset(%__MODULE__{} = webhook, params) do webhook - |> cast(params, [:url, :events, :enabled]) + |> cast(params, [:url, :events, :enabled, :internal]) |> validate_required([:url, :events]) |> unique_constraint(:url) |> strip_events() @@ -41,7 +42,7 @@ def changeset(%__MODULE__{} = webhook, params) do def update_changeset(%__MODULE__{} = webhook, params \\ %{}) do webhook - |> cast(params, [:url, :events, :enabled]) + |> cast(params, [:url, :events, :enabled, :internal]) |> unique_constraint(:url) |> strip_events() end diff --git a/priv/repo/migrations/20221029171353_add_internal_to_webhooks.exs b/priv/repo/migrations/20221029171353_add_internal_to_webhooks.exs new file mode 100644 index 0000000000..8c8ce7fc74 --- /dev/null +++ b/priv/repo/migrations/20221029171353_add_internal_to_webhooks.exs @@ -0,0 +1,13 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Repo.Migrations.AddInternalToWebhooks do + use Ecto.Migration + + def change do + alter table(:webhooks) do + add(:internal, :boolean, default: false, null: false) + end + end +end diff --git a/test/pleroma/web/admin_api/controllers/webhook_controller_test.exs b/test/pleroma/web/admin_api/controllers/webhook_controller_test.exs index 6a1586ff1d..0cd00e7534 100644 --- a/test/pleroma/web/admin_api/controllers/webhook_controller_test.exs +++ b/test/pleroma/web/admin_api/controllers/webhook_controller_test.exs @@ -64,6 +64,20 @@ test "edits a webhook", %{conn: conn} do assert %{events: [:"report.created", :"account.created"]} = Webhook.get(id) end + + test "can't edit an internal webhook", %{conn: conn} do + %{id: id} = + Webhook.create(%{url: "https://example.com/webhook1", events: [], internal: true}) + + conn + |> put_req_header("content-type", "application/json") + |> patch("/api/pleroma/admin/webhooks/#{id}", %{ + events: ["report.created", "account.created"] + }) + |> json_response_and_validate_schema(:forbidden) + + assert %{events: []} = Webhook.get(id) + end end describe "DELETE /api/pleroma/admin/webhooks" do