Improve group refreshment and fixed date signature generation

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
Thomas Citharel 2021-11-13 18:45:01 +01:00
parent 6d599441a9
commit 55af776df9
No known key found for this signature in database
GPG key ID: A061B9DDE0CA0773
27 changed files with 1009 additions and 704 deletions

View file

@ -90,6 +90,8 @@ config :mobilizon, Mobilizon.Web.Upload.Uploader.Local, uploads: "/var/lib/mobil
config :tz_world, data_dir: "/var/lib/mobilizon/timezones"
config :mobilizon, Timex.Gettext, default_locale: "en"
config :mobilizon, :media_proxy,
enabled: true,
proxy_opts: [

View file

@ -32,14 +32,14 @@ defmodule Mobilizon.Federation.ActivityPub.Actor do
case Actors.get_actor_by_url(url, preload) do
{:ok, %Actor{} = cached_actor} ->
if Actors.needs_update?(cached_actor) do
__MODULE__.make_actor_from_url(url, preload)
__MODULE__.make_actor_from_url(url, preload: preload)
else
{:ok, cached_actor}
end
{:error, :actor_not_found} ->
# For tests, see https://github.com/jjh42/mock#not-supported---mocking-internal-function-calls and Mobilizon.Federation.ActivityPubTest
__MODULE__.make_actor_from_url(url, preload)
__MODULE__.make_actor_from_url(url, preload: preload)
end
end
@ -48,15 +48,15 @@ defmodule Mobilizon.Federation.ActivityPub.Actor do
@doc """
Create an actor locally by its URL (AP ID)
"""
@spec make_actor_from_url(url :: String.t(), preload :: boolean()) ::
@spec make_actor_from_url(url :: String.t(), options :: Keyword.t()) ::
{:ok, Actor.t()} | {:error, make_actor_errors | Ecto.Changeset.t()}
def make_actor_from_url(url, preload \\ false) do
def make_actor_from_url(url, options \\ []) do
if are_same_origin?(url, Endpoint.url()) do
{:error, :actor_is_local}
else
case Fetcher.fetch_and_prepare_actor_from_url(url) do
case Fetcher.fetch_and_prepare_actor_from_url(url, options) do
{:ok, data} when is_map(data) ->
Actors.upsert_actor(data, preload)
Actors.upsert_actor(data, Keyword.get(options, :preload, false))
# Request returned 410
{:error, :actor_deleted} ->
@ -78,13 +78,13 @@ defmodule Mobilizon.Federation.ActivityPub.Actor do
case Actors.get_actor_by_name_with_preload(nickname, type) do
%Actor{url: actor_url} = actor ->
if Actors.needs_update?(actor) do
make_actor_from_url(actor_url, true)
make_actor_from_url(actor_url, preload: true)
else
{:ok, actor}
end
nil ->
make_actor_from_nickname(nickname, true)
make_actor_from_nickname(nickname, preload: true)
end
end
@ -100,7 +100,7 @@ defmodule Mobilizon.Federation.ActivityPub.Actor do
def make_actor_from_nickname(nickname, preload \\ false) do
case WebFinger.finger(nickname) do
{:ok, url} when is_binary(url) ->
make_actor_from_url(url, preload)
make_actor_from_url(url, preload: preload)
{:error, e} ->
{:error, e}

View file

@ -49,8 +49,11 @@ defmodule Mobilizon.Federation.ActivityPub.Fetcher do
{:error, :content_not_json}
{:ok, %Tesla.Env{} = res} ->
Logger.debug("Resource returned bad HTTP code inspect #{res}")
Logger.debug("Resource returned bad HTTP code #{inspect(res)}")
{:error, :http_error}
{:error, err} ->
{:error, err}
end
else
{:error, :invalid_url}
@ -122,39 +125,25 @@ defmodule Mobilizon.Federation.ActivityPub.Fetcher do
"""
@spec fetch_and_prepare_actor_from_url(String.t()) ::
{:ok, map()} | {:error, fetch_actor_errors}
def fetch_and_prepare_actor_from_url(url) do
def fetch_and_prepare_actor_from_url(url, options \\ []) do
Logger.debug("Fetching and preparing actor from url")
Logger.debug(inspect(url))
case Tesla.get(url,
headers: [{"Accept", "application/activity+json"}],
follow_redirect: true
) do
{:ok, %{status: 200, body: body}} ->
Logger.debug("response okay, now decoding json")
case fetch(url, options) do
{:ok, data} ->
case ActorConverter.as_to_model_data(data) do
{:error, :actor_not_allowed_type} ->
{:error, :actor_not_allowed_type}
case Jason.decode(body) do
{:ok, data} when is_map(data) ->
Logger.debug("Got activity+json response at actor's endpoint, now converting data")
case ActorConverter.as_to_model_data(data) do
{:error, :actor_not_allowed_type} ->
{:error, :actor_not_allowed_type}
map when is_map(map) ->
{:ok, map}
end
{:error, %Jason.DecodeError{} = e} ->
Logger.warn("Could not decode actor at fetch #{url}, #{inspect(e)}")
{:error, :json_decode_error}
map when is_map(map) ->
{:ok, map}
end
{:ok, %{status: 410}} ->
{:error, :http_gone} ->
Logger.info("Response HTTP 410")
{:error, :actor_deleted}
{:ok, %Tesla.Env{}} ->
{:error, :http_error} ->
Logger.info("Non 200 HTTP Code")
{:error, :http_error}

View file

@ -52,7 +52,7 @@ defmodule Mobilizon.Federation.ActivityPub.Refresher do
@spec fetch_group(String.t(), Actor.t()) :: :ok | {:error, fetch_actor_errors}
def fetch_group(group_url, %Actor{} = on_behalf_of) do
case ActivityPubActor.make_actor_from_url(group_url) do
case ActivityPubActor.make_actor_from_url(group_url, on_behalf_of: on_behalf_of) do
{:error, err}
when err in [:actor_deleted, :http_error, :json_decode_error, :actor_is_local] ->
Logger.debug("Error while making actor")

View file

@ -114,8 +114,12 @@ defmodule Mobilizon.Federation.HTTPSignatures.Signature do
Logger.debug("headers")
Logger.debug(inspect(headers))
with {:ok, key} <- prepare_public_key(keys) do
HTTPSignatures.sign(key, actor.url <> "#main-key", headers)
case prepare_public_key(keys) do
{:ok, key} ->
HTTPSignatures.sign(key, actor.url <> "#main-key", headers)
{:error, :pem_decode_error} ->
raise ArgumentError, message: "Failed to prepare public keys for #{actor.url}"
end
end
@ -129,7 +133,7 @@ defmodule Mobilizon.Federation.HTTPSignatures.Signature do
@spec generate_date_header(NaiveDateTime.t()) :: String.t()
def generate_date_header(%NaiveDateTime{} = date) do
Timex.format!(date, "{WDshort}, {0D} {Mshort} {YYYY} {h24}:{m}:{s} GMT")
Timex.lformat!(date, "{WDshort}, {0D} {Mshort} {YYYY} {h24}:{m}:{s} GMT", "en")
end
@spec generate_request_target(String.t(), String.t()) :: String.t()

View file

@ -48,6 +48,7 @@ defmodule Mobilizon.Web.Plugs.HTTPSignatures do
signature_valid = HTTPSignatures.validate_conn(conn)
Logger.debug("Is signature valid ? #{inspect(signature_valid)}")
date_valid = date_valid?(conn)
Logger.debug("Is date valid ? #{inspect(date_valid)}")
assign(conn, :valid_signature, signature_valid && date_valid)
end
end

View file

@ -48,9 +48,10 @@ defmodule Mobilizon.Web.Plugs.MappedSignatureToIdentity do
# if this has payload make sure it is signed by the same actor that made it
def call(%{assigns: %{valid_signature: true}, params: %{"actor" => actor}} = conn, _opts) do
with actor_id <- Utils.get_url(actor),
with actor_id when actor_id != nil <- Utils.get_url(actor),
{:actor, %Actor{} = actor} <- {:actor, actor_from_key_id(conn)},
{:actor_match, true} <- {:actor_match, actor.url == actor_id} do
Logger.debug("Mapped identity to #{actor.url} from actor param")
assign(conn, :actor, actor)
else
{:actor_match, false} ->
@ -58,7 +59,7 @@ defmodule Mobilizon.Web.Plugs.MappedSignatureToIdentity do
Logger.debug("key_id=#{key_id_from_conn(conn)}, actor=#{actor}")
assign(conn, :valid_signature, false)
# remove me once testsuite uses mapped capabilities instead of what we do now
# TODO: remove me once testsuite uses mapped capabilities instead of what we do now
{:actor, nil} ->
Logger.debug("Failed to map identity from signature (lookup failure)")
Logger.debug("key_id=#{key_id_from_conn(conn)}, actor=#{actor}")
@ -70,6 +71,7 @@ defmodule Mobilizon.Web.Plugs.MappedSignatureToIdentity do
def call(%{assigns: %{valid_signature: true}} = conn, _opts) do
case actor_from_key_id(conn) do
%Actor{} = actor ->
Logger.debug("Mapped identity to #{actor.url} from signed fetch")
assign(conn, :actor, actor)
_ ->

View file

@ -12,6 +12,7 @@ defmodule Mobilizon.Web.ActivityPub.ActorView do
alias Mobilizon.Resources.Resource
alias Mobilizon.Storage.Page
alias Mobilizon.Todos.TodoList
require Logger
@private_visibility_empty_collection %{elements: [], total: 0}
@json_ld_header Utils.make_json_ld_header()
@ -39,9 +40,16 @@ defmodule Mobilizon.Web.ActivityPub.ActorView do
page = Map.get(args, :page, 1)
collection_name = String.trim_trailing(view_name, ".json")
collection_name = String.to_existing_atom(collection_name)
actor_applicant = Map.get(args, :actor_applicant)
Logger.debug("Rendering actor collection #{inspect(collection_name)}")
Logger.debug(
"Using authenticated fetch with actor #{if actor_applicant, do: actor_applicant.url, else: nil}"
)
%{total: total, elements: elements} =
if can_get_collection?(collection_name, actor, Map.get(args, :actor_applicant)),
if can_get_collection?(collection_name, actor, actor_applicant),
do: fetch_collection(collection_name, actor, page),
else: default_collection(collection_name, actor, page)
@ -127,8 +135,13 @@ defmodule Mobilizon.Web.ActivityPub.ActorView do
when visibility in [:public, :unlisted] and collection in [:outbox, :followers, :following],
do: true
defp can_get_collection?(_collection_name, %Actor{} = actor, %Actor{} = actor_applicant),
do: actor_applicant_group_member?(actor, actor_applicant)
defp can_get_collection?(_collection_name, %Actor{} = actor, %Actor{} = actor_applicant) do
Logger.debug(
"Testing if #{actor_applicant.url} can be allowed access to #{actor.url} private collections"
)
actor_applicant_group_member?(actor, actor_applicant)
end
defp can_get_collection?(_, _, _), do: false

View file

@ -47,10 +47,17 @@ defmodule Mobilizon.Federation.ActivityPubTest do
File.read!("test/fixtures/mastodon-status-2.json")
|> Jason.decode!()
actor_data =
File.read!("test/fixtures/mastodon-actor.json")
|> Jason.decode!()
Mock
|> expect(:call, fn
|> expect(:call, 2, fn
%{method: :get, url: ^url}, _opts ->
{:ok, %Tesla.Env{status: 200, body: data}}
%{method: :get, url: "https://framapiaf.org/users/Framasoft"}, _opts ->
{:ok, %Tesla.Env{status: 200, body: actor_data}}
end)
{:ok, object} = ActivityPub.fetch_object_from_url(url)
@ -72,13 +79,20 @@ defmodule Mobilizon.Federation.ActivityPubTest do
File.read!("test/fixtures/mastodon-status-4.json")
|> Jason.decode!()
actor_data =
File.read!("test/fixtures/mastodon-actor.json")
|> Jason.decode!()
Mock
|> expect(:call, 2, fn
|> expect(:call, 3, fn
%{method: :get, url: ^url}, _opts ->
{:ok, %Tesla.Env{status: 200, body: data}}
%{method: :get, url: ^reply_to_url}, _opts ->
{:ok, %Tesla.Env{status: 200, body: reply_to_data}}
%{method: :get, url: "https://pirateradio.social/users/captain"}, _opts ->
{:ok, %Tesla.Env{status: 200, body: actor_data}}
end)
{:ok, object} = ActivityPub.fetch_object_from_url(url)
@ -98,13 +112,26 @@ defmodule Mobilizon.Federation.ActivityPubTest do
File.read!("test/fixtures/peertube-video.json")
|> Jason.decode!()
actor_data =
File.read!("test/fixtures/mastodon-actor.json")
|> Jason.decode!()
Mock
|> expect(:call, 2, fn
|> expect(:call, 5, fn
%{method: :get, url: ^url}, _opts ->
{:ok, %Tesla.Env{status: 200, body: data}}
%{method: :get, url: ^origin_url}, _opts ->
{:ok, %Tesla.Env{status: 200, body: origin_data}}
%{method: :get, url: "https://diaspodon.fr/users/dada"}, _opts ->
{:ok, %Tesla.Env{status: 200, body: actor_data}}
%{method: :get, url: "https://framatube.org/accounts/framasoft"}, _opts ->
{:ok, %Tesla.Env{status: 200, body: actor_data}}
%{method: :get, url: "https://framapiaf.org/users/Pouhiou"}, _opts ->
{:ok, %Tesla.Env{status: 200, body: actor_data}}
end)
{:ok, object} = ActivityPub.fetch_object_from_url(url)

View file

@ -1,39 +1,72 @@
defmodule Mobilizon.Federation.ActivityPub.ActorTest do
use ExVCR.Mock, adapter: ExVCR.Adapter.Hackney
use Mobilizon.DataCase
import Mox
import Mock
alias Mobilizon.Actors
alias Mobilizon.Actors.Actor
alias Mobilizon.Federation.ActivityPub.Actor, as: ActivityPubActor
alias Mobilizon.Federation.ActivityPub.{Fetcher, Relay}
alias Mobilizon.Federation.ActivityPub.Relay
alias Mobilizon.Service.HTTP.ActivityPub.Mock
describe "fetching actor from its url" do
@actor_url "https://framapiaf.org/users/tcit"
test "returns an actor from nickname" do
use_cassette "activity_pub/fetch_tcit@framapiaf.org" do
assert {:ok,
%Actor{preferred_username: "tcit", domain: "framapiaf.org", visibility: :public} =
_actor} = ActivityPubActor.make_actor_from_nickname("tcit@framapiaf.org")
end
actor_data =
File.read!("test/fixtures/mastodon-actor.json")
|> Jason.decode!()
|> Map.put("id", @actor_url)
|> Map.put("preferredUsername", "tcit")
|> Map.put("discoverable", true)
use_cassette "activity_pub/fetch_tcit@framapiaf.org_not_discoverable" do
assert {:ok,
%Actor{preferred_username: "tcit", domain: "framapiaf.org", visibility: :unlisted} =
_actor} = ActivityPubActor.make_actor_from_nickname("tcit@framapiaf.org")
end
Mock
|> expect(:call, fn
%{method: :get, url: @actor_url}, _opts ->
{:ok, %Tesla.Env{status: 200, body: actor_data}}
end)
assert {:ok,
%Actor{preferred_username: "tcit", domain: "framapiaf.org", visibility: :public} =
_actor} = ActivityPubActor.make_actor_from_nickname("tcit@framapiaf.org")
end
test "returns an actor from nickname when not discoverable" do
actor_data =
File.read!("test/fixtures/mastodon-actor.json")
|> Jason.decode!()
|> Map.put("id", @actor_url)
|> Map.put("preferredUsername", "tcit")
Mock
|> expect(:call, fn
%{method: :get, url: @actor_url}, _opts ->
{:ok, %Tesla.Env{status: 200, body: actor_data}}
end)
assert {:ok,
%Actor{preferred_username: "tcit", domain: "framapiaf.org", visibility: :unlisted} =
_actor} = ActivityPubActor.make_actor_from_nickname("tcit@framapiaf.org")
end
@actor_url "https://framapiaf.org/users/tcit"
test "returns an actor from url" do
actor_data =
File.read!("test/fixtures/mastodon-actor.json")
|> Jason.decode!()
|> Map.put("id", @actor_url)
|> Map.put("preferredUsername", "tcit")
Mock
|> expect(:call, fn
%{method: :get, url: @actor_url}, _opts ->
{:ok, %Tesla.Env{status: 200, body: actor_data}}
end)
# Initial fetch
use_cassette "activity_pub/fetch_framapiaf.org_users_tcit" do
# Unlisted because discoverable is not present in the JSON payload
assert {:ok,
%Actor{preferred_username: "tcit", domain: "framapiaf.org", visibility: :unlisted}} =
ActivityPubActor.get_or_fetch_actor_by_url(@actor_url)
end
# Unlisted because discoverable is not present in the JSON payload
assert {:ok,
%Actor{preferred_username: "tcit", domain: "framapiaf.org", visibility: :unlisted}} =
ActivityPubActor.get_or_fetch_actor_by_url(@actor_url)
# Fetch uses cache if Actors.needs_update? returns false
with_mocks([
@ -49,7 +82,7 @@ defmodule Mobilizon.Federation.ActivityPub.ActorTest do
needs_update?: fn _ -> false end
]},
{ActivityPubActor, [:passthrough],
make_actor_from_url: fn @actor_url, false ->
make_actor_from_url: fn @actor_url, preload: false ->
{:ok,
%Actor{
preferred_username: "tcit",
@ -61,7 +94,7 @@ defmodule Mobilizon.Federation.ActivityPub.ActorTest do
ActivityPubActor.get_or_fetch_actor_by_url(@actor_url)
assert_called(Actors.needs_update?(:_))
refute called(ActivityPubActor.make_actor_from_url(@actor_url, false))
refute called(ActivityPubActor.make_actor_from_url(@actor_url, preload: false))
end
# Fetch doesn't use cache if Actors.needs_update? returns true
@ -78,7 +111,7 @@ defmodule Mobilizon.Federation.ActivityPub.ActorTest do
needs_update?: fn _ -> true end
]},
{ActivityPubActor, [:passthrough],
make_actor_from_url: fn @actor_url, false ->
make_actor_from_url: fn @actor_url, preload: false ->
{:ok,
%Actor{
preferred_username: "tcit",
@ -92,24 +125,21 @@ defmodule Mobilizon.Federation.ActivityPub.ActorTest do
assert_called(ActivityPubActor.get_or_fetch_actor_by_url(@actor_url))
assert_called(Actors.get_actor_by_url(@actor_url, false))
assert_called(Actors.needs_update?(:_))
assert_called(ActivityPubActor.make_actor_from_url(@actor_url, false))
assert_called(ActivityPubActor.make_actor_from_url(@actor_url, preload: false))
end
end
test "handles remote actor being deleted" do
with_mocks([
{Fetcher, [:passthrough],
fetch_and_prepare_actor_from_url: fn @actor_url ->
{:error, :actor_deleted}
end}
]) do
assert match?(
{:error, :actor_deleted},
ActivityPubActor.make_actor_from_url(@actor_url, false)
)
Mock
|> expect(:call, fn
%{method: :get, url: @actor_url}, _opts ->
{:ok, %Tesla.Env{status: 410, body: ""}}
end)
assert_called(Fetcher.fetch_and_prepare_actor_from_url(@actor_url))
end
assert match?(
{:error, :actor_deleted},
ActivityPubActor.make_actor_from_url(@actor_url, preload: false)
)
end
@public_url "https://www.w3.org/ns/activitystreams#Public"

View file

@ -36,12 +36,79 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier.CommentsTest do
end
test "it fetches replied-to activities if we don't have them" do
data =
File.read!("test/fixtures/mastodon-post-activity.json")
actor_data = File.read!("test/fixtures/mastodon-actor.json") |> Jason.decode!()
status_data = File.read!("test/fixtures/mastodon-status-2.json") |> Jason.decode!()
reply_to_data =
File.read!("test/fixtures/pleroma-comment-object.json")
|> Jason.decode!()
reply_to_url = "https://fedi.absturztau.be/objects/1726cdc7-4f2a-4ddb-9c68-03d27c98c3d9"
Mock
|> expect(:call, 5, fn
%{method: :get, url: "https://framapiaf.org/users/admin"}, _opts ->
{:ok,
%Tesla.Env{
status: 200,
body:
actor_data
|> Map.put("id", "https://framapiaf.org/users/admin")
|> Map.put("preferredUsername", "admin")
}}
%{method: :get, url: "https://framapiaf.org/users/tcit"}, _opts ->
{:ok,
%Tesla.Env{
status: 200,
body:
actor_data
|> Map.put("id", "https://framapiaf.org/users/tcit")
|> Map.put("preferredUsername", "tcit")
}}
%{method: :get, url: "https://framapiaf.org/users/Framasoft"}, _opts ->
{:ok,
%Tesla.Env{
status: 200,
body:
actor_data
|> Map.put("id", "https://framapiaf.org/users/Framasoft")
|> Map.put("preferredUsername", "Framasoft")
}}
%{
method: :get,
url: "https://fedi.absturztau.be/objects/1726cdc7-4f2a-4ddb-9c68-03d27c98c3d9"
},
_opts ->
{:ok,
%Tesla.Env{
status: 200,
body:
status_data
|> Map.put(
"id",
"https://fedi.absturztau.be/objects/1726cdc7-4f2a-4ddb-9c68-03d27c98c3d9"
)
|> Map.put("actor", "https://fedi.absturztau.be/users/dqn")
}}
%{method: :get, url: "https://fedi.absturztau.be/users/dqn"}, _opts ->
{:ok,
%Tesla.Env{
status: 200,
body: Map.put(actor_data, "id", "https://fedi.absturztau.be/users/dqn")
}}
%{method: :get, url: ^reply_to_url}, _opts ->
{:ok, %Tesla.Env{status: 200, body: reply_to_data}}
end)
data =
File.read!("test/fixtures/mastodon-post-activity.json")
|> Jason.decode!()
object =
data["object"]
|> Map.put("inReplyTo", reply_to_url)
@ -50,16 +117,6 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier.CommentsTest do
data
|> Map.put("object", object)
reply_to_data =
File.read!("test/fixtures/pleroma-comment-object.json")
|> Jason.decode!()
Mock
|> expect(:call, fn
%{method: :get, url: ^reply_to_url}, _opts ->
{:ok, %Tesla.Env{status: 200, body: reply_to_data}}
end)
{:ok, returned_activity, _} = Transmogrifier.handle_incoming(data)
%Comment{} =
@ -75,6 +132,25 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier.CommentsTest do
end
test "it doesn't saves replies to an event if the event doesn't accept comments" do
actor_data = File.read!("test/fixtures/mastodon-actor.json") |> Jason.decode!()
Mock
|> expect(:call, 2, fn
%{method: :get, url: "https://framapiaf.org/users/admin"}, _opts ->
{:ok,
%Tesla.Env{
status: 200,
body: Map.put(actor_data, "id", "https://framapiaf.org/users/admin")
}}
%{method: :get, url: "https://framapiaf.org/users/tcit"}, _opts ->
{:ok,
%Tesla.Env{
status: 200,
body: Map.put(actor_data, "id", "https://framapiaf.org/users/tcit")
}}
end)
data =
File.read!("test/fixtures/mastodon-post-activity.json")
|> Jason.decode!()
@ -94,6 +170,31 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier.CommentsTest do
@url_404 "https://404.site/whatever"
test "it does not crash if the object in inReplyTo can't be fetched" do
actor_data = File.read!("test/fixtures/mastodon-actor.json") |> Jason.decode!()
Mock
|> expect(:call, 2, fn
%{method: :get, url: "https://framapiaf.org/users/admin"}, _opts ->
{:ok,
%Tesla.Env{
status: 200,
body:
actor_data
|> Map.put("id", "https://framapiaf.org/users/admin")
|> Map.put("preferredUsername", "admin")
}}
%{method: :get, url: "https://framapiaf.org/users/tcit"}, _opts ->
{:ok,
%Tesla.Env{
status: 200,
body:
actor_data
|> Map.put("id", "https://framapiaf.org/users/tcit")
|> Map.put("preferredUsername", "tcit")
}}
end)
data =
File.read!("test/fixtures/mastodon-post-activity.json")
|> Jason.decode!()
@ -118,6 +219,31 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier.CommentsTest do
end
test "it ignores incoming private notes" do
actor_data = File.read!("test/fixtures/mastodon-actor.json") |> Jason.decode!()
Mock
|> expect(:call, 2, fn
%{method: :get, url: "https://framapiaf.org/users/admin"}, _opts ->
{:ok,
%Tesla.Env{
status: 200,
body:
actor_data
|> Map.put("id", "https://framapiaf.org/users/admin")
|> Map.put("preferredUsername", "admin")
}}
%{method: :get, url: "https://framapiaf.org/users/tcit"}, _opts ->
{:ok,
%Tesla.Env{
status: 200,
body:
actor_data
|> Map.put("id", "https://framapiaf.org/users/tcit")
|> Map.put("preferredUsername", "tcit")
}}
end)
data = File.read!("test/fixtures/mastodon-post-activity-private.json") |> Jason.decode!()
event = insert(:event)
object = data["object"]
@ -128,16 +254,33 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier.CommentsTest do
end
test "it works for incoming notices" do
actor_data = File.read!("test/fixtures/mastodon-actor.json") |> Jason.decode!()
data = File.read!("test/fixtures/mastodon-post-activity.json") |> Jason.decode!()
Mock
|> expect(:call, 2, fn
%{method: :get, url: "https://framapiaf.org/users/admin"}, _opts ->
{:ok,
%Tesla.Env{
status: 200,
body: Map.put(actor_data, "id", "https://framapiaf.org/users/admin")
}}
%{method: :get, url: "https://framapiaf.org/users/tcit"}, _opts ->
{:ok,
%Tesla.Env{
status: 200,
body: Map.put(actor_data, "id", "https://framapiaf.org/users/tcit")
}}
end)
{:ok, %Activity{data: data, local: false}, _} = Transmogrifier.handle_incoming(data)
assert data["id"] ==
"https://framapiaf.org/users/admin/statuses/99512778738411822/activity"
assert data["to"] == [
"https://www.w3.org/ns/activitystreams#Public",
"https://framapiaf.org/users/tcit"
"https://www.w3.org/ns/activitystreams#Public"
]
# assert data["cc"] == [
@ -164,6 +307,31 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier.CommentsTest do
end
test "it works for incoming notices with hashtags" do
actor_data = File.read!("test/fixtures/mastodon-actor.json") |> Jason.decode!()
Mock
|> expect(:call, 2, fn
%{method: :get, url: "https://framapiaf.org/users/admin"}, _opts ->
{:ok,
%Tesla.Env{
status: 200,
body:
actor_data
|> Map.put("id", "https://framapiaf.org/users/admin")
|> Map.put("preferredUsername", "admin")
}}
%{method: :get, url: "https://framapiaf.org/users/tcit"}, _opts ->
{:ok,
%Tesla.Env{
status: 200,
body:
actor_data
|> Map.put("id", "https://framapiaf.org/users/tcit")
|> Map.put("preferredUsername", "tcit")
}}
end)
data = File.read!("test/fixtures/mastodon-post-activity-hashtag.json") |> Jason.decode!()
{:ok, %Activity{data: data, local: false}, _} = Transmogrifier.handle_incoming(data)

View file

@ -56,6 +56,19 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier.DeleteTest do
end
test "it fails for incoming deletes with spoofed origin" do
actor_data =
File.read!("test/fixtures/mastodon-actor.json")
|> Jason.decode!()
Mock
|> expect(:call, 2, fn
%{method: :get, url: "https://framapiaf.org/users/peertube"}, _opts ->
{:ok, %Tesla.Env{status: 200, body: actor_data}}
%{method: :get, url: "http://mastodon.example.org/users/gargron"}, _opts ->
{:ok, %Tesla.Env{status: 200, body: actor_data}}
end)
comment = insert(:comment)
announce_data =
@ -77,7 +90,7 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier.DeleteTest do
data
|> Map.put("object", object)
{:error, :unknown_actor} = Transmogrifier.handle_incoming(data)
:error = Transmogrifier.handle_incoming(data)
assert Discussions.get_comment_from_url(comment.url)
end
@ -132,12 +145,12 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier.DeleteTest do
|> Map.put("id", deleted_actor_url)
Mock
|> expect(:call, fn
|> expect(:call, 2, fn
%{url: ^deleted_actor_url}, _opts ->
{:ok, %Tesla.Env{status: 200, body: deleted_actor_data}}
end)
assert :error == Transmogrifier.handle_incoming(data)
assert {:error, "Group object URL remote"} == Transmogrifier.handle_incoming(data)
assert Actors.get_actor_by_url(url)
end

View file

@ -1,16 +1,31 @@
defmodule Mobilizon.Federation.ActivityPub.Transmogrifier.FollowTest do
use Mobilizon.DataCase
import Mox
import ExUnit.CaptureLog
import Mobilizon.Factory
alias Mobilizon.Actors
alias Mobilizon.Actors.Follower
alias Mobilizon.Federation.ActivityPub.{Actions, Activity, Transmogrifier}
alias Mobilizon.Service.HTTP.ActivityPub.Mock
describe "handle incoming follow requests" do
test "it works only for groups" do
actor = insert(:actor)
actor_data =
File.read!("test/fixtures/mastodon-actor.json")
|> Jason.decode!()
Mock
|> expect(:call, fn
%{method: :get, url: "https://social.tcit.fr/users/tcit"}, _opts ->
{:ok,
%Tesla.Env{
status: 200,
body: Map.put(actor_data, "id", "https://social.tcit.fr/users/tcit")
}}
end)
data =
File.read!("test/fixtures/mastodon-follow-activity.json")
|> Jason.decode!()
@ -27,6 +42,20 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier.FollowTest do
test "it works for incoming follow requests" do
actor = insert(:group)
actor_data =
File.read!("test/fixtures/mastodon-actor.json")
|> Jason.decode!()
Mock
|> expect(:call, fn
%{method: :get, url: "https://social.tcit.fr/users/tcit"}, _opts ->
{:ok,
%Tesla.Env{
status: 200,
body: Map.put(actor_data, "id", "https://social.tcit.fr/users/tcit")
}}
end)
data =
File.read!("test/fixtures/mastodon-follow-activity.json")
|> Jason.decode!()

View file

@ -22,9 +22,12 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier.UndoTest do
|> Jason.decode!()
Mock
|> expect(:call, fn
|> expect(:call, 2, fn
%{method: :get, url: "https://framapiaf.org/users/Framasoft"}, _opts ->
{:ok, %Tesla.Env{status: 200, body: actor_data}}
%{method: :get, url: "https://framapiaf.org/users/peertube"}, _opts ->
{:ok, %Tesla.Env{status: 200, body: actor_data}}
end)
{:ok, _, %Comment{}} = Transmogrifier.handle_incoming(announce_data)

View file

@ -1,8 +1,8 @@
defmodule Mobilizon.Federation.ActivityPub.Transmogrifier.UpdateTest do
use Mobilizon.DataCase
use ExVCR.Mock, adapter: ExVCR.Adapter.Hackney
use Oban.Testing, repo: Mobilizon.Storage.Repo
import Mobilizon.Factory
import Mox
alias Mobilizon.{Actors, Events, Posts}
alias Mobilizon.Actors.{Actor, Member}
@ -10,79 +10,100 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier.UpdateTest do
alias Mobilizon.Posts.Post
alias Mobilizon.Federation.ActivityPub.{Activity, Transmogrifier}
alias Mobilizon.Federation.ActivityStream.Convertible
alias Mobilizon.Service.HTTP.ActivityPub.Mock
describe "handle incoming update activities" do
test "it works for incoming update activities on actors" do
use_cassette "activity_pub/update_actor_activity" do
data = File.read!("test/fixtures/mastodon-post-activity.json") |> Jason.decode!()
data = File.read!("test/fixtures/mastodon-post-activity.json") |> Jason.decode!()
{:ok, %Activity{data: data, local: false}, _} = Transmogrifier.handle_incoming(data)
update_data = File.read!("test/fixtures/mastodon-update.json") |> Jason.decode!()
actor_data =
File.read!("test/fixtures/mastodon-actor.json")
|> Jason.decode!()
object =
update_data["object"]
|> Map.put("actor", data["actor"])
|> Map.put("id", data["actor"])
Mock
|> expect(:call, 2, fn
%{method: :get, url: "https://framapiaf.org/users/admin"}, _opts ->
{:ok, %Tesla.Env{status: 200, body: actor_data}}
update_data =
update_data
|> Map.put("actor", data["actor"])
|> Map.put("object", object)
%{method: :get, url: "https://framapiaf.org/users/tcit"}, _opts ->
{:ok, %Tesla.Env{status: 200, body: actor_data}}
end)
{:ok, %Activity{data: _data, local: false}, _} =
Transmogrifier.handle_incoming(update_data)
{:ok, %Activity{data: data, local: false}, _} = Transmogrifier.handle_incoming(data)
update_data = File.read!("test/fixtures/mastodon-update.json") |> Jason.decode!()
{:ok, %Actor{} = actor} = Actors.get_actor_by_url(update_data["actor"])
assert actor.name == "nextsoft"
object =
update_data["object"]
|> Map.put("actor", data["actor"])
|> Map.put("id", data["actor"])
assert actor.summary == "<p>Some bio</p>"
end
update_data =
update_data
|> Map.put("actor", data["actor"])
|> Map.put("object", object)
{:ok, %Activity{data: _data, local: false}, _} = Transmogrifier.handle_incoming(update_data)
{:ok, %Actor{} = actor} = Actors.get_actor_by_url(update_data["actor"])
assert actor.name == "nextsoft"
assert actor.summary == "<p>Some bio</p>"
end
test "it works for incoming update activities on events" do
use_cassette "activity_pub/event_update_activities" do
data = File.read!("test/fixtures/mobilizon-post-activity.json") |> Jason.decode!()
data = File.read!("test/fixtures/mobilizon-post-activity.json") |> Jason.decode!()
{:ok, %Activity{data: data, local: false}, %Event{id: event_id}} =
Transmogrifier.handle_incoming(data)
actor_data =
File.read!("test/fixtures/mastodon-actor.json")
|> Jason.decode!()
assert_enqueued(
worker: Mobilizon.Service.Workers.BuildSearch,
args: %{event_id: event_id, op: :insert_search_event}
)
Mock
|> expect(:call, 2, fn
%{method: :get, url: "https://mobilizon.fr/@metacartes"}, _opts ->
{:ok, %Tesla.Env{status: 200, body: actor_data}}
assert %{success: 1, snoozed: 0, failure: 0} == Oban.drain_queue(queue: :search)
%{method: :get, url: "https://framapiaf.org/users/tcit"}, _opts ->
{:ok, %Tesla.Env{status: 200, body: actor_data}}
end)
update_data = File.read!("test/fixtures/mastodon-update.json") |> Jason.decode!()
{:ok, %Activity{data: data, local: false}, %Event{id: event_id}} =
Transmogrifier.handle_incoming(data)
object =
data["object"]
|> Map.put("actor", data["actor"])
|> Map.put("name", "My updated event")
|> Map.put("id", data["object"]["id"])
|> Map.put("type", "Event")
assert_enqueued(
worker: Mobilizon.Service.Workers.BuildSearch,
args: %{event_id: event_id, op: :insert_search_event}
)
update_data =
update_data
|> Map.put("actor", data["actor"])
|> Map.put("object", object)
assert %{success: 1, snoozed: 0, failure: 0} == Oban.drain_queue(queue: :search)
{:ok, %Activity{data: data, local: false}, _} =
Transmogrifier.handle_incoming(update_data)
update_data = File.read!("test/fixtures/mastodon-update.json") |> Jason.decode!()
%Event{} = event = Events.get_event_by_url(data["object"]["id"])
object =
data["object"]
|> Map.put("actor", data["actor"])
|> Map.put("name", "My updated event")
|> Map.put("id", data["object"]["id"])
|> Map.put("type", "Event")
assert_enqueued(
worker: Mobilizon.Service.Workers.BuildSearch,
args: %{event_id: event_id, op: :update_search_event}
)
update_data =
update_data
|> Map.put("actor", data["actor"])
|> Map.put("object", object)
assert %{success: 1, snoozed: 0, failure: 0} == Oban.drain_queue(queue: :search)
{:ok, %Activity{data: data, local: false}, _} = Transmogrifier.handle_incoming(update_data)
assert event.title == "My updated event"
%Event{} = event = Events.get_event_by_url(data["object"]["id"])
assert event.description == data["object"]["content"]
end
assert_enqueued(
worker: Mobilizon.Service.Workers.BuildSearch,
args: %{event_id: event_id, op: :update_search_event}
)
assert %{success: 1, snoozed: 0, failure: 0} == Oban.drain_queue(queue: :search)
assert event.title == "My updated event"
assert event.description == data["object"]["content"]
end
# test "it works for incoming update activities which lock the account" do

View file

@ -4,8 +4,6 @@
# Upstream: https://git.pleroma.social/pleroma/pleroma/blob/develop/test/web/activity_pub/transmogrifier_test.exs
defmodule Mobilizon.Federation.ActivityPub.TransmogrifierTest do
use ExVCR.Mock, adapter: ExVCR.Adapter.Hackney
use Mobilizon.DataCase
use Oban.Testing, repo: Mobilizon.Storage.Repo
@ -33,54 +31,75 @@ defmodule Mobilizon.Federation.ActivityPub.TransmogrifierTest do
describe "handle incoming events" do
test "it works for incoming events" do
use_cassette "activity_pub/fetch_mobilizon_post_activity" do
data = File.read!("test/fixtures/mobilizon-post-activity.json") |> Jason.decode!()
actor_data = File.read!("test/fixtures/mastodon-actor.json") |> Jason.decode!()
{:ok, %Activity{data: data, local: false}, %Event{} = event} =
Transmogrifier.handle_incoming(data)
Mock
|> expect(:call, 2, fn
%{method: :get, url: url}, _opts ->
case url do
"https://mobilizon.fr/@metacartes" ->
actor_data = Map.put(actor_data, "id", "https://mobilizon.fr/@metacartes")
{:ok, %Tesla.Env{status: 200, body: actor_data}}
assert data["id"] ==
"https://mobilizon.fr/events/39a0c4a6-f2b6-41dc-bbe2-fc5bff76cc93/activity"
"https://framapiaf.org/users/tcit" ->
actor_data = Map.put(actor_data, "id", "https://framapiaf.org/users/tcit")
{:ok, %Tesla.Env{status: 200, body: actor_data}}
end
end)
assert data["to"] == ["https://www.w3.org/ns/activitystreams#Public"]
data = File.read!("test/fixtures/mobilizon-post-activity.json") |> Jason.decode!()
#
# assert data["cc"] == [
# "https://framapiaf.org/users/admin/followers",
# "http://localtesting.pleroma.lol/users/lain"
# ]
{:ok, %Activity{data: data, local: false}, %Event{} = event} =
Transmogrifier.handle_incoming(data)
assert data["actor"] == "https://mobilizon.fr/@metacartes"
assert data["id"] ==
"https://mobilizon.fr/events/39a0c4a6-f2b6-41dc-bbe2-fc5bff76cc93/activity"
object = data["object"]
assert data["to"] == ["https://www.w3.org/ns/activitystreams#Public"]
assert object["id"] ==
"https://mobilizon.fr/events/39a0c4a6-f2b6-41dc-bbe2-fc5bff76cc93"
#
# assert data["cc"] == [
# "https://framapiaf.org/users/admin/followers",
# "http://localtesting.pleroma.lol/users/lain"
# ]
assert object["to"] == ["https://www.w3.org/ns/activitystreams#Public"]
assert data["actor"] == "https://mobilizon.fr/@metacartes"
# assert object["cc"] == [
# "https://framapiaf.org/users/admin/followers",
# "http://localtesting.pleroma.lol/users/lain"
# ]
object = data["object"]
assert object["actor"] == "https://mobilizon.fr/@metacartes"
assert object["location"]["name"] == "Locaux de Framasoft"
# assert object["attributedTo"] == "https://mobilizon.fr/@metacartes"
assert object["id"] ==
"https://mobilizon.fr/events/39a0c4a6-f2b6-41dc-bbe2-fc5bff76cc93"
assert event.physical_address.street == "10 Rue Jangot"
assert object["to"] == ["https://www.w3.org/ns/activitystreams#Public"]
assert event.physical_address.url ==
"https://event1.tcit.fr/address/eeecc11d-0030-43e8-a897-6422876372jd"
# assert object["cc"] == [
# "https://framapiaf.org/users/admin/followers",
# "http://localtesting.pleroma.lol/users/lain"
# ]
assert event.online_address == "https://google.com"
assert object["actor"] == "https://mobilizon.fr/@metacartes"
assert object["location"]["name"] == "Locaux de Framasoft"
# assert object["attributedTo"] == "https://mobilizon.fr/@metacartes"
{:ok, %Actor{}} = Actors.get_actor_by_url(object["actor"])
end
assert event.physical_address.street == "10 Rue Jangot"
assert event.physical_address.url ==
"https://event1.tcit.fr/address/eeecc11d-0030-43e8-a897-6422876372jd"
assert event.online_address == "https://google.com"
{:ok, %Actor{}} = Actors.get_actor_by_url(object["actor"])
end
test "it works for incoming events from Gancio" do
data = File.read!("test/fixtures/gancio-event-activity.json") |> Jason.decode!()
actor_data = File.read!("test/fixtures/gancio-actor.json") |> Jason.decode!()
Mock
|> expect(:call, fn
%{method: :get, url: "https://demo.gancio.org/federation/u/gancio"}, _opts ->
{:ok, %Tesla.Env{status: 200, body: actor_data}}
end)
{:ok, %Activity{data: data, local: false}, %Event{} = event} =
Transmogrifier.handle_incoming(data)
@ -524,6 +543,12 @@ defmodule Mobilizon.Federation.ActivityPub.TransmogrifierTest do
end
test "it accepts incoming resources and handles group being not found" do
Mock
|> expect(:call, fn
%{method: :get, url: "https://someurl.com/notfound"}, _opts ->
{:ok, %Tesla.Env{status: 404, body: ""}}
end)
creator =
insert(:actor,
domain: "mobilizon.app",
@ -649,12 +674,18 @@ defmodule Mobilizon.Federation.ActivityPub.TransmogrifierTest do
test "it works for incoming announces" do
data = File.read!("test/fixtures/mastodon-announce.json") |> Jason.decode!()
status_data = File.read!("test/fixtures/mastodon-status.json") |> Jason.decode!()
actor_data = File.read!("test/fixtures/mastodon-actor.json") |> Jason.decode!()
Mock
|> expect(:call, fn
%{method: :get, url: "https://framapiaf.org/users/peertube/statuses/104584600044284729"},
_opts ->
{:ok, %Tesla.Env{status: 200, body: status_data}}
|> expect(:call, 2, fn
%{method: :get, url: url}, _opts ->
case url do
"https://framapiaf.org/users/peertube/statuses/104584600044284729" ->
{:ok, %Tesla.Env{status: 200, body: status_data}}
"https://framapiaf.org/users/peertube" ->
{:ok, %Tesla.Env{status: 200, body: actor_data}}
end
end)
{:ok, _, %Comment{actor: %Actor{url: actor_url}, url: comment_url}} =
@ -677,10 +708,12 @@ defmodule Mobilizon.Federation.ActivityPub.TransmogrifierTest do
File.read!("test/fixtures/mastodon-announce.json")
|> Jason.decode!()
|> Map.put("object", comment_url)
|> Map.put("actor", actor_url)
Mock
|> expect(:call, fn
%{method: :get, url: ^actor_url}, _opts ->
actor_data = Map.put(actor_data, "id", actor_url)
{:ok, %Tesla.Env{status: 200, body: actor_data}}
end)
@ -926,10 +959,21 @@ defmodule Mobilizon.Federation.ActivityPub.TransmogrifierTest do
File.read!("test/fixtures/https__info.pleroma.site_activity.json")
|> Jason.decode!()
actor_data =
File.read!("test/fixtures/mastodon-actor.json")
|> Jason.decode!()
|> Map.put("id", "https://framapiaf.org/users/admin")
Mock
|> expect(:call, fn
%{method: :get, url: "https://info.pleroma.site/activity.json"}, _opts ->
{:ok, %Tesla.Env{status: 200, body: data}}
|> expect(:call, 2, fn
%{method: :get, url: url}, _opts ->
case url do
"https://info.pleroma.site/activity.json" ->
{:ok, %Tesla.Env{status: 200, body: data}}
"https://framapiaf.org/users/admin" ->
{:ok, %Tesla.Env{status: 200, body: actor_data}}
end
end)
data = %{

36
test/fixtures/gancio-actor.json vendored Normal file
View file

@ -0,0 +1,36 @@
{
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1",
{
"toot": "http://joinmastodon.org/ns#",
"schema": "http://schema.org#",
"ProperyValue": "schema:PropertyValue",
"value": "schema:value"
}
],
"id": "https://demo.gancio.org/federation/u/gancio",
"type": "Application",
"name": "gancio",
"preferredUsername": "gancio",
"inbox": "https://demo.gancio.org/federation/u/gancio/inbox",
"outbox": "https://demo.gancio.org/federation/u/gancio/outbox",
"discoverable": true,
"attachment": [
{
"type": "PropertyValue",
"name": "Website",
"value": "<a href='https://demo.gancio.org'>https://demo.gancio.org</a>"
}
],
"icon": {
"type": "Image",
"mediaType": "image/png",
"url": "https://demo.gancio.org/logo.png"
},
"publicKey": {
"id": "https://demo.gancio.org/federation/u/gancio#main-key",
"owner": "https://demo.gancio.org/federation/u/gancio",
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxp9BQ8TvVqu+0xXk7VuZ\nnuO42cHxVI+z/3TQ80AfX5aoUnK/uP7lIPy+NiIgRRu0L4hsjEs+HP6Ny9NAKFtC\nddS3pUrgIDz/AUyKeYRsCycw4XyeX7gaqIan4vCx+ANPDVTc3twDenynHhaXbPsP\nzGeKiAsGIFKRUxc5I5xnQBk6Fy6LZvGwfif07AcECER+nzffSOMPYFVbhlRuBwOg\n/tJcut77KOEpJIQSwqzT0FOw4oFtkvJt/nhpQMkXwOjEuiMOVpPoXUIpWjnbvNmy\nIPXdnKN4QqHi0fAE+FvKGbNmr18vqApT/D4Yen6W1ZWCRdUR1jjl8LNFBkPH/Tad\nkOj+UyRRJjRRqY5mXCI72Bmhwmi/YdS4gt9K73okOZ3atM+9Kfj3azZm8pP7fRkK\n/lwRP8RZFSSpz4w9JtzYmR7P8qTaxwMuq8VrxtFmf1IBChFpyNHUDtmC9MzLBRE7\n+fnpr1bARR3OwO83/xtT+vKNE+2SBvsf7zeFRXa+p5dGaih90rQOwL8EsUItiG61\nm4y9n3Q7BM7XwrZ7sGe3Hey5SWveOEgemfP4ANJBiMQpU69LKM9dGW1FcEX4FlwW\nZx/135nzMXE2cF+y+q/yY2FlacXPqJXMY32mIc+rHMzvFY/ZDzjRY/7Gg2ekjXuN\n1o7Ag7a+5k+r+XkWBNKIHp8CAwEAAQ==\n-----END PUBLIC KEY-----\n"
}
}

119
test/fixtures/mastodon-tcit-tcit.json vendored Normal file
View file

@ -0,0 +1,119 @@
{
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1",
{
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
"toot": "http://joinmastodon.org/ns#",
"featured": {
"@id": "toot:featured",
"@type": "@id"
},
"featuredTags": {
"@id": "toot:featuredTags",
"@type": "@id"
},
"alsoKnownAs": {
"@id": "as:alsoKnownAs",
"@type": "@id"
},
"movedTo": {
"@id": "as:movedTo",
"@type": "@id"
},
"schema": "http://schema.org#",
"PropertyValue": "schema:PropertyValue",
"value": "schema:value",
"IdentityProof": "toot:IdentityProof",
"discoverable": "toot:discoverable",
"Device": "toot:Device",
"Ed25519Signature": "toot:Ed25519Signature",
"Ed25519Key": "toot:Ed25519Key",
"Curve25519Key": "toot:Curve25519Key",
"EncryptedMessage": "toot:EncryptedMessage",
"publicKeyBase64": "toot:publicKeyBase64",
"deviceId": "toot:deviceId",
"claim": {
"@type": "@id",
"@id": "toot:claim"
},
"fingerprintKey": {
"@type": "@id",
"@id": "toot:fingerprintKey"
},
"identityKey": {
"@type": "@id",
"@id": "toot:identityKey"
},
"devices": {
"@type": "@id",
"@id": "toot:devices"
},
"messageFranking": "toot:messageFranking",
"messageType": "toot:messageType",
"cipherText": "toot:cipherText",
"suspended": "toot:suspended",
"focalPoint": {
"@container": "@list",
"@id": "toot:focalPoint"
}
}
],
"id": "https://social.tcit.fr/users/tcit",
"type": "Person",
"following": "https://social.tcit.fr/users/tcit/following",
"followers": "https://social.tcit.fr/users/tcit/followers",
"inbox": "https://social.tcit.fr/users/tcit/inbox",
"outbox": "https://social.tcit.fr/users/tcit/outbox",
"featured": "https://social.tcit.fr/users/tcit/collections/featured",
"featuredTags": "https://social.tcit.fr/users/tcit/collections/tags",
"preferredUsername": "tcit",
"name": "🦄 Thomas Citharel",
"summary": "<p>Hoping to make people&apos;s life better with free software at <span class=\"h-card\"><a href=\"https://framapiaf.org/@Framasoft\" class=\"u-url mention\">@<span>Framasoft</span></a></span>.</p><p>ᕕ(ᐛ)ᕗ</p>",
"url": "https://social.tcit.fr/@tcit",
"manuallyApprovesFollowers": false,
"discoverable": true,
"published": "2017-04-03T00:00:00Z",
"devices": "https://social.tcit.fr/users/tcit/collections/devices",
"publicKey": {
"id": "https://social.tcit.fr/users/tcit#main-key",
"owner": "https://social.tcit.fr/users/tcit",
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApXwYMUdFg3XUd+bGsh8C\nyiMRGpRGAWuCdM5pDWx5uM4pW2pM3xbHbcI21j9h8BmlAiPg6hbZD73KGly2N8Rt\n5iIS0I+l6i8kA1JCCdlAaDTRd41RKMggZDoQvjVZQtsyE1VzMeU2kbqqTFN6ew7H\nvbd6O0NhixoKoZ5f3jwuBDZoT0p1TAcaMdmG8oqHD97isizkDnRn8cOBA6wtI+xb\n5xP2zxZMsLpTDZLiKU8XcPKZCw4OfQfmDmKkHtrFb77jCAQj/s/FxjVnvxRwmfhN\nnWy0D+LUV/g63nHh/b5zXIeV92QZLvDYbgbezmzUzv9UeA1s70GGbaDqCIy85gw9\n+wIDAQAB\n-----END PUBLIC KEY-----\n"
},
"tag": [],
"attachment": [
{
"type": "PropertyValue",
"name": "Works at",
"value": "<span class=\"h-card\"><a href=\"https://framapiaf.org/@Framasoft\" class=\"u-url mention\">@<span>Framasoft@framapiaf.org</span></a></span>"
},
{
"type": "PropertyValue",
"name": "Pronouns",
"value": "He/Him"
},
{
"type": "PropertyValue",
"name": "Work Account",
"value": "<a href=\"https://framapiaf.org/@tcit\" rel=\"me nofollow noopener noreferrer\" target=\"_blank\"><span class=\"invisible\">https://</span><span class=\"\">framapiaf.org/@tcit</span><span class=\"invisible\"></span></a>"
},
{
"type": "PropertyValue",
"name": "Site",
"value": "<a href=\"https://tcit.fr\" rel=\"me nofollow noopener noreferrer\" target=\"_blank\"><span class=\"invisible\">https://</span><span class=\"\">tcit.fr</span><span class=\"invisible\"></span></a>"
}
],
"endpoints": {
"sharedInbox": "https://social.tcit.fr/inbox"
},
"icon": {
"type": "Image",
"mediaType": "image/jpeg",
"url": "https://social.tcit.fr/system/accounts/avatars/000/000/001/original/a28c50ce5f2b13fd.jpg"
},
"image": {
"type": "Image",
"mediaType": "image/jpeg",
"url": "https://social.tcit.fr/system/accounts/headers/000/000/001/original/ac9c4a71083bd9a1.jpg"
}
}

View file

@ -0,0 +1,112 @@
{
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1",
{
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
"toot": "http://joinmastodon.org/ns#",
"featured": {
"@id": "toot:featured",
"@type": "@id"
},
"alsoKnownAs": {
"@id": "as:alsoKnownAs",
"@type": "@id"
},
"movedTo": {
"@id": "as:movedTo",
"@type": "@id"
},
"schema": "http://schema.org#",
"PropertyValue": "schema:PropertyValue",
"value": "schema:value",
"IdentityProof": "toot:IdentityProof",
"discoverable": "toot:discoverable",
"Device": "toot:Device",
"Ed25519Signature": "toot:Ed25519Signature",
"Ed25519Key": "toot:Ed25519Key",
"Curve25519Key": "toot:Curve25519Key",
"EncryptedMessage": "toot:EncryptedMessage",
"publicKeyBase64": "toot:publicKeyBase64",
"deviceId": "toot:deviceId",
"claim": {
"@type": "@id",
"@id": "toot:claim"
},
"fingerprintKey": {
"@type": "@id",
"@id": "toot:fingerprintKey"
},
"identityKey": {
"@type": "@id",
"@id": "toot:identityKey"
},
"devices": {
"@type": "@id",
"@id": "toot:devices"
},
"messageFranking": "toot:messageFranking",
"messageType": "toot:messageType",
"cipherText": "toot:cipherText",
"focalPoint": {
"@container": "@list",
"@id": "toot:focalPoint"
}
}
],
"id": "https://framapiaf.org/users/admin",
"type": "Service",
"following": "https://framapiaf.org/users/admin/following",
"followers": "https://framapiaf.org/users/admin/followers",
"inbox": "https://framapiaf.org/users/admin/inbox",
"outbox": "https://framapiaf.org/users/admin/outbox",
"featured": "https://framapiaf.org/users/admin/collections/featured",
"preferredUsername": "admin",
"name": "Administrateur",
"summary": "<p>Je ne suis qu&apos;un compte inutile. Merci nous de contacter via <a href=\"https://contact.framasoft.org/\" rel=\"nofollow noopener noreferrer\" target=\"_blank\"><span class=\"invisible\">https://</span><span class=\"\">contact.framasoft.org/</span><span class=\"invisible\"></span></a></p>",
"url": "https://framapiaf.org/@admin",
"manuallyApprovesFollowers": false,
"discoverable": null,
"devices": "https://framapiaf.org/users/admin/collections/devices",
"publicKey": {
"id": "https://framapiaf.org/users/admin#main-key",
"owner": "https://framapiaf.org/users/admin",
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyHaU/AZ5dWtSxZXkPa89\nDUQ4z+JQHGGUG/xkGuq0v8P6qJfQqtHPBO5vH0IQJqluXWQS96gqTwjZnYevcpNA\nveYv0K25DWszx5Ehz6JX2/sSvu2rNUcQ3YZvSjdo/Yy1u5Fuc5lLmvw8uFzXYekD\nWovTMOnp4mIKpVEm/G/v4w8jvFEKw88h743vwaEIim88GEQItMxzGAV6zSqV1DWO\nLxtoRsinslJYfAG46ex4YUATFveWvOUeWk5W1sEa5f3c0moaTmBM/PAAo8vLxhlw\nJhsHihsCH+BcXKVMjW8OCqYYqISMxEifUBX63HcJt78ELHpOuc1c2eG59PomtTjQ\nywIDAQAB\n-----END PUBLIC KEY-----\n"
},
"tag": [],
"attachment": [
{
"type": "PropertyValue",
"name": "News",
"value": "<span class=\"h-card\"><a href=\"https://framapiaf.org/@Framasoft\" class=\"u-url mention\">@<span>Framasoft</span></a></span>"
},
{
"type": "PropertyValue",
"name": "Support",
"value": "<a href=\"https://contact.framasoft.org/\" rel=\"me nofollow noopener noreferrer\" target=\"_blank\"><span class=\"invisible\">https://</span><span class=\"\">contact.framasoft.org/</span><span class=\"invisible\"></span></a>"
},
{
"type": "PropertyValue",
"name": "Soutenir",
"value": "<a href=\"https://soutenir.framasoft.org/\" rel=\"me nofollow noopener noreferrer\" target=\"_blank\"><span class=\"invisible\">https://</span><span class=\"\">soutenir.framasoft.org/</span><span class=\"invisible\"></span></a>"
},
{
"type": "PropertyValue",
"name": "Site",
"value": "<a href=\"https://framasoft.org/\" rel=\"me nofollow noopener noreferrer\" target=\"_blank\"><span class=\"invisible\">https://</span><span class=\"\">framasoft.org/</span><span class=\"invisible\"></span></a>"
}
],
"endpoints": {
"sharedInbox": "https://framapiaf.org/inbox"
},
"icon": {
"type": "Image",
"mediaType": "image/jpeg",
"url": "https://framapiaf.s3.framasoft.org/framapiaf/accounts/avatars/000/000/002/original/85fbb27ad5e3cf71.jpg"
},
"image": {
"type": "Image",
"mediaType": "image/jpeg",
"url": "https://framapiaf.s3.framasoft.org/framapiaf/accounts/headers/000/000/002/original/6aba75f1ab1ab6de.jpg"
}
}

55
test/fixtures/signature/nyu_rye.json vendored Normal file
View file

@ -0,0 +1,55 @@
{
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1",
{
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
"toot": "http://joinmastodon.org/ns#",
"featured": { "@id": "toot:featured", "@type": "@id" },
"alsoKnownAs": { "@id": "as:alsoKnownAs", "@type": "@id" },
"movedTo": { "@id": "as:movedTo", "@type": "@id" },
"schema": "http://schema.org#",
"PropertyValue": "schema:PropertyValue",
"value": "schema:value",
"IdentityProof": "toot:IdentityProof",
"discoverable": "toot:discoverable",
"Device": "toot:Device",
"Ed25519Signature": "toot:Ed25519Signature",
"Ed25519Key": "toot:Ed25519Key",
"Curve25519Key": "toot:Curve25519Key",
"EncryptedMessage": "toot:EncryptedMessage",
"publicKeyBase64": "toot:publicKeyBase64",
"deviceId": "toot:deviceId",
"claim": { "@type": "@id", "@id": "toot:claim" },
"fingerprintKey": { "@type": "@id", "@id": "toot:fingerprintKey" },
"identityKey": { "@type": "@id", "@id": "toot:identityKey" },
"devices": { "@type": "@id", "@id": "toot:devices" },
"messageFranking": "toot:messageFranking",
"messageType": "toot:messageType",
"cipherText": "toot:cipherText",
"focalPoint": { "@container": "@list", "@id": "toot:focalPoint" }
}
],
"id": "https://niu.moe/users/rye",
"type": "Person",
"following": "https://niu.moe/users/rye/following",
"followers": "https://niu.moe/users/rye/followers",
"inbox": "https://niu.moe/users/rye/inbox",
"outbox": "https://niu.moe/users/rye/outbox",
"featured": "https://niu.moe/users/rye/collections/featured",
"preferredUsername": "rye",
"name": "♡ rye ♡",
"summary": "\\u003cp\\u003ecome back with a warrant\\u003c/p\\u003e",
"url": "https://niu.moe/@rye",
"manuallyApprovesFollowers": false,
"discoverable": false,
"devices": "https://niu.moe/users/rye/collections/devices",
"publicKey": {
"id": "https://niu.moe/users/rye#main-key",
"owner": "https://niu.moe/users/rye",
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA83uRWjCFO35FwfA38mzv\\nEL0TUaXB7+2hYvPwNrn1WY6me5DRbqB5zzMrzWMGr0HSooqNqEYBafGsmVTWUqIk\\nKM9ehtIBraJI+mT5X7DPR3LrXOJF4a9EEslg8XvAk8MN9IrAhm6UljnvB67RtDcA\\nTNB01VWy9yWnxFRtz9o/EMoBPyw5giOaXE2ibVNP8lQIqGKuuBKPzPjSJygdvQ5q\\nxfow2z1TpKRqdsNDqn4n6U6zCXYTzkr0J71/tGw7fsgfv78l0Wjrc7EcuBk74OaG\\nC65UDiu3X4Q6kxCfCEhPSfuwLN+UZkzxcn6goWR0iYpWs57+4tFKu9nJYP4QJ0K9\\nTwIDAQAB\\n-----END PUBLIC KEY-----\\n"
},
"tag": [],
"attachment": [],
"endpoints": { "sharedInbox": "https://niu.moe/inbox" }
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,20 +1,19 @@
defmodule Mobilizon.ActorsTest do
use ExVCR.Mock, adapter: ExVCR.Adapter.Hackney
use Mobilizon.DataCase
use Oban.Testing, repo: Mobilizon.Storage.Repo
import Mox
import Mobilizon.Factory
alias Mobilizon.{Actors, Config, Discussions, Events, Users}
alias Mobilizon.Actors.{Actor, Bot, Follower, Member}
alias Mobilizon.Discussions.Comment
alias Mobilizon.Events.Event
alias Mobilizon.Federation.ActivityPub.Actor, as: ActivityPubActor
alias Mobilizon.Medias.File, as: FileModel
alias Mobilizon.Service.HTTP.ActivityPub.Mock
alias Mobilizon.Service.Workers
alias Mobilizon.Storage.Page
alias Mobilizon.Federation.ActivityPub.Actor, as: ActivityPubActor
alias Mobilizon.Web.Upload.Uploader
describe "actors" do
@ -99,25 +98,32 @@ defmodule Mobilizon.ActorsTest do
end
test "get_actor_by_name/1 returns a remote actor" do
use_cassette "actors/remote_actor_mastodon_tcit" do
{:ok,
%Actor{
id: actor_id,
preferred_username: preferred_username,
domain: domain,
avatar: %FileModel{name: picture_name} = _picture
} = _actor} = ActivityPubActor.get_or_fetch_actor_by_url(@remote_account_url)
tcit_social_tcit =
"test/fixtures/mastodon-tcit-tcit.json" |> File.read!() |> Jason.decode!()
assert picture_name == "a28c50ce5f2b13fd.jpg"
Mock
|> expect(:call, fn
%{method: :get, url: @remote_account_url}, _opts ->
{:ok, %Tesla.Env{status: 200, body: tcit_social_tcit}}
end)
%Actor{
id: actor_found_id,
avatar: %FileModel{name: picture_name} = _picture
} = Actors.get_actor_by_name("#{preferred_username}@#{domain}")
{:ok,
%Actor{
id: actor_id,
preferred_username: preferred_username,
domain: domain,
avatar: %FileModel{name: picture_name} = _picture
} = _actor} = ActivityPubActor.get_or_fetch_actor_by_url(@remote_account_url)
assert actor_found_id == actor_id
assert picture_name == "a28c50ce5f2b13fd.jpg"
end
assert picture_name == "a28c50ce5f2b13fd.jpg"
%Actor{
id: actor_found_id,
avatar: %FileModel{name: picture_name} = _picture
} = Actors.get_actor_by_name("#{preferred_username}@#{domain}")
assert actor_found_id == actor_id
assert picture_name == "a28c50ce5f2b13fd.jpg"
end
test "get_local_actor_by_name_with_preload!/1 returns the local actor with its organized events",
@ -155,22 +161,29 @@ defmodule Mobilizon.ActorsTest do
end
test "get_actor_by_name_with_preload!/1 returns the remote actor with its organized events" do
use_cassette "actors/remote_actor_mastodon_tcit" do
with {:ok, %Actor{} = actor} <-
ActivityPubActor.get_or_fetch_actor_by_url(@remote_account_url) do
assert Actors.get_actor_by_name_with_preload(
"#{actor.preferred_username}@#{actor.domain}"
).organized_events == []
tcit_social_tcit =
"test/fixtures/mastodon-tcit-tcit.json" |> File.read!() |> Jason.decode!()
event = insert(:event, organizer_actor: actor)
Mock
|> expect(:call, fn
%{method: :get, url: @remote_account_url}, _opts ->
{:ok, %Tesla.Env{status: 200, body: tcit_social_tcit}}
end)
event_found_id =
Actors.get_actor_by_name_with_preload("#{actor.preferred_username}@#{actor.domain}").organized_events
|> hd
|> Map.get(:id)
with {:ok, %Actor{} = actor} <-
ActivityPubActor.get_or_fetch_actor_by_url(@remote_account_url) do
assert Actors.get_actor_by_name_with_preload(
"#{actor.preferred_username}@#{actor.domain}"
).organized_events == []
assert event_found_id == event.id
end
event = insert(:event, organizer_actor: actor)
event_found_id =
Actors.get_actor_by_name_with_preload("#{actor.preferred_username}@#{actor.domain}").organized_events
|> hd
|> Map.get(:id)
assert event_found_id == event.id
end
end
@ -185,19 +198,26 @@ defmodule Mobilizon.ActorsTest do
test "test search_actors/4 returns actors with similar usernames",
%{actor: %Actor{id: actor_id}} do
use_cassette "actors/remote_actor_mastodon_tcit" do
with {:ok, %Actor{id: actor2_id}} <-
ActivityPubActor.get_or_fetch_actor_by_url(@remote_account_url) do
%Page{total: 2, elements: actors} =
Actors.search_actors("tcit",
actor_type: :Person,
minimum_visibility: :private
)
tcit_social_tcit =
"test/fixtures/mastodon-tcit-tcit.json" |> File.read!() |> Jason.decode!()
actors_ids = actors |> Enum.map(& &1.id)
Mock
|> expect(:call, fn
%{method: :get, url: @remote_account_url}, _opts ->
{:ok, %Tesla.Env{status: 200, body: tcit_social_tcit}}
end)
assert MapSet.new(actors_ids) == MapSet.new([actor2_id, actor_id])
end
with {:ok, %Actor{id: actor2_id}} <-
ActivityPubActor.get_or_fetch_actor_by_url(@remote_account_url) do
%Page{total: 2, elements: actors} =
Actors.search_actors("tcit",
actor_type: :Person,
minimum_visibility: :private
)
actors_ids = actors |> Enum.map(& &1.id)
assert MapSet.new(actors_ids) == MapSet.new([actor2_id, actor_id])
end
end

View file

@ -120,7 +120,6 @@ defmodule Mobilizon.Web.ActivityPubControllerTest do
describe "/@:preferred_username/inbox" do
test "it inserts an incoming event into the database", %{conn: conn} do
# use_cassette "activity_pub_controller/mastodon-post-activity_actor_call" do
data = File.read!("test/fixtures/mastodon-post-activity.json") |> Jason.decode!()
%Actor{url: remote_actor_url} =
@ -151,6 +150,9 @@ defmodule Mobilizon.Web.ActivityPubControllerTest do
^local_actor_url ->
{:ok, %Tesla.Env{status: 200, body: local_actor_data}}
"https://framapiaf.org/users/tcit" ->
{:ok, %Tesla.Env{status: 404, body: ""}}
"https://framapiaf.org/users/admin/statuses/99512778738411822" ->
{:ok, %Tesla.Env{status: 404, body: ""}}
end
@ -168,7 +170,6 @@ defmodule Mobilizon.Web.ActivityPubControllerTest do
ActivityPub.fetch_object_from_url(data["object"]["id"])
assert comment.actor.id == remote_actor.id
# end
end
end

View file

@ -5,8 +5,9 @@
defmodule Mobilizon.Web.Plugs.MappedSignatureToIdentityTest do
use Mobilizon.Web.ConnCase
use ExVCR.Mock, adapter: ExVCR.Adapter.Hackney
import Mox
alias Mobilizon.Service.HTTP.ActivityPub.Mock
alias Mobilizon.Web.Plugs.MappedSignatureToIdentity
defp set_signature(conn, key_id) do
@ -15,47 +16,100 @@ defmodule Mobilizon.Web.Plugs.MappedSignatureToIdentityTest do
|> assign(:valid_signature, true)
end
test "it successfully maps a valid identity with a valid signature" do
use_cassette "activity_pub/signature/valid" do
conn =
build_conn(:get, "/doesntmattter")
|> set_signature("https://framapiaf.org/users/admin")
|> MappedSignatureToIdentity.call(%{})
defp framapiaf_admin do
"test/fixtures/signature/framapiaf_admin.json"
|> File.read!()
|> Jason.decode!()
end
refute is_nil(conn.assigns.actor)
end
defp nyu_rye do
"test/fixtures/signature/nyu_rye.json"
|> File.read!()
|> Jason.decode!()
end
test "it successfully maps a valid identity with a valid signature" do
Mock
|> expect(:call, fn
%{method: :get, url: "https://framapiaf.org/users/admin"}, _opts ->
{:ok, %Tesla.Env{status: 200, body: framapiaf_admin()}}
end)
Mock
|> expect(:call, fn
%{method: :get, url: "/doesntmattter"}, _opts ->
{:ok, %Tesla.Env{status: 200, body: ""}}
end)
conn =
build_conn(:get, "/doesntmattter")
|> set_signature("https://framapiaf.org/users/admin")
|> MappedSignatureToIdentity.call(%{})
refute is_nil(conn.assigns.actor)
end
test "it successfully maps a valid identity with a valid signature with payload" do
use_cassette "activity_pub/signature/valid_payload" do
conn =
build_conn(:post, "/doesntmattter", %{"actor" => "https://framapiaf.org/users/admin"})
|> set_signature("https://framapiaf.org/users/admin")
|> MappedSignatureToIdentity.call(%{})
Mock
|> expect(:call, fn
%{method: :get, url: "https://framapiaf.org/users/admin"}, _opts ->
{:ok, %Tesla.Env{status: 200, body: framapiaf_admin()}}
end)
refute is_nil(conn.assigns.actor)
end
Mock
|> expect(:call, fn
%{method: :post, url: "/doesntmattter"}, _opts ->
{:ok, %Tesla.Env{status: 200, body: ""}}
end)
conn =
build_conn(:post, "/doesntmattter", %{"actor" => "https://framapiaf.org/users/admin"})
|> set_signature("https://framapiaf.org/users/admin")
|> MappedSignatureToIdentity.call(%{})
refute is_nil(conn.assigns.actor)
end
test "it considers a mapped identity to be invalid when it mismatches a payload" do
use_cassette "activity_pub/signature/invalid_payload" do
conn =
build_conn(:post, "/doesntmattter", %{"actor" => "https://framapiaf.org/users/admin"})
|> set_signature("https://niu.moe/users/rye")
|> MappedSignatureToIdentity.call(%{})
Mock
|> expect(:call, fn
%{method: :get, url: "https://niu.moe/users/rye"}, _opts ->
{:ok, %Tesla.Env{status: 200, body: nyu_rye()}}
end)
assert %{valid_signature: false} == conn.assigns
end
Mock
|> expect(:call, fn
%{method: :post, url: "/doesntmattter"}, _opts ->
{:ok, %Tesla.Env{status: 200, body: ""}}
end)
conn =
build_conn(:post, "/doesntmattter", %{"actor" => "https://framapiaf.org/users/admin"})
|> set_signature("https://niu.moe/users/rye")
|> MappedSignatureToIdentity.call(%{})
assert %{valid_signature: false} == conn.assigns
end
@tag skip: "Available again when lib/web/plugs/mapped_signature_to_identity.ex#62 is fixed"
test "it considers a mapped identity to be invalid when the identity cannot be found" do
use_cassette "activity_pub/signature/invalid_not_found" do
conn =
build_conn(:post, "/doesntmattter", %{"actor" => "https://framapiaf.org/users/admin"})
|> set_signature("https://mastodon.social/users/gargron")
|> MappedSignatureToIdentity.call(%{})
Mock
|> expect(:call, fn
%{method: :get, url: "https://mastodon.social/users/gargron"}, _opts ->
{:ok, %Tesla.Env{status: 404, body: ""}}
end)
assert %{valid_signature: false} == conn.assigns
end
Mock
|> expect(:call, fn
%{method: :post, url: "/doesntmattter"}, _opts ->
{:ok, %Tesla.Env{status: 200, body: ""}}
end)
conn =
build_conn(:post, "/doesntmattter", %{"actor" => "https://framapiaf.org/users/admin"})
|> set_signature("https://mastodon.social/users/gargron")
|> MappedSignatureToIdentity.call(%{})
assert %{valid_signature: false} == conn.assigns
end
end