diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/pleroma/object/containment.ex | 2 | ||||
| -rw-r--r-- | lib/pleroma/web/activity_pub/activity_pub.ex | 13 | ||||
| -rw-r--r-- | lib/pleroma/web/activity_pub/builder.ex | 15 | ||||
| -rw-r--r-- | lib/pleroma/web/activity_pub/object_validator.ex | 6 | ||||
| -rw-r--r-- | lib/pleroma/web/activity_pub/object_validators/answer_validator.ex | 7 | ||||
| -rw-r--r-- | lib/pleroma/web/activity_pub/object_validators/common_validations.ex | 38 | ||||
| -rw-r--r-- | lib/pleroma/web/activity_pub/object_validators/create_generic_validator.ex (renamed from lib/pleroma/web/activity_pub/object_validators/create_question_validator.ex) | 39 | ||||
| -rw-r--r-- | lib/pleroma/web/activity_pub/object_validators/question_validator.ex | 28 | ||||
| -rw-r--r-- | lib/pleroma/web/activity_pub/side_effects.ex | 30 | ||||
| -rw-r--r-- | lib/pleroma/web/activity_pub/transmogrifier.ex | 49 | ||||
| -rw-r--r-- | lib/pleroma/web/common_api/common_api.ex | 25 | ||||
| -rw-r--r-- | lib/pleroma/web/common_api/utils.ex | 11 | 
12 files changed, 189 insertions, 74 deletions
| diff --git a/lib/pleroma/object/containment.ex b/lib/pleroma/object/containment.ex index 99608b8a5..bc88e8a0c 100644 --- a/lib/pleroma/object/containment.ex +++ b/lib/pleroma/object/containment.ex @@ -55,7 +55,7 @@ defmodule Pleroma.Object.Containment do    defp compare_uris(_id_uri, _other_uri), do: :error    @doc """ -  Checks that an imported AP object's actor matches the domain it came from. +  Checks that an imported AP object's actor matches the host it came from.    """    def contain_origin(_id, %{"actor" => nil}), do: :error diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index d8cc8d24f..9d13a06c4 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -66,7 +66,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do    defp check_remote_limit(_), do: true -  defp increase_note_count_if_public(actor, object) do +  def increase_note_count_if_public(actor, object) do      if is_public?(object), do: User.increase_note_count(actor), else: {:ok, actor}    end @@ -85,16 +85,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do    defp increase_replies_count_if_reply(_create_data), do: :noop -  defp increase_poll_votes_if_vote(%{ -         "object" => %{"inReplyTo" => reply_ap_id, "name" => name}, -         "type" => "Create", -         "actor" => actor -       }) do -    Object.increase_vote_count(reply_ap_id, name, actor) -  end - -  defp increase_poll_votes_if_vote(_create_data), do: :noop -    @object_types ["ChatMessage", "Question", "Answer"]    @spec persist(map(), keyword()) :: {:ok, Activity.t() | Object.t()}    def persist(%{"type" => type} = object, meta) when type in @object_types do @@ -258,7 +248,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do      with {:ok, activity} <- insert(create_data, local, fake),           {:fake, false, activity} <- {:fake, fake, activity},           _ <- increase_replies_count_if_reply(create_data), -         _ <- increase_poll_votes_if_vote(create_data),           {:quick_insert, false, activity} <- {:quick_insert, quick_insert?, activity},           {:ok, _actor} <- increase_note_count_if_public(actor, activity),           _ <- notify_and_stream(activity), diff --git a/lib/pleroma/web/activity_pub/builder.ex b/lib/pleroma/web/activity_pub/builder.ex index d5f3610ed..e97381954 100644 --- a/lib/pleroma/web/activity_pub/builder.ex +++ b/lib/pleroma/web/activity_pub/builder.ex @@ -115,6 +115,21 @@ defmodule Pleroma.Web.ActivityPub.Builder do      end    end +  def answer(user, object, name) do +    {:ok, +     %{ +       "type" => "Answer", +       "actor" => user.ap_id, +       "cc" => [object.data["actor"]], +       "to" => [], +       "name" => name, +       "inReplyTo" => object.data["id"], +       "context" => object.data["context"], +       "published" => DateTime.utc_now() |> DateTime.to_iso8601(), +       "id" => Utils.generate_object_id() +     }, []} +  end +    @spec tombstone(String.t(), String.t()) :: {:ok, map(), keyword()}    def tombstone(actor, id) do      {:ok, diff --git a/lib/pleroma/web/activity_pub/object_validator.ex b/lib/pleroma/web/activity_pub/object_validator.ex index c89311187..a24aaf00c 100644 --- a/lib/pleroma/web/activity_pub/object_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validator.ex @@ -17,7 +17,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do    alias Pleroma.Web.ActivityPub.ObjectValidators.BlockValidator    alias Pleroma.Web.ActivityPub.ObjectValidators.ChatMessageValidator    alias Pleroma.Web.ActivityPub.ObjectValidators.CreateChatMessageValidator -  alias Pleroma.Web.ActivityPub.ObjectValidators.CreateQuestionValidator +  alias Pleroma.Web.ActivityPub.ObjectValidators.CreateGenericValidator    alias Pleroma.Web.ActivityPub.ObjectValidators.DeleteValidator    alias Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator    alias Pleroma.Web.ActivityPub.ObjectValidators.FollowValidator @@ -162,7 +162,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do           meta = Keyword.put(meta, :object_data, object_data |> stringify_keys),           {:ok, create_activity} <-             create_activity -           |> CreateQuestionValidator.cast_and_validate(meta) +           |> CreateGenericValidator.cast_and_validate(meta)             |> Ecto.Changeset.apply_action(:insert) do        create_activity = stringify_keys(create_activity)        {:ok, create_activity, meta} @@ -188,7 +188,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do    end    def cast_and_apply(%{"type" => "Answer"} = object) do -    QuestionValidator.cast_and_apply(object) +    AnswerValidator.cast_and_apply(object)    end    def cast_and_apply(o), do: {:error, {:validator_not_set, o}} diff --git a/lib/pleroma/web/activity_pub/object_validators/answer_validator.ex b/lib/pleroma/web/activity_pub/object_validators/answer_validator.ex index 0b51eccfa..8d4c92520 100644 --- a/lib/pleroma/web/activity_pub/object_validators/answer_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/answer_validator.ex @@ -13,22 +13,25 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnswerValidator do    @primary_key false    @derive Jason.Encoder -  # Extends from NoteValidator    embedded_schema do      field(:id, Types.ObjectID, primary_key: true)      field(:to, {:array, :string}, default: [])      field(:cc, {:array, :string}, default: []) + +    # is this actually needed?      field(:bto, {:array, :string}, default: [])      field(:bcc, {:array, :string}, default: []) +      field(:type, :string)      field(:name, :string)      field(:inReplyTo, :string)      field(:attributedTo, Types.ObjectID) +    field(:actor, Types.ObjectID)    end    def cast_and_apply(data) do      data -    |> cast_data +    |> cast_data()      |> apply_action(:insert)    end diff --git a/lib/pleroma/web/activity_pub/object_validators/common_validations.ex b/lib/pleroma/web/activity_pub/object_validators/common_validations.ex index e746b9360..140555a45 100644 --- a/lib/pleroma/web/activity_pub/object_validators/common_validations.ex +++ b/lib/pleroma/web/activity_pub/object_validators/common_validations.ex @@ -42,6 +42,19 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations do      end)    end +  def validate_actor_is_active(cng, options \\ []) do +    field_name = Keyword.get(options, :field_name, :actor) + +    cng +    |> validate_change(field_name, fn field_name, actor -> +      if %User{deactivated: false} = User.get_cached_by_ap_id(actor) do +        [] +      else +        [{field_name, "can't find user (or deactivated)"}] +      end +    end) +  end +    def validate_object_presence(cng, options \\ []) do      field_name = Keyword.get(options, :field_name, :object)      allowed_types = Keyword.get(options, :allowed_types, false) @@ -77,4 +90,29 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations do      if actor_cng.valid?, do: actor_cng, else: object_cng    end + +  def validate_host_match(cng, fields \\ [:id, :actor]) do +    unique_hosts = +      fields +      |> Enum.map(fn field -> +        %URI{host: host} = +          cng +          |> get_field(field) +          |> URI.parse() + +        host +      end) +      |> Enum.uniq() +      |> Enum.count() + +    if unique_hosts == 1 do +      cng +    else +      fields +      |> Enum.reduce(cng, fn field, cng -> +        cng +        |> add_error(field, "hosts of #{inspect(fields)} aren't matching") +      end) +    end +  end  end diff --git a/lib/pleroma/web/activity_pub/object_validators/create_question_validator.ex b/lib/pleroma/web/activity_pub/object_validators/create_generic_validator.ex index 6d3f71566..4ad4ca0de 100644 --- a/lib/pleroma/web/activity_pub/object_validators/create_question_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/create_generic_validator.ex @@ -4,9 +4,8 @@  # Code based on CreateChatMessageValidator  # NOTES -# - Can probably be a generic create validator  # - doesn't embed, will only get the object id -defmodule Pleroma.Web.ActivityPub.ObjectValidators.CreateQuestionValidator do +defmodule Pleroma.Web.ActivityPub.ObjectValidators.CreateGenericValidator do    use Ecto.Schema    alias Pleroma.Object @@ -26,29 +25,53 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CreateQuestionValidator do      field(:object, Types.ObjectID)    end +  def cast_data(data) do +    %__MODULE__{} +    |> changeset(data) +  end +    def cast_and_apply(data) do      data      |> cast_data      |> apply_action(:insert)    end -  def cast_data(data) do -    cast(%__MODULE__{}, data, __schema__(:fields)) -  end -    def cast_and_validate(data, meta \\ []) do -    cast_data(data) +    data +    |> cast_data      |> validate_data(meta)    end +  def changeset(struct, data) do +    struct +    |> cast(data, __schema__(:fields)) +  end +    def validate_data(cng, meta \\ []) do      cng      |> validate_required([:actor, :type, :object])      |> validate_inclusion(:type, ["Create"]) -    |> validate_actor_presence() +    |> validate_actor_is_active()      |> validate_any_presence([:to, :cc])      |> validate_actors_match(meta)      |> validate_object_nonexistence() +    |> validate_object_containment() +  end + +  def validate_object_containment(cng) do +    actor = get_field(cng, :actor) + +    cng +    |> validate_change(:object, fn :object, object_id -> +      %URI{host: object_id_host} = URI.parse(object_id) +      %URI{host: actor_host} = URI.parse(actor) + +      if object_id_host == actor_host do +        [] +      else +        [{:object, "The host of the object id doesn't match with the host of the actor"}] +      end +    end)    end    def validate_object_nonexistence(cng) do diff --git a/lib/pleroma/web/activity_pub/object_validators/question_validator.ex b/lib/pleroma/web/activity_pub/object_validators/question_validator.ex index 211f520c4..f7f3b1354 100644 --- a/lib/pleroma/web/activity_pub/object_validators/question_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/question_validator.ex @@ -5,6 +5,7 @@  defmodule Pleroma.Web.ActivityPub.ObjectValidators.QuestionValidator do    use Ecto.Schema +  alias Pleroma.Web.ActivityPub.Utils    alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations    alias Pleroma.Web.ActivityPub.ObjectValidators.QuestionOptionsValidator    alias Pleroma.Web.ActivityPub.ObjectValidators.Types @@ -40,13 +41,12 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.QuestionValidator do      field(:announcement_count, :integer, default: 0)      field(:inReplyTo, :string)      field(:uri, Types.Uri) +    # short identifier for PleromaFE to group statuses by context +    field(:context_id, :integer)      field(:likes, {:array, :string}, default: [])      field(:announcements, {:array, :string}, default: []) -    # see if needed -    field(:context_id, :string) -      field(:closed, Types.DateTime)      field(:voters, {:array, Types.ObjectID}, default: [])      embeds_many(:anyOf, QuestionOptionsValidator) @@ -70,7 +70,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.QuestionValidator do      |> changeset(data)    end -  def fix(data) do +  defp fix_closed(data) do      cond do        is_binary(data["closed"]) -> data        is_binary(data["endTime"]) -> Map.put(data, "closed", data["endTime"]) @@ -78,6 +78,23 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.QuestionValidator do      end    end +  # based on Pleroma.Web.ActivityPub.Utils.lazy_put_objects_defaults +  defp fix_defaults(data) do +    %{data: %{"id" => context}, id: context_id} = Utils.create_context(data["context"]) + +    data +    |> Map.put_new_lazy("id", &Utils.generate_object_id/0) +    |> Map.put_new_lazy("published", &Utils.make_date/0) +    |> Map.put_new("context", context) +    |> Map.put_new("context_id", context_id) +  end + +  defp fix(data) do +    data +    |> fix_closed() +    |> fix_defaults() +  end +    def changeset(struct, data) do      data = fix(data) @@ -92,7 +109,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.QuestionValidator do      |> validate_inclusion(:type, ["Question"])      |> validate_required([:id, :actor, :type, :content, :context])      |> CommonValidations.validate_any_presence([:cc, :to]) -    |> CommonValidations.validate_actor_presence() +    |> CommonValidations.validate_actor_is_active()      |> CommonValidations.validate_any_presence([:oneOf, :anyOf]) +    |> CommonValidations.validate_host_match()    end  end diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex index a78ec411f..c17197bec 100644 --- a/lib/pleroma/web/activity_pub/side_effects.ex +++ b/lib/pleroma/web/activity_pub/side_effects.ex @@ -7,6 +7,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do    """    alias Pleroma.Activity    alias Pleroma.Activity.Ir.Topics +  alias Pleroma.ActivityExpiration    alias Pleroma.Chat    alias Pleroma.Chat.MessageReference    alias Pleroma.FollowingRelationship @@ -19,6 +20,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do    alias Pleroma.Web.ActivityPub.Utils    alias Pleroma.Web.Push    alias Pleroma.Web.Streamer +  alias Pleroma.Workers.BackgroundWorker    def handle(object, meta \\ []) @@ -135,10 +137,24 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do    # Tasks this handles    # - Actually create object    # - Rollback if we couldn't create it +  # - Increase the user note count +  # - Increase the reply count    # - Set up notifications    def handle(%{data: %{"type" => "Create"}} = activity, meta) do -    with {:ok, _object, meta} <- handle_object_creation(meta[:object_data], meta) do +    with {:ok, object, meta} <- handle_object_creation(meta[:object_data], meta), +         %User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do        {:ok, notifications} = Notification.create_notifications(activity, do_send: false) +      {:ok, _user} = ActivityPub.increase_note_count_if_public(user, object) + +      if in_reply_to = object.data["inReplyTo"] do +        Object.increase_replies_count(in_reply_to) +      end + +      if expires_at = activity.data["expires_at"] do +        ActivityExpiration.create(activity, expires_at) +      end + +      BackgroundWorker.enqueue("fetch_data_for_activity", %{"activity_id" => activity.id})        meta =          meta @@ -268,6 +284,18 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do      end    end +  def handle_object_creation(%{"type" => "Answer"} = object_map, meta) do +    with {:ok, object, meta} <- Pipeline.common_pipeline(object_map, meta) do +      Object.increase_vote_count( +        object.data["inReplyTo"], +        object.data["name"], +        object.data["actor"] +      ) + +      {:ok, object, meta} +    end +  end +    def handle_object_creation(%{"type" => "Question"} = object, meta) do      with {:ok, object, meta} <- Pipeline.common_pipeline(object, meta) do        {:ok, object, meta} diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 9900602e4..26325d5de 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -419,6 +419,29 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do      end)    end +  # Compatibility wrapper for Mastodon votes +  defp handle_create(%{"object" => %{"type" => "Answer"}} = data, _user) do +    handle_incoming(data) +  end + +  defp handle_create(%{"object" => object} = data, user) do +    %{ +      to: data["to"], +      object: object, +      actor: user, +      context: object["context"], +      local: false, +      published: data["published"], +      additional: +        Map.take(data, [ +          "cc", +          "directMessage", +          "id" +        ]) +    } +    |> ActivityPub.create() +  end +    def handle_incoming(data, options \\ [])    # Flag objects are placed ahead of the ID check because Mastodon 2.8 and earlier send them @@ -461,26 +484,14 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do      actor = Containment.get_actor(data)      with nil <- Activity.get_create_by_object_ap_id(object["id"]), -         {:ok, %User{} = user} <- User.get_or_fetch_by_ap_id(actor), -         data <- Map.put(data, "actor", actor) |> fix_addressing() do -      object = fix_object(object, options) - -      params = %{ -        to: data["to"], -        object: object, -        actor: user, -        context: object["context"], -        local: false, -        published: data["published"], -        additional: -          Map.take(data, [ -            "cc", -            "directMessage", -            "id" -          ]) -      } +         {:ok, %User{} = user} <- User.get_or_fetch_by_ap_id(actor) do +      data = +        data +        |> Map.put("object", fix_object(object, options)) +        |> Map.put("actor", actor) +        |> fix_addressing() -      with {:ok, created_activity} <- ActivityPub.create(params) do +      with {:ok, created_activity} <- handle_create(data, user) do          reply_depth = (options[:depth] || 0) + 1          if Federator.allowed_thread_distance?(reply_depth) do diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex index 692ceab1e..c08e0ffeb 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -308,18 +308,19 @@ defmodule Pleroma.Web.CommonAPI do           {:ok, options, choices} <- normalize_and_validate_choices(choices, object) do        answer_activities =          Enum.map(choices, fn index -> -          answer_data = make_answer_data(user, object, Enum.at(options, index)["name"]) - -          {:ok, activity} = -            ActivityPub.create(%{ -              to: answer_data["to"], -              actor: user, -              context: object.data["context"], -              object: answer_data, -              additional: %{"cc" => answer_data["cc"]} -            }) - -          activity +          {:ok, answer_object, _meta} = +            Builder.answer(user, object, Enum.at(options, index)["name"]) + +          {:ok, activity_data, _meta} = Builder.create(user, answer_object, []) + +          {:ok, activity, _meta} = +            activity_data +            |> Map.put("cc", answer_object["cc"]) +            |> Map.put("context", answer_object["context"]) +            |> Pipeline.common_pipeline(local: true) + +          # TODO: Do preload of Pleroma.Object in Pipeline +          Activity.normalize(activity.data)          end)        object = Object.get_cached_by_ap_id(object.data["id"]) diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex index 9c38b73eb..9d7b24eb2 100644 --- a/lib/pleroma/web/common_api/utils.ex +++ b/lib/pleroma/web/common_api/utils.ex @@ -548,17 +548,6 @@ defmodule Pleroma.Web.CommonAPI.Utils do      end    end -  def make_answer_data(%User{ap_id: ap_id}, object, name) do -    %{ -      "type" => "Answer", -      "actor" => ap_id, -      "cc" => [object.data["actor"]], -      "to" => [], -      "name" => name, -      "inReplyTo" => object.data["id"] -    } -  end -    def validate_character_limit("" = _full_payload, [] = _attachments) do      {:error, dgettext("errors", "Cannot post an empty status without attachments")}    end | 
