aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CONFIGURATION.md4
-rw-r--r--config/config.exs3
-rw-r--r--lib/pleroma/user.ex26
-rw-r--r--lib/pleroma/web/activity_pub/activity_pub.ex18
-rw-r--r--lib/pleroma/web/activity_pub/mrf/simple_policy.ex12
-rw-r--r--lib/pleroma/web/activity_pub/transmogrifier.ex12
-rw-r--r--lib/pleroma/web/mastodon_api/mastodon_api_controller.ex56
-rw-r--r--lib/pleroma/web/mastodon_api/mastodon_socket.ex12
-rw-r--r--lib/pleroma/web/mastodon_api/views/account_view.ex2
-rw-r--r--lib/pleroma/web/router.ex7
-rw-r--r--priv/repo/migrations/20180617221540_create_activities_in_reply_to_index.exs8
-rw-r--r--test/fixtures/mastodon-post-activity-contentmap.json67
-rw-r--r--test/user_test.exs55
-rw-r--r--test/web/activity_pub/transmogrifier_test.exs41
-rw-r--r--test/web/mastodon_api/account_view_test.exs4
15 files changed, 316 insertions, 11 deletions
diff --git a/CONFIGURATION.md b/CONFIGURATION.md
index 4174dd114..6b2821b21 100644
--- a/CONFIGURATION.md
+++ b/CONFIGURATION.md
@@ -49,7 +49,8 @@ Restricts the visibility of posts from certain instances.
media_removal: [],
media_nsfw: [],
federated_timeline_removal: [],
- reject: []
+ reject: [],
+ accept: []
* `media_removal`: posts from these instances will have attachments
removed
@@ -58,6 +59,7 @@ Restricts the visibility of posts from certain instances.
* `federated_timeline_removal`: posts from these instances will be
marked as unlisted
* `reject`: posts from these instances will be dropped
+* `accept`: if not empty, only posts from these instances will be accepted
### RejectNonPublic
diff --git a/config/config.exs b/config/config.exs
index 6ff7b30d0..e180cc302 100644
--- a/config/config.exs
+++ b/config/config.exs
@@ -68,7 +68,8 @@ config :pleroma, :mrf_simple,
media_removal: [],
media_nsfw: [],
federated_timeline_removal: [],
- reject: []
+ reject: [],
+ accept: []
config :pleroma, :media_proxy,
enabled: false,
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index b27397e13..aba8742a0 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -505,15 +505,33 @@ defmodule Pleroma.User do
Repo.all(q)
end
- def block(user, %{ap_id: ap_id}) do
- blocks = user.info["blocks"] || []
+ def block(blocker, %User{ap_id: ap_id} = blocked) do
+ # sever any follow relationships to prevent leaks per activitypub (Pleroma issue #213)
+ blocker =
+ if following?(blocker, blocked) do
+ {:ok, blocker, _} = unfollow(blocker, blocked)
+ blocker
+ else
+ blocker
+ end
+
+ if following?(blocked, blocker) do
+ unfollow(blocked, blocker)
+ end
+
+ blocks = blocker.info["blocks"] || []
new_blocks = Enum.uniq([ap_id | blocks])
- new_info = Map.put(user.info, "blocks", new_blocks)
+ new_info = Map.put(blocker.info, "blocks", new_blocks)
- cs = User.info_changeset(user, %{info: new_info})
+ cs = User.info_changeset(blocker, %{info: new_info})
update_and_set_cache(cs)
end
+ # helper to handle the block given only an actor's AP id
+ def block(blocker, %{ap_id: ap_id}) do
+ block(blocker, User.get_by_ap_id(ap_id))
+ end
+
def unblock(user, %{ap_id: ap_id}) do
blocks = user.info["blocks"] || []
new_blocks = List.delete(blocks, ap_id)
diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex
index 267427a23..dfcc5b9ed 100644
--- a/lib/pleroma/web/activity_pub/activity_pub.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub.ex
@@ -65,6 +65,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
if activity.local do
Pleroma.Web.Streamer.stream("public:local", activity)
end
+
+ if activity.data["object"]["attachment"] != [] do
+ Pleroma.Web.Streamer.stream("public:media", activity)
+
+ if activity.local do
+ Pleroma.Web.Streamer.stream("public:local:media", activity)
+ end
+ end
else
if !Enum.member?(activity.data["cc"] || [], public) &&
!Enum.member?(
@@ -430,6 +438,15 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
defp restrict_media(query, _), do: query
+ defp restrict_replies(query, %{"exclude_replies" => val}) when val == "true" or val == "1" do
+ from(
+ activity in query,
+ where: fragment("?->'object'->>'inReplyTo' is null", activity.data)
+ )
+ end
+
+ defp restrict_replies(query, _), do: query
+
# Only search through last 100_000 activities by default
defp restrict_recent(query, %{"whole_db" => true}), do: query
@@ -487,6 +504,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|> restrict_blocked(opts)
|> restrict_media(opts)
|> restrict_visibility(opts)
+ |> restrict_replies(opts)
end
def fetch_activities(recipients, opts \\ %{}) do
diff --git a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex
index 8d770387d..7fecb8a4f 100644
--- a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex
@@ -4,6 +4,15 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
@mrf_policy Application.get_env(:pleroma, :mrf_simple)
+ @accept Keyword.get(@mrf_policy, :accept)
+ defp check_accept(actor_info, object) do
+ if length(@accept) > 0 and not (actor_info.host in @accept) do
+ {:reject, nil}
+ else
+ {:ok, object}
+ end
+ end
+
@reject Keyword.get(@mrf_policy, :reject)
defp check_reject(actor_info, object) do
if actor_info.host in @reject do
@@ -74,7 +83,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
def filter(object) do
actor_info = URI.parse(object["actor"])
- with {:ok, object} <- check_reject(actor_info, object),
+ with {:ok, object} <- check_accept(actor_info, object),
+ {:ok, object} <- check_reject(actor_info, object),
{:ok, object} <- check_media_removal(actor_info, object),
{:ok, object} <- check_media_nsfw(actor_info, object),
{:ok, object} <- check_ftl_removal(actor_info, object) do
diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex
index 300e0fcdd..30cd70fb6 100644
--- a/lib/pleroma/web/activity_pub/transmogrifier.ex
+++ b/lib/pleroma/web/activity_pub/transmogrifier.ex
@@ -24,6 +24,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|> fix_in_reply_to
|> fix_emoji
|> fix_tag
+ |> fix_content_map
end
def fix_in_reply_to(%{"inReplyTo" => in_reply_to_id} = object)
@@ -107,6 +108,17 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|> Map.put("tag", combined)
end
+ # content map usually only has one language so this will do for now.
+ def fix_content_map(%{"contentMap" => content_map} = object) do
+ content_groups = Map.to_list(content_map)
+ {_, content} = Enum.at(content_groups, 0)
+
+ object
+ |> Map.put("content", content)
+ end
+
+ def fix_content_map(object), do: object
+
# TODO: validate those with a Ecto scheme
# - tags
# - emoji
diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
index 8a8d1e050..dab255ee2 100644
--- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
+++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
@@ -621,6 +621,58 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
json(conn, %{})
end
+ def search2(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
+ accounts = User.search(query, params["resolve"] == "true")
+
+ fetched =
+ if Regex.match?(~r/https?:/, query) do
+ with {:ok, activities} <- OStatus.fetch_activity_from_url(query) do
+ activities
+ |> Enum.filter(fn
+ %{data: %{"type" => "Create"}} -> true
+ _ -> false
+ end)
+ else
+ _e -> []
+ end
+ end || []
+
+ q =
+ from(
+ a in Activity,
+ where: fragment("?->>'type' = 'Create'", a.data),
+ where: "https://www.w3.org/ns/activitystreams#Public" in a.recipients,
+ where:
+ fragment(
+ "to_tsvector('english', ?->'object'->>'content') @@ plainto_tsquery('english', ?)",
+ a.data,
+ ^query
+ ),
+ limit: 20,
+ order_by: [desc: :id]
+ )
+
+ statuses = Repo.all(q) ++ fetched
+
+ tags_path = Web.base_url() <> "/tag/"
+
+ tags =
+ String.split(query)
+ |> Enum.uniq()
+ |> Enum.filter(fn tag -> String.starts_with?(tag, "#") end)
+ |> Enum.map(fn tag -> String.slice(tag, 1..-1) end)
+ |> Enum.map(fn tag -> %{name: tag, url: tags_path <> tag} end)
+
+ res = %{
+ "accounts" => AccountView.render("accounts.json", users: accounts, for: user, as: :user),
+ "statuses" =>
+ StatusView.render("index.json", activities: statuses, for: user, as: :activity),
+ "hashtags" => tags
+ }
+
+ json(conn, res)
+ end
+
def search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
accounts = User.search(query, params["resolve"] == "true")
@@ -812,7 +864,9 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
boost_modal: false,
delete_modal: true,
auto_play_gif: false,
- reduce_motion: false
+ display_sensitive_media: false,
+ reduce_motion: false,
+ max_toot_chars: Keyword.get(@instance, :limit)
},
compose: %{
me: "#{user.id}",
diff --git a/lib/pleroma/web/mastodon_api/mastodon_socket.ex b/lib/pleroma/web/mastodon_api/mastodon_socket.ex
index 46648c366..174293906 100644
--- a/lib/pleroma/web/mastodon_api/mastodon_socket.ex
+++ b/lib/pleroma/web/mastodon_api/mastodon_socket.ex
@@ -15,8 +15,16 @@ defmodule Pleroma.Web.MastodonAPI.MastodonSocket do
with token when not is_nil(token) <- params["access_token"],
%Token{user_id: user_id} <- Repo.get_by(Token, token: token),
%User{} = user <- Repo.get(User, user_id),
- stream when stream in ["public", "public:local", "user", "direct", "list"] <-
- params["stream"] do
+ stream
+ when stream in [
+ "public",
+ "public:local",
+ "public:media",
+ "public:local:media",
+ "user",
+ "direct",
+ "list"
+ ] <- params["stream"] do
topic = if stream == "list", do: "list:#{params["list"]}", else: stream
socket =
diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex
index 9db683f44..f33d615cf 100644
--- a/lib/pleroma/web/mastodon_api/views/account_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/account_view.ex
@@ -30,6 +30,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
avatar_static: image,
header: header,
header_static: header,
+ emojis: [],
+ fields: [],
source: %{
note: "",
privacy: "public",
diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex
index 13bd393ab..ebe68218b 100644
--- a/lib/pleroma/web/router.ex
+++ b/lib/pleroma/web/router.ex
@@ -170,9 +170,16 @@ defmodule Pleroma.Web.Router do
get("/accounts/:id/following", MastodonAPIController, :following)
get("/accounts/:id", MastodonAPIController, :user)
+ get("/trends", MastodonAPIController, :empty_array)
+
get("/search", MastodonAPIController, :search)
end
+ scope "/api/v2", Pleroma.Web.MastodonAPI do
+ pipe_through(:api)
+ get("/search", MastodonAPIController, :search2)
+ end
+
scope "/api", Pleroma.Web do
pipe_through(:config)
diff --git a/priv/repo/migrations/20180617221540_create_activities_in_reply_to_index.exs b/priv/repo/migrations/20180617221540_create_activities_in_reply_to_index.exs
new file mode 100644
index 000000000..1fee6fd7a
--- /dev/null
+++ b/priv/repo/migrations/20180617221540_create_activities_in_reply_to_index.exs
@@ -0,0 +1,8 @@
+defmodule Pleroma.Repo.Migrations.CreateActivitiesInReplyToIndex do
+ use Ecto.Migration
+ @disable_ddl_transaction true
+
+ def change do
+ create index(:activities, ["(data->'object'->>'inReplyTo')"], concurrently: true, name: :activities_in_reply_to)
+ end
+end
diff --git a/test/fixtures/mastodon-post-activity-contentmap.json b/test/fixtures/mastodon-post-activity-contentmap.json
new file mode 100644
index 000000000..2e94380b6
--- /dev/null
+++ b/test/fixtures/mastodon-post-activity-contentmap.json
@@ -0,0 +1,67 @@
+{
+ "@context": [
+ "https://www.w3.org/ns/activitystreams",
+ "https://w3id.org/security/v1",
+ {
+ "Emoji": "toot:Emoji",
+ "Hashtag": "as:Hashtag",
+ "atomUri": "ostatus:atomUri",
+ "conversation": "ostatus:conversation",
+ "inReplyToAtomUri": "ostatus:inReplyToAtomUri",
+ "manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
+ "movedTo": "as:movedTo",
+ "ostatus": "http://ostatus.org#",
+ "sensitive": "as:sensitive",
+ "toot": "http://joinmastodon.org/ns#"
+ }
+ ],
+ "actor": "http://mastodon.example.org/users/admin",
+ "cc": [
+ "http://mastodon.example.org/users/admin/followers",
+ "http://localtesting.pleroma.lol/users/lain"
+ ],
+ "id": "http://mastodon.example.org/users/admin/statuses/99512778738411822/activity",
+ "nickname": "lain",
+ "object": {
+ "atomUri": "http://mastodon.example.org/users/admin/statuses/99512778738411822",
+ "attachment": [],
+ "attributedTo": "http://mastodon.example.org/users/admin",
+ "cc": [
+ "http://mastodon.example.org/users/admin/followers",
+ "http://localtesting.pleroma.lol/users/lain"
+ ],
+ "contentMap": {
+ "en": "<p><span class=\"h-card\"><a href=\"http://localtesting.pleroma.lol/users/lain\" class=\"u-url mention\">@<span>lain</span></a></span></p>"
+ },
+ "conversation": "tag:mastodon.example.org,2018-02-12:objectId=20:objectType=Conversation",
+ "id": "http://mastodon.example.org/users/admin/statuses/99512778738411822",
+ "inReplyTo": null,
+ "inReplyToAtomUri": null,
+ "published": "2018-02-12T14:08:20Z",
+ "sensitive": true,
+ "summary": "cw",
+ "tag": [
+ {
+ "href": "http://localtesting.pleroma.lol/users/lain",
+ "name": "@lain@localtesting.pleroma.lol",
+ "type": "Mention"
+ }
+ ],
+ "to": [
+ "https://www.w3.org/ns/activitystreams#Public"
+ ],
+ "type": "Note",
+ "url": "http://mastodon.example.org/@admin/99512778738411822"
+ },
+ "published": "2018-02-12T14:08:20Z",
+ "signature": {
+ "created": "2018-02-12T14:08:20Z",
+ "creator": "http://mastodon.example.org/users/admin#main-key",
+ "signatureValue": "rnNfcopkc6+Ju73P806popcfwrK9wGYHaJVG1/ZvrlEbWVDzaHjkXqj9Q3/xju5l8CSn9tvSgCCtPFqZsFQwn/pFIFUcw7ZWB2xi4bDm3NZ3S4XQ8JRaaX7og5hFxAhWkGhJhAkfxVnOg2hG+w2d/7d7vRVSC1vo5ip4erUaA/PkWusZvPIpxnRWoXaxJsFmVx0gJgjpJkYDyjaXUlp+jmaoseeZ4EPQUWqHLKJ59PRG0mg8j2xAjYH9nQaN14qMRmTGPxY8gfv/CUFcatA+8VJU9KEsJkDAwLVvglydNTLGrxpAJU78a2eaht0foV43XUIZGe3DKiJPgE+UOKGCJw==",
+ "type": "RsaSignature2017"
+ },
+ "to": [
+ "https://www.w3.org/ns/activitystreams#Public"
+ ],
+ "type": "Create"
+}
diff --git a/test/user_test.exs b/test/user_test.exs
index 200352981..352a16687 100644
--- a/test/user_test.exs
+++ b/test/user_test.exs
@@ -359,6 +359,61 @@ defmodule Pleroma.UserTest do
refute User.blocks?(user, blocked_user)
end
+
+ test "blocks tear down cyclical follow relationships" do
+ blocker = insert(:user)
+ blocked = insert(:user)
+
+ {:ok, blocker} = User.follow(blocker, blocked)
+ {:ok, blocked} = User.follow(blocked, blocker)
+
+ assert User.following?(blocker, blocked)
+ assert User.following?(blocked, blocker)
+
+ {:ok, blocker} = User.block(blocker, blocked)
+ blocked = Repo.get(User, blocked.id)
+
+ assert User.blocks?(blocker, blocked)
+
+ refute User.following?(blocker, blocked)
+ refute User.following?(blocked, blocker)
+ end
+
+ test "blocks tear down blocker->blocked follow relationships" do
+ blocker = insert(:user)
+ blocked = insert(:user)
+
+ {:ok, blocker} = User.follow(blocker, blocked)
+
+ assert User.following?(blocker, blocked)
+ refute User.following?(blocked, blocker)
+
+ {:ok, blocker} = User.block(blocker, blocked)
+ blocked = Repo.get(User, blocked.id)
+
+ assert User.blocks?(blocker, blocked)
+
+ refute User.following?(blocker, blocked)
+ refute User.following?(blocked, blocker)
+ end
+
+ test "blocks tear down blocked->blocker follow relationships" do
+ blocker = insert(:user)
+ blocked = insert(:user)
+
+ {:ok, blocked} = User.follow(blocked, blocker)
+
+ refute User.following?(blocker, blocked)
+ assert User.following?(blocked, blocker)
+
+ {:ok, blocker} = User.block(blocker, blocked)
+ blocked = Repo.get(User, blocked.id)
+
+ assert User.blocks?(blocker, blocked)
+
+ refute User.following?(blocker, blocked)
+ refute User.following?(blocked, blocker)
+ end
end
describe "domain blocking" do
diff --git a/test/web/activity_pub/transmogrifier_test.exs b/test/web/activity_pub/transmogrifier_test.exs
index 7e771b9f8..838ae169d 100644
--- a/test/web/activity_pub/transmogrifier_test.exs
+++ b/test/web/activity_pub/transmogrifier_test.exs
@@ -102,6 +102,16 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
assert Enum.at(data["object"]["tag"], 2) == "moo"
end
+ test "it works for incoming notices with contentMap" do
+ data =
+ File.read!("test/fixtures/mastodon-post-activity-contentmap.json") |> Poison.decode!()
+
+ {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
+
+ assert data["object"]["content"] ==
+ "<p><span class=\"h-card\"><a href=\"http://localtesting.pleroma.lol/users/lain\" class=\"u-url mention\">@<span>lain</span></a></span></p>"
+ end
+
test "it works for incoming follow requests" do
user = insert(:user)
@@ -382,6 +392,37 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
assert User.blocks?(blocker, user)
end
+ test "incoming blocks successfully tear down any follow relationship" do
+ blocker = insert(:user)
+ blocked = insert(:user)
+
+ data =
+ File.read!("test/fixtures/mastodon-block-activity.json")
+ |> Poison.decode!()
+ |> Map.put("object", blocked.ap_id)
+ |> Map.put("actor", blocker.ap_id)
+
+ {:ok, blocker} = User.follow(blocker, blocked)
+ {:ok, blocked} = User.follow(blocked, blocker)
+
+ assert User.following?(blocker, blocked)
+ assert User.following?(blocked, blocker)
+
+ {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
+
+ assert data["type"] == "Block"
+ assert data["object"] == blocked.ap_id
+ assert data["actor"] == blocker.ap_id
+
+ blocker = User.get_by_ap_id(data["actor"])
+ blocked = User.get_by_ap_id(data["object"])
+
+ assert User.blocks?(blocker, blocked)
+
+ refute User.following?(blocker, blocked)
+ refute User.following?(blocked, blocker)
+ end
+
test "it works for incoming unblocks with an existing block" do
user = insert(:user)
diff --git a/test/web/mastodon_api/account_view_test.exs b/test/web/mastodon_api/account_view_test.exs
index 597690bf7..b93418b3f 100644
--- a/test/web/mastodon_api/account_view_test.exs
+++ b/test/web/mastodon_api/account_view_test.exs
@@ -28,6 +28,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
avatar_static: "http://localhost:4001/images/avi.png",
header: "http://localhost:4001/images/banner.png",
header_static: "http://localhost:4001/images/banner.png",
+ emojis: [],
+ fields: [],
source: %{
note: "",
privacy: "public",
@@ -60,7 +62,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
expected = %{
id: to_string(other_user.id),
- following: true,
+ following: false,
followed_by: false,
blocking: true,
muting: false,