diff options
| -rw-r--r-- | docs/API/admin_api.md | 121 | ||||
| -rw-r--r-- | lib/pleroma/chat.ex | 10 | ||||
| -rw-r--r-- | lib/pleroma/moderation_log.ex | 24 | ||||
| -rw-r--r-- | lib/pleroma/web/admin_api/controllers/admin_api_controller.ex | 22 | ||||
| -rw-r--r-- | lib/pleroma/web/admin_api/controllers/chat_controller.ex | 85 | ||||
| -rw-r--r-- | lib/pleroma/web/admin_api/views/chat_view.ex | 30 | ||||
| -rw-r--r-- | lib/pleroma/web/admin_api/views/status_view.ex | 3 | ||||
| -rw-r--r-- | lib/pleroma/web/api_spec/operations/admin/chat_operation.ex | 96 | ||||
| -rw-r--r-- | lib/pleroma/web/common_api/common_api.ex | 17 | ||||
| -rw-r--r-- | lib/pleroma/web/mastodon_api/views/status_view.ex | 25 | ||||
| -rw-r--r-- | lib/pleroma/web/pleroma_api/controllers/chat_controller.ex | 7 | ||||
| -rw-r--r-- | lib/pleroma/web/pleroma_api/views/scrobble_view.ex | 4 | ||||
| -rw-r--r-- | lib/pleroma/web/router.ex | 5 | ||||
| -rw-r--r-- | test/web/admin_api/controllers/admin_api_controller_test.exs | 50 | ||||
| -rw-r--r-- | test/web/admin_api/controllers/chat_controller_test.exs | 219 | ||||
| -rw-r--r-- | test/web/common_api/common_api_test.exs | 20 | 
16 files changed, 707 insertions, 31 deletions
| diff --git a/docs/API/admin_api.md b/docs/API/admin_api.md index c0ea074f0..bc96abbf0 100644 --- a/docs/API/admin_api.md +++ b/docs/API/admin_api.md @@ -1334,3 +1334,124 @@ Loads json generated from `config/descriptions.exs`.  { }  ``` + +## GET /api/pleroma/admin/users/:nickname/chats + +### List a user's chats + +- Params: None + +- Response: + +```json +[ +   { +      "sender": { +        "id": "someflakeid", +        "username": "somenick", +        ... +      }, +      "receiver": { +        "id": "someflakeid", +        "username": "somenick", +        ... +      }, +      "id" : "1", +      "unread" : 2, +      "last_message" : {...}, // The last message in that chat +      "updated_at": "2020-04-21T15:11:46.000Z" +   } +] +``` + +## GET /api/pleroma/admin/chats/:chat_id + +### View a single chat + +- Params: None + +- Response: + +```json +{ +  "sender": { +    "id": "someflakeid", +    "username": "somenick", +    ... +  }, +  "receiver": { +    "id": "someflakeid", +    "username": "somenick", +    ... +  }, +  "id" : "1", +  "unread" : 2, +  "last_message" : {...}, // The last message in that chat +  "updated_at": "2020-04-21T15:11:46.000Z" +} +``` + +## GET /api/pleroma/admin/chats/:chat_id/messages + +### List the messages in a chat + +- Params: `max_id`, `min_id` + +- Response: + +```json +[ +  { +    "account_id": "someflakeid", +    "chat_id": "1", +    "content": "Check this out :firefox:", +    "created_at": "2020-04-21T15:11:46.000Z", +    "emojis": [ +      { +        "shortcode": "firefox", +        "static_url": "https://dontbulling.me/emoji/Firefox.gif", +        "url": "https://dontbulling.me/emoji/Firefox.gif", +        "visible_in_picker": false +      } +    ], +    "id": "13", +    "unread": true +  }, +  { +    "account_id": "someflakeid", +    "chat_id": "1", +    "content": "Whats' up?", +    "created_at": "2020-04-21T15:06:45.000Z", +    "emojis": [], +    "id": "12", +    "unread": false +  } +] +``` + +## DELETE /api/pleroma/admin/chats/:chat_id/messages/:message_id + +### Delete a single message + +- Params: None + +- Response: + +```json +{ +  "account_id": "someflakeid", +  "chat_id": "1", +  "content": "Check this out :firefox:", +  "created_at": "2020-04-21T15:11:46.000Z", +  "emojis": [ +    { +      "shortcode": "firefox", +      "static_url": "https://dontbulling.me/emoji/Firefox.gif", +      "url": "https://dontbulling.me/emoji/Firefox.gif", +      "visible_in_picker": false +    } +  ], +  "id": "13", +  "unread": false +} +``` diff --git a/lib/pleroma/chat.ex b/lib/pleroma/chat.ex index 24a86371e..84f8806a0 100644 --- a/lib/pleroma/chat.ex +++ b/lib/pleroma/chat.ex @@ -6,7 +6,9 @@ defmodule Pleroma.Chat do    use Ecto.Schema    import Ecto.Changeset +  import Ecto.Query +  alias Pleroma.Chat    alias Pleroma.Repo    alias Pleroma.User @@ -69,4 +71,12 @@ defmodule Pleroma.Chat do        conflict_target: [:user_id, :recipient]      )    end + +  @spec for_user_query(FlakeId.Ecto.CompatType.t()) :: Ecto.Query.t() +  def for_user_query(user_id) do +    from(c in Chat, +      where: c.user_id == ^user_id, +      order_by: [desc: c.updated_at] +    ) +  end  end diff --git a/lib/pleroma/moderation_log.ex b/lib/pleroma/moderation_log.ex index 31c9afe2a..47036a6f6 100644 --- a/lib/pleroma/moderation_log.ex +++ b/lib/pleroma/moderation_log.ex @@ -320,6 +320,19 @@ defmodule Pleroma.ModerationLog do      |> insert_log_entry_with_message()    end +  @spec insert_log(%{actor: User, action: String.t(), subject_id: String.t()}) :: +          {:ok, ModerationLog} | {:error, any} +  def insert_log(%{actor: %User{} = actor, action: "chat_message_delete", subject_id: subject_id}) do +    %ModerationLog{ +      data: %{ +        "actor" => %{"nickname" => actor.nickname}, +        "action" => "chat_message_delete", +        "subject_id" => subject_id +      } +    } +    |> insert_log_entry_with_message() +  end +    @spec insert_log_entry_with_message(ModerationLog) :: {:ok, ModerationLog} | {:error, any}    defp insert_log_entry_with_message(entry) do      entry.data["message"] @@ -627,6 +640,17 @@ defmodule Pleroma.ModerationLog do      "@#{actor_nickname} updated users: #{users_to_nicknames_string(subjects)}"    end +  @spec get_log_entry_message(ModerationLog) :: String.t() +  def get_log_entry_message(%ModerationLog{ +        data: %{ +          "actor" => %{"nickname" => actor_nickname}, +          "action" => "chat_message_delete", +          "subject_id" => subject_id +        } +      }) do +    "@#{actor_nickname} deleted chat message ##{subject_id}" +  end +    defp nicknames_to_string(nicknames) do      nicknames      |> Enum.map(&"@#{&1}") diff --git a/lib/pleroma/web/admin_api/controllers/admin_api_controller.ex b/lib/pleroma/web/admin_api/controllers/admin_api_controller.ex index f5e4d49f9..d5713c3dd 100644 --- a/lib/pleroma/web/admin_api/controllers/admin_api_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/admin_api_controller.ex @@ -23,8 +23,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do    alias Pleroma.Web.Endpoint    alias Pleroma.Web.Router -  require Logger -    @users_page_size 50    plug( @@ -70,6 +68,12 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do    plug(      OAuthScopesPlug, +    %{scopes: ["read:chats"], admin: true} +    when action in [:list_user_chats] +  ) + +  plug( +    OAuthScopesPlug,      %{scopes: ["read"], admin: true}      when action in [             :list_log, @@ -256,6 +260,20 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do      end    end +  def list_user_chats(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname} = _params) do +    with %User{id: user_id} <- User.get_cached_by_nickname_or_id(nickname, for: admin) do +      chats = +        Pleroma.Chat.for_user_query(user_id) +        |> Pleroma.Repo.all() + +      conn +      |> put_view(AdminAPI.ChatView) +      |> render("index.json", chats: chats) +    else +      _ -> {:error, :not_found} +    end +  end +    def user_toggle_activation(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do      user = User.get_cached_by_nickname(nickname) diff --git a/lib/pleroma/web/admin_api/controllers/chat_controller.ex b/lib/pleroma/web/admin_api/controllers/chat_controller.ex new file mode 100644 index 000000000..967600d69 --- /dev/null +++ b/lib/pleroma/web/admin_api/controllers/chat_controller.ex @@ -0,0 +1,85 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.AdminAPI.ChatController do +  use Pleroma.Web, :controller + +  alias Pleroma.Activity +  alias Pleroma.Chat +  alias Pleroma.Chat.MessageReference +  alias Pleroma.ModerationLog +  alias Pleroma.Pagination +  alias Pleroma.Plugs.OAuthScopesPlug +  alias Pleroma.Web.AdminAPI +  alias Pleroma.Web.CommonAPI +  alias Pleroma.Web.PleromaAPI.Chat.MessageReferenceView + +  require Logger + +  plug(Pleroma.Web.ApiSpec.CastAndValidate) + +  plug( +    OAuthScopesPlug, +    %{scopes: ["read:chats"], admin: true} when action in [:show, :messages] +  ) + +  plug( +    OAuthScopesPlug, +    %{scopes: ["write:chats"], admin: true} when action in [:delete_message] +  ) + +  action_fallback(Pleroma.Web.AdminAPI.FallbackController) + +  defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.Admin.ChatOperation + +  def delete_message(%{assigns: %{user: user}} = conn, %{ +        message_id: message_id, +        id: chat_id +      }) do +    with %MessageReference{object: %{data: %{"id" => object_ap_id}}} = cm_ref <- +           MessageReference.get_by_id(message_id), +         ^chat_id <- to_string(cm_ref.chat_id), +         %Activity{id: activity_id} <- Activity.get_create_by_object_ap_id(object_ap_id), +         {:ok, _} <- CommonAPI.delete(activity_id, user) do +      ModerationLog.insert_log(%{ +        action: "chat_message_delete", +        actor: user, +        subject_id: message_id +      }) + +      conn +      |> put_view(MessageReferenceView) +      |> render("show.json", chat_message_reference: cm_ref) +    else +      _e -> +        {:error, :could_not_delete} +    end +  end + +  def messages(conn, %{id: id} = params) do +    with %Chat{} = chat <- Chat.get_by_id(id) do +      cm_refs = +        chat +        |> MessageReference.for_chat_query() +        |> Pagination.fetch_paginated(params) + +      conn +      |> put_view(MessageReferenceView) +      |> render("index.json", chat_message_references: cm_refs) +    else +      _ -> +        conn +        |> put_status(:not_found) +        |> json(%{error: "not found"}) +    end +  end + +  def show(conn, %{id: id}) do +    with %Chat{} = chat <- Chat.get_by_id(id) do +      conn +      |> put_view(AdminAPI.ChatView) +      |> render("show.json", chat: chat) +    end +  end +end diff --git a/lib/pleroma/web/admin_api/views/chat_view.ex b/lib/pleroma/web/admin_api/views/chat_view.ex new file mode 100644 index 000000000..847df1423 --- /dev/null +++ b/lib/pleroma/web/admin_api/views/chat_view.ex @@ -0,0 +1,30 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.AdminAPI.ChatView do +  use Pleroma.Web, :view + +  alias Pleroma.Chat +  alias Pleroma.User +  alias Pleroma.Web.MastodonAPI +  alias Pleroma.Web.PleromaAPI + +  def render("index.json", %{chats: chats} = opts) do +    render_many(chats, __MODULE__, "show.json", Map.delete(opts, :chats)) +  end + +  def render("show.json", %{chat: %Chat{user_id: user_id}} = opts) do +    user = User.get_by_id(user_id) +    sender = MastodonAPI.AccountView.render("show.json", user: user, skip_visibility_check: true) + +    serialized_chat = PleromaAPI.ChatView.render("show.json", opts) + +    serialized_chat +    |> Map.put(:sender, sender) +    |> Map.put(:receiver, serialized_chat[:account]) +    |> Map.delete(:account) +  end + +  def render(view, opts), do: PleromaAPI.ChatView.render(view, opts) +end diff --git a/lib/pleroma/web/admin_api/views/status_view.ex b/lib/pleroma/web/admin_api/views/status_view.ex index 500800be2..6042a22b6 100644 --- a/lib/pleroma/web/admin_api/views/status_view.ex +++ b/lib/pleroma/web/admin_api/views/status_view.ex @@ -8,6 +8,7 @@ defmodule Pleroma.Web.AdminAPI.StatusView do    require Pleroma.Constants    alias Pleroma.Web.AdminAPI +  alias Pleroma.Web.CommonAPI    alias Pleroma.Web.MastodonAPI    defdelegate merge_account_views(user), to: AdminAPI.AccountView @@ -17,7 +18,7 @@ defmodule Pleroma.Web.AdminAPI.StatusView do    end    def render("show.json", %{activity: %{data: %{"object" => _object}} = activity} = opts) do -    user = MastodonAPI.StatusView.get_user(activity.data["actor"]) +    user = CommonAPI.get_user(activity.data["actor"])      MastodonAPI.StatusView.render("show.json", opts)      |> Map.merge(%{account: merge_account_views(user)}) diff --git a/lib/pleroma/web/api_spec/operations/admin/chat_operation.ex b/lib/pleroma/web/api_spec/operations/admin/chat_operation.ex new file mode 100644 index 000000000..d3e5dfc1c --- /dev/null +++ b/lib/pleroma/web/api_spec/operations/admin/chat_operation.ex @@ -0,0 +1,96 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.Admin.ChatOperation do +  alias OpenApiSpex.Operation +  alias Pleroma.Web.ApiSpec.Schemas.Chat +  alias Pleroma.Web.ApiSpec.Schemas.ChatMessage + +  import Pleroma.Web.ApiSpec.Helpers + +  def open_api_operation(action) do +    operation = String.to_existing_atom("#{action}_operation") +    apply(__MODULE__, operation, []) +  end + +  def delete_message_operation do +    %Operation{ +      tags: ["admin", "chat"], +      summary: "Delete an individual chat message", +      operationId: "AdminAPI.ChatController.delete_message", +      parameters: [ +        Operation.parameter(:id, :path, :string, "The ID of the Chat"), +        Operation.parameter(:message_id, :path, :string, "The ID of the message") +      ], +      responses: %{ +        200 => +          Operation.response( +            "The deleted ChatMessage", +            "application/json", +            ChatMessage +          ) +      }, +      security: [ +        %{ +          "oAuth" => ["write:chats"] +        } +      ] +    } +  end + +  def messages_operation do +    %Operation{ +      tags: ["admin", "chat"], +      summary: "Get the most recent messages of the chat", +      operationId: "AdminAPI.ChatController.messages", +      parameters: +        [Operation.parameter(:id, :path, :string, "The ID of the Chat")] ++ +          pagination_params(), +      responses: %{ +        200 => +          Operation.response( +            "The messages in the chat", +            "application/json", +            Pleroma.Web.ApiSpec.ChatOperation.chat_messages_response() +          ) +      }, +      security: [ +        %{ +          "oAuth" => ["read:chats"] +        } +      ] +    } +  end + +  def show_operation do +    %Operation{ +      tags: ["chat"], +      summary: "Create a chat", +      operationId: "AdminAPI.ChatController.show", +      parameters: [ +        Operation.parameter( +          :id, +          :path, +          :string, +          "The id of the chat", +          required: true, +          example: "1234" +        ) +      ], +      responses: %{ +        200 => +          Operation.response( +            "The existing chat", +            "application/json", +            Chat +          ) +      }, +      security: [ +        %{ +          "oAuth" => ["read"] +        } +      ] +    } +  end +end diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex index 500c3883e..a8c83bc8f 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -550,4 +550,21 @@ defmodule Pleroma.Web.CommonAPI do    def show_reblogs(%User{} = user, %User{} = target) do      UserRelationship.delete_reblog_mute(user, target)    end + +  def get_user(ap_id, fake_record_fallback \\ true) do +    cond do +      user = User.get_cached_by_ap_id(ap_id) -> +        user + +      user = User.get_by_guessed_nickname(ap_id) -> +        user + +      fake_record_fallback -> +        # TODO: refactor (fake records is never a good idea) +        User.error_user(ap_id) + +      true -> +        nil +    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 ca42917fc..94b8dc8c6 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -55,23 +55,6 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do      end)    end -  def get_user(ap_id, fake_record_fallback \\ true) do -    cond do -      user = User.get_cached_by_ap_id(ap_id) -> -        user - -      user = User.get_by_guessed_nickname(ap_id) -> -        user - -      fake_record_fallback -> -        # TODO: refactor (fake records is never a good idea) -        User.error_user(ap_id) - -      true -> -        nil -    end -  end -    defp get_context_id(%{data: %{"context_id" => context_id}}) when not is_nil(context_id),      do: context_id @@ -119,7 +102,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do            # Note: unresolved users are filtered out            actors =              (activities ++ parent_activities) -            |> Enum.map(&get_user(&1.data["actor"], false)) +            |> Enum.map(&CommonAPI.get_user(&1.data["actor"], false))              |> Enum.filter(& &1)            UserRelationship.view_relationships_option(reading_user, actors, subset: :source_mutes) @@ -138,7 +121,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do          "show.json",          %{activity: %{data: %{"type" => "Announce", "object" => _object}} = activity} = opts        ) do -    user = get_user(activity.data["actor"]) +    user = CommonAPI.get_user(activity.data["actor"])      created_at = Utils.to_masto_date(activity.data["published"])      activity_object = Object.normalize(activity) @@ -211,7 +194,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do    def render("show.json", %{activity: %{data: %{"object" => _object}} = activity} = opts) do      object = Object.normalize(activity) -    user = get_user(activity.data["actor"]) +    user = CommonAPI.get_user(activity.data["actor"])      user_follower_address = user.follower_address      like_count = object.data["like_count"] || 0 @@ -265,7 +248,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do      reply_to = get_reply_to(activity, opts) -    reply_to_user = reply_to && get_user(reply_to.data["actor"]) +    reply_to_user = reply_to && CommonAPI.get_user(reply_to.data["actor"])      content =        object diff --git a/lib/pleroma/web/pleroma_api/controllers/chat_controller.ex b/lib/pleroma/web/pleroma_api/controllers/chat_controller.ex index e8a1746d4..27c9a2e0f 100644 --- a/lib/pleroma/web/pleroma_api/controllers/chat_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/chat_controller.ex @@ -146,11 +146,8 @@ defmodule Pleroma.Web.PleromaAPI.ChatController do      blocked_ap_ids = User.blocked_users_ap_ids(user)      chats = -      from(c in Chat, -        where: c.user_id == ^user_id, -        where: c.recipient not in ^blocked_ap_ids, -        order_by: [desc: c.updated_at] -      ) +      Chat.for_user_query(user_id) +      |> where([c], c.recipient not in ^blocked_ap_ids)        |> Repo.all()      conn diff --git a/lib/pleroma/web/pleroma_api/views/scrobble_view.ex b/lib/pleroma/web/pleroma_api/views/scrobble_view.ex index bbff93abe..95bd4c368 100644 --- a/lib/pleroma/web/pleroma_api/views/scrobble_view.ex +++ b/lib/pleroma/web/pleroma_api/views/scrobble_view.ex @@ -10,14 +10,14 @@ defmodule Pleroma.Web.PleromaAPI.ScrobbleView do    alias Pleroma.Activity    alias Pleroma.HTML    alias Pleroma.Object +  alias Pleroma.Web.CommonAPI    alias Pleroma.Web.CommonAPI.Utils    alias Pleroma.Web.MastodonAPI.AccountView -  alias Pleroma.Web.MastodonAPI.StatusView    def render("show.json", %{activity: %Activity{data: %{"type" => "Listen"}} = activity} = opts) do      object = Object.normalize(activity) -    user = StatusView.get_user(activity.data["actor"]) +    user = CommonAPI.get_user(activity.data["actor"])      created_at = Utils.to_masto_date(activity.data["published"])      %{ diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index c6433cc53..e4440d442 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -178,6 +178,7 @@ defmodule Pleroma.Web.Router do      get("/users", AdminAPIController, :list_users)      get("/users/:nickname", AdminAPIController, :user_show)      get("/users/:nickname/statuses", AdminAPIController, :list_user_statuses) +    get("/users/:nickname/chats", AdminAPIController, :list_user_chats)      get("/instances/:instance/statuses", AdminAPIController, :list_instance_statuses) @@ -214,6 +215,10 @@ defmodule Pleroma.Web.Router do      get("/media_proxy_caches", MediaProxyCacheController, :index)      post("/media_proxy_caches/delete", MediaProxyCacheController, :delete)      post("/media_proxy_caches/purge", MediaProxyCacheController, :purge) + +    get("/chats/:id", ChatController, :show) +    get("/chats/:id/messages", ChatController, :messages) +    delete("/chats/:id/messages/:message_id", ChatController, :delete_message)    end    scope "/api/pleroma/emoji", Pleroma.Web.PleromaAPI do diff --git a/test/web/admin_api/controllers/admin_api_controller_test.exs b/test/web/admin_api/controllers/admin_api_controller_test.exs index 3bc88c6a9..e4d3512de 100644 --- a/test/web/admin_api/controllers/admin_api_controller_test.exs +++ b/test/web/admin_api/controllers/admin_api_controller_test.exs @@ -1510,6 +1510,56 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do      end    end +  describe "GET /api/pleroma/admin/users/:nickname/chats" do +    setup do +      user = insert(:user) +      recipients = insert_list(3, :user) + +      Enum.each(recipients, fn recipient -> +        CommonAPI.post_chat_message(user, recipient, "yo") +      end) + +      %{user: user} +    end + +    test "renders user's chats", %{conn: conn, user: user} do +      conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/chats") + +      assert json_response(conn, 200) |> length() == 3 +    end +  end + +  describe "GET /api/pleroma/admin/users/:nickname/chats unauthorized" do +    setup do +      user = insert(:user) +      recipient = insert(:user) +      CommonAPI.post_chat_message(user, recipient, "yo") +      %{conn: conn} = oauth_access(["read:chats"]) +      %{conn: conn, user: user} +    end + +    test "returns 403", %{conn: conn, user: user} do +      conn +      |> get("/api/pleroma/admin/users/#{user.nickname}/chats") +      |> json_response(403) +    end +  end + +  describe "GET /api/pleroma/admin/users/:nickname/chats unauthenticated" do +    setup do +      user = insert(:user) +      recipient = insert(:user) +      CommonAPI.post_chat_message(user, recipient, "yo") +      %{conn: build_conn(), user: user} +    end + +    test "returns 403", %{conn: conn, user: user} do +      conn +      |> get("/api/pleroma/admin/users/#{user.nickname}/chats") +      |> json_response(403) +    end +  end +    describe "GET /api/pleroma/admin/moderation_log" do      setup do        moderator = insert(:user, is_moderator: true) diff --git a/test/web/admin_api/controllers/chat_controller_test.exs b/test/web/admin_api/controllers/chat_controller_test.exs new file mode 100644 index 000000000..bd4c9c9d1 --- /dev/null +++ b/test/web/admin_api/controllers/chat_controller_test.exs @@ -0,0 +1,219 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.AdminAPI.ChatControllerTest do +  use Pleroma.Web.ConnCase + +  import Pleroma.Factory + +  alias Pleroma.Chat +  alias Pleroma.Chat.MessageReference +  alias Pleroma.Config +  alias Pleroma.ModerationLog +  alias Pleroma.Object +  alias Pleroma.Repo +  alias Pleroma.Web.CommonAPI + +  defp admin_setup do +    admin = insert(:user, is_admin: true) +    token = insert(:oauth_admin_token, user: admin) + +    conn = +      build_conn() +      |> assign(:user, admin) +      |> assign(:token, token) + +    {:ok, %{admin: admin, token: token, conn: conn}} +  end + +  describe "DELETE /api/pleroma/admin/chats/:id/messages/:message_id" do +    setup do: admin_setup() + +    test "it deletes a message from the chat", %{conn: conn, admin: admin} do +      user = insert(:user) +      recipient = insert(:user) + +      {:ok, message} = +        CommonAPI.post_chat_message(user, recipient, "Hello darkness my old friend") + +      object = Object.normalize(message, false) + +      chat = Chat.get(user.id, recipient.ap_id) +      recipient_chat = Chat.get(recipient.id, user.ap_id) + +      cm_ref = MessageReference.for_chat_and_object(chat, object) +      recipient_cm_ref = MessageReference.for_chat_and_object(recipient_chat, object) + +      result = +        conn +        |> put_req_header("content-type", "application/json") +        |> delete("/api/pleroma/admin/chats/#{chat.id}/messages/#{cm_ref.id}") +        |> json_response_and_validate_schema(200) + +      log_entry = Repo.one(ModerationLog) + +      assert ModerationLog.get_log_entry_message(log_entry) == +               "@#{admin.nickname} deleted chat message ##{cm_ref.id}" + +      assert result["id"] == cm_ref.id +      refute MessageReference.get_by_id(cm_ref.id) +      refute MessageReference.get_by_id(recipient_cm_ref.id) +      assert %{data: %{"type" => "Tombstone"}} = Object.get_by_id(object.id) +    end +  end + +  describe "GET /api/pleroma/admin/chats/:id/messages" do +    setup do: admin_setup() + +    test "it paginates", %{conn: conn} do +      user = insert(:user) +      recipient = insert(:user) + +      Enum.each(1..30, fn _ -> +        {:ok, _} = CommonAPI.post_chat_message(user, recipient, "hey") +      end) + +      chat = Chat.get(user.id, recipient.ap_id) + +      result = +        conn +        |> get("/api/pleroma/admin/chats/#{chat.id}/messages") +        |> json_response_and_validate_schema(200) + +      assert length(result) == 20 + +      result = +        conn +        |> get("/api/pleroma/admin/chats/#{chat.id}/messages?max_id=#{List.last(result)["id"]}") +        |> json_response_and_validate_schema(200) + +      assert length(result) == 10 +    end + +    test "it returns the messages for a given chat", %{conn: conn} do +      user = insert(:user) +      other_user = insert(:user) +      third_user = insert(:user) + +      {:ok, _} = CommonAPI.post_chat_message(user, other_user, "hey") +      {:ok, _} = CommonAPI.post_chat_message(user, third_user, "hey") +      {:ok, _} = CommonAPI.post_chat_message(user, other_user, "how are you?") +      {:ok, _} = CommonAPI.post_chat_message(other_user, user, "fine, how about you?") + +      chat = Chat.get(user.id, other_user.ap_id) + +      result = +        conn +        |> get("/api/pleroma/admin/chats/#{chat.id}/messages") +        |> json_response_and_validate_schema(200) + +      result +      |> Enum.each(fn message -> +        assert message["chat_id"] == chat.id |> to_string() +      end) + +      assert length(result) == 3 +    end +  end + +  describe "GET /api/pleroma/admin/chats/:id" do +    setup do: admin_setup() + +    test "it returns a chat", %{conn: conn} do +      user = insert(:user) +      other_user = insert(:user) + +      {:ok, chat} = Chat.get_or_create(user.id, other_user.ap_id) + +      result = +        conn +        |> get("/api/pleroma/admin/chats/#{chat.id}") +        |> json_response_and_validate_schema(200) + +      assert result["id"] == to_string(chat.id) +      assert %{} = result["sender"] +      assert %{} = result["receiver"] +      refute result["account"] +    end +  end + +  describe "unauthorized chat moderation" do +    setup do +      user = insert(:user) +      recipient = insert(:user) + +      {:ok, message} = CommonAPI.post_chat_message(user, recipient, "Yo") +      object = Object.normalize(message, false) +      chat = Chat.get(user.id, recipient.ap_id) +      cm_ref = MessageReference.for_chat_and_object(chat, object) + +      %{conn: conn} = oauth_access(["read:chats", "write:chats"]) +      %{conn: conn, chat: chat, cm_ref: cm_ref} +    end + +    test "DELETE /api/pleroma/admin/chats/:id/messages/:message_id", %{ +      conn: conn, +      chat: chat, +      cm_ref: cm_ref +    } do +      conn +      |> put_req_header("content-type", "application/json") +      |> delete("/api/pleroma/admin/chats/#{chat.id}/messages/#{cm_ref.id}") +      |> json_response(403) + +      assert MessageReference.get_by_id(cm_ref.id) == cm_ref +    end + +    test "GET /api/pleroma/admin/chats/:id/messages", %{conn: conn, chat: chat} do +      conn +      |> get("/api/pleroma/admin/chats/#{chat.id}/messages") +      |> json_response(403) +    end + +    test "GET /api/pleroma/admin/chats/:id", %{conn: conn, chat: chat} do +      conn +      |> get("/api/pleroma/admin/chats/#{chat.id}") +      |> json_response(403) +    end +  end + +  describe "unauthenticated chat moderation" do +    setup do +      user = insert(:user) +      recipient = insert(:user) + +      {:ok, message} = CommonAPI.post_chat_message(user, recipient, "Yo") +      object = Object.normalize(message, false) +      chat = Chat.get(user.id, recipient.ap_id) +      cm_ref = MessageReference.for_chat_and_object(chat, object) + +      %{conn: build_conn(), chat: chat, cm_ref: cm_ref} +    end + +    test "DELETE /api/pleroma/admin/chats/:id/messages/:message_id", %{ +      conn: conn, +      chat: chat, +      cm_ref: cm_ref +    } do +      conn +      |> put_req_header("content-type", "application/json") +      |> delete("/api/pleroma/admin/chats/#{chat.id}/messages/#{cm_ref.id}") +      |> json_response(403) + +      assert MessageReference.get_by_id(cm_ref.id) == cm_ref +    end + +    test "GET /api/pleroma/admin/chats/:id/messages", %{conn: conn, chat: chat} do +      conn +      |> get("/api/pleroma/admin/chats/#{chat.id}/messages") +      |> json_response(403) +    end + +    test "GET /api/pleroma/admin/chats/:id", %{conn: conn, chat: chat} do +      conn +      |> get("/api/pleroma/admin/chats/#{chat.id}") +      |> json_response(403) +    end +  end +end diff --git a/test/web/common_api/common_api_test.exs b/test/web/common_api/common_api_test.exs index 5afb0a6dc..f5559f932 100644 --- a/test/web/common_api/common_api_test.exs +++ b/test/web/common_api/common_api_test.exs @@ -1193,4 +1193,24 @@ defmodule Pleroma.Web.CommonAPITest do        assert Visibility.get_visibility(activity) == "private"      end    end + +  describe "get_user/1" do +    test "gets user by ap_id" do +      user = insert(:user) +      assert CommonAPI.get_user(user.ap_id) == user +    end + +    test "gets user by guessed nickname" do +      user = insert(:user, ap_id: "", nickname: "mario@mushroom.kingdom") +      assert CommonAPI.get_user("https://mushroom.kingdom/users/mario") == user +    end + +    test "fallback" do +      assert %User{ +               name: "", +               ap_id: "", +               nickname: "erroruser@example.com" +             } = CommonAPI.get_user("") +    end +  end  end | 
