forked from AkkomaGang/akkoma
Oneric
f07eb4cb55
In order to properly process incoming notes we need to be able to map the key id back to an actor. Also, check collections actually belong to the same server. Key ids of Hubzilla and Bridgy samples were updated to what modern versions of those output. If anything still uses the old format, we would not be able to verify their posts anyway.
93 lines
2.6 KiB
Elixir
93 lines
2.6 KiB
Elixir
# Akkoma: Magically expressive social media
|
|
# Copyright © 2024 Akkoma Authors <https://akkoma.dev/>
|
|
# SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
defmodule Pleroma.Web.ActivityPub.ObjectValidators.UserValidator do
|
|
@moduledoc """
|
|
Checks whether ActivityPub data represents a valid user
|
|
|
|
Users don't go through the same ingest pipeline like activities or other objects.
|
|
To ensure this can only match a user and no users match in the other pipeline,
|
|
this is a separate from the generic ObjectValidator.
|
|
"""
|
|
|
|
@behaviour Pleroma.Web.ActivityPub.ObjectValidator.Validating
|
|
|
|
alias Pleroma.Object.Containment
|
|
alias Pleroma.Signature
|
|
|
|
@impl true
|
|
def validate(object, meta)
|
|
|
|
def validate(%{"type" => type, "id" => _id} = data, meta)
|
|
when type in ["Person", "Organization", "Group", "Application"] do
|
|
with :ok <- validate_pubkey(data),
|
|
:ok <- validate_inbox(data),
|
|
:ok <- contain_collection_origin(data) do
|
|
{:ok, data, meta}
|
|
else
|
|
{:error, e} -> {:error, e}
|
|
e -> {:error, e}
|
|
end
|
|
end
|
|
|
|
def validate(_, _), do: {:error, "Not a user object"}
|
|
|
|
defp mabye_validate_owner(nil, _actor), do: :ok
|
|
defp mabye_validate_owner(actor, actor), do: :ok
|
|
defp mabye_validate_owner(_owner, _actor), do: :error
|
|
|
|
defp validate_pubkey(
|
|
%{"id" => id, "publicKey" => %{"id" => pk_id, "publicKeyPem" => _key}} = data
|
|
)
|
|
when id != nil do
|
|
with {_, {:ok, kactor}} <- {:key, Signature.key_id_to_actor_id(pk_id)},
|
|
true <- id == kactor,
|
|
:ok <- mabye_validate_owner(Map.get(data, "owner"), id) do
|
|
:ok
|
|
else
|
|
{:key, _} ->
|
|
{:error, "Unable to determine actor id from key id"}
|
|
|
|
false ->
|
|
{:error, "Key id does not relate to user id"}
|
|
|
|
_ ->
|
|
{:error, "Actor does not own its public key"}
|
|
end
|
|
end
|
|
|
|
# pubkey is optional atm
|
|
defp validate_pubkey(_data), do: :ok
|
|
|
|
defp validate_inbox(%{"id" => id, "inbox" => inbox}) do
|
|
case Containment.same_origin(id, inbox) do
|
|
:ok -> :ok
|
|
:error -> {:error, "Inbox on different doamin"}
|
|
end
|
|
end
|
|
|
|
defp validate_inbox(_), do: {:error, "No inbox"}
|
|
|
|
defp check_field_value(%{"id" => id} = _data, value) do
|
|
Containment.same_origin(id, value)
|
|
end
|
|
|
|
defp maybe_check_field(data, field) do
|
|
with val when val != nil <- data[field],
|
|
:ok <- check_field_value(data, val) do
|
|
:ok
|
|
else
|
|
nil -> :ok
|
|
_ -> {:error, "#{field} on different domain"}
|
|
end
|
|
end
|
|
|
|
defp contain_collection_origin(data) do
|
|
Enum.reduce(["followers", "following", "featured"], :ok, fn
|
|
field, :ok -> maybe_check_field(data, field)
|
|
_, error -> error
|
|
end)
|
|
end
|
|
end
|