Compare commits

..

No commits in common. "8257c07d26a1d75a5f205bebd1931cbbf512f9d7" and "6f9a4f42f34aacacb69cd4ef75ee0f1703c4fe1e" have entirely different histories.

7 changed files with 47 additions and 101 deletions

View File

@ -19,29 +19,11 @@ defmodule Vonbraun.ActivityPub.Handler do
end) end)
end end
@doc """ @spec handle(%{type: String.t()}, map()) :: :ok | {:ok, atom()} | {:error, any()}
Passed a list of types, gets the first handler that applies to a type. def handle(activity = %{"type" => type}, actor = %{}) when is_binary(type) do
""" Agent.get(__MODULE__, fn map ->
def get_first_matching_handler(types) when is_list(types) do func = Map.get(map, type, fn _, _ -> {:ok, :type} end)
Agent.get(__MODULE__, fn handler_map ->
Enum.reduce_while(types, nil, fn
type, _ ->
case Map.get(handler_map, type) do
nil -> {:cont, nil}
func -> {:halt, func}
end
end)
end)
end
@spec handle(%{type: String.t()}, map()) :: {:ok, atom()} | {:error, any()}
def handle(activity = %{"@type" => types}, actor = %{}) when is_list(types) do
case get_first_matching_handler(types) do
nil ->
{:ok, :ignore}
func ->
apply(func, [activity, actor]) apply(func, [activity, actor])
end end)
end end
end end

View File

@ -5,54 +5,43 @@ defmodule Vonbraun.ActivityPub.Handler.Accept do
alias Vonbraun.Util alias Vonbraun.Util
alias Vonbraun.Ecto.Schema.Actor alias Vonbraun.Ecto.Schema.Actor
@verb "https://www.w3.org/ns/activitystreams#Accept" @verb "Accept"
@follow "https://www.w3.org/ns/activitystreams#Follow"
def type, do: @verb def type, do: @verb
defp is_follow_object?(%{"@type" => types}) when is_list(types), do: @follow in types def extract_follow_object_actor(%{"type" => "Follow", "actor" => actor_id})
when is_binary(actor_id) do
# The actor is specified here so we can just use it. {:ok, actor_id}
@spec extract_follow_object_actor_id(map()) :: :error | {:error, :not_found} | {:ok, binary()}
def extract_follow_object_actor_id(object = %{"actor" => actor_value}) do
if is_follow_object?(object) do
Util.get_only_id(actor_value)
else
{:error, :not_found}
end
end end
# Only the ID was provided, so extract actor from the ID. def extract_follow_object_actor(%{"type" => "Follow", "id" => provided_follow_activity_id})
def extract_follow_object_actor_id(object = %{"@id" => _}) do when is_binary(provided_follow_activity_id) do
domain = Application.fetch_env!(:vonbraun, :domain) domain = Application.fetch_env!(:vonbraun, :domain)
actual_activity_id_prefix = "https://#{domain}/id/follow:" actual_activity_id_prefix = "https://#{domain}/id/follow:"
with {:ok, provided_follow_activity_id} <- Util.get_only_id(object), with ^actual_activity_id_prefix <> actor_id <- provided_follow_activity_id do
^actual_activity_id_prefix <> raw_actor_id <- provided_follow_activity_id do {:ok, actor_id}
{:ok, URI.decode(raw_actor_id)}
else else
_ -> _ ->
{:error, :not_found} {:error, :not_found}
end end
end end
def extract_follow_object_actor_id(_) do def extract_follow_object_actor(_) do
{:error, :not_found} {:error, :not_found}
end end
# Lots of kinds of things can be accepted but for right now only follows. # Lots of kinds of things can be accepted but for right now only follows.
def handle( def handle(
%{ %{
"actor" => actor_value, "type" => @verb,
"object" => object_value "actor" => actor_id,
"object" => object = %{"type" => "Follow"}
}, },
%{} %{}
) do ) do
my_id = Util.my_id() with {:actor, {:ok, follow_actor_id}} <- {:actor, extract_follow_object_actor(object)},
{:match, true} <- {:match, follow_actor_id == Util.my_id()},
with {:object, {:ok, object}} <- {:object, Util.get_only_property(object_value)},
{:actor, {:ok, ^my_id}} <- {:actor, extract_follow_object_actor_id(object)},
{:actor_id, {:ok, actor_id}} <- {:actor_id, Util.get_only_id(actor_value)},
{:asked, {:ok, %Actor{:blocked => nil, :following_state => "accepted"}}} <- {:asked, {:ok, %Actor{:blocked => nil, :following_state => "accepted"}}} <-
{:asked, Actor.mark_pending_follow(actor_id, "accepted", force: true)} do {:asked, Actor.mark_pending_follow(actor_id, "accepted", force: true)} do
Logger.info("Now following: #{actor_id}") Logger.info("Now following: #{actor_id}")
@ -66,7 +55,7 @@ defmodule Vonbraun.ActivityPub.Handler.Accept do
{:asked, {:ok, %Actor{:blocked => nil, :following_state => following_state}}} -> {:asked, {:ok, %Actor{:blocked => nil, :following_state => following_state}}} ->
Logger.error( Logger.error(
"Weird following state after received Accept: #{following_state} from actor: #{inspect(actor_value)} this should not happen." "Weird following state after received Accept: #{following_state} from actor: #{actor_id} this should not happen."
) )
{:error, :following_state} {:error, :following_state}
@ -74,10 +63,7 @@ defmodule Vonbraun.ActivityPub.Handler.Accept do
{:actor, {:error, _error}} -> {:actor, {:error, _error}} ->
{:ok, :unauthorized} {:ok, :unauthorized}
{:actor_id, :error} -> {:match, false} ->
{:ok, :unauthorized}
{:object, :error} ->
{:ok, :unauthorized} {:ok, :unauthorized}
end end
end end

