diff --git a/lib/pleroma/activity_expiration.ex b/lib/pleroma/activity_expiration.ex index a0af5255b..bf57abca4 100644 --- a/lib/pleroma/activity_expiration.ex +++ b/lib/pleroma/activity_expiration.ex @@ -14,6 +14,7 @@ defmodule Pleroma.ActivityExpiration do import Ecto.Query @type t :: %__MODULE__{} + @min_activity_lifetime :timer.hours(1) schema "activity_expirations" do belongs_to(:activity, Activity, type: FlakeId) @@ -24,6 +25,7 @@ defmodule Pleroma.ActivityExpiration do expiration |> cast(attrs, [:scheduled_at]) |> validate_required([:scheduled_at]) + |> validate_scheduled_at() end def get_by_activity_id(activity_id) do @@ -47,4 +49,20 @@ defmodule Pleroma.ActivityExpiration do |> where([exp], exp.scheduled_at < ^naive_datetime) |> Repo.all() end + + def validate_scheduled_at(changeset) do + validate_change(changeset, :scheduled_at, fn _, scheduled_at -> + if not expires_late_enough?(scheduled_at) do + [scheduled_at: "an ephemeral activity must live for at least one hour"] + else + [] + end + end) + end + + def expires_late_enough?(scheduled_at) do + now = NaiveDateTime.utc_now() + diff = NaiveDateTime.diff(scheduled_at, now, :millisecond) + diff >= @min_activity_lifetime + end end diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex index 0f287af4e..261d60392 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -196,6 +196,16 @@ defmodule Pleroma.Web.CommonAPI do end end + defp check_expiry_date(expiry_str) do + {:ok, expiry} = Ecto.Type.cast(:naive_datetime, expiry_str) + + if is_nil(expiry) || ActivityExpiration.expires_late_enough?(expiry) do + {:ok, expiry} + else + {:error, "Expiry date is too soon"} + end + end + def post(user, %{"status" => status} = data) do limit = Pleroma.Config.get([:instance, :limit]) @@ -219,7 +229,7 @@ defmodule Pleroma.Web.CommonAPI do context <- make_context(in_reply_to), cw <- data["spoiler_text"] || "", sensitive <- data["sensitive"] || Enum.member?(tags, {"#nsfw", "nsfw"}), - {:ok, expires_at} <- Ecto.Type.cast(:naive_datetime, data["expires_at"]), + {:ok, expires_at} <- check_expiry_date(data["expires_at"]), full_payload <- String.trim(status <> cw), :ok <- validate_character_limit(full_payload, attachments, limit), object <- @@ -258,7 +268,7 @@ defmodule Pleroma.Web.CommonAPI do if expires_at do with {:ok, activity} <- result do - ActivityExpiration.create(activity, expires_at) + {:ok, _} = ActivityExpiration.create(activity, expires_at) end end diff --git a/test/activity_expiration_test.exs b/test/activity_expiration_test.exs index 20566a186..4948fae16 100644 --- a/test/activity_expiration_test.exs +++ b/test/activity_expiration_test.exs @@ -18,4 +18,10 @@ defmodule Pleroma.ActivityExpirationTest do assert length(expirations) == 1 assert hd(expirations) == expiration_due end + + test "denies expirations that don't live long enough" do + activity = insert(:note_activity) + now = NaiveDateTime.utc_now() + assert {:error, _} = ActivityExpiration.create(activity, now) + end end diff --git a/test/support/factory.ex b/test/support/factory.ex index 63fe3a66d..7a2ddcada 100644 --- a/test/support/factory.ex +++ b/test/support/factory.ex @@ -158,7 +158,7 @@ defmodule Pleroma.Factory do end def expiration_in_the_future_factory(attrs \\ %{}) do - expiration_offset_by_minutes(attrs, 60) + expiration_offset_by_minutes(attrs, 61) end def article_activity_factory do