Merge branch 'develop' of https://git.pleroma.social/pleroma/pleroma into develop
This commit is contained in:
commit
ef7466921b
|
@ -23,11 +23,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- ActivityPub C2S: follower/following collection pages being inaccessible even when authentifucated if `hide_followers`/ `hide_follows` was set
|
- ActivityPub C2S: follower/following collection pages being inaccessible even when authentifucated if `hide_followers`/ `hide_follows` was set
|
||||||
- Existing user id not being preserved on insert conflict
|
- Existing user id not being preserved on insert conflict
|
||||||
- Rich Media: Parser failing when no TTL can be found by image TTL setters
|
- Rich Media: Parser failing when no TTL can be found by image TTL setters
|
||||||
|
- ActivityPub S2S: sharedInbox usage has been mostly aligned with the rules in the AP specification.
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
- MRF: Support for priming the mediaproxy cache (`Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy`)
|
- MRF: Support for priming the mediaproxy cache (`Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy`)
|
||||||
- MRF: Support for excluding specific domains from Transparency.
|
- MRF: Support for excluding specific domains from Transparency.
|
||||||
- MRF: Support for filtering posts based on who they mention (`Pleroma.Web.ActivityPub.MRF.MentionPolicy`)
|
- MRF: Support for filtering posts based on who they mention (`Pleroma.Web.ActivityPub.MRF.MentionPolicy`)
|
||||||
|
- MRF (Simple Policy): Support for wildcard domains.
|
||||||
|
- Support for wildcard domains in user domain blocks setting.
|
||||||
|
- Configuration: `quarantined_instances` support wildcard domains.
|
||||||
- Configuration: `federation_incoming_replies_max_depth` option
|
- Configuration: `federation_incoming_replies_max_depth` option
|
||||||
- Mastodon API: Support for the [`tagged` filter](https://github.com/tootsuite/mastodon/pull/9755) in [`GET /api/v1/accounts/:id/statuses`](https://docs.joinmastodon.org/api/rest/accounts/#get-api-v1-accounts-id-statuses)
|
- Mastodon API: Support for the [`tagged` filter](https://github.com/tootsuite/mastodon/pull/9755) in [`GET /api/v1/accounts/:id/statuses`](https://docs.joinmastodon.org/api/rest/accounts/#get-api-v1-accounts-id-statuses)
|
||||||
- Mastodon API, streaming: Add support for passing the token in the `Sec-WebSocket-Protocol` header
|
- Mastodon API, streaming: Add support for passing the token in the `Sec-WebSocket-Protocol` header
|
||||||
|
|
|
@ -564,6 +564,7 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
|
||||||
|
|
||||||
## `/api/pleroma/admin/config`
|
## `/api/pleroma/admin/config`
|
||||||
### List config settings
|
### List config settings
|
||||||
|
List config settings only works with `:pleroma => :instance => :dynamic_configuration` setting to `true`.
|
||||||
- Method `GET`
|
- Method `GET`
|
||||||
- Params: none
|
- Params: none
|
||||||
- Response:
|
- Response:
|
||||||
|
@ -582,6 +583,7 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
|
||||||
|
|
||||||
## `/api/pleroma/admin/config`
|
## `/api/pleroma/admin/config`
|
||||||
### Update config settings
|
### Update config settings
|
||||||
|
Updating config settings only works with `:pleroma => :instance => :dynamic_configuration` setting to `true`.
|
||||||
Module name can be passed as string, which starts with `Pleroma`, e.g. `"Pleroma.Upload"`.
|
Module name can be passed as string, which starts with `Pleroma`, e.g. `"Pleroma.Upload"`.
|
||||||
Atom keys and values can be passed with `:` in the beginning, e.g. `":upload"`.
|
Atom keys and values can be passed with `:` in the beginning, e.g. `":upload"`.
|
||||||
Tuples can be passed as `{"tuple": ["first_val", Pleroma.Module, []]}`.
|
Tuples can be passed as `{"tuple": ["first_val", Pleroma.Module, []]}`.
|
||||||
|
|
|
@ -10,9 +10,18 @@ defmodule Pleroma.Signature do
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
|
|
||||||
def key_id_to_actor_id(key_id) do
|
def key_id_to_actor_id(key_id) do
|
||||||
|
uri =
|
||||||
URI.parse(key_id)
|
URI.parse(key_id)
|
||||||
|> Map.put(:fragment, nil)
|
|> Map.put(:fragment, nil)
|
||||||
|> URI.to_string()
|
|
||||||
|
uri =
|
||||||
|
if String.ends_with?(uri.path, "/publickey") do
|
||||||
|
Map.put(uri, :path, String.replace(uri.path, "/publickey", ""))
|
||||||
|
else
|
||||||
|
uri
|
||||||
|
end
|
||||||
|
|
||||||
|
URI.to_string(uri)
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_public_key(conn) do
|
def fetch_public_key(conn) do
|
||||||
|
|
|
@ -586,12 +586,23 @@ def get_followers_query(user, page) do
|
||||||
@spec get_followers_query(User.t()) :: Ecto.Query.t()
|
@spec get_followers_query(User.t()) :: Ecto.Query.t()
|
||||||
def get_followers_query(user), do: get_followers_query(user, nil)
|
def get_followers_query(user), do: get_followers_query(user, nil)
|
||||||
|
|
||||||
|
@spec get_followers(User.t(), pos_integer()) :: {:ok, list(User.t())}
|
||||||
def get_followers(user, page \\ nil) do
|
def get_followers(user, page \\ nil) do
|
||||||
q = get_followers_query(user, page)
|
q = get_followers_query(user, page)
|
||||||
|
|
||||||
{:ok, Repo.all(q)}
|
{:ok, Repo.all(q)}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec get_external_followers(User.t(), pos_integer()) :: {:ok, list(User.t())}
|
||||||
|
def get_external_followers(user, page \\ nil) do
|
||||||
|
q =
|
||||||
|
user
|
||||||
|
|> get_followers_query(page)
|
||||||
|
|> User.Query.build(%{external: true})
|
||||||
|
|
||||||
|
{:ok, Repo.all(q)}
|
||||||
|
end
|
||||||
|
|
||||||
def get_followers_ids(user, page \\ nil) do
|
def get_followers_ids(user, page \\ nil) do
|
||||||
q = get_followers_query(user, page)
|
q = get_followers_query(user, page)
|
||||||
|
|
||||||
|
@ -873,12 +884,17 @@ def muted_notifications?(user, %{ap_id: ap_id}),
|
||||||
|
|
||||||
def blocks?(%User{info: info} = _user, %{ap_id: ap_id}) do
|
def blocks?(%User{info: info} = _user, %{ap_id: ap_id}) do
|
||||||
blocks = info.blocks
|
blocks = info.blocks
|
||||||
domain_blocks = info.domain_blocks
|
|
||||||
|
domain_blocks = Pleroma.Web.ActivityPub.MRF.subdomains_regex(info.domain_blocks)
|
||||||
|
|
||||||
%{host: host} = URI.parse(ap_id)
|
%{host: host} = URI.parse(ap_id)
|
||||||
|
|
||||||
Enum.member?(blocks, ap_id) || Enum.any?(domain_blocks, &(&1 == host))
|
Enum.member?(blocks, ap_id) ||
|
||||||
|
Pleroma.Web.ActivityPub.MRF.subdomain_match?(domain_blocks, host)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def blocks?(nil, _), do: false
|
||||||
|
|
||||||
def subscribed_to?(user, %{ap_id: ap_id}) do
|
def subscribed_to?(user, %{ap_id: ap_id}) do
|
||||||
with %User{} = target <- get_cached_by_ap_id(ap_id) do
|
with %User{} = target <- get_cached_by_ap_id(ap_id) do
|
||||||
Enum.member?(target.info.subscribers, user.ap_id)
|
Enum.member?(target.info.subscribers, user.ap_id)
|
||||||
|
|
|
@ -25,4 +25,14 @@ def get_policies do
|
||||||
defp get_policies(policy) when is_atom(policy), do: [policy]
|
defp get_policies(policy) when is_atom(policy), do: [policy]
|
||||||
defp get_policies(policies) when is_list(policies), do: policies
|
defp get_policies(policies) when is_list(policies), do: policies
|
||||||
defp get_policies(_), do: []
|
defp get_policies(_), do: []
|
||||||
|
|
||||||
|
@spec subdomains_regex([String.t()]) :: [Regex.t()]
|
||||||
|
def subdomains_regex(domains) when is_list(domains) do
|
||||||
|
for domain <- domains, do: ~r(^#{String.replace(domain, "*.", "(.*\\.)*")}$)
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec subdomain_match?([Regex.t()], String.t()) :: boolean()
|
||||||
|
def subdomain_match?(domains, host) do
|
||||||
|
Enum.any?(domains, fn domain -> Regex.match?(domain, host) end)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,22 +4,29 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
|
defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web.ActivityPub.MRF
|
||||||
@moduledoc "Filter activities depending on their origin instance"
|
@moduledoc "Filter activities depending on their origin instance"
|
||||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
@behaviour MRF
|
||||||
|
|
||||||
defp check_accept(%{host: actor_host} = _actor_info, object) do
|
defp check_accept(%{host: actor_host} = _actor_info, object) do
|
||||||
accepts = Pleroma.Config.get([:mrf_simple, :accept])
|
accepts =
|
||||||
|
Pleroma.Config.get([:mrf_simple, :accept])
|
||||||
|
|> MRF.subdomains_regex()
|
||||||
|
|
||||||
cond do
|
cond do
|
||||||
accepts == [] -> {:ok, object}
|
accepts == [] -> {:ok, object}
|
||||||
actor_host == Pleroma.Config.get([Pleroma.Web.Endpoint, :url, :host]) -> {:ok, object}
|
actor_host == Pleroma.Config.get([Pleroma.Web.Endpoint, :url, :host]) -> {:ok, object}
|
||||||
Enum.member?(accepts, actor_host) -> {:ok, object}
|
MRF.subdomain_match?(accepts, actor_host) -> {:ok, object}
|
||||||
true -> {:reject, nil}
|
true -> {:reject, nil}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp check_reject(%{host: actor_host} = _actor_info, object) do
|
defp check_reject(%{host: actor_host} = _actor_info, object) do
|
||||||
if Enum.member?(Pleroma.Config.get([:mrf_simple, :reject]), actor_host) do
|
rejects =
|
||||||
|
Pleroma.Config.get([:mrf_simple, :reject])
|
||||||
|
|> MRF.subdomains_regex()
|
||||||
|
|
||||||
|
if MRF.subdomain_match?(rejects, actor_host) do
|
||||||
{:reject, nil}
|
{:reject, nil}
|
||||||
else
|
else
|
||||||
{:ok, object}
|
{:ok, object}
|
||||||
|
@ -31,8 +38,12 @@ defp check_media_removal(
|
||||||
%{"type" => "Create", "object" => %{"attachment" => child_attachment}} = object
|
%{"type" => "Create", "object" => %{"attachment" => child_attachment}} = object
|
||||||
)
|
)
|
||||||
when length(child_attachment) > 0 do
|
when length(child_attachment) > 0 do
|
||||||
|
media_removal =
|
||||||
|
Pleroma.Config.get([:mrf_simple, :media_removal])
|
||||||
|
|> MRF.subdomains_regex()
|
||||||
|
|
||||||
object =
|
object =
|
||||||
if Enum.member?(Pleroma.Config.get([:mrf_simple, :media_removal]), actor_host) do
|
if MRF.subdomain_match?(media_removal, actor_host) do
|
||||||
child_object = Map.delete(object["object"], "attachment")
|
child_object = Map.delete(object["object"], "attachment")
|
||||||
Map.put(object, "object", child_object)
|
Map.put(object, "object", child_object)
|
||||||
else
|
else
|
||||||
|
@ -51,8 +62,12 @@ defp check_media_nsfw(
|
||||||
"object" => child_object
|
"object" => child_object
|
||||||
} = object
|
} = object
|
||||||
) do
|
) do
|
||||||
|
media_nsfw =
|
||||||
|
Pleroma.Config.get([:mrf_simple, :media_nsfw])
|
||||||
|
|> MRF.subdomains_regex()
|
||||||
|
|
||||||
object =
|
object =
|
||||||
if Enum.member?(Pleroma.Config.get([:mrf_simple, :media_nsfw]), actor_host) do
|
if MRF.subdomain_match?(media_nsfw, actor_host) do
|
||||||
tags = (child_object["tag"] || []) ++ ["nsfw"]
|
tags = (child_object["tag"] || []) ++ ["nsfw"]
|
||||||
child_object = Map.put(child_object, "tag", tags)
|
child_object = Map.put(child_object, "tag", tags)
|
||||||
child_object = Map.put(child_object, "sensitive", true)
|
child_object = Map.put(child_object, "sensitive", true)
|
||||||
|
@ -67,12 +82,12 @@ defp check_media_nsfw(
|
||||||
defp check_media_nsfw(_actor_info, object), do: {:ok, object}
|
defp check_media_nsfw(_actor_info, object), do: {:ok, object}
|
||||||
|
|
||||||
defp check_ftl_removal(%{host: actor_host} = _actor_info, object) do
|
defp check_ftl_removal(%{host: actor_host} = _actor_info, object) do
|
||||||
|
timeline_removal =
|
||||||
|
Pleroma.Config.get([:mrf_simple, :federated_timeline_removal])
|
||||||
|
|> MRF.subdomains_regex()
|
||||||
|
|
||||||
object =
|
object =
|
||||||
with true <-
|
with true <- MRF.subdomain_match?(timeline_removal, actor_host),
|
||||||
Enum.member?(
|
|
||||||
Pleroma.Config.get([:mrf_simple, :federated_timeline_removal]),
|
|
||||||
actor_host
|
|
||||||
),
|
|
||||||
user <- User.get_cached_by_ap_id(object["actor"]),
|
user <- User.get_cached_by_ap_id(object["actor"]),
|
||||||
true <- "https://www.w3.org/ns/activitystreams#Public" in object["to"] do
|
true <- "https://www.w3.org/ns/activitystreams#Public" in object["to"] do
|
||||||
to =
|
to =
|
||||||
|
@ -94,7 +109,11 @@ defp check_ftl_removal(%{host: actor_host} = _actor_info, object) do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp check_report_removal(%{host: actor_host} = _actor_info, %{"type" => "Flag"} = object) do
|
defp check_report_removal(%{host: actor_host} = _actor_info, %{"type" => "Flag"} = object) do
|
||||||
if actor_host in Pleroma.Config.get([:mrf_simple, :report_removal]) do
|
report_removal =
|
||||||
|
Pleroma.Config.get([:mrf_simple, :report_removal])
|
||||||
|
|> MRF.subdomains_regex()
|
||||||
|
|
||||||
|
if MRF.subdomain_match?(report_removal, actor_host) do
|
||||||
{:reject, nil}
|
{:reject, nil}
|
||||||
else
|
else
|
||||||
{:ok, object}
|
{:ok, object}
|
||||||
|
@ -104,7 +123,11 @@ defp check_report_removal(%{host: actor_host} = _actor_info, %{"type" => "Flag"}
|
||||||
defp check_report_removal(_actor_info, object), do: {:ok, object}
|
defp check_report_removal(_actor_info, object), do: {:ok, object}
|
||||||
|
|
||||||
defp check_avatar_removal(%{host: actor_host} = _actor_info, %{"icon" => _icon} = object) do
|
defp check_avatar_removal(%{host: actor_host} = _actor_info, %{"icon" => _icon} = object) do
|
||||||
if actor_host in Pleroma.Config.get([:mrf_simple, :avatar_removal]) do
|
avatar_removal =
|
||||||
|
Pleroma.Config.get([:mrf_simple, :avatar_removal])
|
||||||
|
|> MRF.subdomains_regex()
|
||||||
|
|
||||||
|
if MRF.subdomain_match?(avatar_removal, actor_host) do
|
||||||
{:ok, Map.delete(object, "icon")}
|
{:ok, Map.delete(object, "icon")}
|
||||||
else
|
else
|
||||||
{:ok, object}
|
{:ok, object}
|
||||||
|
@ -114,7 +137,11 @@ defp check_avatar_removal(%{host: actor_host} = _actor_info, %{"icon" => _icon}
|
||||||
defp check_avatar_removal(_actor_info, object), do: {:ok, object}
|
defp check_avatar_removal(_actor_info, object), do: {:ok, object}
|
||||||
|
|
||||||
defp check_banner_removal(%{host: actor_host} = _actor_info, %{"image" => _image} = object) do
|
defp check_banner_removal(%{host: actor_host} = _actor_info, %{"image" => _image} = object) do
|
||||||
if actor_host in Pleroma.Config.get([:mrf_simple, :banner_removal]) do
|
banner_removal =
|
||||||
|
Pleroma.Config.get([:mrf_simple, :banner_removal])
|
||||||
|
|> MRF.subdomains_regex()
|
||||||
|
|
||||||
|
if MRF.subdomain_match?(banner_removal, actor_host) do
|
||||||
{:ok, Map.delete(object, "image")}
|
{:ok, Map.delete(object, "image")}
|
||||||
else
|
else
|
||||||
{:ok, object}
|
{:ok, object}
|
||||||
|
|
|
@ -87,18 +87,23 @@ defp should_federate?(inbox, public) do
|
||||||
if public do
|
if public do
|
||||||
true
|
true
|
||||||
else
|
else
|
||||||
inbox_info = URI.parse(inbox)
|
%{host: host} = URI.parse(inbox)
|
||||||
!Enum.member?(Config.get([:instance, :quarantined_instances], []), inbox_info.host)
|
|
||||||
|
quarantined_instances =
|
||||||
|
Config.get([:instance, :quarantined_instances], [])
|
||||||
|
|> Pleroma.Web.ActivityPub.MRF.subdomains_regex()
|
||||||
|
|
||||||
|
!Pleroma.Web.ActivityPub.MRF.subdomain_match?(quarantined_instances, host)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec recipients(User.t(), Activity.t()) :: list(User.t()) | []
|
||||||
defp recipients(actor, activity) do
|
defp recipients(actor, activity) do
|
||||||
followers =
|
{:ok, followers} =
|
||||||
if actor.follower_address in activity.recipients do
|
if actor.follower_address in activity.recipients do
|
||||||
{:ok, followers} = User.get_followers(actor)
|
User.get_external_followers(actor)
|
||||||
Enum.filter(followers, &(!&1.local))
|
|
||||||
else
|
else
|
||||||
[]
|
{:ok, []}
|
||||||
end
|
end
|
||||||
|
|
||||||
Pleroma.Web.Salmon.remote_users(actor, activity) ++ followers
|
Pleroma.Web.Salmon.remote_users(actor, activity) ++ followers
|
||||||
|
@ -112,6 +117,45 @@ defp get_cc_ap_ids(ap_id, recipients) do
|
||||||
|> Enum.map(& &1.ap_id)
|
|> Enum.map(& &1.ap_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@as_public "https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
|
||||||
|
defp maybe_use_sharedinbox(%User{info: %{source_data: data}}),
|
||||||
|
do: (is_map(data["endpoints"]) && Map.get(data["endpoints"], "sharedInbox")) || data["inbox"]
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Determine a user inbox to use based on heuristics. These heuristics
|
||||||
|
are based on an approximation of the ``sharedInbox`` rules in the
|
||||||
|
[ActivityPub specification][ap-sharedinbox].
|
||||||
|
|
||||||
|
Please do not edit this function (or its children) without reading
|
||||||
|
the spec, as editing the code is likely to introduce some breakage
|
||||||
|
without some familiarity.
|
||||||
|
|
||||||
|
[ap-sharedinbox]: https://www.w3.org/TR/activitypub/#shared-inbox-delivery
|
||||||
|
"""
|
||||||
|
def determine_inbox(
|
||||||
|
%Activity{data: activity_data},
|
||||||
|
%User{info: %{source_data: data}} = user
|
||||||
|
) do
|
||||||
|
to = activity_data["to"] || []
|
||||||
|
cc = activity_data["cc"] || []
|
||||||
|
type = activity_data["type"]
|
||||||
|
|
||||||
|
cond do
|
||||||
|
type == "Delete" ->
|
||||||
|
maybe_use_sharedinbox(user)
|
||||||
|
|
||||||
|
@as_public in to || @as_public in cc ->
|
||||||
|
maybe_use_sharedinbox(user)
|
||||||
|
|
||||||
|
length(to) + length(cc) > 1 ->
|
||||||
|
maybe_use_sharedinbox(user)
|
||||||
|
|
||||||
|
true ->
|
||||||
|
data["inbox"]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Publishes an activity with BCC to all relevant peers.
|
Publishes an activity with BCC to all relevant peers.
|
||||||
"""
|
"""
|
||||||
|
@ -166,8 +210,8 @@ def publish(%User{} = actor, %Activity{} = activity) do
|
||||||
|
|
||||||
recipients(actor, activity)
|
recipients(actor, activity)
|
||||||
|> Enum.filter(fn user -> User.ap_enabled?(user) end)
|
|> Enum.filter(fn user -> User.ap_enabled?(user) end)
|
||||||
|> Enum.map(fn %{info: %{source_data: data}} ->
|
|> Enum.map(fn %User{} = user ->
|
||||||
(is_map(data["endpoints"]) && Map.get(data["endpoints"], "sharedInbox")) || data["inbox"]
|
determine_inbox(activity, user)
|
||||||
end)
|
end)
|
||||||
|> Enum.uniq()
|
|> Enum.uniq()
|
||||||
|> Enum.filter(fn inbox -> should_federate?(inbox, public) end)
|
|> Enum.filter(fn inbox -> should_federate?(inbox, public) end)
|
||||||
|
|
|
@ -8,14 +8,14 @@ defmodule Pleroma.Web.ActivityPub.Visibility do
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
|
||||||
|
@public "https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
|
||||||
|
@spec is_public?(Object.t() | Activity.t() | map()) :: boolean()
|
||||||
def is_public?(%Object{data: %{"type" => "Tombstone"}}), do: false
|
def is_public?(%Object{data: %{"type" => "Tombstone"}}), do: false
|
||||||
def is_public?(%Object{data: data}), do: is_public?(data)
|
def is_public?(%Object{data: data}), do: is_public?(data)
|
||||||
def is_public?(%Activity{data: data}), do: is_public?(data)
|
def is_public?(%Activity{data: data}), do: is_public?(data)
|
||||||
def is_public?(%{"directMessage" => true}), do: false
|
def is_public?(%{"directMessage" => true}), do: false
|
||||||
|
def is_public?(data), do: @public in (data["to"] ++ (data["cc"] || []))
|
||||||
def is_public?(data) do
|
|
||||||
"https://www.w3.org/ns/activitystreams#Public" in (data["to"] ++ (data["cc"] || []))
|
|
||||||
end
|
|
||||||
|
|
||||||
def is_private?(activity) do
|
def is_private?(activity) do
|
||||||
with false <- is_public?(activity),
|
with false <- is_public?(activity),
|
||||||
|
@ -69,15 +69,14 @@ def entire_thread_visible_for_user?(%Activity{} = activity, %User{} = user) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_visibility(object) do
|
def get_visibility(object) do
|
||||||
public = "https://www.w3.org/ns/activitystreams#Public"
|
|
||||||
to = object.data["to"] || []
|
to = object.data["to"] || []
|
||||||
cc = object.data["cc"] || []
|
cc = object.data["cc"] || []
|
||||||
|
|
||||||
cond do
|
cond do
|
||||||
public in to ->
|
@public in to ->
|
||||||
"public"
|
"public"
|
||||||
|
|
||||||
public in cc ->
|
@public in cc ->
|
||||||
"unlisted"
|
"unlisted"
|
||||||
|
|
||||||
# this should use the sql for the object's activity
|
# this should use the sql for the object's activity
|
||||||
|
|
|
@ -84,6 +84,7 @@ defp do_convert(entity) when is_map(entity) do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp do_convert({:dispatch, [entity]}), do: %{"tuple" => [":dispatch", [inspect(entity)]]}
|
defp do_convert({:dispatch, [entity]}), do: %{"tuple" => [":dispatch", [inspect(entity)]]}
|
||||||
|
defp do_convert({:partial_chain, entity}), do: %{"tuple" => [":partial_chain", inspect(entity)]}
|
||||||
|
|
||||||
defp do_convert(entity) when is_tuple(entity),
|
defp do_convert(entity) when is_tuple(entity),
|
||||||
do: %{"tuple" => do_convert(Tuple.to_list(entity))}
|
do: %{"tuple" => do_convert(Tuple.to_list(entity))}
|
||||||
|
@ -113,11 +114,15 @@ def transform(entity), do: :erlang.term_to_binary(entity)
|
||||||
defp do_transform(%Regex{} = entity) when is_map(entity), do: entity
|
defp do_transform(%Regex{} = entity) when is_map(entity), do: entity
|
||||||
|
|
||||||
defp do_transform(%{"tuple" => [":dispatch", [entity]]}) do
|
defp do_transform(%{"tuple" => [":dispatch", [entity]]}) do
|
||||||
cleaned_string = String.replace(entity, ~r/[^\w|^{:,[|^,|^[|^\]^}|^\/|^\.|^"]^\s/, "")
|
{dispatch_settings, []} = do_eval(entity)
|
||||||
{dispatch_settings, []} = Code.eval_string(cleaned_string, [], requires: [], macros: [])
|
|
||||||
{:dispatch, [dispatch_settings]}
|
{:dispatch, [dispatch_settings]}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp do_transform(%{"tuple" => [":partial_chain", entity]}) do
|
||||||
|
{partial_chain, []} = do_eval(entity)
|
||||||
|
{:partial_chain, partial_chain}
|
||||||
|
end
|
||||||
|
|
||||||
defp do_transform(%{"tuple" => entity}) do
|
defp do_transform(%{"tuple" => entity}) do
|
||||||
Enum.reduce(entity, {}, fn val, acc -> Tuple.append(acc, do_transform(val)) end)
|
Enum.reduce(entity, {}, fn val, acc -> Tuple.append(acc, do_transform(val)) end)
|
||||||
end
|
end
|
||||||
|
@ -149,4 +154,9 @@ defp do_transform_string(value) do
|
||||||
do: String.to_existing_atom("Elixir." <> value),
|
do: String.to_existing_atom("Elixir." <> value),
|
||||||
else: value
|
else: value
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp do_eval(entity) do
|
||||||
|
cleaned_string = String.replace(entity, ~r/[^\w|^{:,[|^,|^[|^\]^}|^\/|^\.|^"]^\s/, "")
|
||||||
|
Code.eval_string(cleaned_string, [], requires: [], macros: [])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -439,6 +439,13 @@ def maybe_notify_mentioned_recipients(
|
||||||
|
|
||||||
def maybe_notify_mentioned_recipients(recipients, _), do: recipients
|
def maybe_notify_mentioned_recipients(recipients, _), do: recipients
|
||||||
|
|
||||||
|
# Do not notify subscribers if author is making a reply
|
||||||
|
def maybe_notify_subscribers(recipients, %Activity{
|
||||||
|
object: %Object{data: %{"inReplyTo" => _ap_id}}
|
||||||
|
}) do
|
||||||
|
recipients
|
||||||
|
end
|
||||||
|
|
||||||
def maybe_notify_subscribers(
|
def maybe_notify_subscribers(
|
||||||
recipients,
|
recipients,
|
||||||
%Activity{data: %{"actor" => actor, "type" => type}} = activity
|
%Activity{data: %{"actor" => actor, "type" => type}} = activity
|
||||||
|
|
2
mix.exs
2
mix.exs
|
@ -143,7 +143,7 @@ defp deps do
|
||||||
{:telemetry, "~> 0.3"},
|
{:telemetry, "~> 0.3"},
|
||||||
{:prometheus_ex, "~> 3.0"},
|
{:prometheus_ex, "~> 3.0"},
|
||||||
{:prometheus_plugs, "~> 1.1"},
|
{:prometheus_plugs, "~> 1.1"},
|
||||||
{:prometheus_phoenix, "~> 1.2"},
|
{:prometheus_phoenix, "~> 1.3"},
|
||||||
{:prometheus_ecto, "~> 1.4"},
|
{:prometheus_ecto, "~> 1.4"},
|
||||||
{:recon, github: "ferd/recon", tag: "2.4.0"},
|
{:recon, github: "ferd/recon", tag: "2.4.0"},
|
||||||
{:quack, "~> 0.1.1"},
|
{:quack, "~> 0.1.1"},
|
||||||
|
|
|
@ -42,6 +42,28 @@ test "it creates a notification for subscribed users" do
|
||||||
|
|
||||||
assert notification.user_id == subscriber.id
|
assert notification.user_id == subscriber.id
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "does not create a notification for subscribed users if status is a reply" do
|
||||||
|
user = insert(:user)
|
||||||
|
other_user = insert(:user)
|
||||||
|
subscriber = insert(:user)
|
||||||
|
|
||||||
|
User.subscribe(subscriber, other_user)
|
||||||
|
|
||||||
|
{:ok, activity} = CommonAPI.post(user, %{"status" => "test post"})
|
||||||
|
|
||||||
|
{:ok, _reply_activity} =
|
||||||
|
CommonAPI.post(other_user, %{
|
||||||
|
"status" => "test reply",
|
||||||
|
"in_reply_to_status_id" => activity.id
|
||||||
|
})
|
||||||
|
|
||||||
|
user_notifications = Notification.for_user(user)
|
||||||
|
assert length(user_notifications) == 1
|
||||||
|
|
||||||
|
subscriber_notifications = Notification.for_user(subscriber)
|
||||||
|
assert Enum.empty?(subscriber_notifications)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "create_notification" do
|
describe "create_notification" do
|
||||||
|
|
|
@ -9,7 +9,6 @@ defmodule Pleroma.Plugs.AuthenticationPlugTest do
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
|
||||||
import ExUnit.CaptureLog
|
import ExUnit.CaptureLog
|
||||||
import Mock
|
|
||||||
|
|
||||||
setup %{conn: conn} do
|
setup %{conn: conn} do
|
||||||
user = %User{
|
user = %User{
|
||||||
|
@ -67,14 +66,13 @@ test "check pbkdf2 hash" do
|
||||||
refute AuthenticationPlug.checkpw("test-password1", hash)
|
refute AuthenticationPlug.checkpw("test-password1", hash)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@tag :skip_on_mac
|
||||||
test "check sha512-crypt hash" do
|
test "check sha512-crypt hash" do
|
||||||
hash =
|
hash =
|
||||||
"$6$9psBWV8gxkGOZWBz$PmfCycChoxeJ3GgGzwvhlgacb9mUoZ.KUXNCssekER4SJ7bOK53uXrHNb2e4i8yPFgSKyzaW9CcmrDXWIEMtD1"
|
"$6$9psBWV8gxkGOZWBz$PmfCycChoxeJ3GgGzwvhlgacb9mUoZ.KUXNCssekER4SJ7bOK53uXrHNb2e4i8yPFgSKyzaW9CcmrDXWIEMtD1"
|
||||||
|
|
||||||
with_mock :crypt, crypt: fn _password, password_hash -> password_hash end do
|
|
||||||
assert AuthenticationPlug.checkpw("password", hash)
|
assert AuthenticationPlug.checkpw("password", hash)
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
test "it returns false when hash invalid" do
|
test "it returns false when hash invalid" do
|
||||||
hash =
|
hash =
|
||||||
|
|
|
@ -5,19 +5,18 @@
|
||||||
defmodule Pleroma.Plugs.LegacyAuthenticationPlugTest do
|
defmodule Pleroma.Plugs.LegacyAuthenticationPlugTest do
|
||||||
use Pleroma.Web.ConnCase
|
use Pleroma.Web.ConnCase
|
||||||
|
|
||||||
|
import Pleroma.Factory
|
||||||
|
|
||||||
alias Pleroma.Plugs.LegacyAuthenticationPlug
|
alias Pleroma.Plugs.LegacyAuthenticationPlug
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
|
||||||
import Mock
|
|
||||||
|
|
||||||
setup do
|
setup do
|
||||||
# password is "password"
|
user =
|
||||||
user = %User{
|
insert(:user,
|
||||||
id: 1,
|
password: "password",
|
||||||
name: "dude",
|
|
||||||
password_hash:
|
password_hash:
|
||||||
"$6$9psBWV8gxkGOZWBz$PmfCycChoxeJ3GgGzwvhlgacb9mUoZ.KUXNCssekER4SJ7bOK53uXrHNb2e4i8yPFgSKyzaW9CcmrDXWIEMtD1"
|
"$6$9psBWV8gxkGOZWBz$PmfCycChoxeJ3GgGzwvhlgacb9mUoZ.KUXNCssekER4SJ7bOK53uXrHNb2e4i8yPFgSKyzaW9CcmrDXWIEMtD1"
|
||||||
}
|
)
|
||||||
|
|
||||||
%{user: user}
|
%{user: user}
|
||||||
end
|
end
|
||||||
|
@ -36,6 +35,7 @@ test "it does nothing if a user is assigned", %{conn: conn, user: user} do
|
||||||
assert ret_conn == conn
|
assert ret_conn == conn
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@tag :skip_on_mac
|
||||||
test "it authenticates the auth_user if present and password is correct and resets the password",
|
test "it authenticates the auth_user if present and password is correct and resets the password",
|
||||||
%{
|
%{
|
||||||
conn: conn,
|
conn: conn,
|
||||||
|
@ -46,22 +46,12 @@ test "it authenticates the auth_user if present and password is correct and rese
|
||||||
|> assign(:auth_credentials, %{username: "dude", password: "password"})
|
|> assign(:auth_credentials, %{username: "dude", password: "password"})
|
||||||
|> assign(:auth_user, user)
|
|> assign(:auth_user, user)
|
||||||
|
|
||||||
conn =
|
conn = LegacyAuthenticationPlug.call(conn, %{})
|
||||||
with_mocks([
|
|
||||||
{:crypt, [], [crypt: fn _password, password_hash -> password_hash end]},
|
assert conn.assigns.user.id == user.id
|
||||||
{User, [],
|
|
||||||
[
|
|
||||||
reset_password: fn user, %{password: password, password_confirmation: password} ->
|
|
||||||
{:ok, user}
|
|
||||||
end
|
|
||||||
]}
|
|
||||||
]) do
|
|
||||||
LegacyAuthenticationPlug.call(conn, %{})
|
|
||||||
end
|
|
||||||
|
|
||||||
assert conn.assigns.user == user
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@tag :skip_on_mac
|
||||||
test "it does nothing if the password is wrong", %{
|
test "it does nothing if the password is wrong", %{
|
||||||
conn: conn,
|
conn: conn,
|
||||||
user: user
|
user: user
|
||||||
|
|
|
@ -48,16 +48,14 @@ test "it returns key" do
|
||||||
|
|
||||||
test "it returns error when not found user" do
|
test "it returns error when not found user" do
|
||||||
assert capture_log(fn ->
|
assert capture_log(fn ->
|
||||||
assert Signature.fetch_public_key(make_fake_conn("test-ap_id")) ==
|
assert Signature.fetch_public_key(make_fake_conn("test-ap_id")) == {:error, :error}
|
||||||
{:error, :error}
|
|
||||||
end) =~ "[error] Could not decode user"
|
end) =~ "[error] Could not decode user"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it returns error if public key is empty" do
|
test "it returns error if public key is empty" do
|
||||||
user = insert(:user, %{info: %{source_data: %{"publicKey" => %{}}}})
|
user = insert(:user, %{info: %{source_data: %{"publicKey" => %{}}}})
|
||||||
|
|
||||||
assert Signature.fetch_public_key(make_fake_conn(user.ap_id)) ==
|
assert Signature.fetch_public_key(make_fake_conn(user.ap_id)) == {:error, :error}
|
||||||
{:error, :error}
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -65,8 +63,7 @@ test "it returns error if public key is empty" do
|
||||||
test "it returns key" do
|
test "it returns key" do
|
||||||
ap_id = "https://mastodon.social/users/lambadalambda"
|
ap_id = "https://mastodon.social/users/lambadalambda"
|
||||||
|
|
||||||
assert Signature.refetch_public_key(make_fake_conn(ap_id)) ==
|
assert Signature.refetch_public_key(make_fake_conn(ap_id)) == {:ok, @rsa_public_key}
|
||||||
{:ok, @rsa_public_key}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it returns error when not found user" do
|
test "it returns error when not found user" do
|
||||||
|
@ -105,4 +102,16 @@ test "it returns error" do
|
||||||
) == {:error, []}
|
) == {:error, []}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "key_id_to_actor_id/1" do
|
||||||
|
test "it properly deduces the actor id for misskey" do
|
||||||
|
assert Signature.key_id_to_actor_id("https://example.com/users/1234/publickey") ==
|
||||||
|
"https://example.com/users/1234"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it properly deduces the actor id for mastodon and pleroma" do
|
||||||
|
assert Signature.key_id_to_actor_id("https://example.com/users/1234#main-key") ==
|
||||||
|
"https://example.com/users/1234"
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -118,9 +118,11 @@ def direct_note_activity_factory do
|
||||||
def note_activity_factory(attrs \\ %{}) do
|
def note_activity_factory(attrs \\ %{}) do
|
||||||
user = attrs[:user] || insert(:user)
|
user = attrs[:user] || insert(:user)
|
||||||
note = attrs[:note] || insert(:note, user: user)
|
note = attrs[:note] || insert(:note, user: user)
|
||||||
attrs = Map.drop(attrs, [:user, :note])
|
data_attrs = attrs[:data_attrs] || %{}
|
||||||
|
attrs = Map.drop(attrs, [:user, :note, :data_attrs])
|
||||||
|
|
||||||
data = %{
|
data =
|
||||||
|
%{
|
||||||
"id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id(),
|
"id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id(),
|
||||||
"type" => "Create",
|
"type" => "Create",
|
||||||
"actor" => note.data["actor"],
|
"actor" => note.data["actor"],
|
||||||
|
@ -129,6 +131,7 @@ def note_activity_factory(attrs \\ %{}) do
|
||||||
"published" => DateTime.utc_now() |> DateTime.to_iso8601(),
|
"published" => DateTime.utc_now() |> DateTime.to_iso8601(),
|
||||||
"context" => note.data["context"]
|
"context" => note.data["context"]
|
||||||
}
|
}
|
||||||
|
|> Map.merge(data_attrs)
|
||||||
|
|
||||||
%Pleroma.Activity{
|
%Pleroma.Activity{
|
||||||
data: data,
|
data: data,
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
|
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
ExUnit.start()
|
os_exclude = if :os.type() == {:unix, :darwin}, do: [skip_on_mac: true], else: []
|
||||||
|
ExUnit.start(exclude: os_exclude)
|
||||||
Ecto.Adapters.SQL.Sandbox.mode(Pleroma.Repo, :manual)
|
Ecto.Adapters.SQL.Sandbox.mode(Pleroma.Repo, :manual)
|
||||||
Mox.defmock(Pleroma.ReverseProxy.ClientMock, for: Pleroma.ReverseProxy.Client)
|
Mox.defmock(Pleroma.ReverseProxy.ClientMock, for: Pleroma.ReverseProxy.Client)
|
||||||
{:ok, _} = Application.ensure_all_started(:ex_machina)
|
{:ok, _} = Application.ensure_all_started(:ex_machina)
|
||||||
|
|
|
@ -824,6 +824,48 @@ test "blocks domains" do
|
||||||
assert User.blocks?(user, collateral_user)
|
assert User.blocks?(user, collateral_user)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "does not block domain with same end" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
collateral_user =
|
||||||
|
insert(:user, %{ap_id: "https://another-awful-and-rude-instance.com/user/bully"})
|
||||||
|
|
||||||
|
{:ok, user} = User.block_domain(user, "awful-and-rude-instance.com")
|
||||||
|
|
||||||
|
refute User.blocks?(user, collateral_user)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "does not block domain with same end if wildcard added" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
collateral_user =
|
||||||
|
insert(:user, %{ap_id: "https://another-awful-and-rude-instance.com/user/bully"})
|
||||||
|
|
||||||
|
{:ok, user} = User.block_domain(user, "*.awful-and-rude-instance.com")
|
||||||
|
|
||||||
|
refute User.blocks?(user, collateral_user)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "blocks domain with wildcard for subdomain" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
user_from_subdomain =
|
||||||
|
insert(:user, %{ap_id: "https://subdomain.awful-and-rude-instance.com/user/bully"})
|
||||||
|
|
||||||
|
user_with_two_subdomains =
|
||||||
|
insert(:user, %{
|
||||||
|
ap_id: "https://subdomain.second_subdomain.awful-and-rude-instance.com/user/bully"
|
||||||
|
})
|
||||||
|
|
||||||
|
user_domain = insert(:user, %{ap_id: "https://awful-and-rude-instance.com/user/bully"})
|
||||||
|
|
||||||
|
{:ok, user} = User.block_domain(user, "*.awful-and-rude-instance.com")
|
||||||
|
|
||||||
|
assert User.blocks?(user, user_from_subdomain)
|
||||||
|
assert User.blocks?(user, user_with_two_subdomains)
|
||||||
|
assert User.blocks?(user, user_domain)
|
||||||
|
end
|
||||||
|
|
||||||
test "unblocks domains" do
|
test "unblocks domains" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
collateral_user = insert(:user, %{ap_id: "https://awful-and-rude-instance.com/user/bully"})
|
collateral_user = insert(:user, %{ap_id: "https://awful-and-rude-instance.com/user/bully"})
|
||||||
|
|
|
@ -6,11 +6,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
||||||
use Pleroma.DataCase
|
use Pleroma.DataCase
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Builders.ActivityBuilder
|
alias Pleroma.Builders.ActivityBuilder
|
||||||
alias Pleroma.Instances
|
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
alias Pleroma.Web.ActivityPub.Publisher
|
|
||||||
alias Pleroma.Web.ActivityPub.Utils
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
|
|
||||||
|
@ -1083,113 +1081,6 @@ test "it can create a Flag activity" do
|
||||||
} = activity
|
} = activity
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "publish_one/1" do
|
|
||||||
test_with_mock "calls `Instances.set_reachable` on successful federation if `unreachable_since` is not specified",
|
|
||||||
Instances,
|
|
||||||
[:passthrough],
|
|
||||||
[] do
|
|
||||||
actor = insert(:user)
|
|
||||||
inbox = "http://200.site/users/nick1/inbox"
|
|
||||||
|
|
||||||
assert {:ok, _} = Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1})
|
|
||||||
|
|
||||||
assert called(Instances.set_reachable(inbox))
|
|
||||||
end
|
|
||||||
|
|
||||||
test_with_mock "calls `Instances.set_reachable` on successful federation if `unreachable_since` is set",
|
|
||||||
Instances,
|
|
||||||
[:passthrough],
|
|
||||||
[] do
|
|
||||||
actor = insert(:user)
|
|
||||||
inbox = "http://200.site/users/nick1/inbox"
|
|
||||||
|
|
||||||
assert {:ok, _} =
|
|
||||||
Publisher.publish_one(%{
|
|
||||||
inbox: inbox,
|
|
||||||
json: "{}",
|
|
||||||
actor: actor,
|
|
||||||
id: 1,
|
|
||||||
unreachable_since: NaiveDateTime.utc_now()
|
|
||||||
})
|
|
||||||
|
|
||||||
assert called(Instances.set_reachable(inbox))
|
|
||||||
end
|
|
||||||
|
|
||||||
test_with_mock "does NOT call `Instances.set_reachable` on successful federation if `unreachable_since` is nil",
|
|
||||||
Instances,
|
|
||||||
[:passthrough],
|
|
||||||
[] do
|
|
||||||
actor = insert(:user)
|
|
||||||
inbox = "http://200.site/users/nick1/inbox"
|
|
||||||
|
|
||||||
assert {:ok, _} =
|
|
||||||
Publisher.publish_one(%{
|
|
||||||
inbox: inbox,
|
|
||||||
json: "{}",
|
|
||||||
actor: actor,
|
|
||||||
id: 1,
|
|
||||||
unreachable_since: nil
|
|
||||||
})
|
|
||||||
|
|
||||||
refute called(Instances.set_reachable(inbox))
|
|
||||||
end
|
|
||||||
|
|
||||||
test_with_mock "calls `Instances.set_unreachable` on target inbox on non-2xx HTTP response code",
|
|
||||||
Instances,
|
|
||||||
[:passthrough],
|
|
||||||
[] do
|
|
||||||
actor = insert(:user)
|
|
||||||
inbox = "http://404.site/users/nick1/inbox"
|
|
||||||
|
|
||||||
assert {:error, _} = Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1})
|
|
||||||
|
|
||||||
assert called(Instances.set_unreachable(inbox))
|
|
||||||
end
|
|
||||||
|
|
||||||
test_with_mock "it calls `Instances.set_unreachable` on target inbox on request error of any kind",
|
|
||||||
Instances,
|
|
||||||
[:passthrough],
|
|
||||||
[] do
|
|
||||||
actor = insert(:user)
|
|
||||||
inbox = "http://connrefused.site/users/nick1/inbox"
|
|
||||||
|
|
||||||
assert {:error, _} = Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1})
|
|
||||||
|
|
||||||
assert called(Instances.set_unreachable(inbox))
|
|
||||||
end
|
|
||||||
|
|
||||||
test_with_mock "does NOT call `Instances.set_unreachable` if target is reachable",
|
|
||||||
Instances,
|
|
||||||
[:passthrough],
|
|
||||||
[] do
|
|
||||||
actor = insert(:user)
|
|
||||||
inbox = "http://200.site/users/nick1/inbox"
|
|
||||||
|
|
||||||
assert {:ok, _} = Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1})
|
|
||||||
|
|
||||||
refute called(Instances.set_unreachable(inbox))
|
|
||||||
end
|
|
||||||
|
|
||||||
test_with_mock "does NOT call `Instances.set_unreachable` if target instance has non-nil `unreachable_since`",
|
|
||||||
Instances,
|
|
||||||
[:passthrough],
|
|
||||||
[] do
|
|
||||||
actor = insert(:user)
|
|
||||||
inbox = "http://connrefused.site/users/nick1/inbox"
|
|
||||||
|
|
||||||
assert {:error, _} =
|
|
||||||
Publisher.publish_one(%{
|
|
||||||
inbox: inbox,
|
|
||||||
json: "{}",
|
|
||||||
actor: actor,
|
|
||||||
id: 1,
|
|
||||||
unreachable_since: NaiveDateTime.utc_now()
|
|
||||||
})
|
|
||||||
|
|
||||||
refute called(Instances.set_unreachable(inbox))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
test "fetch_activities/2 returns activities addressed to a list " do
|
test "fetch_activities/2 returns activities addressed to a list " do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
member = insert(:user)
|
member = insert(:user)
|
||||||
|
|
46
test/web/activity_pub/mrf/mrf_test.exs
Normal file
46
test/web/activity_pub/mrf/mrf_test.exs
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
defmodule Pleroma.Web.ActivityPub.MRFTest do
|
||||||
|
use ExUnit.Case, async: true
|
||||||
|
alias Pleroma.Web.ActivityPub.MRF
|
||||||
|
|
||||||
|
test "subdomains_regex/1" do
|
||||||
|
assert MRF.subdomains_regex(["unsafe.tld", "*.unsafe.tld"]) == [
|
||||||
|
~r/^unsafe.tld$/,
|
||||||
|
~r/^(.*\.)*unsafe.tld$/
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "subdomain_match/2" do
|
||||||
|
test "common domains" do
|
||||||
|
regexes = MRF.subdomains_regex(["unsafe.tld", "unsafe2.tld"])
|
||||||
|
|
||||||
|
assert regexes == [~r/^unsafe.tld$/, ~r/^unsafe2.tld$/]
|
||||||
|
|
||||||
|
assert MRF.subdomain_match?(regexes, "unsafe.tld")
|
||||||
|
assert MRF.subdomain_match?(regexes, "unsafe2.tld")
|
||||||
|
|
||||||
|
refute MRF.subdomain_match?(regexes, "example.com")
|
||||||
|
end
|
||||||
|
|
||||||
|
test "wildcard domains with one subdomain" do
|
||||||
|
regexes = MRF.subdomains_regex(["*.unsafe.tld"])
|
||||||
|
|
||||||
|
assert regexes == [~r/^(.*\.)*unsafe.tld$/]
|
||||||
|
|
||||||
|
assert MRF.subdomain_match?(regexes, "unsafe.tld")
|
||||||
|
assert MRF.subdomain_match?(regexes, "sub.unsafe.tld")
|
||||||
|
refute MRF.subdomain_match?(regexes, "anotherunsafe.tld")
|
||||||
|
refute MRF.subdomain_match?(regexes, "unsafe.tldanother")
|
||||||
|
end
|
||||||
|
|
||||||
|
test "wildcard domains with two subdomains" do
|
||||||
|
regexes = MRF.subdomains_regex(["*.unsafe.tld"])
|
||||||
|
|
||||||
|
assert regexes == [~r/^(.*\.)*unsafe.tld$/]
|
||||||
|
|
||||||
|
assert MRF.subdomain_match?(regexes, "unsafe.tld")
|
||||||
|
assert MRF.subdomain_match?(regexes, "sub.sub.unsafe.tld")
|
||||||
|
refute MRF.subdomain_match?(regexes, "sub.anotherunsafe.tld")
|
||||||
|
refute MRF.subdomain_match?(regexes, "sub.unsafe.tldanother")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -49,6 +49,19 @@ test "has a matching host" do
|
||||||
|
|
||||||
assert SimplePolicy.filter(local_message) == {:ok, local_message}
|
assert SimplePolicy.filter(local_message) == {:ok, local_message}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "match with wildcard domain" do
|
||||||
|
Config.put([:mrf_simple, :media_removal], ["*.remote.instance"])
|
||||||
|
media_message = build_media_message()
|
||||||
|
local_message = build_local_message()
|
||||||
|
|
||||||
|
assert SimplePolicy.filter(media_message) ==
|
||||||
|
{:ok,
|
||||||
|
media_message
|
||||||
|
|> Map.put("object", Map.delete(media_message["object"], "attachment"))}
|
||||||
|
|
||||||
|
assert SimplePolicy.filter(local_message) == {:ok, local_message}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "when :media_nsfw" do
|
describe "when :media_nsfw" do
|
||||||
|
@ -74,6 +87,20 @@ test "has a matching host" do
|
||||||
|
|
||||||
assert SimplePolicy.filter(local_message) == {:ok, local_message}
|
assert SimplePolicy.filter(local_message) == {:ok, local_message}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "match with wildcard domain" do
|
||||||
|
Config.put([:mrf_simple, :media_nsfw], ["*.remote.instance"])
|
||||||
|
media_message = build_media_message()
|
||||||
|
local_message = build_local_message()
|
||||||
|
|
||||||
|
assert SimplePolicy.filter(media_message) ==
|
||||||
|
{:ok,
|
||||||
|
media_message
|
||||||
|
|> put_in(["object", "tag"], ["foo", "nsfw"])
|
||||||
|
|> put_in(["object", "sensitive"], true)}
|
||||||
|
|
||||||
|
assert SimplePolicy.filter(local_message) == {:ok, local_message}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp build_media_message do
|
defp build_media_message do
|
||||||
|
@ -106,6 +133,15 @@ test "has a matching host" do
|
||||||
assert SimplePolicy.filter(report_message) == {:reject, nil}
|
assert SimplePolicy.filter(report_message) == {:reject, nil}
|
||||||
assert SimplePolicy.filter(local_message) == {:ok, local_message}
|
assert SimplePolicy.filter(local_message) == {:ok, local_message}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "match with wildcard domain" do
|
||||||
|
Config.put([:mrf_simple, :report_removal], ["*.remote.instance"])
|
||||||
|
report_message = build_report_message()
|
||||||
|
local_message = build_local_message()
|
||||||
|
|
||||||
|
assert SimplePolicy.filter(report_message) == {:reject, nil}
|
||||||
|
assert SimplePolicy.filter(local_message) == {:ok, local_message}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp build_report_message do
|
defp build_report_message do
|
||||||
|
@ -146,6 +182,27 @@ test "has a matching host" do
|
||||||
assert SimplePolicy.filter(local_message) == {:ok, local_message}
|
assert SimplePolicy.filter(local_message) == {:ok, local_message}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "match with wildcard domain" do
|
||||||
|
{actor, ftl_message} = build_ftl_actor_and_message()
|
||||||
|
|
||||||
|
ftl_message_actor_host =
|
||||||
|
ftl_message
|
||||||
|
|> Map.fetch!("actor")
|
||||||
|
|> URI.parse()
|
||||||
|
|> Map.fetch!(:host)
|
||||||
|
|
||||||
|
Config.put([:mrf_simple, :federated_timeline_removal], ["*." <> ftl_message_actor_host])
|
||||||
|
local_message = build_local_message()
|
||||||
|
|
||||||
|
assert {:ok, ftl_message} = SimplePolicy.filter(ftl_message)
|
||||||
|
assert actor.follower_address in ftl_message["to"]
|
||||||
|
refute actor.follower_address in ftl_message["cc"]
|
||||||
|
refute "https://www.w3.org/ns/activitystreams#Public" in ftl_message["to"]
|
||||||
|
assert "https://www.w3.org/ns/activitystreams#Public" in ftl_message["cc"]
|
||||||
|
|
||||||
|
assert SimplePolicy.filter(local_message) == {:ok, local_message}
|
||||||
|
end
|
||||||
|
|
||||||
test "has a matching host but only as:Public in to" do
|
test "has a matching host but only as:Public in to" do
|
||||||
{_actor, ftl_message} = build_ftl_actor_and_message()
|
{_actor, ftl_message} = build_ftl_actor_and_message()
|
||||||
|
|
||||||
|
@ -192,6 +249,14 @@ test "has a matching host" do
|
||||||
|
|
||||||
assert SimplePolicy.filter(remote_message) == {:reject, nil}
|
assert SimplePolicy.filter(remote_message) == {:reject, nil}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "match with wildcard domain" do
|
||||||
|
Config.put([:mrf_simple, :reject], ["*.remote.instance"])
|
||||||
|
|
||||||
|
remote_message = build_remote_message()
|
||||||
|
|
||||||
|
assert SimplePolicy.filter(remote_message) == {:reject, nil}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "when :accept" do
|
describe "when :accept" do
|
||||||
|
@ -224,6 +289,16 @@ test "has a matching host" do
|
||||||
assert SimplePolicy.filter(local_message) == {:ok, local_message}
|
assert SimplePolicy.filter(local_message) == {:ok, local_message}
|
||||||
assert SimplePolicy.filter(remote_message) == {:ok, remote_message}
|
assert SimplePolicy.filter(remote_message) == {:ok, remote_message}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "match with wildcard domain" do
|
||||||
|
Config.put([:mrf_simple, :accept], ["*.remote.instance"])
|
||||||
|
|
||||||
|
local_message = build_local_message()
|
||||||
|
remote_message = build_remote_message()
|
||||||
|
|
||||||
|
assert SimplePolicy.filter(local_message) == {:ok, local_message}
|
||||||
|
assert SimplePolicy.filter(remote_message) == {:ok, remote_message}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "when :avatar_removal" do
|
describe "when :avatar_removal" do
|
||||||
|
@ -251,6 +326,15 @@ test "has a matching host" do
|
||||||
|
|
||||||
refute filtered["icon"]
|
refute filtered["icon"]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "match with wildcard domain" do
|
||||||
|
Config.put([:mrf_simple, :avatar_removal], ["*.remote.instance"])
|
||||||
|
|
||||||
|
remote_user = build_remote_user()
|
||||||
|
{:ok, filtered} = SimplePolicy.filter(remote_user)
|
||||||
|
|
||||||
|
refute filtered["icon"]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "when :banner_removal" do
|
describe "when :banner_removal" do
|
||||||
|
@ -278,6 +362,15 @@ test "has a matching host" do
|
||||||
|
|
||||||
refute filtered["image"]
|
refute filtered["image"]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "match with wildcard domain" do
|
||||||
|
Config.put([:mrf_simple, :banner_removal], ["*.remote.instance"])
|
||||||
|
|
||||||
|
remote_user = build_remote_user()
|
||||||
|
{:ok, filtered} = SimplePolicy.filter(remote_user)
|
||||||
|
|
||||||
|
refute filtered["image"]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp build_local_message do
|
defp build_local_message do
|
||||||
|
|
266
test/web/activity_pub/publisher_test.exs
Normal file
266
test/web/activity_pub/publisher_test.exs
Normal file
|
@ -0,0 +1,266 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.ActivityPub.PublisherTest do
|
||||||
|
use Pleroma.DataCase
|
||||||
|
|
||||||
|
import Pleroma.Factory
|
||||||
|
import Tesla.Mock
|
||||||
|
import Mock
|
||||||
|
|
||||||
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Instances
|
||||||
|
alias Pleroma.Web.ActivityPub.Publisher
|
||||||
|
|
||||||
|
@as_public "https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
|
||||||
|
setup do
|
||||||
|
mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "determine_inbox/2" do
|
||||||
|
test "it returns sharedInbox for messages involving as:Public in to" do
|
||||||
|
user =
|
||||||
|
insert(:user, %{
|
||||||
|
info: %{source_data: %{"endpoints" => %{"sharedInbox" => "http://example.com/inbox"}}}
|
||||||
|
})
|
||||||
|
|
||||||
|
activity = %Activity{
|
||||||
|
data: %{"to" => [@as_public], "cc" => [user.follower_address]}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert Publisher.determine_inbox(activity, user) == "http://example.com/inbox"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it returns sharedInbox for messages involving as:Public in cc" do
|
||||||
|
user =
|
||||||
|
insert(:user, %{
|
||||||
|
info: %{source_data: %{"endpoints" => %{"sharedInbox" => "http://example.com/inbox"}}}
|
||||||
|
})
|
||||||
|
|
||||||
|
activity = %Activity{
|
||||||
|
data: %{"cc" => [@as_public], "to" => [user.follower_address]}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert Publisher.determine_inbox(activity, user) == "http://example.com/inbox"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it returns sharedInbox for messages involving multiple recipients in to" do
|
||||||
|
user =
|
||||||
|
insert(:user, %{
|
||||||
|
info: %{source_data: %{"endpoints" => %{"sharedInbox" => "http://example.com/inbox"}}}
|
||||||
|
})
|
||||||
|
|
||||||
|
user_two = insert(:user)
|
||||||
|
user_three = insert(:user)
|
||||||
|
|
||||||
|
activity = %Activity{
|
||||||
|
data: %{"cc" => [], "to" => [user.ap_id, user_two.ap_id, user_three.ap_id]}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert Publisher.determine_inbox(activity, user) == "http://example.com/inbox"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it returns sharedInbox for messages involving multiple recipients in cc" do
|
||||||
|
user =
|
||||||
|
insert(:user, %{
|
||||||
|
info: %{source_data: %{"endpoints" => %{"sharedInbox" => "http://example.com/inbox"}}}
|
||||||
|
})
|
||||||
|
|
||||||
|
user_two = insert(:user)
|
||||||
|
user_three = insert(:user)
|
||||||
|
|
||||||
|
activity = %Activity{
|
||||||
|
data: %{"to" => [], "cc" => [user.ap_id, user_two.ap_id, user_three.ap_id]}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert Publisher.determine_inbox(activity, user) == "http://example.com/inbox"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it returns sharedInbox for messages involving multiple recipients in total" do
|
||||||
|
user =
|
||||||
|
insert(:user, %{
|
||||||
|
info: %{
|
||||||
|
source_data: %{
|
||||||
|
"inbox" => "http://example.com/personal-inbox",
|
||||||
|
"endpoints" => %{"sharedInbox" => "http://example.com/inbox"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
user_two = insert(:user)
|
||||||
|
|
||||||
|
activity = %Activity{
|
||||||
|
data: %{"to" => [user_two.ap_id], "cc" => [user.ap_id]}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert Publisher.determine_inbox(activity, user) == "http://example.com/inbox"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it returns inbox for messages involving single recipients in total" do
|
||||||
|
user =
|
||||||
|
insert(:user, %{
|
||||||
|
info: %{
|
||||||
|
source_data: %{
|
||||||
|
"inbox" => "http://example.com/personal-inbox",
|
||||||
|
"endpoints" => %{"sharedInbox" => "http://example.com/inbox"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
activity = %Activity{
|
||||||
|
data: %{"to" => [user.ap_id], "cc" => []}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert Publisher.determine_inbox(activity, user) == "http://example.com/personal-inbox"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "publish_one/1" do
|
||||||
|
test_with_mock "calls `Instances.set_reachable` on successful federation if `unreachable_since` is not specified",
|
||||||
|
Instances,
|
||||||
|
[:passthrough],
|
||||||
|
[] do
|
||||||
|
actor = insert(:user)
|
||||||
|
inbox = "http://200.site/users/nick1/inbox"
|
||||||
|
|
||||||
|
assert {:ok, _} = Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1})
|
||||||
|
|
||||||
|
assert called(Instances.set_reachable(inbox))
|
||||||
|
end
|
||||||
|
|
||||||
|
test_with_mock "calls `Instances.set_reachable` on successful federation if `unreachable_since` is set",
|
||||||
|
Instances,
|
||||||
|
[:passthrough],
|
||||||
|
[] do
|
||||||
|
actor = insert(:user)
|
||||||
|
inbox = "http://200.site/users/nick1/inbox"
|
||||||
|
|
||||||
|
assert {:ok, _} =
|
||||||
|
Publisher.publish_one(%{
|
||||||
|
inbox: inbox,
|
||||||
|
json: "{}",
|
||||||
|
actor: actor,
|
||||||
|
id: 1,
|
||||||
|
unreachable_since: NaiveDateTime.utc_now()
|
||||||
|
})
|
||||||
|
|
||||||
|
assert called(Instances.set_reachable(inbox))
|
||||||
|
end
|
||||||
|
|
||||||
|
test_with_mock "does NOT call `Instances.set_reachable` on successful federation if `unreachable_since` is nil",
|
||||||
|
Instances,
|
||||||
|
[:passthrough],
|
||||||
|
[] do
|
||||||
|
actor = insert(:user)
|
||||||
|
inbox = "http://200.site/users/nick1/inbox"
|
||||||
|
|
||||||
|
assert {:ok, _} =
|
||||||
|
Publisher.publish_one(%{
|
||||||
|
inbox: inbox,
|
||||||
|
json: "{}",
|
||||||
|
actor: actor,
|
||||||
|
id: 1,
|
||||||
|
unreachable_since: nil
|
||||||
|
})
|
||||||
|
|
||||||
|
refute called(Instances.set_reachable(inbox))
|
||||||
|
end
|
||||||
|
|
||||||
|
test_with_mock "calls `Instances.set_unreachable` on target inbox on non-2xx HTTP response code",
|
||||||
|
Instances,
|
||||||
|
[:passthrough],
|
||||||
|
[] do
|
||||||
|
actor = insert(:user)
|
||||||
|
inbox = "http://404.site/users/nick1/inbox"
|
||||||
|
|
||||||
|
assert {:error, _} = Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1})
|
||||||
|
|
||||||
|
assert called(Instances.set_unreachable(inbox))
|
||||||
|
end
|
||||||
|
|
||||||
|
test_with_mock "it calls `Instances.set_unreachable` on target inbox on request error of any kind",
|
||||||
|
Instances,
|
||||||
|
[:passthrough],
|
||||||
|
[] do
|
||||||
|
actor = insert(:user)
|
||||||
|
inbox = "http://connrefused.site/users/nick1/inbox"
|
||||||
|
|
||||||
|
assert {:error, _} = Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1})
|
||||||
|
|
||||||
|
assert called(Instances.set_unreachable(inbox))
|
||||||
|
end
|
||||||
|
|
||||||
|
test_with_mock "does NOT call `Instances.set_unreachable` if target is reachable",
|
||||||
|
Instances,
|
||||||
|
[:passthrough],
|
||||||
|
[] do
|
||||||
|
actor = insert(:user)
|
||||||
|
inbox = "http://200.site/users/nick1/inbox"
|
||||||
|
|
||||||
|
assert {:ok, _} = Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1})
|
||||||
|
|
||||||
|
refute called(Instances.set_unreachable(inbox))
|
||||||
|
end
|
||||||
|
|
||||||
|
test_with_mock "does NOT call `Instances.set_unreachable` if target instance has non-nil `unreachable_since`",
|
||||||
|
Instances,
|
||||||
|
[:passthrough],
|
||||||
|
[] do
|
||||||
|
actor = insert(:user)
|
||||||
|
inbox = "http://connrefused.site/users/nick1/inbox"
|
||||||
|
|
||||||
|
assert {:error, _} =
|
||||||
|
Publisher.publish_one(%{
|
||||||
|
inbox: inbox,
|
||||||
|
json: "{}",
|
||||||
|
actor: actor,
|
||||||
|
id: 1,
|
||||||
|
unreachable_since: NaiveDateTime.utc_now()
|
||||||
|
})
|
||||||
|
|
||||||
|
refute called(Instances.set_unreachable(inbox))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "publish/2" do
|
||||||
|
test_with_mock "publishes an activity with BCC to all relevant peers.",
|
||||||
|
Pleroma.Web.Federator.Publisher,
|
||||||
|
[:passthrough],
|
||||||
|
[] do
|
||||||
|
follower =
|
||||||
|
insert(:user,
|
||||||
|
local: false,
|
||||||
|
info: %{
|
||||||
|
ap_enabled: true,
|
||||||
|
source_data: %{"inbox" => "https://domain.com/users/nick1/inbox"}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
actor = insert(:user, follower_address: follower.ap_id)
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, _follower_one} = Pleroma.User.follow(follower, actor)
|
||||||
|
actor = refresh_record(actor)
|
||||||
|
|
||||||
|
note_activity =
|
||||||
|
insert(:note_activity,
|
||||||
|
recipients: [follower.ap_id],
|
||||||
|
data_attrs: %{"bcc" => [user.ap_id]}
|
||||||
|
)
|
||||||
|
|
||||||
|
res = Publisher.publish(actor, note_activity)
|
||||||
|
assert res == :ok
|
||||||
|
|
||||||
|
assert called(
|
||||||
|
Pleroma.Web.Federator.Publisher.enqueue_one(Publisher, %{
|
||||||
|
inbox: "https://domain.com/users/nick1/inbox",
|
||||||
|
actor: actor,
|
||||||
|
id: note_activity.data["id"]
|
||||||
|
})
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1571,7 +1571,8 @@ test "common config example", %{conn: conn} do
|
||||||
%{"tuple" => [":method", "Pleroma.Captcha.Kocaptcha"]},
|
%{"tuple" => [":method", "Pleroma.Captcha.Kocaptcha"]},
|
||||||
%{"tuple" => [":seconds_valid", 60]},
|
%{"tuple" => [":seconds_valid", 60]},
|
||||||
%{"tuple" => [":path", ""]},
|
%{"tuple" => [":path", ""]},
|
||||||
%{"tuple" => [":key1", nil]}
|
%{"tuple" => [":key1", nil]},
|
||||||
|
%{"tuple" => [":partial_chain", "&:hackney_connect.partial_chain/1"]}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -1587,7 +1588,8 @@ test "common config example", %{conn: conn} do
|
||||||
%{"tuple" => [":method", "Pleroma.Captcha.Kocaptcha"]},
|
%{"tuple" => [":method", "Pleroma.Captcha.Kocaptcha"]},
|
||||||
%{"tuple" => [":seconds_valid", 60]},
|
%{"tuple" => [":seconds_valid", 60]},
|
||||||
%{"tuple" => [":path", ""]},
|
%{"tuple" => [":path", ""]},
|
||||||
%{"tuple" => [":key1", nil]}
|
%{"tuple" => [":key1", nil]},
|
||||||
|
%{"tuple" => [":partial_chain", "&:hackney_connect.partial_chain/1"]}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -238,6 +238,14 @@ test "simple keyword" do
|
||||||
assert Config.from_binary(binary) == [key: "value"]
|
assert Config.from_binary(binary) == [key: "value"]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "keyword with partial_chain key" do
|
||||||
|
binary =
|
||||||
|
Config.transform([%{"tuple" => [":partial_chain", "&:hackney_connect.partial_chain/1"]}])
|
||||||
|
|
||||||
|
assert binary == :erlang.term_to_binary(partial_chain: &:hackney_connect.partial_chain/1)
|
||||||
|
assert Config.from_binary(binary) == [partial_chain: &:hackney_connect.partial_chain/1]
|
||||||
|
end
|
||||||
|
|
||||||
test "keyword" do
|
test "keyword" do
|
||||||
binary =
|
binary =
|
||||||
Config.transform([
|
Config.transform([
|
||||||
|
|
|
@ -22,6 +22,15 @@ defmodule Pleroma.Web.FederatorTest do
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "Publisher.perform" do
|
||||||
|
test "call `perform` with unknown task" do
|
||||||
|
assert {
|
||||||
|
:error,
|
||||||
|
"Don't know what to do with this"
|
||||||
|
} = Pleroma.Web.Federator.Publisher.perform("test", :ok, :ok)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "Publish an activity" do
|
describe "Publish an activity" do
|
||||||
setup do
|
setup do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
|
|
@ -3786,6 +3786,20 @@ test "does not return users who have favorited the status but are blocked", %{
|
||||||
|
|
||||||
assert Enum.empty?(response)
|
assert Enum.empty?(response)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
|
||||||
|
other_user = insert(:user)
|
||||||
|
{:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> assign(:user, nil)
|
||||||
|
|> get("/api/v1/statuses/#{activity.id}/favourited_by")
|
||||||
|
|> json_response(:ok)
|
||||||
|
|
||||||
|
[%{"id" => id}] = response
|
||||||
|
assert id == other_user.id
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "GET /api/v1/statuses/:id/reblogged_by" do
|
describe "GET /api/v1/statuses/:id/reblogged_by" do
|
||||||
|
@ -3843,6 +3857,20 @@ test "does not return users who have reblogged the status but are blocked", %{
|
||||||
|
|
||||||
assert Enum.empty?(response)
|
assert Enum.empty?(response)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
|
||||||
|
other_user = insert(:user)
|
||||||
|
{:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> assign(:user, nil)
|
||||||
|
|> get("/api/v1/statuses/#{activity.id}/reblogged_by")
|
||||||
|
|> json_response(:ok)
|
||||||
|
|
||||||
|
[%{"id" => id}] = response
|
||||||
|
assert id == other_user.id
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "POST /auth/password, with valid parameters" do
|
describe "POST /auth/password, with valid parameters" do
|
||||||
|
|
Loading…
Reference in a new issue