View File

@ -1,7 +1,7 @@
defmodule Vonbraun.ActivityPub.Handler.Delete do defmodule Vonbraun.ActivityPub.Handler.Delete do
@behaviour Vonbraun.ActivityPub.HandlerBehaviour @behaviour Vonbraun.ActivityPub.HandlerBehaviour
@verb "https://www.w3.org/ns/activitystreams#Delete" @verb "Delete"
def type, do: @verb def type, do: @verb

View File

@ -7,23 +7,21 @@ defmodule Vonbraun.ActivityPub.Handler.Follow do
alias Vonbraun.ActivityPub.Object alias Vonbraun.ActivityPub.Object
alias Vonbraun.Util alias Vonbraun.Util
@verb "https://www.w3.org/ns/activitystreams#Follow" @verb "Follow"
def type, do: @verb def type, do: @verb
def handle( def handle(
activity = %{ %{
"actor" => actor_value, "id" => activity_id,
"type" => @verb,
"actor" => follow_requester_id,
"object" => follow_target "object" => follow_target
}, },
actor = %{} actor = %{}
) )
when is_binary(follow_target) do when is_binary(follow_requester_id) and is_binary(follow_target) and is_binary(activity_id) do
my_id = Util.my_id() with {:valid_target, true} <- {:valid_target, Util.my_id() == follow_target},
with {:activity_id, {:ok, activity_id}} <- {:activity_id, Util.get_only_id(activity)},
{:actor_id, {:ok, follow_requester_id}} <- {:actor_id, Util.get_only_id(actor_value)},
{:valid_target, {:ok, ^my_id}} <- {:valid_target, Util.get_only_id(follow_target)},
{:add, {:ok, %Actor{:blocked => nil, :follows_me_state => follows_me_state}}} {:add, {:ok, %Actor{:blocked => nil, :follows_me_state => follows_me_state}}}
when not is_nil(follows_me_state) <- when not is_nil(follows_me_state) <-
{:add, Actor.maybe_add_follower(follow_requester_id)} do {:add, Actor.maybe_add_follower(follow_requester_id)} do
@ -68,13 +66,7 @@ defmodule Vonbraun.ActivityPub.Handler.Follow do
{:ok, :ignored} {:ok, :ignored}
end end
else else
{:actor_id, :error} -> {:valid_target, false} ->
{:ok, :unauthorized}
{:activity_id, :error} ->
{:ok, :unauthorized}
{:valid_target, _} ->
{:ok, :unauthorized} {:ok, :unauthorized}
{:add, {:ok, %Actor{:blocked => blocked_ts}}} when not is_nil(blocked_ts) -> {:add, {:ok, %Actor{:blocked => blocked_ts}}} when not is_nil(blocked_ts) ->

View File

