diff options
41 files changed, 1042 insertions, 388 deletions
diff --git a/docs/Admin-API.md b/docs/Admin-API.md index 2edb31f3c..53b68ffd4 100644 --- a/docs/Admin-API.md +++ b/docs/Admin-API.md @@ -8,10 +8,15 @@ Authentication is required and the user must be an admin. - Method `GET` - Query Params: - - `query`: **string** *optional* search term - - `local_only`: **bool** *optional* whether to return only local users - - `page`: **integer** *optional* page number - - `page_size`: **integer** *optional* number of users per page (default is `50`) + - *optional* `query`: **string** search term + - *optional* `filters`: **string** comma-separated string of filters: + - `local`: only local users + - `external`: only external users + - `active`: only active users + - `deactivated`: only deactivated users + - *optional* `page`: **integer** page number + - *optional* `page_size`: **integer** number of users per page (default is `50`) +- Example: `https://mypleroma.org/api/pleroma/admin/users?query=john&filters=local,active&page=1&page_size=10` - Response: ```JSON @@ -22,7 +27,13 @@ Authentication is required and the user must be an admin. { "deactivated": bool, "id": integer, - "nickname": string + "nickname": string, + "roles": { + "admin": bool, + "moderator": bool + }, + "local": bool, + "tags": array }, ... ] @@ -99,7 +110,7 @@ Authentication is required and the user must be an admin. Note: Available `:permission_group` is currently moderator and admin. 404 is returned when the permission group doesn’t exist. -### Get user user permission groups membership +### Get user user permission groups membership per permission group - Method: `GET` - Params: none @@ -138,6 +149,17 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret - `nickname` - `status` BOOLEAN field, false value means deactivation. +## `/api/pleroma/admin/users/:nickname` + +### Retrive the details of a user + +- Method: `GET` +- Params: + - `nickname` +- Response: + - On failure: `Not found` + - On success: JSON of the user + ## `/api/pleroma/admin/relay` ### Follow a Relay diff --git a/lib/pleroma/activity.ex b/lib/pleroma/activity.ex index 04c3bb673..bc3f8caba 100644 --- a/lib/pleroma/activity.ex +++ b/lib/pleroma/activity.ex @@ -7,6 +7,7 @@ defmodule Pleroma.Activity do alias Pleroma.Activity alias Pleroma.Notification + alias Pleroma.Object alias Pleroma.Repo import Ecto.Query @@ -33,9 +34,42 @@ defmodule Pleroma.Activity do field(:recipients, {:array, :string}) has_many(:notifications, Notification, on_delete: :delete_all) + # Attention: this is a fake relation, don't try to preload it blindly and expect it to work! + # The foreign key is embedded in a jsonb field. + # + # To use it, you probably want to do an inner join and a preload: + # + # ``` + # |> join(:inner, [activity], o in Object, + # on: fragment("(?->>'id') = COALESCE((?)->'object'->> 'id', (?)->>'object')", + # o.data, activity.data, activity.data)) + # |> preload([activity, object], [object: object]) + # ``` + # + # As a convenience, Activity.with_preloaded_object() sets up an inner join and preload for the + # typical case. + has_one(:object, Object, on_delete: :nothing, foreign_key: :id) + timestamps() end + def with_preloaded_object(query) do + query + |> join( + :inner, + [activity], + o in Object, + on: + fragment( + "(?->>'id') = COALESCE(?->'object'->>'id', ?->>'object')", + o.data, + activity.data, + activity.data + ) + ) + |> preload([activity, object], object: object) + end + def get_by_ap_id(ap_id) do Repo.one( from( @@ -45,10 +79,44 @@ defmodule Pleroma.Activity do ) end + def get_by_ap_id_with_object(ap_id) do + Repo.one( + from( + activity in Activity, + where: fragment("(?)->>'id' = ?", activity.data, ^to_string(ap_id)), + left_join: o in Object, + on: + fragment( + "(?->>'id') = COALESCE(?->'object'->>'id', ?->>'object')", + o.data, + activity.data, + activity.data + ), + preload: [object: o] + ) + ) + end + def get_by_id(id) do Repo.get(Activity, id) end + def get_by_id_with_object(id) do + from(activity in Activity, + where: activity.id == ^id, + inner_join: o in Object, + on: + fragment( + "(?->>'id') = COALESCE(?->'object'->>'id', ?->>'object')", + o.data, + activity.data, + activity.data + ), + preload: [object: o] + ) + |> Repo.one() + end + def by_object_ap_id(ap_id) do from( activity in Activity, @@ -76,7 +144,7 @@ defmodule Pleroma.Activity do ) end - def create_by_object_ap_id(ap_id) do + def create_by_object_ap_id(ap_id) when is_binary(ap_id) do from( activity in Activity, where: @@ -90,6 +158,8 @@ defmodule Pleroma.Activity do ) end + def create_by_object_ap_id(_), do: nil + def get_all_create_by_object_ap_id(ap_id) do Repo.all(create_by_object_ap_id(ap_id)) end @@ -101,8 +171,39 @@ defmodule Pleroma.Activity do def get_create_by_object_ap_id(_), do: nil - def normalize(obj) when is_map(obj), do: Activity.get_by_ap_id(obj["id"]) - def normalize(ap_id) when is_binary(ap_id), do: Activity.get_by_ap_id(ap_id) + def create_by_object_ap_id_with_object(ap_id) when is_binary(ap_id) do + from( + activity in Activity, + where: + fragment( + "coalesce((?)->'object'->>'id', (?)->>'object') = ?", + activity.data, + activity.data, + ^to_string(ap_id) + ), + where: fragment("(?)->>'type' = 'Create'", activity.data), + inner_join: o in Object, + on: + fragment( + "(?->>'id') = COALESCE(?->'object'->>'id', ?->>'object')", + o.data, + activity.data, + activity.data + ), + preload: [object: o] + ) + end + + def create_by_object_ap_id_with_object(_), do: nil + + def get_create_by_object_ap_id_with_object(ap_id) do + ap_id + |> create_by_object_ap_id_with_object() + |> Repo.one() + end + + def normalize(obj) when is_map(obj), do: get_by_ap_id_with_object(obj["id"]) + def normalize(ap_id) when is_binary(ap_id), do: get_by_ap_id_with_object(ap_id) def normalize(_), do: nil def get_in_reply_to_activity(%Activity{data: %{"object" => %{"inReplyTo" => ap_id}}}) do @@ -144,4 +245,50 @@ defmodule Pleroma.Activity do |> where([s], s.actor == ^actor) |> Repo.all() end + + def increase_replies_count(id) do + Activity + |> where(id: ^id) + |> update([a], + set: [ + data: + fragment( + """ + jsonb_set(?, '{object, repliesCount}', + (coalesce((?->'object'->>'repliesCount')::int, 0) + 1)::varchar::jsonb, true) + """, + a.data, + a.data + ) + ] + ) + |> Repo.update_all([]) + |> case do + {1, [activity]} -> activity + _ -> {:error, "Not found"} + end + end + + def decrease_replies_count(id) do + Activity + |> where(id: ^id) + |> update([a], + set: [ + data: + fragment( + """ + jsonb_set(?, '{object, repliesCount}', + (greatest(0, (?->'object'->>'repliesCount')::int - 1))::varchar::jsonb, true) + """, + a.data, + a.data + ) + ] + ) + |> Repo.update_all([]) + |> case do + {1, [activity]} -> activity + _ -> {:error, "Not found"} + end + end end diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex index a98649b63..cac10f24a 100644 --- a/lib/pleroma/notification.ex +++ b/lib/pleroma/notification.ex @@ -7,6 +7,7 @@ defmodule Pleroma.Notification do alias Pleroma.Activity alias Pleroma.Notification + alias Pleroma.Object alias Pleroma.Pagination alias Pleroma.Repo alias Pleroma.User @@ -33,7 +34,15 @@ defmodule Pleroma.Notification do Notification |> where(user_id: ^user.id) |> join(:inner, [n], activity in assoc(n, :activity)) - |> preload(:activity) + |> join(:left, [n, a], object in Object, + on: + fragment( + "(?->>'id') = COALESCE((? -> 'object'::text) ->> 'id'::text)", + object.data, + a.data + ) + ) + |> preload([n, a, o], activity: {a, object: o}) end def for_user(user, opts \\ %{}) do diff --git a/lib/pleroma/object.ex b/lib/pleroma/object.ex index 58e46ef1d..8a670645d 100644 --- a/lib/pleroma/object.ex +++ b/lib/pleroma/object.ex @@ -14,6 +14,8 @@ defmodule Pleroma.Object do import Ecto.Query import Ecto.Changeset + require Logger + schema "objects" do field(:data, :map) @@ -38,6 +40,33 @@ defmodule Pleroma.Object do Repo.one(from(object in Object, where: fragment("(?)->>'id' = ?", object.data, ^ap_id))) end + # If we pass an Activity to Object.normalize(), we can try to use the preloaded object. + # Use this whenever possible, especially when walking graphs in an O(N) loop! + def normalize(%Activity{object: %Object{} = object}), do: object + + # Catch and log Object.normalize() calls where the Activity's child object is not + # preloaded. + def normalize(%Activity{data: %{"object" => %{"id" => ap_id}}}) do + Logger.debug( + "Object.normalize() called without preloaded object (#{ap_id}). Consider preloading the object!" + ) + + Logger.debug("Backtrace: #{inspect(Process.info(:erlang.self(), :current_stacktrace))}") + + normalize(ap_id) + end + + def normalize(%Activity{data: %{"object" => ap_id}}) do + Logger.debug( + "Object.normalize() called without preloaded object (#{ap_id}). Consider preloading the object!" + ) + + Logger.debug("Backtrace: #{inspect(Process.info(:erlang.self(), :current_stacktrace))}") + + normalize(ap_id) + end + + # Old way, try fetching the object through cache. def normalize(%{"id" => ap_id}), do: normalize(ap_id) def normalize(ap_id) when is_binary(ap_id), do: get_cached_by_ap_id(ap_id) def normalize(_), do: nil @@ -104,4 +133,50 @@ defmodule Pleroma.Object do e -> e end end + + def increase_replies_count(ap_id) do + Object + |> where([o], fragment("?->>'id' = ?::text", o.data, ^to_string(ap_id))) + |> update([o], + set: [ + data: + fragment( + """ + jsonb_set(?, '{repliesCount}', + (coalesce((?->>'repliesCount')::int, 0) + 1)::varchar::jsonb, true) + """, + o.data, + o.data + ) + ] + ) + |> Repo.update_all([]) + |> case do + {1, [object]} -> set_cache(object) + _ -> {:error, "Not found"} + end + end + + def decrease_replies_count(ap_id) do + Object + |> where([o], fragment("?->>'id' = ?::text", o.data, ^to_string(ap_id))) + |> update([o], + set: [ + data: + fragment( + """ + jsonb_set(?, '{repliesCount}', + (greatest(0, (?->>'repliesCount')::int - 1))::varchar::jsonb, true) + """, + o.data, + o.data + ) + ] + ) + |> Repo.update_all([]) + |> case do + {1, [object]} -> set_cache(object) + _ -> {:error, "Not found"} + end + end end diff --git a/lib/pleroma/uploaders/s3.ex b/lib/pleroma/uploaders/s3.ex index e7de3f3e0..521daa93b 100644 --- a/lib/pleroma/uploaders/s3.ex +++ b/lib/pleroma/uploaders/s3.ex @@ -13,10 +13,15 @@ defmodule Pleroma.Uploaders.S3 do bucket = Keyword.fetch!(config, :bucket) bucket_with_namespace = - if namespace = Keyword.get(config, :bucket_namespace) do - namespace <> ":" <> bucket - else - bucket + cond do + truncated_namespace = Keyword.get(config, :truncated_namespace) -> + truncated_namespace + + namespace = Keyword.get(config, :bucket_namespace) -> + namespace <> ":" <> bucket + + true -> + bucket end {:ok, diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index f2ef0a838..728b00a56 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -60,14 +60,10 @@ defmodule Pleroma.User do timestamps() end - def auth_active?(%User{local: false}), do: true - - def auth_active?(%User{info: %User.Info{confirmation_pending: false}}), do: true - def auth_active?(%User{info: %User.Info{confirmation_pending: true}}), do: !Pleroma.Config.get([:instance, :account_activation_required]) - def auth_active?(_), do: false + def auth_active?(%User{}), do: true def visible_for?(user, for_user \\ nil) @@ -83,17 +79,17 @@ defmodule Pleroma.User do def superuser?(%User{local: true, info: %User.Info{is_moderator: true}}), do: true def superuser?(_), do: false - def avatar_url(user) do + def avatar_url(user, options \\ []) do case user.avatar do %{"url" => [%{"href" => href} | _]} -> href - _ -> "#{Web.base_url()}/images/avi.png" + _ -> !options[:no_default] && "#{Web.base_url()}/images/avi.png" end end - def banner_url(user) do + def banner_url(user, options \\ []) do case user.info.banner do %{"url" => [%{"href" => href} | _]} -> href - _ -> "#{Web.base_url()}/images/banner.png" + _ -> !options[:no_default] && "#{Web.base_url()}/images/banner.png" end end @@ -772,52 +768,6 @@ defmodule Pleroma.User do Repo.all(query) end - @spec search_for_admin(%{ - local: boolean(), - page: number(), - page_size: number() - }) :: {:ok, [Pleroma.User.t()], number()} - def search_for_admin(%{query: nil, local: local, page: page, page_size: page_size}) do - query = - from(u in User, order_by: u.nickname) - |> maybe_local_user_query(local) - - paginated_query = - query - |> paginate(page, page_size) - - count = - query - |> Repo.aggregate(:count, :id) - - {:ok, Repo.all(paginated_query), count} - end - - @spec search_for_admin(%{ - query: binary(), - local: boolean(), - page: number(), - page_size: number() - }) :: {:ok, [Pleroma.User.t()], number()} - def search_for_admin(%{ - query: term, - local: local, - page: page, - page_size: page_size - }) do - maybe_local_query = User |> maybe_local_user_query(local) - - search_query = from(u in maybe_local_query, where: ilike(u.nickname, ^"%#{term}%")) - count = search_query |> Repo.aggregate(:count, :id) - - results = - search_query - |> paginate(page, page_size) - |> Repo.all() - - {:ok, results, count} - end - def search(query, resolve \\ false, for_user \\ nil) do # Strip the beginning @ off if there is a query query = String.trim_leading(query, "@") @@ -856,7 +806,7 @@ defmodule Pleroma.User do search_rank: fragment( """ - CASE WHEN (?) THEN (?) * 1.3 + CASE WHEN (?) THEN (?) * 1.3 WHEN (?) THEN (?) * 1.2 WHEN (?) THEN (?) * 1.1 ELSE (?) END @@ -1071,6 +1021,42 @@ defmodule Pleroma.User do ) end + def maybe_external_user_query(query, external) do + if external, do: external_user_query(query), else: query + end + + def external_user_query(query \\ User) do + from( + u in query, + where: u.local == false, + where: not is_nil(u.nickname) + ) + end + + def maybe_active_user_query(query, active) do + if active, do: active_user_query(query), else: query + end + + def active_user_query(query \\ User) do + from( + u in query, + where: fragment("not (?->'deactivated' @> 'true')", u.info), + where: not is_nil(u.nickname) + ) + end + + def maybe_deactivated_user_query(query, deactivated) do + if deactivated, do: deactivated_user_query(query), else: query + end + + def deactivated_user_query(query \\ User) do + from( + u in query, + where: fragment("(?->'deactivated' @> 'true')", u.info), + where: not is_nil(u.nickname) + ) + end + def active_local_user_query do from( u in local_user_query(), @@ -1110,13 +1096,15 @@ defmodule Pleroma.User do friends |> Enum.each(fn followed -> User.unfollow(user, followed) end) - query = from(a in Activity, where: a.actor == ^user.ap_id) + query = + from(a in Activity, where: a.actor == ^user.ap_id) + |> Activity.with_preloaded_object() Repo.all(query) |> Enum.each(fn activity -> case activity.data["type"] do "Create" -> - ActivityPub.delete(Object.normalize(activity.data["object"])) + ActivityPub.delete(Object.normalize(activity)) # TODO: Do something with likes, follows, repeats. _ -> diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 2470b4a71..6e1ed7ec9 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -89,13 +89,37 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do if is_public?(object), do: User.decrease_note_count(actor), else: {:ok, actor} end + def increase_replies_count_if_reply(%{ + "object" => + %{"inReplyTo" => reply_ap_id, "inReplyToStatusId" => reply_status_id} = object, + "type" => "Create" + }) do + if is_public?(object) do + Activity.increase_replies_count(reply_status_id) + Object.increase_replies_count(reply_ap_id) + end + end + + def increase_replies_count_if_reply(_create_data), do: :noop + + def decrease_replies_count_if_reply(%Object{ + data: %{"inReplyTo" => reply_ap_id, "inReplyToStatusId" => reply_status_id} = object + }) do + if is_public?(object) do + Activity.decrease_replies_count(reply_status_id) + Object.decrease_replies_count(reply_ap_id) + end + end + + def decrease_replies_count_if_reply(_object), do: :noop + def insert(map, local \\ true) when is_map(map) do with nil <- Activity.normalize(map), map <- lazy_put_activity_defaults(map), :ok <- check_actor_is_active(map["actor"]), {_, true} <- {:remote_limit_error, check_remote_limit(map)}, {:ok, map} <- MRF.filter(map), - :ok <- insert_full_object(map) do + {:ok, object} <- insert_full_object(map) do {recipients, _, _} = get_recipients(map) {:ok, activity} = @@ -106,6 +130,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do recipients: recipients }) + # Splice in the child object if we have one. + activity = + if !is_nil(object) do + Map.put(activity, :object, object) + else + activity + end + Task.start(fn -> Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) end) @@ -170,6 +202,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do additional ), {:ok, activity} <- insert(create_data, local), + _ <- increase_replies_count_if_reply(create_data), # Changing note count prior to enqueuing federation task in order to avoid # race conditions on updating user.info {:ok, _actor} <- increase_note_count_if_public(actor, activity), @@ -321,6 +354,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do "deleted_activity_id" => activity && activity.id }, {:ok, activity} <- insert(data, local), + _ <- decrease_replies_count_if_reply(object), # Changing note count prior to enqueuing federation task in order to avoid # race conditions on updating user.info {:ok, _actor} <- decrease_note_count_if_public(user, object), @@ -430,6 +464,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do ), order_by: [desc: :id] ) + |> Activity.with_preloaded_object() Repo.all(query) end @@ -702,13 +737,25 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do from( activity in query, - where: fragment("not ?->>'type' = 'Announce'", activity.data), - where: fragment("not ? = ANY(?)", activity.actor, ^muted_reblogs) + where: + fragment( + "not ( ?->>'type' = 'Announce' and ? = ANY(?))", + activity.data, + activity.actor, + ^muted_reblogs + ) ) end defp restrict_muted_reblogs(query, _), do: query + defp maybe_preload_objects(query, %{"skip_preload" => true}), do: query + + defp maybe_preload_objects(query, _) do + query + |> Activity.with_preloaded_object() + end + def fetch_activities_query(recipients, opts \\ %{}) do base_query = from( @@ -718,6 +765,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do ) base_query + |> maybe_preload_objects(opts) |> restrict_recipients(recipients, opts["user"]) |> restrict_tag(opts) |> restrict_tag_reject(opts) @@ -940,7 +988,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do }, :ok <- Transmogrifier.contain_origin(id, params), {:ok, activity} <- Transmogrifier.handle_incoming(params) do - {:ok, Object.normalize(activity.data["object"])} + {:ok, Object.normalize(activity)} else {:error, {:reject, nil}} -> {:reject, nil} @@ -952,7 +1000,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do Logger.info("Couldn't get object via AP, trying out OStatus fetching...") case OStatus.fetch_activity_from_url(id) do - {:ok, [activity | _]} -> {:ok, Object.normalize(activity.data["object"])} + {:ok, [activity | _]} -> {:ok, Object.normalize(activity)} e -> e end end diff --git a/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex b/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex index 25d5f9cd3..e8dfba672 100644 --- a/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex @@ -4,6 +4,10 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do @behaviour Pleroma.Web.ActivityPub.MRF + defp string_matches?(string, _) when not is_binary(string) do + false + end + defp string_matches?(string, pattern) when is_binary(pattern) do String.contains?(string, pattern) end @@ -44,6 +48,20 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do end defp check_replace(%{"object" => %{"content" => content, "summary" => summary}} = message) do + content = + if is_binary(content) do + content + else + "" + end + + summary = + if is_binary(summary) do + summary + else + "" + end + {content, summary} = Enum.reduce( Pleroma.Config.get([:mrf_keyword, :replace]), @@ -61,11 +79,6 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do end @impl true - def filter(%{"object" => %{"content" => nil}} = message) do - {:ok, message} - end - - @impl true def filter(%{"type" => "Create", "object" => %{"content" => _content}} = message) do with {:ok, message} <- check_reject(message), {:ok, message} <- check_ftl_removal(message), diff --git a/lib/pleroma/web/activity_pub/relay.ex b/lib/pleroma/web/activity_pub/relay.ex index 01fef71b9..a7a20ca37 100644 --- a/lib/pleroma/web/activity_pub/relay.ex +++ b/lib/pleroma/web/activity_pub/relay.ex @@ -41,7 +41,7 @@ defmodule Pleroma.Web.ActivityPub.Relay do def publish(%Activity{data: %{"type" => "Create"}} = activity) do with %User{} = user <- get_actor(), - %Object{} = object <- Object.normalize(activity.data["object"]["id"]) do + %Object{} = object <- Object.normalize(activity) do ActivityPub.announce(user, object, nil, true, false) else e -> Logger.error("error: #{inspect(e)}") diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex index af317245f..2e9ffe41c 100644 --- a/lib/pleroma/web/activity_pub/utils.ex +++ b/lib/pleroma/web/activity_pub/utils.ex @@ -209,12 +209,12 @@ defmodule Pleroma.Web.ActivityPub.Utils do """ def insert_full_object(%{"object" => %{"type" => type} = object_data}) when is_map(object_data) and type in @supported_object_types do - with {:ok, _} <- Object.create(object_data) do - :ok + with {:ok, object} <- Object.create(object_data) do + {:ok, object} end end - def insert_full_object(_), do: :ok + def insert_full_object(_), do: {:ok, nil} def update_object_in_activities(%{data: %{"id" => id}} = object) do # TODO diff --git a/lib/pleroma/web/activity_pub/views/object_view.ex b/lib/pleroma/web/activity_pub/views/object_view.ex index 84fa94e32..6028b773c 100644 --- a/lib/pleroma/web/activity_pub/views/object_view.ex +++ b/lib/pleroma/web/activity_pub/views/object_view.ex @@ -17,7 +17,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectView do def render("object.json", %{object: %Activity{data: %{"type" => "Create"}} = activity}) do base = Pleroma.Web.ActivityPub.Utils.make_json_ld_header() - object = Object.normalize(activity.data["object"]) + object = Object.normalize(activity) additional = Transmogrifier.prepare_object(activity.data) @@ -28,7 +28,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectView do def render("object.json", %{object: %Activity{} = activity}) do base = Pleroma.Web.ActivityPub.Utils.make_json_ld_header() - object = Object.normalize(activity.data["object"]) + object = Object.normalize(activity) additional = Transmogrifier.prepare_object(activity.data) diff --git a/lib/pleroma/web/activity_pub/views/user_view.ex b/lib/pleroma/web/activity_pub/views/user_view.ex index 3d00dcbf2..5926a3294 100644 --- a/lib/pleroma/web/activity_pub/views/user_view.ex +++ b/lib/pleroma/web/activity_pub/views/user_view.ex @@ -87,16 +87,10 @@ defmodule Pleroma.Web.ActivityPub.UserView do "publicKeyPem" => public_key }, "endpoints" => endpoints, - "icon" => %{ - "type" => "Image", - "url" => User.avatar_url(user) - }, - "image" => %{ - "type" => "Image", - "url" => User.banner_url(user) - }, "tag" => user.info.source_data["tag"] || [] } + |> Map.merge(maybe_make_image(&User.avatar_url/2, "icon", user)) + |> Map.merge(maybe_make_image(&User.banner_url/2, "image", user)) |> Map.merge(Utils.make_json_ld_header()) end @@ -294,4 +288,17 @@ defmodule Pleroma.Web.ActivityPub.UserView do map end end + + defp maybe_make_image(func, key, user) do + if image = func.(user, no_default: true) do + %{ + key => %{ + "type" => "Image", + "url" => image + } + } + else + %{} + end + end end diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex index 6d9bf2895..b3a09e49e 100644 --- a/lib/pleroma/web/admin_api/admin_api_controller.ex +++ b/lib/pleroma/web/admin_api/admin_api_controller.ex @@ -3,17 +3,18 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.AdminAPI.AdminAPIController do - @users_page_size 50 - use Pleroma.Web, :controller alias Pleroma.User alias Pleroma.Web.ActivityPub.Relay alias Pleroma.Web.AdminAPI.AccountView + alias Pleroma.Web.AdminAPI.Search import Pleroma.Web.ControllerHelper, only: [json_response: 3] require Logger + @users_page_size 50 + action_fallback(:errors) def user_delete(conn, %{"nickname" => nickname}) do @@ -44,6 +45,15 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do |> json(user.nickname) end + def user_show(conn, %{"nickname" => nickname}) do + with %User{} = user <- User.get_by_nickname(nickname) do + conn + |> json(AccountView.render("show.json", %{user: user})) + else + _ -> {:error, :not_found} + end + end + def user_toggle_activation(conn, %{"nickname" => nickname}) do user = User.get_by_nickname(nickname) @@ -63,17 +73,17 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do do: json_response(conn, :no_content, "") end - def list_users(%{assigns: %{user: admin}} = conn, params) do + def list_users(conn, params) do {page, page_size} = page_params(params) + filters = maybe_parse_filters(params["filters"]) - with {:ok, users, count} <- - User.search_for_admin(%{ - query: params["query"], - admin: admin, - local: params["local_only"] == "true", - page: page, - page_size: page_size - }), + search_params = %{ + query: params["query"], + page: page, + page_size: page_size + } + + with {:ok, users, count} <- Search.user(Map.merge(search_params, filters)), do: conn |> json( @@ -85,6 +95,19 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do ) end + @filters ~w(local external active deactivated) + + defp maybe_parse_filters(filters) when is_nil(filters) or filters == "", do: %{} + + @spec maybe_parse_filters(String.t()) :: %{required(String.t()) => true} | %{} + defp maybe_parse_filters(filters) do + filters + |> String.split(",") + |> Enum.filter(&Enum.member?(@filters, &1)) + |> Enum.map(&String.to_atom(&1)) + |> Enum.into(%{}, &{&1, true}) + end + def right_add(conn, %{"permission_group" => permission_group, "nickname" => nickname}) when permission_group in ["moderator", "admin"] do user = User.get_by_nickname(nickname) @@ -217,6 +240,12 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do |> json(token.token) end + def errors(conn, {:error, :not_found}) do + conn + |> put_status(404) + |> json("Not found") + end + def errors(conn, {:param_cast, _}) do conn |> put_status(400) diff --git a/lib/pleroma/web/admin_api/search.ex b/lib/pleroma/web/admin_api/search.ex new file mode 100644 index 000000000..9a8e41c2a --- /dev/null +++ b/lib/pleroma/web/admin_api/search.ex @@ -0,0 +1,54 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.AdminAPI.Search do + import Ecto.Query + + alias Pleroma.Repo + alias Pleroma.User + + @page_size 50 + + def user(%{query: term} = params) when is_nil(term) or term == "" do + query = maybe_filtered_query(params) + + paginated_query = + maybe_filtered_query(params) + |> paginate(params[:page] || 1, params[:page_size] || @page_size) + + count = query |> Repo.aggregate(:count, :id) + + results = Repo.all(paginated_query) + + {:ok, results, count} + end + + def user(%{query: term} = params) when is_binary(term) do + search_query = from(u in maybe_filtered_query(params), where: ilike(u.nickname, ^"%#{term}%")) + + count = search_query |> Repo.aggregate(:count, :id) + + results = + search_query + |> paginate(params[:page] || 1, params[:page_size] || @page_size) + |> Repo.all() + + {:ok, results, count} + end + + defp maybe_filtered_query(params) do + from(u in User, order_by: u.nickname) + |> User.maybe_local_user_query(params[:local]) + |> User.maybe_external_user_query(params[:external]) + |> User.maybe_active_user_query(params[:active]) + |> User.maybe_deactivated_user_query(params[:deactivated]) + end + + defp paginate(query, page, page_size) do + from(u in query, + limit: ^page_size, + offset: ^((page - 1) * page_size) + ) + end +end diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex index 50d60aade..25b990677 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -6,7 +6,6 @@ defmodule Pleroma.Web.CommonAPI do alias Pleroma.Activity alias Pleroma.Formatter alias Pleroma.Object - alias Pleroma.Repo alias Pleroma.ThreadMute alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub @@ -64,8 +63,9 @@ defmodule Pleroma.Web.CommonAPI do end def delete(activity_id, user) do - with %Activity{data: %{"object" => %{"id" => object_id}}} <- Repo.get(Activity, activity_id), - %Object{} = object <- Object.normalize(object_id), + with %Activity{data: %{"object" => _}} = activity <- + Activity.get_by_id_with_object(activity_id), + %Object{} = object <- Object.normalize(activity), true <- User.superuser?(user) || user.ap_id == object.data["actor"], {:ok, _} <- unpin(activity_id, user), {:ok, delete} <- ActivityPub.delete(object) do @@ -75,7 +75,7 @@ defmodule Pleroma.Web.CommonAPI do def repeat(id_or_ap_id, user) do with %Activity{} = activity <- get_by_id_or_ap_id(id_or_ap_id), - object <- Object.normalize(activity.data["object"]["id"]), + object <- Object.normalize(activity), nil <- Utils.get_existing_announce(user.ap_id, object) do ActivityPub.announce(user, object) else @@ -86,7 +86,7 @@ defmodule Pleroma.Web.CommonAPI do def unrepeat(id_or_ap_id, user) do with %Activity{} = activity <- get_by_id_or_ap_id(id_or_ap_id), - object <- Object.normalize(activity.data["object"]["id"]) do + object <- Object.normalize(activity) do ActivityPub.unannounce(user, object) else _ -> @@ -96,7 +96,7 @@ defmodule Pleroma.Web.CommonAPI do def favorite(id_or_ap_id, user) do with %Activity{} = activity <- get_by_id_or_ap_id(id_or_ap_id), - object <- Object.normalize(activity.data["object"]["id"]), + object <- Object.normalize(activity), nil <- Utils.get_existing_like(user.ap_id, object) do ActivityPub.like(user, object) else @@ -107,7 +107,7 @@ defmodule Pleroma.Web.CommonAPI do def unfavorite(id_or_ap_id, user) do with %Activity{} = activity <- get_by_id_or_ap_id(id_or_ap_id), - object <- Object.normalize(activity.data["object"]["id"]) do + object <- Object.normalize(activity) do ActivityPub.unlike(user, object) else _ -> diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex index 3e807a5b7..f596f703b 100644 --- a/lib/pleroma/web/common_api/utils.ex +++ b/lib/pleroma/web/common_api/utils.ex @@ -17,13 +17,14 @@ defmodule Pleroma.Web.CommonAPI.Utils do # This is a hack for twidere. def get_by_id_or_ap_id(id) do - activity = Repo.get(Activity, id) || Activity.get_create_by_object_ap_id(id) + activity = + Activity.get_by_id_with_object(id) || Activity.get_create_by_object_ap_id_with_object(id) activity && if activity.data["type"] == "Create" do activity else - Activity.get_create_by_object_ap_id(activity.data["object"]) + Activity.get_create_by_object_ap_id_with_object(activity.data["object"]) end end @@ -302,10 +303,10 @@ defmodule Pleroma.Web.CommonAPI.Utils do def maybe_notify_mentioned_recipients( recipients, - %Activity{data: %{"to" => _to, "type" => type} = data} = _activity + %Activity{data: %{"to" => _to, "type" => type} = data} = activity ) when type == "Create" do - object = Object.normalize(data["object"]) + object = Object.normalize(activity) object_data = cond do diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index 2eb1da561..eee4e7678 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -18,6 +18,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do alias Pleroma.Web.ActivityPub.Visibility alias Pleroma.Web.CommonAPI alias Pleroma.Web.MastodonAPI.AccountView + alias Pleroma.Web.MastodonAPI.AppView alias Pleroma.Web.MastodonAPI.FilterView alias Pleroma.Web.MastodonAPI.ListView alias Pleroma.Web.MastodonAPI.MastodonAPI @@ -51,16 +52,9 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do with cs <- App.register_changeset(%App{}, app_attrs), false <- cs.changes[:client_name] == @local_mastodon_name, {:ok, app} <- Repo.insert(cs) do - res = %{ - id: app.id |> to_string, - name: app.client_name, - client_id: app.client_id, - client_secret: app.client_secret, - redirect_uri: app.redirect_uris, - website: app.website - } - - json(conn, res) + conn + |> put_view(AppView) + |> render("show.json", %{app: app}) end end @@ -132,6 +126,14 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do json(conn, account) end + def verify_app_credentials(%{assigns: %{user: _user, token: token}} = conn, _) do + with %Token{app: %App{} = app} <- Repo.preload(token, :app) do + conn + |> put_view(AppView) + |> render("short.json", %{app: app}) + end + end + def user(%{assigns: %{user: for_user}} = conn, %{"id" => nickname_or_id}) do with %User{} = user <- User.get_cached_by_nickname_or_id(nickname_or_id), true <- User.auth_active?(user) || user.id == for_user.id || User.superuser?(for_user) do @@ -161,6 +163,9 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do }, stats: Stats.get_stats(), thumbnail: Web.base_url() <> "/instance/thumbnail.jpeg", + languages: ["en"], + registrations: Pleroma.Config.get([:instance, :registrations_open]), + # Extra (not present in Mastodon): max_toot_chars: Keyword.get(instance, :limit) } @@ -944,12 +949,14 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do end def favourites(%{assigns: %{user: user}} = conn, params) do - activities = + params = params |> Map.put("type", "Create") |> Map.put("favorited_by", user.ap_id) |> Map.put("blocking_user", user) - |> ActivityPub.fetch_public_activities() + + activities = + ActivityPub.fetch_activities([], params) |> Enum.reverse() conn diff --git a/lib/pleroma/web/mastodon_api/views/app_view.ex b/lib/pleroma/web/mastodon_api/views/app_view.ex new file mode 100644 index 000000000..f52b693a6 --- /dev/null +++ b/lib/pleroma/web/mastodon_api/views/app_view.ex @@ -0,0 +1,41 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.AppView do + use Pleroma.Web, :view + + alias Pleroma.Web.OAuth.App + + @vapid_key :web_push_encryption + |> Application.get_env(:vapid_details, []) + |> Keyword.get(:public_key) + + def render("show.json", %{app: %App{} = app}) do + %{ + id: app.id |> to_string, + name: app.client_name, + client_id: app.client_id, + client_secret: app.client_secret, + redirect_uri: app.redirect_uris, + website: app.website + } + |> with_vapid_key() + end + + def render("short.json", %{app: %App{website: webiste, client_name: name}}) do + %{ + name: name, + website: webiste + } + |> with_vapid_key() + end + + defp with_vapid_key(data) do + if @vapid_key do + Map.put(data, "vapid_key", @vapid_key) + else + data + end + end +end diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index 1ca8338cc..200bb453d 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -174,7 +174,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do content: content, created_at: created_at, reblogs_count: announcement_count, - replies_count: 0, + replies_count: object["repliesCount"] || 0, favourites_count: like_count, reblogged: present?(repeated), favourited: present?(favorited), diff --git a/lib/pleroma/web/oauth/oauth_controller.ex b/lib/pleroma/web/oauth/oauth_controller.ex index d151efe9e..ebb3dd253 100644 --- a/lib/pleroma/web/oauth/oauth_controller.ex +++ b/lib/pleroma/web/oauth/oauth_controller.ex @@ -83,14 +83,18 @@ defmodule Pleroma.Web.OAuth.OAuthController do end else {scopes_issue, _} when scopes_issue in [:unsupported_scopes, :missing_scopes] -> + # Per https://github.com/tootsuite/mastodon/blob/ + # 51e154f5e87968d6bb115e053689767ab33e80cd/app/controllers/api/base_controller.rb#L39 conn - |> put_flash(:error, "Permissions not specified.") + |> put_flash(:error, "This action is outside the authorized scopes") |> put_status(:unauthorized) |> authorize(auth_params) {:auth_active, false} -> + # Per https://github.com/tootsuite/mastodon/blob/ + # 51e154f5e87968d6bb115e053689767ab33e80cd/app/controllers/api/base_controller.rb#L76 conn - |> put_flash(:error, "Account confirmation pending.") + |> put_flash(:error, "Your login is missing a confirmed e-mail address") |> put_status(:forbidden) |> authorize(auth_params) @@ -149,9 +153,11 @@ defmodule Pleroma.Web.OAuth.OAuthController do json(conn, response) else {:auth_active, false} -> + # Per https://github.com/tootsuite/mastodon/blob/ + # 51e154f5e87968d6bb115e053689767ab33e80cd/app/controllers/api/base_controller.rb#L76 conn |> put_status(:forbidden) - |> json(%{error: "Account confirmation pending"}) + |> json(%{error: "Your login is missing a confirmed e-mail address"}) _error -> put_status(conn, 400) diff --git a/lib/pleroma/web/ostatus/handlers/note_handler.ex b/lib/pleroma/web/ostatus/handlers/note_handler.ex index 770a71a0a..db995ec77 100644 --- a/lib/pleroma/web/ostatus/handlers/note_handler.ex +++ b/lib/pleroma/web/ostatus/handlers/note_handler.ex @@ -106,7 +106,7 @@ defmodule Pleroma.Web.OStatus.NoteHandler do # TODO: Clean this up a bit. def handle_note(entry, doc \\ nil) do with id <- XML.string_from_xpath("//id", entry), - activity when is_nil(activity) <- Activity.get_create_by_object_ap_id(id), + activity when is_nil(activity) <- Activity.get_create_by_object_ap_id_with_object(id), [author] <- :xmerl_xpath.string('//author[1]', doc), {:ok, actor} <- OStatus.find_make_or_update_user(author), content_html <- OStatus.get_content(entry), diff --git a/lib/pleroma/web/ostatus/ostatus.ex b/lib/pleroma/web/ostatus/ostatus.ex index 266f86bf4..9a34d7ad5 100644 --- a/lib/pleroma/web/ostatus/ostatus.ex +++ b/lib/pleroma/web/ostatus/ostatus.ex @@ -23,8 +23,8 @@ defmodule Pleroma.Web.OStatus do alias Pleroma.Web.WebFinger alias Pleroma.Web.Websub - def is_representable?(%Activity{data: data}) do - object = Object.normalize(data["object"]) + def is_representable?(%Activity{} = activity) do + object = Object.normalize(activity) cond do is_nil(object) -> @@ -119,7 +119,7 @@ defmodule Pleroma.Web.OStatus do def make_share(entry, doc, retweeted_activity) do with {:ok, actor} <- find_make_or_update_user(doc), - %Object{} = object <- Object.normalize(retweeted_activity.data["object"]), + %Object{} = object <- Object.normalize(retweeted_activity), id when not is_nil(id) <- string_from_xpath("/entry/id", entry), {:ok, activity, _object} = ActivityPub.announce(actor, object, id, false) do {:ok, activity} @@ -137,7 +137,7 @@ defmodule Pleroma.Web.OStatus do def make_favorite(entry, doc, favorited_activity) do with {:ok, actor} <- find_make_or_update_user(doc), - %Object{} = object <- Object.normalize(favorited_activity.data["object"]), + %Object{} = object <- Object.normalize(favorited_activity), id when not is_nil(id) <- string_from_xpath("/entry/id", entry), {:ok, activity, _object} = ActivityPub.like(actor, object, id, false) do {:ok, activity} @@ -159,7 +159,7 @@ defmodule Pleroma.Web.OStatus do Logger.debug("Trying to get entry from db") with id when not is_nil(id) <- string_from_xpath("//activity:object[1]/id", entry), - %Activity{} = activity <- Activity.get_create_by_object_ap_id(id) do + %Activity{} = activity <- Activity.get_create_by_object_ap_id_with_object(id) do {:ok, activity} else _ -> diff --git a/lib/pleroma/web/ostatus/ostatus_controller.ex b/lib/pleroma/web/ostatus/ostatus_controller.ex index 0579a5f3d..2fb6ce41b 100644 --- a/lib/pleroma/web/ostatus/ostatus_controller.ex +++ b/lib/pleroma/web/ostatus/ostatus_controller.ex @@ -102,7 +102,8 @@ defmodule Pleroma.Web.OStatus.OStatusController do ActivityPubController.call(conn, :object) else with id <- o_status_url(conn, :object, uuid), - {_, %Activity{} = activity} <- {:activity, Activity.get_create_by_object_ap_id(id)}, + {_, %Activity{} = activity} <- + {:activity, Activity.get_create_by_object_ap_id_with_object(id)}, {_, true} <- {:public?, Visibility.is_public?(activity)}, %User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do case get_format(conn) do @@ -148,13 +149,13 @@ defmodule Pleroma.Web.OStatus.OStatusController do end def notice(conn, %{"id" => id}) do - with {_, %Activity{} = activity} <- {:activity, Activity.get_by_id(id)}, + with {_, %Activity{} = activity} <- {:activity, Activity.get_by_id_with_object(id)}, {_, true} <- {:public?, Visibility.is_public?(activity)}, %User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do case format = get_format(conn) do "html" -> if activity.data["type"] == "Create" do - %Object{} = object = Object.normalize(activity.data["object"]) + %Object{} = object = Object.normalize(activity) Fallback.RedirectController.redirector_with_meta(conn, %{ activity_id: activity.id, @@ -191,9 +192,9 @@ defmodule Pleroma.Web.OStatus.OStatusController do # Returns an HTML embedded <audio> or <video> player suitable for embed iframes. def notice_player(conn, %{"id" => id}) do - with %Activity{data: %{"type" => "Create"}} = activity <- Activity.get_by_id(id), + with %Activity{data: %{"type" => "Create"}} = activity <- Activity.get_by_id_with_object(id), true <- Visibility.is_public?(activity), - %Object{} = object <- Object.normalize(activity.data["object"]), + %Object{} = object <- Object.normalize(activity), %{data: %{"attachment" => [%{"url" => [url | _]} | _]}} <- object, true <- String.starts_with?(url["mediaType"], ["audio", "video"]) do conn @@ -219,7 +220,7 @@ defmodule Pleroma.Web.OStatus.OStatusController do %Activity{data: %{"type" => "Create"}} = activity, _user ) do - object = Object.normalize(activity.data["object"]) + object = Object.normalize(activity) conn |> put_resp_header("content-type", "application/activity+json") diff --git a/lib/pleroma/web/rich_media/helpers.ex b/lib/pleroma/web/rich_media/helpers.ex index 92c61ff51..f67aaf58b 100644 --- a/lib/pleroma/web/rich_media/helpers.ex +++ b/lib/pleroma/web/rich_media/helpers.ex @@ -21,9 +21,9 @@ defmodule Pleroma.Web.RichMedia.Helpers do defp validate_page_url(%URI{}), do: :ok defp validate_page_url(_), do: :error - def fetch_data_for_activity(%Activity{} = activity) do + def fetch_data_for_activity(%Activity{data: %{"type" => "Create"}} = activity) do with true <- Pleroma.Config.get([:rich_media, :enabled]), - %Object{} = object <- Object.normalize(activity.data["object"]), + %Object{} = object <- Object.normalize(activity), {:ok, page_url} <- HTML.extract_first_external_url(object, object.data["content"]), :ok <- validate_page_url(page_url), {:ok, rich_media} <- Parser.parse(page_url) do @@ -32,4 +32,6 @@ defmodule Pleroma.Web.RichMedia.Helpers do _ -> %{} end end + + def fetch_data_for_activity(_), do: %{} end diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index befd382ba..9ccb4e535 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -140,6 +140,7 @@ defmodule Pleroma.Web.Router do pipe_through([:admin_api, :oauth_write]) get("/users", AdminAPIController, :list_users) + get("/users/:nickname", AdminAPIController, :user_show) delete("/user", AdminAPIController, :user_delete) patch("/users/:nickname/toggle_activation", AdminAPIController, :user_toggle_activation) post("/user", AdminAPIController, :user_create) @@ -328,6 +329,7 @@ defmodule Pleroma.Web.Router do get("/instance", MastodonAPIController, :masto_instance) get("/instance/peers", MastodonAPIController, :peers) post("/apps", MastodonAPIController, :create_app) + get("/apps/verify_credentials", MastodonAPIController, :verify_app_credentials) get("/custom_emojis", MastodonAPIController, :custom_emojis) get("/statuses/:id/card", MastodonAPIController, :status_card) diff --git a/lib/pleroma/web/streamer.ex b/lib/pleroma/web/streamer.ex index 7425bfb54..592749b42 100644 --- a/lib/pleroma/web/streamer.ex +++ b/lib/pleroma/web/streamer.ex @@ -202,7 +202,7 @@ defmodule Pleroma.Web.Streamer do mutes = user.info.mutes || [] reblog_mutes = user.info.muted_reblogs || [] - parent = Object.normalize(item.data["object"]) + parent = Object.normalize(item) unless is_nil(parent) or item.actor in blocks or item.actor in mutes or item.actor in reblog_mutes or not ActivityPub.contain_activity(item, user) or diff --git a/lib/pleroma/web/twitter_api/representers/activity_representer.ex b/lib/pleroma/web/twitter_api/representers/activity_representer.ex deleted file mode 100644 index 55c612ddd..000000000 --- a/lib/pleroma/web/twitter_api/representers/activity_representer.ex +++ /dev/null @@ -1,15 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -# FIXME: Remove this module? -# THIS MODULE IS DEPRECATED! DON'T USE IT! -# USE THE Pleroma.Web.TwitterAPI.Views.ActivityView MODULE! -defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter do - def to_map(activity, opts) do - Pleroma.Web.TwitterAPI.ActivityView.render( - "activity.json", - Map.put(opts, :activity, activity) - ) - end -end diff --git a/lib/pleroma/web/twitter_api/views/activity_view.ex b/lib/pleroma/web/twitter_api/views/activity_view.ex index fe7d49975..aa1d41fa2 100644 --- a/lib/pleroma/web/twitter_api/views/activity_view.ex +++ b/lib/pleroma/web/twitter_api/views/activity_view.ex @@ -266,6 +266,8 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do content |> String.replace(~r/<br\s?\/?>/, "\n") |> HTML.get_cached_stripped_html_for_object(activity, __MODULE__) + else + "" end reply_parent = Activity.get_in_reply_to_activity(activity) @@ -9,6 +9,7 @@ defmodule Pleroma.Mixfile do elixirc_paths: elixirc_paths(Mix.env()), compilers: [:phoenix, :gettext] ++ Mix.compilers(), elixirc_options: [warnings_as_errors: true], + xref: [exclude: [:eldap]], start_permanent: Mix.env() == :prod, aliases: aliases(), deps: deps(), diff --git a/priv/repo/migrations/20190325185009_create_notification_id_index.exs b/priv/repo/migrations/20190325185009_create_notification_id_index.exs new file mode 100644 index 000000000..a6ab38d02 --- /dev/null +++ b/priv/repo/migrations/20190325185009_create_notification_id_index.exs @@ -0,0 +1,7 @@ +defmodule Pleroma.Repo.Migrations.CreateNotificationIdIndex do + use Ecto.Migration + + def change do + create index(:notifications, ["id desc nulls last"]) + end +end diff --git a/test/tasks/relay_test.exs b/test/tasks/relay_test.exs index c9d90fa2e..535dc3756 100644 --- a/test/tasks/relay_test.exs +++ b/test/tasks/relay_test.exs @@ -60,7 +60,8 @@ defmodule Mix.Tasks.Pleroma.RelayTest do ActivityPub.fetch_activities([], %{ "type" => "Undo", "actor_id" => follower_id, - "limit" => 1 + "limit" => 1, + "skip_preload" => true }) assert undo_activity.data["type"] == "Undo" diff --git a/test/user_test.exs b/test/user_test.exs index 442599910..8cf2ba6ab 100644 --- a/test/user_test.exs +++ b/test/user_test.exs @@ -8,6 +8,7 @@ defmodule Pleroma.UserTest do alias Pleroma.Repo alias Pleroma.User alias Pleroma.Web.CommonAPI + use Pleroma.DataCase import Pleroma.Factory @@ -1107,21 +1108,4 @@ defmodule Pleroma.UserTest do assert {:ok, user_state3} = User.bookmark(user, id2) assert user_state3.bookmarks == [id2] end - - describe "search for admin" do - test "it ignores case" do - insert(:user, nickname: "papercoach") - insert(:user, nickname: "CanadaPaperCoach") - - {:ok, _results, count} = - User.search_for_admin(%{ - query: "paper", - local: false, - page: 1, - page_size: 50 - }) - - assert count == 2 - end - end end diff --git a/test/web/activity_pub/activity_pub_test.exs b/test/web/activity_pub/activity_pub_test.exs index 035778218..ac5fbe0a9 100644 --- a/test/web/activity_pub/activity_pub_test.exs +++ b/test/web/activity_pub/activity_pub_test.exs @@ -140,7 +140,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do activity = insert(:note_activity) {:ok, new_activity} = ActivityPub.insert(activity.data) - assert activity == new_activity + assert activity.id == new_activity.id end test "inserts a given map into the activity database, giving it an id if it has none." do @@ -232,6 +232,39 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do user = Repo.get(User, user.id) assert user.info.note_count == 2 end + + test "increases replies count" do + user = insert(:user) + user2 = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{"status" => "1", "visibility" => "public"}) + ap_id = activity.data["id"] + reply_data = %{"status" => "1", "in_reply_to_status_id" => activity.id} + + # public + {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "public")) + assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id) + assert data["object"]["repliesCount"] == 1 + assert object.data["repliesCount"] == 1 + + # unlisted + {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "unlisted")) + assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id) + assert data["object"]["repliesCount"] == 2 + assert object.data["repliesCount"] == 2 + + # private + {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "private")) + assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id) + assert data["object"]["repliesCount"] == 2 + assert object.data["repliesCount"] == 2 + + # direct + {:ok, _} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "direct")) + assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id) + assert data["object"]["repliesCount"] == 2 + assert object.data["repliesCount"] == 2 + end end describe "fetch activities for recipients" do @@ -270,7 +303,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do booster = insert(:user) {:ok, user} = User.block(user, %{ap_id: activity_one.data["actor"]}) - activities = ActivityPub.fetch_activities([], %{"blocking_user" => user}) + activities = + ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true}) assert Enum.member?(activities, activity_two) assert Enum.member?(activities, activity_three) @@ -278,7 +312,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do {:ok, user} = User.unblock(user, %{ap_id: activity_one.data["actor"]}) - activities = ActivityPub.fetch_activities([], %{"blocking_user" => user}) + activities = + ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true}) assert Enum.member?(activities, activity_two) assert Enum.member?(activities, activity_three) @@ -289,14 +324,16 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do %Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id) activity_three = Repo.get(Activity, activity_three.id) - activities = ActivityPub.fetch_activities([], %{"blocking_user" => user}) + activities = + ActivityPub.fetch_activities([], %{"blocking_user" => user, "skip_preload" => true}) assert Enum.member?(activities, activity_two) refute Enum.member?(activities, activity_three) refute Enum.member?(activities, boost_activity) assert Enum.member?(activities, activity_one) - activities = ActivityPub.fetch_activities([], %{"blocking_user" => nil}) + activities = + ActivityPub.fetch_activities([], %{"blocking_user" => nil, "skip_preload" => true}) assert Enum.member?(activities, activity_two) assert Enum.member?(activities, activity_three) @@ -312,14 +349,20 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do booster = insert(:user) {:ok, user} = User.mute(user, %User{ap_id: activity_one.data["actor"]}) - activities = ActivityPub.fetch_activities([], %{"muting_user" => user}) + activities = + ActivityPub.fetch_activities([], %{"muting_user" => user, "skip_preload" => true}) assert Enum.member?(activities, activity_two) assert Enum.member?(activities, activity_three) refute Enum.member?(activities, activity_one) # Calling with 'with_muted' will deliver muted activities, too. - activities = ActivityPub.fetch_activities([], %{"muting_user" => user, "with_muted" => true}) + activities = + ActivityPub.fetch_activities([], %{ + "muting_user" => user, + "with_muted" => true, + "skip_preload" => true + }) assert Enum.member?(activities, activity_two) assert Enum.member?(activities, activity_three) @@ -327,7 +370,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do {:ok, user} = User.unmute(user, %User{ap_id: activity_one.data["actor"]}) - activities = ActivityPub.fetch_activities([], %{"muting_user" => user}) + activities = + ActivityPub.fetch_activities([], %{"muting_user" => user, "skip_preload" => true}) assert Enum.member?(activities, activity_two) assert Enum.member?(activities, activity_three) @@ -338,14 +382,15 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do %Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id) activity_three = Repo.get(Activity, activity_three.id) - activities = ActivityPub.fetch_activities([], %{"muting_user" => user}) + activities = + ActivityPub.fetch_activities([], %{"muting_user" => user, "skip_preload" => true}) assert Enum.member?(activities, activity_two) refute Enum.member?(activities, activity_three) refute Enum.member?(activities, boost_activity) assert Enum.member?(activities, activity_one) - activities = ActivityPub.fetch_activities([], %{"muting_user" => nil}) + activities = ActivityPub.fetch_activities([], %{"muting_user" => nil, "skip_preload" => true}) assert Enum.member?(activities, activity_two) assert Enum.member?(activities, activity_three) @@ -353,6 +398,20 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do assert Enum.member?(activities, activity_one) end + test "does include announces on request" do + activity_three = insert(:note_activity) + user = insert(:user) + booster = insert(:user) + + {:ok, user} = User.follow(user, booster) + + {:ok, announce, _object} = CommonAPI.repeat(activity_three.id, booster) + + [announce_activity] = ActivityPub.fetch_activities([user.ap_id | user.following]) + + assert announce_activity.id == announce.id + end + test "excludes reblogs on request" do user = insert(:user) {:ok, expected_activity} = ActivityBuilder.insert(%{"type" => "Create"}, %{:user => user}) @@ -435,7 +494,21 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do activities = ActivityPub.fetch_activities([], %{"muting_user" => user}) - refute Enum.member?(activities, activity) + refute Enum.any?(activities, fn %{id: id} -> id == activity.id end) + end + + test "returns reblogs for users for whom reblogs have not been muted" do + activity = insert(:note_activity) + user = insert(:user) + booster = insert(:user) + {:ok, user} = CommonAPI.hide_reblogs(user, booster) + {:ok, user} = CommonAPI.show_reblogs(user, booster) + + {:ok, activity, _} = CommonAPI.repeat(activity.id, booster) + + activities = ActivityPub.fetch_activities([], %{"muting_user" => user}) + + assert Enum.any?(activities, fn %{id: id} -> id == activity.id end) end end @@ -725,6 +798,40 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do assert user.ap_id in delete.data["to"] end + + test "decreases reply count" do + user = insert(:user) + user2 = insert(:user) + + {:ok, activity} = CommonAPI.post(user, %{"status" => "1", "visibility" => "public"}) + reply_data = %{"status" => "1", "in_reply_to_status_id" => activity.id} + ap_id = activity.data["id"] + + {:ok, public_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "public")) + {:ok, unlisted_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "unlisted")) + {:ok, private_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "private")) + {:ok, direct_reply} = CommonAPI.post(user2, Map.put(reply_data, "visibility", "direct")) + + _ = CommonAPI.delete(direct_reply.id, user2) + assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id) + assert data["object"]["repliesCount"] == 2 + assert object.data["repliesCount"] == 2 + + _ = CommonAPI.delete(private_reply.id, user2) + assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id) + assert data["object"]["repliesCount"] == 2 + assert object.data["repliesCount"] == 2 + + _ = CommonAPI.delete(public_reply.id, user2) + assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id) + assert data["object"]["repliesCount"] == 1 + assert object.data["repliesCount"] == 1 + + _ = CommonAPI.delete(unlisted_reply.id, user2) + assert %{data: data, object: object} = Activity.get_by_ap_id_with_object(ap_id) + assert data["object"]["repliesCount"] == 0 + assert object.data["repliesCount"] == 0 + end end describe "timeline post-processing" do @@ -763,6 +870,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do activities = ActivityPub.fetch_activities([user1.ap_id | user1.following]) + private_activity_1 = Activity.get_by_ap_id_with_object(private_activity_1.data["id"]) assert [public_activity, private_activity_1, private_activity_3] == activities assert length(activities) == 3 diff --git a/test/web/activity_pub/views/user_view_test.exs b/test/web/activity_pub/views/user_view_test.exs index 0bc1d4728..9fb9455d2 100644 --- a/test/web/activity_pub/views/user_view_test.exs +++ b/test/web/activity_pub/views/user_view_test.exs @@ -16,6 +16,29 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do assert String.contains?(result["publicKey"]["publicKeyPem"], "BEGIN PUBLIC KEY") end + test "Does not add an avatar image if the user hasn't set one" do + user = insert(:user) + {:ok, user} = Pleroma.Web.WebFinger.ensure_keys_present(user) + + result = UserView.render("user.json", %{user: user}) + refute result["icon"] + refute result["image"] + + user = + insert(:user, + avatar: %{"url" => [%{"href" => "https://someurl"}]}, + info: %{ + banner: %{"url" => [%{"href" => "https://somebanner"}]} + } + ) + + {:ok, user} = Pleroma.Web.WebFinger.ensure_keys_present(user) + + result = UserView.render("user.json", %{user: user}) + assert result["icon"]["url"] == "https://someurl" + assert result["image"]["url"] == "https://somebanner" + end + describe "endpoints" do test "local users have a usable endpoints structure" do user = insert(:user) diff --git a/test/web/admin_api/admin_api_controller_test.exs b/test/web/admin_api/admin_api_controller_test.exs index 0aab7f262..2f53416a3 100644 --- a/test/web/admin_api/admin_api_controller_test.exs +++ b/test/web/admin_api/admin_api_controller_test.exs @@ -40,6 +40,41 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do end end + describe "/api/pleroma/admin/users/:nickname" do + test "Show", %{conn: conn} do + admin = insert(:user, info: %{is_admin: true}) + user = insert(:user) + + conn = + conn + |> assign(:user, admin) + |> get("/api/pleroma/admin/users/#{user.nickname}") + + expected = %{ + "deactivated" => false, + "id" => to_string(user.id), + "local" => true, + "nickname" => user.nickname, + "roles" => %{"admin" => false, "moderator" => false}, + "tags" => [] + } + + assert expected == json_response(conn, 200) + end + + test "when the user doesn't exist", %{conn: conn} do + admin = insert(:user, info: %{is_admin: true}) + user = build(:user) + + conn = + conn + |> assign(:user, admin) + |> get("/api/pleroma/admin/users/#{user.nickname}") + + assert "Not found" == json_response(conn, 404) + end + end + describe "PUT /api/pleroma/admin/users/tag" do setup do admin = insert(:user, info: %{is_admin: true}) @@ -408,13 +443,13 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do test "regular search with page size" do admin = insert(:user, info: %{is_admin: true}) - user = insert(:user, nickname: "bob") - user2 = insert(:user, nickname: "bo") + user = insert(:user, nickname: "aalice") + user2 = insert(:user, nickname: "alice") conn = build_conn() |> assign(:user, admin) - |> get("/api/pleroma/admin/users?query=bo&page_size=1&page=1") + |> get("/api/pleroma/admin/users?query=a&page_size=1&page=1") assert json_response(conn, 200) == %{ "count" => 2, @@ -434,7 +469,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do conn = build_conn() |> assign(:user, admin) - |> get("/api/pleroma/admin/users?query=bo&page_size=1&page=2") + |> get("/api/pleroma/admin/users?query=a&page_size=1&page=2") assert json_response(conn, 200) == %{ "count" => 2, @@ -461,7 +496,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do conn = build_conn() |> assign(:user, admin) - |> get("/api/pleroma/admin/users?query=bo&local_only=true") + |> get("/api/pleroma/admin/users?query=bo&filters=local") assert json_response(conn, 200) == %{ "count" => 1, @@ -488,7 +523,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do conn = build_conn() |> assign(:user, admin) - |> get("/api/pleroma/admin/users?local_only=true") + |> get("/api/pleroma/admin/users?filters=local") assert json_response(conn, 200) == %{ "count" => 2, @@ -513,6 +548,34 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do ] } end + + test "it works with multiple filters" do + admin = insert(:user, nickname: "john", info: %{is_admin: true}) + user = insert(:user, nickname: "bob", local: false, info: %{deactivated: true}) + + insert(:user, nickname: "ken", local: true, info: %{deactivated: true}) + insert(:user, nickname: "bobb", local: false, info: %{deactivated: false}) + + conn = + build_conn() + |> assign(:user, admin) + |> get("/api/pleroma/admin/users?filters=deactivated,external") + + assert json_response(conn, 200) == %{ + "count" => 1, + "page_size" => 50, + "users" => [ + %{ + "deactivated" => user.info.deactivated, + "id" => user.id, + "nickname" => user.nickname, + "roles" => %{"admin" => false, "moderator" => false}, + "local" => user.local, + "tags" => [] + } + ] + } + end end test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation" do diff --git a/test/web/admin_api/search_test.exs b/test/web/admin_api/search_test.exs new file mode 100644 index 000000000..3950996ed --- /dev/null +++ b/test/web/admin_api/search_test.exs @@ -0,0 +1,88 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.AdminAPI.SearchTest do + use Pleroma.Web.ConnCase + + alias Pleroma.Web.AdminAPI.Search + + import Pleroma.Factory + + describe "search for admin" do + test "it ignores case" do + insert(:user, nickname: "papercoach") + insert(:user, nickname: "CanadaPaperCoach") + + {:ok, _results, count} = + Search.user(%{ + query: "paper", + local: false, + page: 1, + page_size: 50 + }) + + assert count == 2 + end + + test "it returns local/external users" do + insert(:user, local: true) + insert(:user, local: false) + insert(:user, local: false) + + {:ok, _results, local_count} = + Search.user(%{ + query: "", + local: true + }) + + {:ok, _results, external_count} = + Search.user(%{ + query: "", + external: true + }) + + assert local_count == 1 + assert external_count == 2 + end + + test "it returns active/deactivated users" do + insert(:user, info: %{deactivated: true}) + insert(:user, info: %{deactivated: true}) + insert(:user, info: %{deactivated: false}) + + {:ok, _results, active_count} = + Search.user(%{ + query: "", + active: true + }) + + {:ok, _results, deactivated_count} = + Search.user(%{ + query: "", + deactivated: true + }) + + assert active_count == 1 + assert deactivated_count == 2 + end + + test "it returns specific user" do + insert(:user) + insert(:user) + insert(:user, nickname: "bob", local: true, info: %{deactivated: false}) + + {:ok, _results, total_count} = Search.user(%{query: ""}) + + {:ok, _results, count} = + Search.user(%{ + query: "Bo", + active: true, + local: true + }) + + assert total_count == 3 + assert count == 1 + end + end +end diff --git a/test/web/mastodon_api/mastodon_api_controller_test.exs b/test/web/mastodon_api/mastodon_api_controller_test.exs index b2302422b..d9bcbf5a9 100644 --- a/test/web/mastodon_api/mastodon_api_controller_test.exs +++ b/test/web/mastodon_api/mastodon_api_controller_test.exs @@ -14,7 +14,9 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.CommonAPI alias Pleroma.Web.MastodonAPI.FilterView + alias Pleroma.Web.OAuth.App alias Pleroma.Web.OStatus + alias Pleroma.Web.Push alias Pleroma.Web.TwitterAPI.TwitterAPI import Pleroma.Factory import ExUnit.CaptureLog @@ -332,6 +334,53 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do assert id == to_string(user.id) end + test "apps/verify_credentials", %{conn: conn} do + token = insert(:oauth_token) + + conn = + conn + |> assign(:user, token.user) + |> assign(:token, token) + |> get("/api/v1/apps/verify_credentials") + + app = Repo.preload(token, :app).app + + expected = %{ + "name" => app.client_name, + "website" => app.website, + "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key) + } + + assert expected == json_response(conn, 200) + end + + test "creates an oauth app", %{conn: conn} do + user = insert(:user) + app_attrs = build(:oauth_app) + + conn = + conn + |> assign(:user, user) + |> post("/api/v1/apps", %{ + client_name: app_attrs.client_name, + redirect_uris: app_attrs.redirect_uris + }) + + [app] = Repo.all(App) + + expected = %{ + "name" => app.client_name, + "website" => app.website, + "client_id" => app.client_id, + "client_secret" => app.client_secret, + "id" => app.id |> to_string(), + "redirect_uri" => app.redirect_uris, + "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key) + } + + assert expected == json_response(conn, 200) + end + test "get a status", %{conn: conn} do activity = insert(:note_activity) @@ -1808,6 +1857,27 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do end test "get instance information", %{conn: conn} do + conn = get(conn, "/api/v1/instance") + assert result = json_response(conn, 200) + + # Note: not checking for "max_toot_chars" since it's optional + assert %{ + "uri" => _, + "title" => _, + "description" => _, + "version" => _, + "email" => _, + "urls" => %{ + "streaming_api" => _ + }, + "stats" => _, + "thumbnail" => _, + "languages" => _, + "registrations" => _ + } = result + end + + test "get instance stats", %{conn: conn} do user = insert(:user, %{local: true}) user2 = insert(:user, %{local: true}) diff --git a/test/web/oauth/ldap_authorization_test.exs b/test/web/oauth/ldap_authorization_test.exs index 5bf7eb93c..0eb191c76 100644 --- a/test/web/oauth/ldap_authorization_test.exs +++ b/test/web/oauth/ldap_authorization_test.exs @@ -10,6 +10,8 @@ defmodule Pleroma.Web.OAuth.LDAPAuthorizationTest do import ExUnit.CaptureLog import Mock + @skip if !Code.ensure_loaded?(:eldap), do: :skip + setup_all do ldap_authenticator = Pleroma.Config.get(Pleroma.Web.Auth.Authenticator, Pleroma.Web.Auth.PleromaAuthenticator) @@ -27,6 +29,7 @@ defmodule Pleroma.Web.OAuth.LDAPAuthorizationTest do :ok end + @tag @skip test "authorizes the existing user using LDAP credentials" do password = "testpassword" user = insert(:user, password_hash: Comeonin.Pbkdf2.hashpwsalt(password)) @@ -65,6 +68,7 @@ defmodule Pleroma.Web.OAuth.LDAPAuthorizationTest do end end + @tag @skip test "creates a new user after successful LDAP authorization" do password = "testpassword" user = build(:user) @@ -110,6 +114,7 @@ defmodule Pleroma.Web.OAuth.LDAPAuthorizationTest do end end + @tag @skip test "falls back to the default authorization when LDAP is unavailable" do password = "testpassword" user = insert(:user, password_hash: Comeonin.Pbkdf2.hashpwsalt(password)) @@ -153,6 +158,7 @@ defmodule Pleroma.Web.OAuth.LDAPAuthorizationTest do end end + @tag @skip test "disallow authorization for wrong LDAP credentials" do password = "testpassword" user = insert(:user, password_hash: Comeonin.Pbkdf2.hashpwsalt(password)) diff --git a/test/web/oauth/oauth_controller_test.exs b/test/web/oauth/oauth_controller_test.exs index ff1e56fe9..84ec7b4ee 100644 --- a/test/web/oauth/oauth_controller_test.exs +++ b/test/web/oauth/oauth_controller_test.exs @@ -87,7 +87,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do assert result =~ app.redirect_uris # Error message - assert result =~ "Permissions not specified" + assert result =~ "This action is outside the authorized scopes" end test "returns 401 for scopes beyond app scopes", %{conn: conn} do @@ -113,7 +113,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do assert result =~ app.redirect_uris # Error message - assert result =~ "Permissions not specified" + assert result =~ "This action is outside the authorized scopes" end test "issues a token for an all-body request" do diff --git a/test/web/twitter_api/representers/activity_representer_test.exs b/test/web/twitter_api/representers/activity_representer_test.exs deleted file mode 100644 index d154385a0..000000000 --- a/test/web/twitter_api/representers/activity_representer_test.exs +++ /dev/null @@ -1,170 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenterTest do - use Pleroma.DataCase - alias Pleroma.Activity - alias Pleroma.Object - alias Pleroma.User - alias Pleroma.Web.ActivityPub.ActivityPub - alias Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter - alias Pleroma.Web.TwitterAPI.Representers.ObjectRepresenter - alias Pleroma.Web.TwitterAPI.UserView - import Pleroma.Factory - - test "a like activity" do - user = insert(:user) - note_activity = insert(:note_activity) - object = Object.get_by_ap_id(note_activity.data["object"]["id"]) - - {:ok, like_activity, _object} = ActivityPub.like(user, object) - - status = - ActivityRepresenter.to_map(like_activity, %{user: user, liked_activity: note_activity}) - - assert status["id"] == like_activity.id - assert status["in_reply_to_status_id"] == note_activity.id - - note_activity = Activity.get_by_ap_id(note_activity.data["id"]) - activity_actor = Repo.get_by(User, ap_id: note_activity.data["actor"]) - liked_status = ActivityRepresenter.to_map(note_activity, %{user: activity_actor, for: user}) - assert liked_status["favorited"] == true - assert status["activity_type"] == "like" - end - - test "an activity" do - user = insert(:user) - # {:ok, mentioned_user } = UserBuilder.insert(%{nickname: "shp", ap_id: "shp"}) - mentioned_user = insert(:user, %{nickname: "shp"}) - - # {:ok, follower} = UserBuilder.insert(%{following: [User.ap_followers(user)]}) - follower = insert(:user, %{following: [User.ap_followers(user)]}) - - object = %Object{ - data: %{ - "type" => "Image", - "url" => [ - %{ - "type" => "Link", - "mediaType" => "image/jpg", - "href" => "http://example.org/image.jpg" - } - ], - "uuid" => 1 - } - } - - content_html = - "<script>alert('YAY')</script>Some :2hu: content mentioning <a href='#{mentioned_user.ap_id}'>@shp</shp>" - - content = HtmlSanitizeEx.strip_tags(content_html) - date = DateTime.from_naive!(~N[2016-05-24 13:26:08.003], "Etc/UTC") |> DateTime.to_iso8601() - - {:ok, convo_object} = Object.context_mapping("2hu") |> Repo.insert() - - to = [ - User.ap_followers(user), - "https://www.w3.org/ns/activitystreams#Public", - mentioned_user.ap_id - ] - - activity = %Activity{ - id: 1, - data: %{ - "type" => "Create", - "id" => "id", - "to" => to, - "actor" => User.ap_id(user), - "object" => %{ - "published" => date, - "type" => "Note", - "content" => content_html, - "summary" => "2hu :2hu:", - "inReplyToStatusId" => 213_123, - "attachment" => [ - object - ], - "external_url" => "some url", - "like_count" => 5, - "announcement_count" => 3, - "context" => "2hu", - "tag" => ["content", "mentioning", "nsfw"], - "emoji" => %{ - "2hu" => "corndog.png" - } - }, - "published" => date, - "context" => "2hu" - }, - local: false, - recipients: to - } - - corndog_emojo = ~s(<img height="32px" width="32px" alt="2hu" title="2hu" src="corndog.png" />) - - expected_html = - ~s(<p>2hu ) <> - corndog_emojo <> - ~s(</p>alert\('YAY'\)Some ) <> - corndog_emojo <> - ~s( content mentioning <a href=") <> mentioned_user.ap_id <> ~s(">@shp</a>) - - expected_status = %{ - "id" => activity.id, - "user" => UserView.render("show.json", %{user: user, for: follower}), - "is_local" => false, - "statusnet_html" => expected_html, - "text" => "2hu :2hu:" <> content, - "is_post_verb" => true, - "created_at" => "Tue May 24 13:26:08 +0000 2016", - "in_reply_to_status_id" => 213_123, - "in_reply_to_screen_name" => nil, - "in_reply_to_user_id" => nil, - "in_reply_to_profileurl" => nil, - "in_reply_to_ostatus_uri" => nil, - "statusnet_conversation_id" => convo_object.id, - "attachments" => [ - ObjectRepresenter.to_map(object) - ], - "attentions" => [ - UserView.render("show.json", %{user: mentioned_user, for: follower}) - ], - "fave_num" => 5, - "repeat_num" => 3, - "favorited" => false, - "repeated" => false, - "pinned" => false, - "external_url" => "some url", - "tags" => ["nsfw", "content", "mentioning"], - "activity_type" => "post", - "possibly_sensitive" => true, - "uri" => activity.data["object"]["id"], - "visibility" => "direct", - "card" => nil, - "muted" => false, - "summary" => "2hu :2hu:", - "summary_html" => - "2hu <img height=\"32px\" width=\"32px\" alt=\"2hu\" title=\"2hu\" src=\"corndog.png\" />" - } - - assert ActivityRepresenter.to_map(activity, %{ - user: user, - for: follower, - mentioned: [mentioned_user] - }) == expected_status - end - - test "a delete activity" do - object = insert(:note) - user = User.get_by_ap_id(object.data["actor"]) - - {:ok, delete} = ActivityPub.delete(object) - - map = ActivityRepresenter.to_map(delete, %{user: user}) - - assert map["is_post_verb"] == false - assert map["activity_type"] == "delete" - assert map["uri"] == object.data["id"] - end -end diff --git a/test/web/twitter_api/twitter_api_controller_test.exs b/test/web/twitter_api/twitter_api_controller_test.exs index 1b810c9a0..083540017 100644 --- a/test/web/twitter_api/twitter_api_controller_test.exs +++ b/test/web/twitter_api/twitter_api_controller_test.exs @@ -16,9 +16,9 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.CommonAPI alias Pleroma.Web.OAuth.Token + alias Pleroma.Web.TwitterAPI.ActivityView alias Pleroma.Web.TwitterAPI.Controller alias Pleroma.Web.TwitterAPI.NotificationView - alias Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter alias Pleroma.Web.TwitterAPI.TwitterAPI alias Pleroma.Web.TwitterAPI.UserView @@ -116,7 +116,11 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do |> post(request_path, %{status: "Nice meme.", visibility: "private"}) assert json_response(conn, 200) == - ActivityRepresenter.to_map(Repo.one(Activity), %{user: user, for: user}) + ActivityView.render("activity.json", %{ + activity: Repo.one(Activity), + user: user, + for: user + }) end end @@ -273,7 +277,7 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do response = json_response(conn, 200) - assert response == ActivityRepresenter.to_map(activity, %{user: actor}) + assert response == ActivityView.render("activity.json", %{activity: activity, user: actor}) end end @@ -372,7 +376,8 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do assert response == Enum.map(returned_activities, fn activity -> - ActivityRepresenter.to_map(activity, %{ + ActivityView.render("activity.json", %{ + activity: activity, user: User.get_cached_by_ap_id(activity.data["actor"]), for: current_user }) @@ -469,10 +474,10 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do assert length(response) == 1 assert Enum.at(response, 0) == - ActivityRepresenter.to_map(activity, %{ + ActivityView.render("activity.json", %{ user: current_user, for: current_user, - mentioned: [current_user] + activity: activity }) end @@ -594,7 +599,9 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do conn = get(conn, "/api/statuses/user_timeline.json", %{"user_id" => user.id}) response = json_response(conn, 200) assert length(response) == 1 - assert Enum.at(response, 0) == ActivityRepresenter.to_map(activity, %{user: user}) + + assert Enum.at(response, 0) == + ActivityView.render("activity.json", %{user: user, activity: activity}) end test "with screen_name", %{conn: conn} do @@ -604,7 +611,9 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do conn = get(conn, "/api/statuses/user_timeline.json", %{"screen_name" => user.nickname}) response = json_response(conn, 200) assert length(response) == 1 - assert Enum.at(response, 0) == ActivityRepresenter.to_map(activity, %{user: user}) + + assert Enum.at(response, 0) == + ActivityView.render("activity.json", %{user: user, activity: activity}) end test "with credentials", %{conn: conn, user: current_user} do @@ -620,7 +629,11 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do assert length(response) == 1 assert Enum.at(response, 0) == - ActivityRepresenter.to_map(activity, %{user: current_user, for: current_user}) + ActivityView.render("activity.json", %{ + user: current_user, + for: current_user, + activity: activity + }) end test "with credentials with user_id", %{conn: conn, user: current_user} do @@ -635,7 +648,9 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do response = json_response(conn, 200) assert length(response) == 1 - assert Enum.at(response, 0) == ActivityRepresenter.to_map(activity, %{user: user}) + + assert Enum.at(response, 0) == + ActivityView.render("activity.json", %{user: user, activity: activity}) end test "with credentials screen_name", %{conn: conn, user: current_user} do @@ -650,7 +665,9 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do response = json_response(conn, 200) assert length(response) == 1 - assert Enum.at(response, 0) == ActivityRepresenter.to_map(activity, %{user: user}) + + assert Enum.at(response, 0) == + ActivityView.render("activity.json", %{user: user, activity: activity}) end test "with credentials with user_id, excluding RTs", %{conn: conn, user: current_user} do @@ -669,7 +686,9 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do response = json_response(conn, 200) assert length(response) == 1 - assert Enum.at(response, 0) == ActivityRepresenter.to_map(activity, %{user: user}) + + assert Enum.at(response, 0) == + ActivityView.render("activity.json", %{user: user, activity: activity}) conn = conn @@ -678,7 +697,9 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do response = json_response(conn, 200) assert length(response) == 1 - assert Enum.at(response, 0) == ActivityRepresenter.to_map(activity, %{user: user}) + + assert Enum.at(response, 0) == + ActivityView.render("activity.json", %{user: user, activity: activity}) end end @@ -937,7 +958,11 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do activity_user = Repo.get_by(User, ap_id: note_activity.data["actor"]) assert json_response(response, 200) == - ActivityRepresenter.to_map(activity, %{user: activity_user, for: current_user}) + ActivityView.render("activity.json", %{ + user: activity_user, + for: current_user, + activity: activity + }) end end @@ -971,7 +996,11 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do activity_user = Repo.get_by(User, ap_id: note_activity.data["actor"]) assert json_response(response, 200) == - ActivityRepresenter.to_map(activity, %{user: activity_user, for: current_user}) + ActivityView.render("activity.json", %{ + user: activity_user, + for: current_user, + activity: activity + }) end end @@ -1955,7 +1984,7 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do user = refresh_record(user) assert json_response(response, 200) == - ActivityRepresenter.to_map(activity, %{user: user, for: user}) + ActivityView.render("activity.json", %{user: user, for: user, activity: activity}) end end @@ -1985,7 +2014,7 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do user = refresh_record(user) assert json_response(response, 200) == - ActivityRepresenter.to_map(activity, %{user: user, for: user}) + ActivityView.render("activity.json", %{user: user, for: user, activity: activity}) end end |