Merge branch 'feature/activitypub-semantics' into 'develop'

activitypub semantics

See merge request pleroma/pleroma!1798
This commit is contained in:
kaniini 2019-10-21 07:54:48 +00:00
commit 2ebe8c416a
15 changed files with 168 additions and 10 deletions

View file

@ -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

View file

@ -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, [

View file

@ -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"],

View file

@ -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()}

View file

@ -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)

View file

@ -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}

View file

@ -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

View file

@ -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",

View 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"
}
}

View file

@ -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

View file

@ -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)

View file

@ -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)

View file

@ -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 ->

View file

@ -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)

View file

@ -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!"