forked from AkkomaGang/akkoma
Merge branch 'security/follow-always-async' into 'develop'
AP follows must be always async (closes #306) Closes #306 See merge request pleroma/pleroma!368
This commit is contained in:
commit
4a3a46074d
7 changed files with 53 additions and 32 deletions
|
@ -109,7 +109,8 @@
|
||||||
config :pleroma, :activitypub,
|
config :pleroma, :activitypub,
|
||||||
accept_blocks: true,
|
accept_blocks: true,
|
||||||
unfollow_blocked: true,
|
unfollow_blocked: true,
|
||||||
outgoing_blocks: true
|
outgoing_blocks: true,
|
||||||
|
follow_handshake_timeout: 500
|
||||||
|
|
||||||
config :pleroma, :user, deny_follow_blocked: true
|
config :pleroma, :user, deny_follow_blocked: true
|
||||||
|
|
||||||
|
|
|
@ -185,32 +185,7 @@ def needs_update?(%User{local: false} = user) do
|
||||||
def needs_update?(_), do: true
|
def needs_update?(_), do: true
|
||||||
|
|
||||||
def maybe_direct_follow(%User{} = follower, %User{info: info} = followed) do
|
def maybe_direct_follow(%User{} = follower, %User{info: info} = followed) do
|
||||||
user_config = Application.get_env(:pleroma, :user)
|
if !User.ap_enabled?(followed) do
|
||||||
deny_follow_blocked = Keyword.get(user_config, :deny_follow_blocked)
|
|
||||||
|
|
||||||
user_info = user_info(followed)
|
|
||||||
|
|
||||||
should_direct_follow =
|
|
||||||
cond do
|
|
||||||
# if the account is locked, don't pre-create the relationship
|
|
||||||
user_info[:locked] == true ->
|
|
||||||
false
|
|
||||||
|
|
||||||
# if the users are blocking each other, we shouldn't even be here, but check for it anyway
|
|
||||||
deny_follow_blocked and
|
|
||||||
(User.blocks?(follower, followed) or User.blocks?(followed, follower)) ->
|
|
||||||
false
|
|
||||||
|
|
||||||
# if OStatus, then there is no three-way handshake to follow
|
|
||||||
User.ap_enabled?(followed) != true ->
|
|
||||||
true
|
|
||||||
|
|
||||||
# if there are no other reasons not to, just pre-create the relationship
|
|
||||||
true ->
|
|
||||||
true
|
|
||||||
end
|
|
||||||
|
|
||||||
if should_direct_follow do
|
|
||||||
follow(follower, followed)
|
follow(follower, followed)
|
||||||
else
|
else
|
||||||
{:ok, follower}
|
{:ok, follower}
|
||||||
|
@ -763,4 +738,28 @@ def get_or_fetch(uri_or_nickname) do
|
||||||
get_or_fetch_by_nickname(uri_or_nickname)
|
get_or_fetch_by_nickname(uri_or_nickname)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# wait a period of time and return newest version of the User structs
|
||||||
|
# this is because we have synchronous follow APIs and need to simulate them
|
||||||
|
# with an async handshake
|
||||||
|
def wait_and_refresh(_, %User{local: true} = a, %User{local: true} = b) do
|
||||||
|
with %User{} = a <- Repo.get(User, a.id),
|
||||||
|
%User{} = b <- Repo.get(User, b.id) do
|
||||||
|
{:ok, a, b}
|
||||||
|
else
|
||||||
|
_e ->
|
||||||
|
:error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def wait_and_refresh(timeout, %User{} = a, %User{} = b) do
|
||||||
|
with :ok <- :timer.sleep(timeout),
|
||||||
|
%User{} = a <- Repo.get(User, a.id),
|
||||||
|
%User{} = b <- Repo.get(User, b.id) do
|
||||||
|
{:ok, a, b}
|
||||||
|
else
|
||||||
|
_e ->
|
||||||
|
:error
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -326,6 +326,7 @@ def handle_incoming(
|
||||||
with actor <- get_actor(data),
|
with actor <- get_actor(data),
|
||||||
%User{} = followed <- User.get_or_fetch_by_ap_id(actor),
|
%User{} = followed <- User.get_or_fetch_by_ap_id(actor),
|
||||||
{:ok, follow_activity} <- get_follow_activity(follow_object, followed),
|
{:ok, follow_activity} <- get_follow_activity(follow_object, followed),
|
||||||
|
{:ok, follow_activity} <- Utils.update_follow_state(follow_activity, "accept"),
|
||||||
%User{local: true} = follower <- User.get_cached_by_ap_id(follow_activity.data["actor"]),
|
%User{local: true} = follower <- User.get_cached_by_ap_id(follow_activity.data["actor"]),
|
||||||
{:ok, activity} <-
|
{:ok, activity} <-
|
||||||
ActivityPub.accept(%{
|
ActivityPub.accept(%{
|
||||||
|
@ -351,6 +352,7 @@ def handle_incoming(
|
||||||
with actor <- get_actor(data),
|
with actor <- get_actor(data),
|
||||||
%User{} = followed <- User.get_or_fetch_by_ap_id(actor),
|
%User{} = followed <- User.get_or_fetch_by_ap_id(actor),
|
||||||
{:ok, follow_activity} <- get_follow_activity(follow_object, followed),
|
{:ok, follow_activity} <- get_follow_activity(follow_object, followed),
|
||||||
|
{:ok, follow_activity} <- Utils.update_follow_state(follow_activity, "reject"),
|
||||||
%User{local: true} = follower <- User.get_cached_by_ap_id(follow_activity.data["actor"]),
|
%User{local: true} = follower <- User.get_cached_by_ap_id(follow_activity.data["actor"]),
|
||||||
{:ok, activity} <-
|
{:ok, activity} <-
|
||||||
ActivityPub.accept(%{
|
ActivityPub.accept(%{
|
||||||
|
|
|
@ -247,11 +247,11 @@ def make_follow_data(
|
||||||
"actor" => follower_id,
|
"actor" => follower_id,
|
||||||
"to" => [followed_id],
|
"to" => [followed_id],
|
||||||
"cc" => ["https://www.w3.org/ns/activitystreams#Public"],
|
"cc" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||||
"object" => followed_id
|
"object" => followed_id,
|
||||||
|
"state" => "pending"
|
||||||
}
|
}
|
||||||
|
|
||||||
data = if activity_id, do: Map.put(data, "id", activity_id), else: data
|
data = if activity_id, do: Map.put(data, "id", activity_id), else: data
|
||||||
data = if User.locked?(followed), do: Map.put(data, "state", "pending"), else: data
|
|
||||||
|
|
||||||
data
|
data
|
||||||
end
|
end
|
||||||
|
|
|
@ -571,10 +571,15 @@ def reject_follow_request(%{assigns: %{user: followed}} = conn, %{"id" => id}) d
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@activitypub Application.get_env(:pleroma, :activitypub)
|
||||||
|
@follow_handshake_timeout Keyword.get(@activitypub, :follow_handshake_timeout)
|
||||||
|
|
||||||
def follow(%{assigns: %{user: follower}} = conn, %{"id" => id}) do
|
def follow(%{assigns: %{user: follower}} = conn, %{"id" => id}) do
|
||||||
with %User{} = followed <- Repo.get(User, id),
|
with %User{} = followed <- Repo.get(User, id),
|
||||||
{:ok, follower} <- User.maybe_direct_follow(follower, followed),
|
{:ok, follower} <- User.maybe_direct_follow(follower, followed),
|
||||||
{:ok, _activity} <- ActivityPub.follow(follower, followed) do
|
{:ok, _activity} <- ActivityPub.follow(follower, followed),
|
||||||
|
{:ok, follower, followed} <-
|
||||||
|
User.wait_and_refresh(@follow_handshake_timeout, follower, followed) do
|
||||||
render(conn, AccountView, "relationship.json", %{user: follower, target: followed})
|
render(conn, AccountView, "relationship.json", %{user: follower, target: followed})
|
||||||
else
|
else
|
||||||
{:error, message} ->
|
{:error, message} ->
|
||||||
|
|
|
@ -72,6 +72,15 @@ def render("mention.json", %{user: user}) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def render("relationship.json", %{user: user, target: target}) do
|
def render("relationship.json", %{user: user, target: target}) do
|
||||||
|
follow_activity = Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(user, target)
|
||||||
|
|
||||||
|
requested =
|
||||||
|
if follow_activity do
|
||||||
|
follow_activity.data["state"] == "pending"
|
||||||
|
else
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
%{
|
%{
|
||||||
id: to_string(target.id),
|
id: to_string(target.id),
|
||||||
following: User.following?(user, target),
|
following: User.following?(user, target),
|
||||||
|
@ -79,7 +88,7 @@ def render("relationship.json", %{user: user, target: target}) do
|
||||||
blocking: User.blocks?(user, target),
|
blocking: User.blocks?(user, target),
|
||||||
muting: false,
|
muting: false,
|
||||||
muting_notifications: false,
|
muting_notifications: false,
|
||||||
requested: false,
|
requested: requested,
|
||||||
domain_blocking: false,
|
domain_blocking: false,
|
||||||
showing_reblogs: false,
|
showing_reblogs: false,
|
||||||
endorsed: false
|
endorsed: false
|
||||||
|
|
|
@ -20,10 +20,15 @@ def delete(%User{} = user, id) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@activitypub Application.get_env(:pleroma, :activitypub)
|
||||||
|
@follow_handshake_timeout Keyword.get(@activitypub, :follow_handshake_timeout)
|
||||||
|
|
||||||
def follow(%User{} = follower, params) do
|
def follow(%User{} = follower, params) do
|
||||||
with {:ok, %User{} = followed} <- get_user(params),
|
with {:ok, %User{} = followed} <- get_user(params),
|
||||||
{:ok, follower} <- User.maybe_direct_follow(follower, followed),
|
{:ok, follower} <- User.maybe_direct_follow(follower, followed),
|
||||||
{:ok, activity} <- ActivityPub.follow(follower, followed) do
|
{:ok, activity} <- ActivityPub.follow(follower, followed),
|
||||||
|
{:ok, follower, followed} <-
|
||||||
|
User.wait_and_refresh(@follow_handshake_timeout, follower, followed) do
|
||||||
{:ok, follower, followed, activity}
|
{:ok, follower, followed, activity}
|
||||||
else
|
else
|
||||||
err -> err
|
err -> err
|
||||||
|
|
Loading…
Reference in a new issue