diff options
Diffstat (limited to 'lib/pleroma/web')
22 files changed, 343 insertions, 178 deletions
diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 29cc1a17b..c6744e5f2 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -456,17 +456,18 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do user = User.get_cached_by_ap_id(actor) to = (object.data["to"] || []) ++ (object.data["cc"] || []) - with {:ok, object, activity} <- Object.delete(object), + with create_activity <- Activity.get_create_by_object_ap_id(id), data <- %{ "type" => "Delete", "actor" => actor, "object" => id, "to" => to, - "deleted_activity_id" => activity && activity.id + "deleted_activity_id" => create_activity && create_activity.id } |> maybe_put("id", activity_id), {:ok, activity} <- insert(data, local, false), + {:ok, object, _create_activity} <- Object.delete(object), stream_out_participations(object, user), _ <- decrease_replies_count_if_reply(object), {:ok, _actor} <- decrease_note_count_if_public(user, object), @@ -541,6 +542,30 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end end + def move(%User{} = origin, %User{} = target, local \\ true) do + params = %{ + "type" => "Move", + "actor" => origin.ap_id, + "object" => origin.ap_id, + "target" => target.ap_id + } + + with true <- origin.ap_id in target.also_known_as, + {:ok, activity} <- insert(params, local) do + maybe_federate(activity) + + BackgroundWorker.enqueue("move_following", %{ + "origin_id" => origin.id, + "target_id" => target.id + }) + + {:ok, activity} + else + false -> {:error, "Target account must have the origin in `alsoKnownAs`"} + err -> err + end + end + defp fetch_activities_for_context_query(context, opts) do public = [Pleroma.Constants.as_public()] @@ -724,6 +749,15 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do |> Map.put("whole_db", true) |> Map.put("pinned_activity_ids", user.pinned_activities) + params = + if User.blocks?(reading_user, user) do + params + else + params + |> Map.put("blocking_user", reading_user) + |> Map.put("muting_user", reading_user) + end + recipients = user_activities_recipients(%{ "godmode" => params["godmode"], @@ -895,7 +929,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do defp restrict_muted(query, %{"with_muted" => val}) when val in [true, "true", "1"], do: query defp restrict_muted(query, %{"muting_user" => %User{} = user} = opts) do - mutes = user.mutes + mutes = opts["muted_users_ap_ids"] || User.muted_users_ap_ids(user) query = from([activity] in query, @@ -912,8 +946,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do defp restrict_muted(query, _), do: query - defp restrict_blocked(query, %{"blocking_user" => %User{} = user}) do - blocks = user.blocks || [] + defp restrict_blocked(query, %{"blocking_user" => %User{} = user} = opts) do + blocked_ap_ids = opts["blocked_users_ap_ids"] || User.blocked_users_ap_ids(user) domain_blocks = user.domain_blocks || [] following_ap_ids = @@ -925,14 +959,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do from( [activity, object: o] in query, - where: fragment("not (? = ANY(?))", activity.actor, ^blocks), - where: fragment("not (? && ?)", activity.recipients, ^blocks), + where: fragment("not (? = ANY(?))", activity.actor, ^blocked_ap_ids), + where: fragment("not (? && ?)", activity.recipients, ^blocked_ap_ids), where: fragment( "not (?->>'type' = 'Announce' and ?->'to' \\?| ?)", activity.data, activity.data, - ^blocks + ^blocked_ap_ids ), where: fragment( @@ -973,8 +1007,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do defp restrict_pinned(query, _), do: query - defp restrict_muted_reblogs(query, %{"muting_user" => %User{} = user}) do - muted_reblogs = user.muted_reblogs || [] + defp restrict_muted_reblogs(query, %{"muting_user" => %User{} = user} = opts) do + muted_reblogs = opts["reblog_muted_users_ap_ids"] || User.reblog_muted_users_ap_ids(user) from( activity in query, @@ -1055,7 +1089,33 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do defp maybe_order(query, _), do: query + defp fetch_activities_query_ap_ids_ops(opts) do + source_user = opts["muting_user"] + ap_id_relations = if source_user, do: [:mute, :reblog_mute], else: [] + + ap_id_relations = + ap_id_relations ++ + if opts["blocking_user"] && opts["blocking_user"] == source_user do + [:block] + else + [] + end + + preloaded_ap_ids = User.outgoing_relations_ap_ids(source_user, ap_id_relations) + + restrict_blocked_opts = Map.merge(%{"blocked_users_ap_ids" => preloaded_ap_ids[:block]}, opts) + restrict_muted_opts = Map.merge(%{"muted_users_ap_ids" => preloaded_ap_ids[:mute]}, opts) + + restrict_muted_reblogs_opts = + Map.merge(%{"reblog_muted_users_ap_ids" => preloaded_ap_ids[:reblog_mute]}, opts) + + {restrict_blocked_opts, restrict_muted_opts, restrict_muted_reblogs_opts} + end + def fetch_activities_query(recipients, opts \\ %{}) do + {restrict_blocked_opts, restrict_muted_opts, restrict_muted_reblogs_opts} = + fetch_activities_query_ap_ids_ops(opts) + config = %{ skip_thread_containment: Config.get([:instance, :skip_thread_containment]) } @@ -1075,15 +1135,15 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do |> restrict_type(opts) |> restrict_state(opts) |> restrict_favorited_by(opts) - |> restrict_blocked(opts) - |> restrict_muted(opts) + |> restrict_blocked(restrict_blocked_opts) + |> restrict_muted(restrict_muted_opts) |> restrict_media(opts) |> restrict_visibility(opts) |> restrict_thread_visibility(opts, config) |> restrict_replies(opts) |> restrict_reblogs(opts) |> restrict_pinned(opts) - |> restrict_muted_reblogs(opts) + |> restrict_muted_reblogs(restrict_muted_reblogs_opts) |> restrict_instance(opts) |> Activity.restrict_deactivated_users() |> exclude_poll_votes(opts) @@ -1175,6 +1235,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do data = Transmogrifier.maybe_fix_user_object(data) discoverable = data["discoverable"] || false invisible = data["invisible"] || false + actor_type = data["type"] || "Person" user_data = %{ ap_id: data["id"], @@ -1189,7 +1250,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do name: data["name"], follower_address: data["followers"], following_address: data["following"], - bio: data["summary"] + bio: data["summary"], + actor_type: actor_type, + also_known_as: Map.get(data, "alsoKnownAs", []) } # nickname can be nil because of virtual actors @@ -1251,13 +1314,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end end - defp collection_private(data) do - if is_map(data["first"]) and - data["first"]["type"] in ["CollectionPage", "OrderedCollectionPage"] do + defp collection_private(%{"first" => first}) do + if is_map(first) and + first["type"] in ["CollectionPage", "OrderedCollectionPage"] do {:ok, false} else with {:ok, %{"type" => type}} when type in ["CollectionPage", "OrderedCollectionPage"] <- - Fetcher.fetch_and_contain_remote_object_from_id(data["first"]) do + Fetcher.fetch_and_contain_remote_object_from_id(first) do {:ok, false} else {:error, {:ok, %{status: code}}} when code in [401, 403] -> @@ -1272,6 +1335,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end end + defp collection_private(_data), do: {:ok, true} + def user_data_from_user_object(data) do with {:ok, data} <- MRF.filter(data), {:ok, data} <- object_to_user_data(data) do diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 15612545b..ecba27bef 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -387,7 +387,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do def handle_incoming(%{"id" => nil}, _options), do: :error def handle_incoming(%{"id" => ""}, _options), do: :error # length of https:// = 8, should validate better, but good enough for now. - def handle_incoming(%{"id" => id}, _options) when not (is_binary(id) and length(id) > 8), + def handle_incoming(%{"id" => id}, _options) when is_binary(id) and byte_size(id) < 8, do: :error # TODO: validate those with a Ecto scheme @@ -669,7 +669,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do update_data = new_user_data - |> Map.take([:avatar, :banner, :bio, :name]) + |> Map.take([:avatar, :banner, :bio, :name, :also_known_as]) |> Map.put(:fields, fields) |> Map.put(:locked, locked) |> Map.put(:invisible, invisible) @@ -857,6 +857,24 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do end end + def handle_incoming( + %{ + "type" => "Move", + "actor" => origin_actor, + "object" => origin_actor, + "target" => target_actor + }, + _options + ) do + with %User{} = origin_user <- User.get_cached_by_ap_id(origin_actor), + {:ok, %User{} = target_user} <- User.get_or_fetch_by_ap_id(target_actor), + true <- origin_actor in target_user.also_known_as do + ActivityPub.move(origin_user, target_user, false) + else + _e -> :error + end + end + def handle_incoming(_, _), do: :error @spec get_obj_helper(String.t(), Keyword.t()) :: {:ok, Object.t()} | nil diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex index 01aacbde3..2ca805c09 100644 --- a/lib/pleroma/web/activity_pub/utils.ex +++ b/lib/pleroma/web/activity_pub/utils.ex @@ -722,16 +722,22 @@ defmodule Pleroma.Web.ActivityPub.Utils do act when is_binary(act) -> act end - activity = Activity.get_by_ap_id_with_object(id) - actor = User.get_by_ap_id(activity.object.data["actor"]) + case Activity.get_by_ap_id_with_object(id) do + %Activity{} = activity -> + %{ + "type" => "Note", + "id" => activity.data["id"], + "content" => activity.object.data["content"], + "published" => activity.object.data["published"], + "actor" => + AccountView.render("show.json", %{ + user: User.get_by_ap_id(activity.object.data["actor"]) + }) + } - %{ - "type" => "Note", - "id" => activity.data["id"], - "content" => activity.object.data["content"], - "published" => activity.object.data["published"], - "actor" => AccountView.render("show.json", %{user: actor}) - } + _ -> + %{"id" => id, "deleted" => true} + end end defp build_flag_object(_), do: [] @@ -788,63 +794,76 @@ defmodule Pleroma.Web.ActivityPub.Utils do ActivityPub.fetch_activities([], params, :offset) end - @spec get_reports_grouped_by_status(%{required(:activity) => String.t()}) :: %{ - required(:groups) => [ - %{ - required(:date) => String.t(), - required(:account) => %{}, - required(:status) => %{}, - required(:actors) => [%User{}], - required(:reports) => [%Activity{}] - } - ], - required(:total) => integer - } - def get_reports_grouped_by_status(groups) do - parsed_groups = - groups - |> Enum.map(fn entry -> - activity = - case Jason.decode(entry.activity) do - {:ok, activity} -> activity - _ -> build_flag_object(entry.activity) - end - - parse_report_group(activity) - end) - - %{ - groups: parsed_groups - } - end - def parse_report_group(activity) do reports = get_reports_by_status_id(activity["id"]) max_date = Enum.max_by(reports, &NaiveDateTime.from_iso8601!(&1.data["published"])) actors = Enum.map(reports, & &1.user_actor) + [%{data: %{"object" => [account_id | _]}} | _] = reports + + account = + AccountView.render("show.json", %{ + user: User.get_by_ap_id(account_id) + }) + + status = get_status_data(activity) %{ date: max_date.data["published"], - account: activity["actor"], - status: %{ - id: activity["id"], - content: activity["content"], - published: activity["published"] - }, + account: account, + status: status, actors: Enum.uniq(actors), reports: reports } end + defp get_status_data(status) do + case status["deleted"] do + true -> + %{ + "id" => status["id"], + "deleted" => true + } + + _ -> + Activity.get_by_ap_id(status["id"]) + end + end + def get_reports_by_status_id(ap_id) do from(a in Activity, where: fragment("(?)->>'type' = 'Flag'", a.data), - where: fragment("(?)->'object' @> ?", a.data, ^[%{id: ap_id}]) + where: fragment("(?)->'object' @> ?", a.data, ^[%{id: ap_id}]), + or_where: fragment("(?)->'object' @> ?", a.data, ^[ap_id]) ) |> Activity.with_preloaded_user_actor() |> Repo.all() end + @spec get_reports_grouped_by_status([String.t()]) :: %{ + required(:groups) => [ + %{ + required(:date) => String.t(), + required(:account) => %{}, + required(:status) => %{}, + required(:actors) => [%User{}], + required(:reports) => [%Activity{}] + } + ] + } + def get_reports_grouped_by_status(activity_ids) do + parsed_groups = + activity_ids + |> Enum.map(fn id -> + id + |> build_flag_object() + |> parse_report_group() + end) + + %{ + groups: parsed_groups + } + end + @spec get_reported_activities() :: [ %{ required(:activity) => String.t(), @@ -852,17 +871,23 @@ defmodule Pleroma.Web.ActivityPub.Utils do } ] def get_reported_activities do - from(a in Activity, - where: fragment("(?)->>'type' = 'Flag'", a.data), + reported_activities_query = + from(a in Activity, + where: fragment("(?)->>'type' = 'Flag'", a.data), + select: %{ + activity: fragment("jsonb_array_elements((? #- '{object,0}')->'object')", a.data) + }, + group_by: fragment("activity") + ) + + from(a in subquery(reported_activities_query), + distinct: true, select: %{ - date: fragment("max(?->>'published') date", a.data), - activity: - fragment("jsonb_array_elements_text((? #- '{object,0}')->'object') activity", a.data) - }, - group_by: fragment("activity"), - order_by: fragment("date DESC") + id: fragment("COALESCE(?->>'id'::text, ? #>> '{}')", a.activity, a.activity) + } ) |> Repo.all() + |> Enum.map(& &1.id) end def update_report_state(%Activity{} = activity, state) diff --git a/lib/pleroma/web/activity_pub/views/user_view.ex b/lib/pleroma/web/activity_pub/views/user_view.ex index cf08045c9..9059aa634 100644 --- a/lib/pleroma/web/activity_pub/views/user_view.ex +++ b/lib/pleroma/web/activity_pub/views/user_view.ex @@ -91,7 +91,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do %{ "id" => user.ap_id, - "type" => "Person", + "type" => user.actor_type, "following" => "#{user.ap_id}/following", "followers" => "#{user.ap_id}/followers", "inbox" => "#{user.ap_id}/inbox", diff --git a/lib/pleroma/web/activity_pub/visibility.ex b/lib/pleroma/web/activity_pub/visibility.ex index cd4097493..e172f6d3f 100644 --- a/lib/pleroma/web/activity_pub/visibility.ex +++ b/lib/pleroma/web/activity_pub/visibility.ex @@ -14,6 +14,7 @@ defmodule Pleroma.Web.ActivityPub.Visibility do @spec is_public?(Object.t() | Activity.t() | map()) :: boolean() def is_public?(%Object{data: %{"type" => "Tombstone"}}), do: false def is_public?(%Object{data: data}), do: is_public?(data) + def is_public?(%Activity{data: %{"type" => "Move"}}), do: true def is_public?(%Activity{data: data}), do: is_public?(data) def is_public?(%{"directMessage" => true}), do: false def is_public?(data), do: Utils.label_in_message?(Pleroma.Constants.as_public(), data) diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex index 24fdc3c82..0a8a56cd8 100644 --- a/lib/pleroma/web/admin_api/admin_api_controller.ex +++ b/lib/pleroma/web/admin_api/admin_api_controller.ex @@ -30,13 +30,13 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do plug( OAuthScopesPlug, - %{scopes: ["read:accounts"]} + %{scopes: ["read:accounts"], admin: true} when action in [:list_users, :user_show, :right_get, :invites] ) plug( OAuthScopesPlug, - %{scopes: ["write:accounts"]} + %{scopes: ["write:accounts"], admin: true} when action in [ :get_invite_token, :revoke_invite, @@ -58,35 +58,37 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do plug( OAuthScopesPlug, - %{scopes: ["read:reports"]} when action in [:list_reports, :report_show] + %{scopes: ["read:reports"], admin: true} + when action in [:list_reports, :report_show] ) plug( OAuthScopesPlug, - %{scopes: ["write:reports"]} + %{scopes: ["write:reports"], admin: true} when action in [:report_update_state, :report_respond] ) plug( OAuthScopesPlug, - %{scopes: ["read:statuses"]} when action == :list_user_statuses + %{scopes: ["read:statuses"], admin: true} + when action == :list_user_statuses ) plug( OAuthScopesPlug, - %{scopes: ["write:statuses"]} + %{scopes: ["write:statuses"], admin: true} when action in [:status_update, :status_delete] ) plug( OAuthScopesPlug, - %{scopes: ["read"]} + %{scopes: ["read"], admin: true} when action in [:config_show, :migrate_to_db, :migrate_from_db, :list_log] ) plug( OAuthScopesPlug, - %{scopes: ["write"]} + %{scopes: ["write"], admin: true} when action in [:relay_follow, :relay_unfollow, :config_update] ) @@ -647,11 +649,11 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do end def list_grouped_reports(conn, _params) do - reports = Utils.get_reported_activities() + statuses = Utils.get_reported_activities() conn |> put_view(ReportView) - |> render("index_grouped.json", Utils.get_reports_grouped_by_status(reports)) + |> render("index_grouped.json", Utils.get_reports_grouped_by_status(statuses)) end def report_show(conn, %{"id" => id}) do diff --git a/lib/pleroma/web/admin_api/views/report_view.ex b/lib/pleroma/web/admin_api/views/report_view.ex index ca88595c7..13602efd9 100644 --- a/lib/pleroma/web/admin_api/views/report_view.ex +++ b/lib/pleroma/web/admin_api/views/report_view.ex @@ -4,6 +4,7 @@ defmodule Pleroma.Web.AdminAPI.ReportView do use Pleroma.Web, :view + alias Pleroma.Activity alias Pleroma.HTML alias Pleroma.User alias Pleroma.Web.AdminAPI.Report @@ -45,10 +46,16 @@ defmodule Pleroma.Web.AdminAPI.ReportView do def render("index_grouped.json", %{groups: groups}) do reports = Enum.map(groups, fn group -> + status = + case group.status do + %Activity{} = activity -> StatusView.render("show.json", %{activity: activity}) + _ -> group.status + end + %{ date: group[:date], account: group[:account], - status: group[:status], + status: Map.put_new(status, "deleted", false), actors: Enum.map(group[:actors], &merge_account_views/1), reports: group[:reports] diff --git a/lib/pleroma/web/chat_channel.ex b/lib/pleroma/web/chat_channel.ex index 08841a3e8..840414933 100644 --- a/lib/pleroma/web/chat_channel.ex +++ b/lib/pleroma/web/chat_channel.ex @@ -20,7 +20,7 @@ defmodule Pleroma.Web.ChatChannel do def handle_in("new_msg", %{"text" => text}, %{assigns: %{user_name: user_name}} = socket) do text = String.trim(text) - if String.length(text) > 0 do + if String.length(text) in 1..Pleroma.Config.get([:instance, :chat_limit]) do author = User.get_cached_by_nickname(user_name) author = Pleroma.Web.MastodonAPI.AccountView.render("show.json", user: author) message = ChatChannelState.add_message(%{text: text, author: author}) diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex index fe6e26a90..2f3bcfc3c 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -10,6 +10,7 @@ defmodule Pleroma.Web.CommonAPI do alias Pleroma.Object alias Pleroma.ThreadMute alias Pleroma.User + alias Pleroma.UserRelationship alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.ActivityPub.Visibility @@ -32,7 +33,7 @@ defmodule Pleroma.Web.CommonAPI do def unfollow(follower, unfollowed) do with {:ok, follower, _follow_activity} <- User.unfollow(follower, unfollowed), {:ok, _activity} <- ActivityPub.unfollow(follower, unfollowed), - {:ok, _unfollowed} <- User.unsubscribe(follower, unfollowed) do + {:ok, _subscription} <- User.unsubscribe(follower, unfollowed) do {:ok, follower} end end @@ -420,15 +421,11 @@ defmodule Pleroma.Web.CommonAPI do defp set_visibility(activity, _), do: {:ok, activity} - def hide_reblogs(user, %{ap_id: ap_id} = _muted) do - if ap_id not in user.muted_reblogs do - User.add_reblog_mute(user, ap_id) - end + def hide_reblogs(%User{} = user, %User{} = target) do + UserRelationship.create_reblog_mute(user, target) end - def show_reblogs(user, %{ap_id: ap_id} = _muted) do - if ap_id in user.muted_reblogs do - User.remove_reblog_mute(user, ap_id) - end + def show_reblogs(%User{} = user, %User{} = target) do + UserRelationship.delete_reblog_mute(user, target) end end diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex index 88a5f434a..a9b164d9a 100644 --- a/lib/pleroma/web/common_api/utils.ex +++ b/lib/pleroma/web/common_api/utils.ex @@ -451,6 +451,8 @@ defmodule Pleroma.Web.CommonAPI.Utils do recipients ++ to end + def maybe_notify_to_recipients(recipients, _), do: recipients + def maybe_notify_mentioned_recipients( recipients, %Activity{data: %{"to" => _to, "type" => type} = data} = activity @@ -492,7 +494,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do with %User{} = user <- User.get_cached_by_ap_id(actor) do subscriber_ids = user - |> User.subscribers() + |> User.subscriber_users() |> Enum.filter(&Visibility.visible_for_user?(activity, &1)) |> Enum.map(& &1.ap_id) @@ -502,6 +504,17 @@ defmodule Pleroma.Web.CommonAPI.Utils do def maybe_notify_subscribers(recipients, _), do: recipients + def maybe_notify_followers(recipients, %Activity{data: %{"type" => "Move"}} = activity) do + with %User{} = user <- User.get_cached_by_ap_id(activity.actor) do + user + |> User.get_followers() + |> Enum.map(& &1.ap_id) + |> Enum.concat(recipients) + end + end + + def maybe_notify_followers(recipients, _), do: recipients + def maybe_extract_mentions(%{"tag" => tag}) do tag |> Enum.filter(fn x -> is_map(x) && x["type"] == "Mention" end) diff --git a/lib/pleroma/web/endpoint.ex b/lib/pleroma/web/endpoint.ex index 49735b5c2..bbea31682 100644 --- a/lib/pleroma/web/endpoint.ex +++ b/lib/pleroma/web/endpoint.ex @@ -61,14 +61,7 @@ defmodule Pleroma.Web.Endpoint do plug(Plug.RequestId) plug(Plug.Logger) - plug( - Plug.Parsers, - parsers: [:urlencoded, :multipart, :json], - pass: ["*/*"], - json_decoder: Jason, - length: Pleroma.Config.get([:instance, :upload_limit]), - body_reader: {Pleroma.Web.Plugs.DigestPlug, :read_body, []} - ) + plug(Pleroma.Plugs.Parsers) plug(Plug.MethodOverride) plug(Plug.Head) diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index 5f2544640..38d14256f 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -152,6 +152,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do :hide_favorites, :show_role, :skip_thread_containment, + :allow_following_move, :discoverable ] |> Enum.reduce(%{}, fn key, acc -> @@ -187,6 +188,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do {:ok, Map.merge(user.pleroma_settings_store, value)} end) |> add_if_present(params, "default_scope", :default_scope) + |> add_if_present(params, "actor_type", :actor_type) emojis_text = (user_params["display_name"] || "") <> (user_params["note"] || "") @@ -248,7 +250,11 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do @doc "GET /api/v1/accounts/:id/statuses" def statuses(%{assigns: %{user: reading_user}} = conn, params) do with %User{} = user <- User.get_cached_by_nickname_or_id(params["id"], for: reading_user) do - params = Map.put(params, "tag", params["tagged"]) + params = + params + |> Map.put("tag", params["tagged"]) + |> Map.delete("godmode") + activities = ActivityPub.fetch_user_activities(user, reading_user, params) conn @@ -323,7 +329,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do def mute(%{assigns: %{user: muter, account: muted}} = conn, params) do notifications? = params |> Map.get("notifications", true) |> truthy_param?() - with {:ok, muter} <- User.mute(muter, muted, notifications?) do + with {:ok, _user_relationships} <- User.mute(muter, muted, notifications?) do render(conn, "relationship.json", user: muter, target: muted) else {:error, message} -> json_response(conn, :forbidden, %{error: message}) @@ -332,7 +338,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do @doc "POST /api/v1/accounts/:id/unmute" def unmute(%{assigns: %{user: muter, account: muted}} = conn, _params) do - with {:ok, muter} <- User.unmute(muter, muted) do + with {:ok, _user_relationships} <- User.unmute(muter, muted) do render(conn, "relationship.json", user: muter, target: muted) else {:error, message} -> json_response(conn, :forbidden, %{error: message}) @@ -341,7 +347,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do @doc "POST /api/v1/accounts/:id/block" def block(%{assigns: %{user: blocker, account: blocked}} = conn, _params) do - with {:ok, blocker} <- User.block(blocker, blocked), + with {:ok, _user_block} <- User.block(blocker, blocked), {:ok, _activity} <- ActivityPub.block(blocker, blocked) do render(conn, "relationship.json", user: blocker, target: blocked) else @@ -351,7 +357,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do @doc "POST /api/v1/accounts/:id/unblock" def unblock(%{assigns: %{user: blocker, account: blocked}} = conn, _params) do - with {:ok, blocker} <- User.unblock(blocker, blocked), + with {:ok, _user_block} <- User.unblock(blocker, blocked), {:ok, _activity} <- ActivityPub.unblock(blocker, blocked) do render(conn, "relationship.json", user: blocker, target: blocked) else @@ -373,12 +379,14 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do @doc "GET /api/v1/mutes" def mutes(%{assigns: %{user: user}} = conn, _) do - render(conn, "index.json", users: User.muted_users(user), for: user, as: :user) + users = User.muted_users(user, _restrict_deactivated = true) + render(conn, "index.json", users: users, for: user, as: :user) end @doc "GET /api/v1/blocks" def blocks(%{assigns: %{user: user}} = conn, _) do - render(conn, "index.json", users: User.blocked_users(user), for: user, as: :user) + users = User.blocked_users(user, _restrict_deactivated = true) + render(conn, "index.json", users: users, for: user, as: :user) end @doc "GET /api/v1/endorsements" diff --git a/lib/pleroma/web/mastodon_api/mastodon_api.ex b/lib/pleroma/web/mastodon_api/mastodon_api.ex index d875a5788..b1816370e 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api.ex @@ -24,19 +24,16 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPI do with {:ok, follower, _followed, _} <- result do options = cast_params(params) - - case reblogs_visibility(options[:reblogs], result) do - {:ok, follower} -> {:ok, follower} - _ -> {:ok, follower} - end + set_reblogs_visibility(options[:reblogs], result) + {:ok, follower} end end - defp reblogs_visibility(false, {:ok, follower, followed, _}) do + defp set_reblogs_visibility(false, {:ok, follower, followed, _}) do CommonAPI.hide_reblogs(follower, followed) end - defp reblogs_visibility(_, {:ok, follower, followed, _}) do + defp set_reblogs_visibility(_, {:ok, follower, followed, _}) do CommonAPI.show_reblogs(follower, followed) end @@ -73,7 +70,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPI do exclude_types: {:array, :string}, exclude_visibilities: {:array, :string}, reblogs: :boolean, - with_muted: :boolean + with_muted: :boolean, + with_move: :boolean } changeset = cast({%{}, param_types}, params, Map.keys(param_types)) diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex index 1068f8823..a5420f480 100644 --- a/lib/pleroma/web/mastodon_api/views/account_view.ex +++ b/lib/pleroma/web/mastodon_api/views/account_view.ex @@ -50,8 +50,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do id: to_string(target.id), following: User.following?(user, target), followed_by: User.following?(target, user), - blocking: User.blocks_ap_id?(user, target), - blocked_by: User.blocks_ap_id?(target, user), + blocking: User.blocks_user?(user, target), + blocked_by: User.blocks_user?(target, user), muting: User.mutes?(user, target), muting_notifications: User.muted_notifications?(user, target), subscribing: User.subscribed_to?(user, target), @@ -86,7 +86,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do 0 end - bot = (user.source_data["type"] || "Person") in ["Application", "Service"] + bot = user.actor_type in ["Application", "Service"] emojis = (user.source_data["tag"] || []) @@ -137,7 +137,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do sensitive: false, fields: user.raw_fields, pleroma: %{ - discoverable: user.discoverable + discoverable: user.discoverable, + actor_type: user.actor_type } }, @@ -162,6 +163,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do |> maybe_put_chat_token(user, opts[:for], opts) |> maybe_put_activation_status(user, opts[:for]) |> maybe_put_follow_requests_count(user, opts[:for]) + |> maybe_put_allow_following_move(user, opts[:for]) |> maybe_put_unread_conversation_count(user, opts[:for]) end @@ -238,6 +240,12 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do defp maybe_put_notification_settings(data, _, _), do: data + defp maybe_put_allow_following_move(data, %User{id: user_id} = user, %User{id: user_id}) do + Kernel.put_in(data, [:pleroma, :allow_following_move], user.allow_following_move) + end + + defp maybe_put_allow_following_move(data, _, _), do: data + defp maybe_put_activation_status(data, user, %User{is_admin: true}) do Kernel.put_in(data, [:pleroma, :deactivated], user.deactivated) end diff --git a/lib/pleroma/web/mastodon_api/views/notification_view.ex b/lib/pleroma/web/mastodon_api/views/notification_view.ex index 5e3dbe728..ddd7f5318 100644 --- a/lib/pleroma/web/mastodon_api/views/notification_view.ex +++ b/lib/pleroma/web/mastodon_api/views/notification_view.ex @@ -37,32 +37,24 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do } case mastodon_type do - "mention" -> - response - |> Map.merge(%{ - status: StatusView.render("show.json", %{activity: activity, for: user}) - }) - - "favourite" -> - response - |> Map.merge(%{ - status: StatusView.render("show.json", %{activity: parent_activity, for: user}) - }) - - "reblog" -> - response - |> Map.merge(%{ - status: StatusView.render("show.json", %{activity: parent_activity, for: user}) - }) - - "follow" -> - response - - _ -> - nil + "mention" -> put_status(response, activity, user) + "favourite" -> put_status(response, parent_activity, user) + "reblog" -> put_status(response, parent_activity, user) + "move" -> put_target(response, activity, user) + "follow" -> response + _ -> nil end else _ -> nil end end + + defp put_status(response, activity, user) do + Map.put(response, :status, StatusView.render("show.json", %{activity: activity, for: user})) + end + + defp put_target(response, activity, user) do + target = User.get_cached_by_ap_id(activity.data["target"]) + Map.put(response, :target, AccountView.render("show.json", %{user: target, for: user})) + end end diff --git a/lib/pleroma/web/oauth/oauth_controller.ex b/lib/pleroma/web/oauth/oauth_controller.ex index 2aee8cab2..87acdec97 100644 --- a/lib/pleroma/web/oauth/oauth_controller.ex +++ b/lib/pleroma/web/oauth/oauth_controller.ex @@ -222,7 +222,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do {:user_active, true} <- {:user_active, !user.deactivated}, {:password_reset_pending, false} <- {:password_reset_pending, user.password_reset_pending}, - {:ok, scopes} <- validate_scopes(app, params), + {:ok, scopes} <- validate_scopes(app, params, user), {:ok, auth} <- Authorization.create_authorization(app, user, scopes), {:ok, token} <- Token.exchange_token(app, auth) do json(conn, Token.Response.build(user, token)) @@ -471,7 +471,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do {:get_user, (user && {:ok, user}) || Authenticator.get_user(conn)}, %App{} = app <- Repo.get_by(App, client_id: client_id), true <- redirect_uri in String.split(app.redirect_uris), - {:ok, scopes} <- validate_scopes(app, auth_attrs), + {:ok, scopes} <- validate_scopes(app, auth_attrs, user), {:auth_active, true} <- {:auth_active, User.auth_active?(user)} do Authorization.create_authorization(app, user, scopes) end @@ -487,12 +487,12 @@ defmodule Pleroma.Web.OAuth.OAuthController do defp put_session_registration_id(%Plug.Conn{} = conn, registration_id), do: put_session(conn, :registration_id, registration_id) - @spec validate_scopes(App.t(), map()) :: + @spec validate_scopes(App.t(), map(), User.t()) :: {:ok, list()} | {:error, :missing_scopes | :unsupported_scopes} - defp validate_scopes(app, params) do + defp validate_scopes(%App{} = app, params, %User{} = user) do params |> Scopes.fetch_scopes(app.scopes) - |> Scopes.validate(app.scopes) + |> Scopes.validate(app.scopes, user) end def default_redirect_uri(%App{} = app) do diff --git a/lib/pleroma/web/oauth/scopes.ex b/lib/pleroma/web/oauth/scopes.ex index 48bd14407..5e04652c2 100644 --- a/lib/pleroma/web/oauth/scopes.ex +++ b/lib/pleroma/web/oauth/scopes.ex @@ -7,6 +7,9 @@ defmodule Pleroma.Web.OAuth.Scopes do Functions for dealing with scopes. """ + alias Pleroma.Plugs.OAuthScopesPlug + alias Pleroma.User + @doc """ Fetch scopes from request params. @@ -53,15 +56,36 @@ defmodule Pleroma.Web.OAuth.Scopes do @doc """ Validates scopes. """ - @spec validate(list() | nil, list()) :: + @spec validate(list() | nil, list(), User.t()) :: {:ok, list()} | {:error, :missing_scopes | :unsupported_scopes} - def validate([], _app_scopes), do: {:error, :missing_scopes} - def validate(nil, _app_scopes), do: {:error, :missing_scopes} + def validate(blank_scopes, _app_scopes, _user) when blank_scopes in [nil, []], + do: {:error, :missing_scopes} - def validate(scopes, app_scopes) do - case Pleroma.Plugs.OAuthScopesPlug.filter_descendants(scopes, app_scopes) do + def validate(scopes, app_scopes, %User{} = user) do + with {:ok, _} <- ensure_scopes_support(scopes, app_scopes), + {:ok, scopes} <- authorize_admin_scopes(scopes, app_scopes, user) do + {:ok, scopes} + end + end + + defp ensure_scopes_support(scopes, app_scopes) do + case OAuthScopesPlug.filter_descendants(scopes, app_scopes) do ^scopes -> {:ok, scopes} _ -> {:error, :unsupported_scopes} end end + + defp authorize_admin_scopes(scopes, app_scopes, %User{} = user) do + if user.is_admin || !contains_admin_scopes?(scopes) || !contains_admin_scopes?(app_scopes) do + {:ok, scopes} + else + {:error, :unsupported_scopes} + end + end + + def contains_admin_scopes?(scopes) do + scopes + |> OAuthScopesPlug.filter_descendants(["admin"]) + |> Enum.any?() + end end diff --git a/lib/pleroma/web/oauth/token/clean_worker.ex b/lib/pleroma/web/oauth/token/clean_worker.ex index f639f9c6f..3c9c580d5 100644 --- a/lib/pleroma/web/oauth/token/clean_worker.ex +++ b/lib/pleroma/web/oauth/token/clean_worker.ex @@ -11,11 +11,6 @@ defmodule Pleroma.Web.OAuth.Token.CleanWorker do @ten_seconds 10_000 @one_day 86_400_000 - @interval Pleroma.Config.get( - [:oauth2, :clean_expired_tokens_interval], - @one_day - ) - alias Pleroma.Web.OAuth.Token alias Pleroma.Workers.BackgroundWorker @@ -29,8 +24,9 @@ defmodule Pleroma.Web.OAuth.Token.CleanWorker do @doc false def handle_info(:perform, state) do BackgroundWorker.enqueue("clean_expired_tokens", %{}) + interval = Pleroma.Config.get([:oauth2, :clean_expired_tokens_interval], @one_day) - Process.send_after(self(), :perform, @interval) + Process.send_after(self(), :perform, interval) {:noreply, state} end diff --git a/lib/pleroma/web/pleroma_api/controllers/account_controller.ex b/lib/pleroma/web/pleroma_api/controllers/account_controller.ex index bc2f1017c..773cd9a97 100644 --- a/lib/pleroma/web/pleroma_api/controllers/account_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/account_controller.ex @@ -144,7 +144,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do @doc "POST /api/v1/pleroma/accounts/:id/subscribe" def subscribe(%{assigns: %{user: user, account: subscription_target}} = conn, _params) do - with {:ok, subscription_target} <- User.subscribe(user, subscription_target) do + with {:ok, _subscription} <- User.subscribe(user, subscription_target) do render(conn, "relationship.json", user: user, target: subscription_target) else {:error, message} -> json_response(conn, :forbidden, %{error: message}) @@ -153,7 +153,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do @doc "POST /api/v1/pleroma/accounts/:id/unsubscribe" def unsubscribe(%{assigns: %{user: user, account: subscription_target}} = conn, _params) do - with {:ok, subscription_target} <- User.unsubscribe(user, subscription_target) do + with {:ok, _subscription} <- User.unsubscribe(user, subscription_target) do render(conn, "relationship.json", user: user, target: subscription_target) else {:error, message} -> json_response(conn, :forbidden, %{error: message}) diff --git a/lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex b/lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex index a474d41d4..69dfa92e3 100644 --- a/lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex @@ -7,7 +7,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do plug( OAuthScopesPlug, - %{scopes: ["write"]} + %{scopes: ["write"], admin: true} when action in [ :create, :delete, diff --git a/lib/pleroma/web/push/impl.ex b/lib/pleroma/web/push/impl.ex index 3de7af708..34ec1d8d9 100644 --- a/lib/pleroma/web/push/impl.ex +++ b/lib/pleroma/web/push/impl.ex @@ -16,14 +16,14 @@ defmodule Pleroma.Web.Push.Impl do require Logger import Ecto.Query - @types ["Create", "Follow", "Announce", "Like"] + @types ["Create", "Follow", "Announce", "Like", "Move"] @doc "Performs sending notifications for user subscriptions" @spec perform(Notification.t()) :: list(any) | :error def perform( %{ - activity: %{data: %{"type" => activity_type}, id: activity_id} = activity, - user_id: user_id + activity: %{data: %{"type" => activity_type}} = activity, + user: %User{id: user_id} } = notif ) when activity_type in @types do @@ -39,18 +39,17 @@ defmodule Pleroma.Web.Push.Impl do for subscription <- fetch_subsriptions(user_id), get_in(subscription.data, ["alerts", type]) do %{ - title: format_title(notif), access_token: subscription.token.token, - body: format_body(notif, actor, object), notification_id: notif.id, notification_type: type, icon: avatar_url, preferred_locale: "en", pleroma: %{ - activity_id: activity_id, + activity_id: notif.activity.id, direct_conversation_id: direct_conversation_id } } + |> Map.merge(build_content(notif, actor, object)) |> Jason.encode!() |> push_message(build_sub(subscription), gcm_api_key, subscription) end @@ -100,6 +99,24 @@ defmodule Pleroma.Web.Push.Impl do } end + def build_content( + %{ + activity: %{data: %{"directMessage" => true}}, + user: %{notification_settings: %{privacy_option: true}} + }, + actor, + _ + ) do + %{title: "New Direct Message", body: "@#{actor.nickname}"} + end + + def build_content(notif, actor, object) do + %{ + title: format_title(notif), + body: format_body(notif, actor, object) + } + end + def format_body( %{activity: %{data: %{"type" => "Create"}}}, actor, diff --git a/lib/pleroma/web/streamer/worker.ex b/lib/pleroma/web/streamer/worker.ex index 33b24840d..a1b445f2f 100644 --- a/lib/pleroma/web/streamer/worker.ex +++ b/lib/pleroma/web/streamer/worker.ex @@ -129,16 +129,17 @@ defmodule Pleroma.Web.Streamer.Worker do end defp should_send?(%User{} = user, %Activity{} = item) do - blocks = user.blocks || [] - mutes = user.mutes || [] - reblog_mutes = user.muted_reblogs || [] - recipient_blocks = MapSet.new(blocks ++ mutes) + %{block: blocked_ap_ids, mute: muted_ap_ids, reblog_mute: reblog_muted_ap_ids} = + User.outgoing_relations_ap_ids(user, [:block, :mute, :reblog_mute]) + + recipient_blocks = MapSet.new(blocked_ap_ids ++ muted_ap_ids) recipients = MapSet.new(item.recipients) domain_blocks = Pleroma.Web.ActivityPub.MRF.subdomains_regex(user.domain_blocks) with parent <- Object.normalize(item) || item, - true <- Enum.all?([blocks, mutes, reblog_mutes], &(item.actor not in &1)), - true <- Enum.all?([blocks, mutes], &(parent.data["actor"] not in &1)), + true <- + Enum.all?([blocked_ap_ids, muted_ap_ids, reblog_muted_ap_ids], &(item.actor not in &1)), + true <- Enum.all?([blocked_ap_ids, muted_ap_ids], &(parent.data["actor"] not in &1)), true <- MapSet.disjoint?(recipients, recipient_blocks), %{host: item_host} <- URI.parse(item.actor), %{host: parent_host} <- URI.parse(parent.data["actor"]), |