@ -4,24 +4,19 @@ defmodule Vonbraun.ActivityPub.Handler.Reject do
require Logger require Logger
alias Vonbraun.Ecto.Schema.Actor alias Vonbraun.Ecto.Schema.Actor
alias Vonbraun.Util alias Vonbraun.Util
import Vonbraun.ActivityPub.Handler.Accept, only: [extract_follow_object_actor: 1]
import Vonbraun.ActivityPub.Handler.Accept, @verb "Reject"
only: [extract_follow_object_actor_id: 1]
@verb "https://www.w3.org/ns/activitystreams#Reject"
def type, do: @verb def type, do: @verb
# Lots of kinds of things can be rejected but for right now only follows. # Lots of kinds of things can be rejected but for right now only follows.
def handle(%{ def handle(%{
"actor" => actor_value, "type" => @verb,
"object" => object_value "actor" => actor_id,
"object" => object = %{"type" => "Follow"}
}) do }) do
my_id = Util.my_id() with {:actor, {:ok, follow_actor_id}} <- {:actor, extract_follow_object_actor(object)},
{:match, true} <- {:match, follow_actor_id == Util.my_id()},
with {:object, {:ok, object}} <- {:object, Util.get_only_property(object_value)},
{:actor, {:ok, ^my_id}} <- {:actor, extract_follow_object_actor_id(object)},
{:actor_id, {:ok, actor_id}} <- {:actor_id, Util.get_only_id(actor_value)},
{:asked, {:ok, %Actor{:blocked => nil, :following_state => "accepted"}}} <- {:asked, {:ok, %Actor{:blocked => nil, :following_state => "accepted"}}} <-
{:asked, Actor.mark_pending_follow(actor_id, "rejected", force: true)} do {:asked, Actor.mark_pending_follow(actor_id, "rejected", force: true)} do
Logger.info("Now following: #{actor_id}") Logger.info("Now following: #{actor_id}")
@ -35,7 +30,7 @@ defmodule Vonbraun.ActivityPub.Handler.Reject do
{:asked, {:ok, %Actor{:blocked => nil, :following_state => following_state}}} -> {:asked, {:ok, %Actor{:blocked => nil, :following_state => following_state}}} ->
Logger.error( Logger.error(
"Weird following state after received Accept: #{following_state} from actor: #{inspect(actor_value)} this should not happen." "Weird following state after received Accept: #{following_state} from actor: #{actor_id} this should not happen."
) )
{:error, :following_state} {:error, :following_state}
@ -43,10 +38,7 @@ defmodule Vonbraun.ActivityPub.Handler.Reject do
{:actor, {:error, _error}} -> {:actor, {:error, _error}} ->
{:ok, :unauthorized} {:ok, :unauthorized}
{:actor_id, :error} -> {:match, false} ->
{:ok, :unauthorized}
{:object, :error} ->
{:ok, :unauthorized} {:ok, :unauthorized}
end end
end end

View File

@ -4,20 +4,17 @@ defmodule Vonbraun.ActivityPub.Handler.Undo do
require Logger require Logger
alias Vonbraun.Ecto.Schema.Actor alias Vonbraun.Ecto.Schema.Actor
alias Vonbraun.Util alias Vonbraun.Util
import Vonbraun.ActivityPub.Handler.Accept, only: [extract_follow_object_actor_id: 1] import Vonbraun.ActivityPub.Handler.Accept, only: [extract_follow_object_actor: 1]
@verb "https://www.w3.org/ns/activitystreams#Undo" @verb "Undo"
def type, do: @verb def type, do: @verb
# Lots of different kinds of things can be undone. # Lots of different kinds of things can be undone.
def handle(%{"actor" => actor_value, "object" => object_value}) do def handle(%{"type" => @verb, "actor" => actor_id, "object" => object = %{"type" => "Follow"}}) do
my_id = Util.my_id() with {:actor, {:ok, follow_actor_id}} <- {:actor, extract_follow_object_actor(object)},
{:match, true} <- {:match, follow_actor_id == Util.my_id()},
with {:object, {:ok, object}} <- {:object, Util.get_only_property(object_value)},
{:actor, {:ok, ^my_id}} <- {:actor, extract_follow_object_actor_id(object)},
{:actor_id, {:ok, actor_id}} <- {:actor_id, Util.get_only_id(actor_value)},
{:asked, {:ok, %Actor{:following_state => nil}}} <- {:asked, {:ok, %Actor{:following_state => nil}}} <-
{:asked, Actor.remove_follower(actor_id)} do {:asked, Actor.remove_follower(actor_id)} do
{:ok, :removed_follower} {:ok, :removed_follower}
@ -25,18 +22,15 @@ defmodule Vonbraun.ActivityPub.Handler.Undo do
{:asked, {:error, error}} -> {:asked, {:error, error}} ->
{:error, error} {:error, error}
{:actor_id, :error} -> {:match, false} ->
{:ok, :unauthorized} {:ok, :unauthorized}
{:actor, {:error, _error}} -> {:actor, {:error, _error}} ->
{:ok, :unauthorized} {:ok, :unauthorized}
{:object, :error} ->
{:ok, :unauthorized}
end end
end end
def handle(%{}, %{}) do def handle(%{"type" => @verb}, %{}) do
{:ok, :unknown} {:ok, :unknown}
end end
end end

View File

@ -30,7 +30,7 @@ defmodule Vonbraun.Util do
""" """
def get_only_property([property]), do: {:ok, property} def get_only_property([property]), do: {:ok, property}
def get_only_property(property) when is_list(property), do: :error def get_only_property(property) when is_list(property), do: {:error}
def get_only_property(property), do: {:ok, property} def get_only_property(property), do: {:ok, property}