Merge branch 'feature/activitypub-semantics' into 'develop'
activitypub semantics See merge request pleroma/pleroma!1798
This commit is contained in:
commit
2ebe8c416a
@ -88,6 +88,9 @@ def superuser?(%User{local: true, info: %User.Info{is_admin: true}}), do: true
|
|||||||
def superuser?(%User{local: true, info: %User.Info{is_moderator: true}}), do: true
|
def superuser?(%User{local: true, info: %User.Info{is_moderator: true}}), do: true
|
||||||
def superuser?(_), do: false
|
def superuser?(_), do: false
|
||||||
|
|
||||||
|
def invisible?(%User{info: %User.Info{invisible: true}}), do: true
|
||||||
|
def invisible?(_), do: false
|
||||||
|
|
||||||
def avatar_url(user, options \\ []) do
|
def avatar_url(user, options \\ []) do
|
||||||
case user.avatar do
|
case user.avatar do
|
||||||
%{"url" => [%{"href" => href} | _]} -> href
|
%{"url" => [%{"href" => href} | _]} -> href
|
||||||
|
@ -53,6 +53,7 @@ defmodule Pleroma.User.Info do
|
|||||||
field(:fields, {:array, :map}, default: nil)
|
field(:fields, {:array, :map}, default: nil)
|
||||||
field(:raw_fields, {:array, :map}, default: [])
|
field(:raw_fields, {:array, :map}, default: [])
|
||||||
field(:discoverable, :boolean, default: false)
|
field(:discoverable, :boolean, default: false)
|
||||||
|
field(:invisible, :boolean, default: false)
|
||||||
|
|
||||||
field(:notification_settings, :map,
|
field(:notification_settings, :map,
|
||||||
default: %{
|
default: %{
|
||||||
@ -266,7 +267,8 @@ def remote_user_creation(info, params) do
|
|||||||
:follower_count,
|
:follower_count,
|
||||||
:fields,
|
:fields,
|
||||||
:following_count,
|
:following_count,
|
||||||
:discoverable
|
:discoverable,
|
||||||
|
:invisible
|
||||||
])
|
])
|
||||||
|> validate_fields(true)
|
|> validate_fields(true)
|
||||||
end
|
end
|
||||||
@ -393,6 +395,14 @@ def set_source_data(info, source_data) do
|
|||||||
|> validate_required([:source_data])
|
|> validate_required([:source_data])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def set_invisible(info, invisible) do
|
||||||
|
params = %{invisible: invisible}
|
||||||
|
|
||||||
|
info
|
||||||
|
|> cast(params, [:invisible])
|
||||||
|
|> validate_required([:invisible])
|
||||||
|
end
|
||||||
|
|
||||||
def admin_api_update(info, params) do
|
def admin_api_update(info, params) do
|
||||||
info
|
info
|
||||||
|> cast(params, [
|
|> cast(params, [
|
||||||
|
@ -1106,6 +1106,7 @@ defp object_to_user_data(data) do
|
|||||||
locked = data["manuallyApprovesFollowers"] || false
|
locked = data["manuallyApprovesFollowers"] || false
|
||||||
data = Transmogrifier.maybe_fix_user_object(data)
|
data = Transmogrifier.maybe_fix_user_object(data)
|
||||||
discoverable = data["discoverable"] || false
|
discoverable = data["discoverable"] || false
|
||||||
|
invisible = data["invisible"] || false
|
||||||
|
|
||||||
user_data = %{
|
user_data = %{
|
||||||
ap_id: data["id"],
|
ap_id: data["id"],
|
||||||
@ -1115,7 +1116,8 @@ defp object_to_user_data(data) do
|
|||||||
banner: banner,
|
banner: banner,
|
||||||
fields: fields,
|
fields: fields,
|
||||||
locked: locked,
|
locked: locked,
|
||||||
discoverable: discoverable
|
discoverable: discoverable,
|
||||||
|
invisible: invisible
|
||||||
},
|
},
|
||||||
avatar: avatar,
|
avatar: avatar,
|
||||||
name: data["name"],
|
name: data["name"],
|
||||||
|
@ -10,8 +10,12 @@ defmodule Pleroma.Web.ActivityPub.Relay do
|
|||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
def get_actor do
|
def get_actor do
|
||||||
|
actor =
|
||||||
"#{Pleroma.Web.Endpoint.url()}/relay"
|
"#{Pleroma.Web.Endpoint.url()}/relay"
|
||||||
|> User.get_or_create_service_actor_by_ap_id()
|
|> User.get_or_create_service_actor_by_ap_id()
|
||||||
|
|
||||||
|
{:ok, actor} = User.update_info(actor, &User.Info.set_invisible(&1, true))
|
||||||
|
actor
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec follow(String.t()) :: {:ok, Activity.t()} | {:error, any()}
|
@spec follow(String.t()) :: {:ok, Activity.t()} | {:error, any()}
|
||||||
|
@ -596,13 +596,19 @@ def handle_incoming(
|
|||||||
data,
|
data,
|
||||||
_options
|
_options
|
||||||
)
|
)
|
||||||
when object_type in ["Person", "Application", "Service", "Organization"] do
|
when object_type in [
|
||||||
|
"Person",
|
||||||
|
"Application",
|
||||||
|
"Service",
|
||||||
|
"Organization"
|
||||||
|
] do
|
||||||
with %User{ap_id: ^actor_id} = actor <- User.get_cached_by_ap_id(object["id"]) do
|
with %User{ap_id: ^actor_id} = actor <- User.get_cached_by_ap_id(object["id"]) do
|
||||||
{:ok, new_user_data} = ActivityPub.user_data_from_user_object(object)
|
{:ok, new_user_data} = ActivityPub.user_data_from_user_object(object)
|
||||||
|
|
||||||
banner = new_user_data[:info][:banner]
|
banner = new_user_data[:info][:banner]
|
||||||
locked = new_user_data[:info][:locked] || false
|
locked = new_user_data[:info][:locked] || false
|
||||||
attachment = get_in(new_user_data, [:info, :source_data, "attachment"]) || []
|
attachment = get_in(new_user_data, [:info, :source_data, "attachment"]) || []
|
||||||
|
invisible = new_user_data[:info][:invisible] || false
|
||||||
|
|
||||||
fields =
|
fields =
|
||||||
attachment
|
attachment
|
||||||
@ -612,7 +618,7 @@ def handle_incoming(
|
|||||||
update_data =
|
update_data =
|
||||||
new_user_data
|
new_user_data
|
||||||
|> Map.take([:name, :bio, :avatar])
|
|> Map.take([:name, :bio, :avatar])
|
||||||
|> Map.put(:info, %{banner: banner, locked: locked, fields: fields})
|
|> Map.put(:info, %{banner: banner, locked: locked, fields: fields, invisible: invisible})
|
||||||
|
|
||||||
actor
|
actor
|
||||||
|> User.upgrade_changeset(update_data, true)
|
|> User.upgrade_changeset(update_data, true)
|
||||||
|
@ -491,11 +491,15 @@ def add_announce_to_object(
|
|||||||
%Activity{data: %{"actor" => actor}},
|
%Activity{data: %{"actor" => actor}},
|
||||||
object
|
object
|
||||||
) do
|
) do
|
||||||
|
unless actor |> User.get_cached_by_ap_id() |> User.invisible?() do
|
||||||
announcements = take_announcements(object)
|
announcements = take_announcements(object)
|
||||||
|
|
||||||
with announcements <- Enum.uniq([actor | announcements]) do
|
with announcements <- Enum.uniq([actor | announcements]) do
|
||||||
update_element_in_object("announcement", announcements, object)
|
update_element_in_object("announcement", announcements, object)
|
||||||
end
|
end
|
||||||
|
else
|
||||||
|
{:ok, object}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_announce_to_object(_, object), do: {:ok, object}
|
def add_announce_to_object(_, object), do: {:ok, object}
|
||||||
|
@ -55,7 +55,8 @@ def render("service.json", %{user: user}) do
|
|||||||
"owner" => user.ap_id,
|
"owner" => user.ap_id,
|
||||||
"publicKeyPem" => public_key
|
"publicKeyPem" => public_key
|
||||||
},
|
},
|
||||||
"endpoints" => endpoints
|
"endpoints" => endpoints,
|
||||||
|
"invisible" => User.invisible?(user)
|
||||||
}
|
}
|
||||||
|> Map.merge(Utils.make_json_ld_header())
|
|> Map.merge(Utils.make_json_ld_header())
|
||||||
end
|
end
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
"value": "schema:value",
|
"value": "schema:value",
|
||||||
"sensitive": "as:sensitive",
|
"sensitive": "as:sensitive",
|
||||||
"litepub": "http://litepub.social/ns#",
|
"litepub": "http://litepub.social/ns#",
|
||||||
|
"invisible": "litepub:invisible",
|
||||||
"directMessage": "litepub:directMessage",
|
"directMessage": "litepub:directMessage",
|
||||||
"listMessage": {
|
"listMessage": {
|
||||||
"@id": "litepub:listMessage",
|
"@id": "litepub:listMessage",
|
||||||
|
55
test/fixtures/tesla_mock/relay@mastdon.example.org.json
vendored
Normal file
55
test/fixtures/tesla_mock/relay@mastdon.example.org.json
vendored
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
{
|
||||||
|
"@context": ["https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1", {
|
||||||
|
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
|
||||||
|
"sensitive": "as:sensitive",
|
||||||
|
"movedTo": "as:movedTo",
|
||||||
|
"Hashtag": "as:Hashtag",
|
||||||
|
"ostatus": "http://ostatus.org#",
|
||||||
|
"atomUri": "ostatus:atomUri",
|
||||||
|
"inReplyToAtomUri": "ostatus:inReplyToAtomUri",
|
||||||
|
"conversation": "ostatus:conversation",
|
||||||
|
"toot": "http://joinmastodon.org/ns#",
|
||||||
|
"Emoji": "toot:Emoji"
|
||||||
|
}],
|
||||||
|
"id": "http://mastodon.example.org/users/admin",
|
||||||
|
"type": "Application",
|
||||||
|
"invisible": true,
|
||||||
|
"following": "http://mastodon.example.org/users/admin/following",
|
||||||
|
"followers": "http://mastodon.example.org/users/admin/followers",
|
||||||
|
"inbox": "http://mastodon.example.org/users/admin/inbox",
|
||||||
|
"outbox": "http://mastodon.example.org/users/admin/outbox",
|
||||||
|
"preferredUsername": "admin",
|
||||||
|
"name": null,
|
||||||
|
"summary": "\u003cp\u003e\u003c/p\u003e",
|
||||||
|
"url": "http://mastodon.example.org/@admin",
|
||||||
|
"manuallyApprovesFollowers": false,
|
||||||
|
"publicKey": {
|
||||||
|
"id": "http://mastodon.example.org/users/admin#main-key",
|
||||||
|
"owner": "http://mastodon.example.org/users/admin",
|
||||||
|
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtc4Tir+3ADhSNF6VKrtW\nOU32T01w7V0yshmQei38YyiVwVvFu8XOP6ACchkdxbJ+C9mZud8qWaRJKVbFTMUG\nNX4+6Q+FobyuKrwN7CEwhDALZtaN2IPbaPd6uG1B7QhWorrY+yFa8f2TBM3BxnUy\nI4T+bMIZIEYG7KtljCBoQXuTQmGtuffO0UwJksidg2ffCF5Q+K//JfQagJ3UzrR+\nZXbKMJdAw4bCVJYs4Z5EhHYBwQWiXCyMGTd7BGlmMkY6Av7ZqHKC/owp3/0EWDNz\nNqF09Wcpr3y3e8nA10X40MJqp/wR+1xtxp+YGbq/Cj5hZGBG7etFOmIpVBrDOhry\nBwIDAQAB\n-----END PUBLIC KEY-----\n"
|
||||||
|
},
|
||||||
|
"attachment": [{
|
||||||
|
"type": "PropertyValue",
|
||||||
|
"name": "foo",
|
||||||
|
"value": "bar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "PropertyValue",
|
||||||
|
"name": "foo1",
|
||||||
|
"value": "bar1"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"endpoints": {
|
||||||
|
"sharedInbox": "http://mastodon.example.org/inbox"
|
||||||
|
},
|
||||||
|
"icon": {
|
||||||
|
"type": "Image",
|
||||||
|
"mediaType": "image/jpeg",
|
||||||
|
"url": "https://cdn.niu.moe/accounts/avatars/000/033/323/original/fd7f8ae0b3ffedc9.jpeg"
|
||||||
|
},
|
||||||
|
"image": {
|
||||||
|
"type": "Image",
|
||||||
|
"mediaType": "image/png",
|
||||||
|
"url": "https://cdn.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png"
|
||||||
|
}
|
||||||
|
}
|
@ -348,6 +348,14 @@ def get("http://mastodon.example.org/users/admin", _, _, Accept: "application/ac
|
|||||||
}}
|
}}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def get("http://mastodon.example.org/users/relay", _, _, Accept: "application/activity+json") do
|
||||||
|
{:ok,
|
||||||
|
%Tesla.Env{
|
||||||
|
status: 200,
|
||||||
|
body: File.read!("test/fixtures/tesla_mock/relay@mastdon.example.org.json")
|
||||||
|
}}
|
||||||
|
end
|
||||||
|
|
||||||
def get("http://mastodon.example.org/users/gargron", _, _, Accept: "application/activity+json") do
|
def get("http://mastodon.example.org/users/gargron", _, _, Accept: "application/activity+json") do
|
||||||
{:error, :nxdomain}
|
{:error, :nxdomain}
|
||||||
end
|
end
|
||||||
|
@ -1232,6 +1232,20 @@ test "returns true for local admins" do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "invisible?/1" do
|
||||||
|
test "returns true for an invisible user" do
|
||||||
|
user = insert(:user, local: true, info: %{invisible: true})
|
||||||
|
|
||||||
|
assert User.invisible?(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns false for a non-invisible user" do
|
||||||
|
user = insert(:user, local: true)
|
||||||
|
|
||||||
|
refute User.invisible?(user)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "visible_for?/2" do
|
describe "visible_for?/2" do
|
||||||
test "returns true when the account is itself" do
|
test "returns true when the account is itself" do
|
||||||
user = insert(:user, local: true)
|
user = insert(:user, local: true)
|
||||||
|
@ -179,6 +179,12 @@ test "it returns a user" do
|
|||||||
assert user.follower_address == "http://mastodon.example.org/users/admin/followers"
|
assert user.follower_address == "http://mastodon.example.org/users/admin/followers"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "it returns a user that is invisible" do
|
||||||
|
user_id = "http://mastodon.example.org/users/relay"
|
||||||
|
{:ok, user} = ActivityPub.make_user_from_ap_id(user_id)
|
||||||
|
assert User.invisible?(user)
|
||||||
|
end
|
||||||
|
|
||||||
test "it fetches the appropriate tag-restricted posts" do
|
test "it fetches the appropriate tag-restricted posts" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ defmodule Pleroma.Web.ActivityPub.RelayTest do
|
|||||||
|
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
alias Pleroma.Web.ActivityPub.Relay
|
alias Pleroma.Web.ActivityPub.Relay
|
||||||
|
|
||||||
@ -19,6 +20,11 @@ test "gets an actor for the relay" do
|
|||||||
assert user.ap_id == "#{Pleroma.Web.Endpoint.url()}/relay"
|
assert user.ap_id == "#{Pleroma.Web.Endpoint.url()}/relay"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "relay actor is invisible" do
|
||||||
|
user = Relay.get_actor()
|
||||||
|
assert User.invisible?(user)
|
||||||
|
end
|
||||||
|
|
||||||
describe "follow/1" do
|
describe "follow/1" do
|
||||||
test "returns errors when user not found" do
|
test "returns errors when user not found" do
|
||||||
assert capture_log(fn ->
|
assert capture_log(fn ->
|
||||||
|
@ -76,6 +76,12 @@ test "Does not add an avatar image if the user hasn't set one" do
|
|||||||
assert result["image"]["url"] == "https://somebanner"
|
assert result["image"]["url"] == "https://somebanner"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "renders an invisible user with the invisible property set to true" do
|
||||||
|
user = insert(:user, %{info: %{invisible: true}})
|
||||||
|
|
||||||
|
assert %{"invisible" => true} = UserView.render("service.json", %{user: user})
|
||||||
|
end
|
||||||
|
|
||||||
describe "endpoints" do
|
describe "endpoints" do
|
||||||
test "local users have a usable endpoints structure" do
|
test "local users have a usable endpoints structure" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
@ -12,12 +12,16 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
|
|||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.ScheduledActivity
|
alias Pleroma.ScheduledActivity
|
||||||
|
alias Pleroma.Tests.ObanHelpers
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
|
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
clear_config([:instance, :federating])
|
||||||
|
clear_config([:instance, :allow_relay])
|
||||||
|
|
||||||
describe "posting statuses" do
|
describe "posting statuses" do
|
||||||
setup do
|
setup do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
@ -29,6 +33,34 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
|
|||||||
[conn: conn]
|
[conn: conn]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "posting a status does not increment reblog_count when relaying", %{conn: conn} do
|
||||||
|
Pleroma.Config.put([:instance, :federating], true)
|
||||||
|
Pleroma.Config.get([:instance, :allow_relay], true)
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> post("api/v1/statuses", %{
|
||||||
|
"content_type" => "text/plain",
|
||||||
|
"source" => "Pleroma FE",
|
||||||
|
"status" => "Hello world",
|
||||||
|
"visibility" => "public"
|
||||||
|
})
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
assert response["reblogs_count"] == 0
|
||||||
|
ObanHelpers.perform_all()
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> get("api/v1/statuses/#{response["id"]}", %{})
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
assert response["reblogs_count"] == 0
|
||||||
|
end
|
||||||
|
|
||||||
test "posting a status", %{conn: conn} do
|
test "posting a status", %{conn: conn} do
|
||||||
idempotency_key = "Pikachu rocks!"
|
idempotency_key = "Pikachu rocks!"
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user