Add internal (not managable from API) webhooks
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
parent
ad014adc1c
commit
1b9b03b494
7 changed files with 62 additions and 12 deletions
|
@ -11,6 +11,12 @@ def call(conn, {:error, :not_found}) do
|
||||||
|> json(%{error: dgettext("errors", "Not found")})
|
|> json(%{error: dgettext("errors", "Not found")})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def call(conn, {:error, :forbidden}) do
|
||||||
|
conn
|
||||||
|
|> put_status(:forbidden)
|
||||||
|
|> json(%{error: dgettext("errors", "Forbidden")})
|
||||||
|
end
|
||||||
|
|
||||||
def call(conn, {:error, reason}) do
|
def call(conn, {:error, reason}) do
|
||||||
conn
|
conn
|
||||||
|> put_status(:bad_request)
|
|> put_status(:bad_request)
|
||||||
|
|
|
@ -46,42 +46,49 @@ def create(%{body_params: params} = conn, _) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def update(%{body_params: params} = conn, %{id: id}) do
|
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
|
webhook <- Webhook.update(webhook, params) do
|
||||||
render(conn, "show.json", webhook: webhook)
|
render(conn, "show.json", webhook: webhook)
|
||||||
|
else
|
||||||
|
%Webhook{internal: true} -> {:error, :forbidden}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def delete(conn, %{id: id}) do
|
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
|
{:ok, webhook} <- Webhook.delete(webhook) do
|
||||||
render(conn, "show.json", webhook: webhook)
|
render(conn, "show.json", webhook: webhook)
|
||||||
|
else
|
||||||
|
%Webhook{internal: true} -> {:error, :forbidden}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def enable(conn, %{id: id}) do
|
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
|
{:ok, webhook} <- Webhook.set_enabled(webhook, true) do
|
||||||
render(conn, "show.json", webhook: webhook)
|
render(conn, "show.json", webhook: webhook)
|
||||||
else
|
else
|
||||||
|
%Webhook{internal: true} -> {:error, :forbidden}
|
||||||
nil -> {:error, :not_found}
|
nil -> {:error, :not_found}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def disable(conn, %{id: id}) do
|
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
|
{:ok, webhook} <- Webhook.set_enabled(webhook, false) do
|
||||||
render(conn, "show.json", webhook: webhook)
|
render(conn, "show.json", webhook: webhook)
|
||||||
else
|
else
|
||||||
|
%Webhook{internal: true} -> {:error, :forbidden}
|
||||||
nil -> {:error, :not_found}
|
nil -> {:error, :not_found}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def rotate_secret(conn, %{id: id}) do
|
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
|
{:ok, webhook} <- Webhook.rotate_secret(webhook) do
|
||||||
render(conn, "show.json", webhook: webhook)
|
render(conn, "show.json", webhook: webhook)
|
||||||
else
|
else
|
||||||
|
%Webhook{internal: true} -> {:error, :forbidden}
|
||||||
nil -> {:error, :not_found}
|
nil -> {:error, :not_found}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -18,6 +18,7 @@ def render("show.json", %{webhook: webhook}) do
|
||||||
events: webhook.events,
|
events: webhook.events,
|
||||||
secret: webhook.secret,
|
secret: webhook.secret,
|
||||||
enabled: webhook.enabled,
|
enabled: webhook.enabled,
|
||||||
|
internal: webhook.internal,
|
||||||
created_at: Utils.to_masto_date(webhook.inserted_at),
|
created_at: Utils.to_masto_date(webhook.inserted_at),
|
||||||
updated_at: Utils.to_masto_date(webhook.updated_at)
|
updated_at: Utils.to_masto_date(webhook.updated_at)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
defmodule Pleroma.Web.ApiSpec.Admin.WebhookOperation do
|
defmodule Pleroma.Web.ApiSpec.Admin.WebhookOperation do
|
||||||
alias OpenApiSpex.Operation
|
alias OpenApiSpex.Operation
|
||||||
alias OpenApiSpex.Schema
|
alias OpenApiSpex.Schema
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
||||||
|
|
||||||
import Pleroma.Web.ApiSpec.Helpers
|
import Pleroma.Web.ApiSpec.Helpers
|
||||||
|
|
||||||
|
@ -88,7 +89,8 @@ def update_operation do
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
responses: %{
|
responses: %{
|
||||||
200 => Operation.response("Webhook", "application/json", webhook())
|
200 => Operation.response("Webhook", "application/json", webhook()),
|
||||||
|
403 => Operation.response("Forbidden", "application/json", ApiError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
@ -101,7 +103,8 @@ def delete_operation do
|
||||||
security: [%{"oAuth" => ["admin:write"]}],
|
security: [%{"oAuth" => ["admin:write"]}],
|
||||||
parameters: [id_param()],
|
parameters: [id_param()],
|
||||||
responses: %{
|
responses: %{
|
||||||
200 => Operation.response("Webhook", "application/json", webhook())
|
200 => Operation.response("Webhook", "application/json", webhook()),
|
||||||
|
403 => Operation.response("Forbidden", "application/json", ApiError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
@ -114,7 +117,8 @@ def enable_operation do
|
||||||
security: [%{"oAuth" => ["admin:write"]}],
|
security: [%{"oAuth" => ["admin:write"]}],
|
||||||
parameters: [id_param()],
|
parameters: [id_param()],
|
||||||
responses: %{
|
responses: %{
|
||||||
200 => Operation.response("Webhook", "application/json", webhook())
|
200 => Operation.response("Webhook", "application/json", webhook()),
|
||||||
|
403 => Operation.response("Forbidden", "application/json", ApiError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
@ -127,7 +131,8 @@ def disable_operation do
|
||||||
security: [%{"oAuth" => ["admin:write"]}],
|
security: [%{"oAuth" => ["admin:write"]}],
|
||||||
parameters: [id_param()],
|
parameters: [id_param()],
|
||||||
responses: %{
|
responses: %{
|
||||||
200 => Operation.response("Webhook", "application/json", webhook())
|
200 => Operation.response("Webhook", "application/json", webhook()),
|
||||||
|
403 => Operation.response("Forbidden", "application/json", ApiError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
@ -140,7 +145,8 @@ def rotate_secret_operation do
|
||||||
security: [%{"oAuth" => ["admin:write"]}],
|
security: [%{"oAuth" => ["admin:write"]}],
|
||||||
parameters: [id_param()],
|
parameters: [id_param()],
|
||||||
responses: %{
|
responses: %{
|
||||||
200 => Operation.response("Webhook", "application/json", webhook())
|
200 => Operation.response("Webhook", "application/json", webhook()),
|
||||||
|
403 => Operation.response("Forbidden", "application/json", ApiError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
@ -156,6 +162,7 @@ defp webhook do
|
||||||
events: event_type(),
|
events: event_type(),
|
||||||
secret: %Schema{type: :string},
|
secret: %Schema{type: :string},
|
||||||
enabled: %Schema{type: :boolean},
|
enabled: %Schema{type: :boolean},
|
||||||
|
internal: %Schema{type: :boolean},
|
||||||
created_at: %Schema{type: :string, format: :"date-time"},
|
created_at: %Schema{type: :string, format: :"date-time"},
|
||||||
updated_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"],
|
"events" => ["report.created"],
|
||||||
"secret" => "D3D8CF4BC11FD9C41FD34DCC38D282E451C8BD34",
|
"secret" => "D3D8CF4BC11FD9C41FD34DCC38D282E451C8BD34",
|
||||||
"enabled" => true,
|
"enabled" => true,
|
||||||
|
"internal" => false,
|
||||||
"created_at" => "2022-06-24T16:19:38.523Z",
|
"created_at" => "2022-06-24T16:19:38.523Z",
|
||||||
"updated_at" => "2022-06-24T16:19:38.523Z"
|
"updated_at" => "2022-06-24T16:19:38.523Z"
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ defmodule Pleroma.Webhook do
|
||||||
field(:events, {:array, Ecto.Enum}, values: @event_types, null: false, default: [])
|
field(:events, {:array, Ecto.Enum}, values: @event_types, null: false, default: [])
|
||||||
field(:secret, :string, null: false, default: "")
|
field(:secret, :string, null: false, default: "")
|
||||||
field(:enabled, :boolean, null: false, default: true)
|
field(:enabled, :boolean, null: false, default: true)
|
||||||
|
field(:internal, :boolean, null: false, default: false)
|
||||||
|
|
||||||
timestamps()
|
timestamps()
|
||||||
end
|
end
|
||||||
|
@ -32,7 +33,7 @@ def get_by_type(type) do
|
||||||
|
|
||||||
def changeset(%__MODULE__{} = webhook, params) do
|
def changeset(%__MODULE__{} = webhook, params) do
|
||||||
webhook
|
webhook
|
||||||
|> cast(params, [:url, :events, :enabled])
|
|> cast(params, [:url, :events, :enabled, :internal])
|
||||||
|> validate_required([:url, :events])
|
|> validate_required([:url, :events])
|
||||||
|> unique_constraint(:url)
|
|> unique_constraint(:url)
|
||||||
|> strip_events()
|
|> strip_events()
|
||||||
|
@ -41,7 +42,7 @@ def changeset(%__MODULE__{} = webhook, params) do
|
||||||
|
|
||||||
def update_changeset(%__MODULE__{} = webhook, params \\ %{}) do
|
def update_changeset(%__MODULE__{} = webhook, params \\ %{}) do
|
||||||
webhook
|
webhook
|
||||||
|> cast(params, [:url, :events, :enabled])
|
|> cast(params, [:url, :events, :enabled, :internal])
|
||||||
|> unique_constraint(:url)
|
|> unique_constraint(:url)
|
||||||
|> strip_events()
|
|> strip_events()
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# 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
|
|
@ -64,6 +64,20 @@ test "edits a webhook", %{conn: conn} do
|
||||||
|
|
||||||
assert %{events: [:"report.created", :"account.created"]} = Webhook.get(id)
|
assert %{events: [:"report.created", :"account.created"]} = Webhook.get(id)
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
describe "DELETE /api/pleroma/admin/webhooks" do
|
describe "DELETE /api/pleroma/admin/webhooks" do
|
||||||
|
|
Loading…
Reference in a new issue