Merge branch 'develop' of git.pleroma.social:pleroma/pleroma into chat-federation...
authorlain <lain@soykaf.club>
Fri, 10 Jul 2020 10:26:53 +0000 (12:26 +0200)
committerlain <lain@soykaf.club>
Fri, 10 Jul 2020 10:26:53 +0000 (12:26 +0200)
19 files changed:
CHANGELOG.md
docs/API/differences_in_mastoapi_responses.md
lib/pleroma/user.ex
lib/pleroma/web/activity_pub/activity_pub.ex
lib/pleroma/web/activity_pub/object_validators/chat_message_validator.ex
lib/pleroma/web/activity_pub/views/user_view.ex
lib/pleroma/web/api_spec/operations/account_operation.ex
lib/pleroma/web/api_spec/schemas/account.ex
lib/pleroma/web/mastodon_api/controllers/account_controller.ex
lib/pleroma/web/mastodon_api/views/account_view.ex
priv/repo/migrations/20200703101031_add_chat_acceptance_to_users.exs [new file with mode: 0644]
priv/static/schemas/litepub-0.1.jsonld
test/fixtures/tesla_mock/admin@mastdon.example.org.json
test/user_test.exs
test/web/activity_pub/activity_pub_test.exs
test/web/activity_pub/object_validators/chat_validation_test.exs
test/web/activity_pub/views/user_view_test.exs
test/web/mastodon_api/controllers/account_controller/update_credentials_test.exs
test/web/mastodon_api/views/account_view_test.exs

index 78eb8e984f43bb4616e303642564cfbf661e96af..3c005a4dc2c3724b8f9b0640727eb849789a6010 100644 (file)
@@ -39,6 +39,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 
 ### Added
 
+- Chats: Added `accepts_chat_messages` field to user, exposed in APIs and federation.
 - Chats: Added support for federated chats. For details, see the docs.
 - ActivityPub: Added support for existing AP ids for instances migrated from Mastodon.
 - Instance: Add `background_image` to configuration and `/api/v1/instance`
index 03c7f4608fd02fd202b99f4a1b7ebad55ae02a09..65f9f1aefc52d4be68300188089508e75f5800bf 100644 (file)
@@ -71,6 +71,7 @@ Has these additional fields under the `pleroma` object:
 - `unread_conversation_count`: The count of unread conversations. Only returned to the account owner.
 - `unread_notifications_count`: The count of unread notifications. Only returned to the account owner.
 - `notification_settings`: object, can be absent. See `/api/pleroma/notification_settings` for the parameters/keys returned.
+- `accepts_chat_messages`: boolean, but can be null if we don't have that information about a user
 - `favicon`: nullable URL string, Favicon image of the user's instance
 
 ### Source
@@ -186,6 +187,7 @@ Additional parameters can be added to the JSON body/Form data:
 - `pleroma_background_image` - sets the background image of the user. Can be set to "" (an empty string) to reset.
 - `discoverable` - if true, discovery of this account in search results and other services is allowed.
 - `actor_type` - the type of this account.
+- `accepts_chat_messages` - if false, this account will reject all chat messages.
 
 All images (avatar, banner and background) can be reset to the default by sending an empty string ("") instead of a file.
 
index 0078f98317777a91e2017f03936da22eae6647d1..b9989f9018194122f2499918afedf2f88d73ad4f 100644 (file)
@@ -138,6 +138,7 @@ defmodule Pleroma.User do
     field(:also_known_as, {:array, :string}, default: [])
     field(:inbox, :string)
     field(:shared_inbox, :string)
+    field(:accepts_chat_messages, :boolean, default: nil)
 
     embeds_one(
       :notification_settings,
@@ -436,7 +437,8 @@ defmodule Pleroma.User do
         :discoverable,
         :invisible,
         :actor_type,
-        :also_known_as
+        :also_known_as,
+        :accepts_chat_messages
       ]
     )
     |> validate_required([:name, :ap_id])
@@ -481,7 +483,8 @@ defmodule Pleroma.User do
         :pleroma_settings_store,
         :discoverable,
         :actor_type,
-        :also_known_as
+        :also_known_as,
+        :accepts_chat_messages
       ]
     )
     |> unique_constraint(:nickname)
@@ -620,6 +623,7 @@ defmodule Pleroma.User do
   def register_changeset(struct, params \\ %{}, opts \\ []) do
     bio_limit = Config.get([:instance, :user_bio_length], 5000)
     name_limit = Config.get([:instance, :user_name_length], 100)
+    params = Map.put_new(params, :accepts_chat_messages, true)
 
     need_confirmation? =
       if is_nil(opts[:need_confirmation]) do
@@ -638,7 +642,8 @@ defmodule Pleroma.User do
       :nickname,
       :password,
       :password_confirmation,
-      :emoji
+      :emoji,
+      :accepts_chat_messages
     ])
     |> validate_required([:name, :nickname, :password, :password_confirmation])
     |> validate_confirmation(:password)
index 1c29088059233c65b83c812edfece12ce2e65115..8da5cf938be96a3a12d5ab6130a9fb11757f173f 100644 (file)
@@ -1226,6 +1226,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
       end)
 
     locked = data["manuallyApprovesFollowers"] || false
+    capabilities = data["capabilities"] || %{}
+    accepts_chat_messages = capabilities["acceptsChatMessages"]
     data = Transmogrifier.maybe_fix_user_object(data)
     discoverable = data["discoverable"] || false
     invisible = data["invisible"] || false
@@ -1264,7 +1266,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
       also_known_as: Map.get(data, "alsoKnownAs", []),
       public_key: public_key,
       inbox: data["inbox"],
-      shared_inbox: shared_inbox
+      shared_inbox: shared_inbox,
+      accepts_chat_messages: accepts_chat_messages
     }
 
     # nickname can be nil because of virtual actors
index c481d79e0483aa11c7ca68da75943127cb275be8..91b475393335c9567b08c57132d50b36f649b4f9 100644 (file)
@@ -93,12 +93,14 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ChatMessageValidator do
   - If both users are in our system
   - If at least one of the users in this ChatMessage is a local user
   - If the recipient is not blocking the actor
+  - If the recipient is explicitly not accepting chat messages
   """
   def validate_local_concern(cng) do
     with actor_ap <- get_field(cng, :actor),
          {_, %User{} = actor} <- {:find_actor, User.get_cached_by_ap_id(actor_ap)},
          {_, %User{} = recipient} <-
            {:find_recipient, User.get_cached_by_ap_id(get_field(cng, :to) |> hd())},
+         {_, false} <- {:not_accepting_chats?, recipient.accepts_chat_messages == false},
          {_, false} <- {:blocking_actor?, User.blocks?(recipient, actor)},
          {_, true} <- {:local?, Enum.any?([actor, recipient], & &1.local)} do
       cng
@@ -107,6 +109,10 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ChatMessageValidator do
         cng
         |> add_error(:actor, "actor is blocked by recipient")
 
+      {:not_accepting_chats?, true} ->
+        cng
+        |> add_error(:to, "recipient does not accept chat messages")
+
       {:local?, false} ->
         cng
         |> add_error(:actor, "actor and recipient are both remote")
index 4a02b09a17dd3f731edf5d74daf62b8bcea839a6..3a4564912c2d1508bac155bc79b23f857c528a1d 100644 (file)
@@ -81,6 +81,15 @@ defmodule Pleroma.Web.ActivityPub.UserView do
 
     fields = Enum.map(user.fields, &Map.put(&1, "type", "PropertyValue"))
 
+    capabilities =
+      if is_boolean(user.accepts_chat_messages) do
+        %{
+          "acceptsChatMessages" => user.accepts_chat_messages
+        }
+      else
+        %{}
+      end
+
     %{
       "id" => user.ap_id,
       "type" => user.actor_type,
@@ -101,7 +110,8 @@ defmodule Pleroma.Web.ActivityPub.UserView do
       "endpoints" => endpoints,
       "attachment" => fields,
       "tag" => emoji_tags,
-      "discoverable" => user.discoverable
+      "discoverable" => user.discoverable,
+      "capabilities" => capabilities
     }
     |> Map.merge(maybe_make_image(&User.avatar_url/2, "icon", user))
     |> Map.merge(maybe_make_image(&User.banner_url/2, "image", user))
index b8c527606602544a25ae795c9eddadef5e91d4dc..952d9347b0013721efaa4a038744609038d07641 100644 (file)
@@ -61,7 +61,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
       description: "Update the user's display and preferences.",
       operationId: "AccountController.update_credentials",
       security: [%{"oAuth" => ["write:accounts"]}],
-      requestBody: request_body("Parameters", update_creadentials_request(), required: true),
+      requestBody: request_body("Parameters", update_credentials_request(), required: true),
       responses: %{
         200 => Operation.response("Account", "application/json", Account),
         403 => Operation.response("Error", "application/json", ApiError)
@@ -474,7 +474,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
     }
   end
 
-  defp update_creadentials_request do
+  defp update_credentials_request do
     %Schema{
       title: "AccountUpdateCredentialsRequest",
       description: "POST body for creating an account",
@@ -508,6 +508,11 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
           nullable: true,
           description: "Whether manual approval of follow requests is required."
         },
+        accepts_chat_messages: %Schema{
+          allOf: [BooleanLike],
+          nullable: true,
+          description: "Whether the user accepts receiving chat messages."
+        },
         fields_attributes: %Schema{
           nullable: true,
           oneOf: [
index e6f163cb757153dd825dd3d9b545128699eb6d25..cf148bc9d944dea753aed36edd64dd3bf25bd39c 100644 (file)
@@ -103,6 +103,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do
             description:
               "A generic map of settings for frontends. Opaque to the backend. Only returned in `verify_credentials` and `update_credentials`"
           },
+          accepts_chat_messages: %Schema{type: :boolean, nullable: true},
           favicon: %Schema{
             type: :string,
             format: :uri,
@@ -175,6 +176,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do
         "is_admin" => false,
         "is_moderator" => false,
         "skip_thread_containment" => false,
+        "accepts_chat_messages" => true,
         "chat_token" =>
           "SFMyNTY.g3QAAAACZAAEZGF0YW0AAAASOXRLaTNlc2JHN09RZ1oyOTIwZAAGc2lnbmVkbgYARNplS3EB.Mb_Iaqew2bN1I1o79B_iP7encmVCpTKC4OtHZRxdjKc",
         "unread_conversation_count" => 0,
index 7434428558d77ffeb302bd6328664f814995d01d..fe5d022f55fe85e959fecc3006eb4f8d484c1aa5 100644 (file)
@@ -163,7 +163,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
         :show_role,
         :skip_thread_containment,
         :allow_following_move,
-        :discoverable
+        :discoverable,
+        :accepts_chat_messages
       ]
       |> Enum.reduce(%{}, fn key, acc ->
         Maps.put_if_present(acc, key, params[key], &{:ok, truthy_param?(&1)})
index 2feba47782dbb553908a6601a329086a3b56c3bf..bc9745044a4bef61f2b0ae00786797b37f987858 100644 (file)
@@ -258,6 +258,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
         relationship: relationship,
         skip_thread_containment: user.skip_thread_containment,
         background_image: image_url(user.background) |> MediaProxy.url(),
+        accepts_chat_messages: user.accepts_chat_messages,
         favicon: favicon
       }
     }
diff --git a/priv/repo/migrations/20200703101031_add_chat_acceptance_to_users.exs b/priv/repo/migrations/20200703101031_add_chat_acceptance_to_users.exs
new file mode 100644 (file)
index 0000000..8dfda89
--- /dev/null
@@ -0,0 +1,17 @@
+defmodule Pleroma.Repo.Migrations.AddChatAcceptanceToUsers do
+  use Ecto.Migration
+
+  def up do
+    alter table(:users) do
+      add(:accepts_chat_messages, :boolean, nullable: true)
+    end
+
+    execute("update users set accepts_chat_messages = true where local = true")
+  end
+
+  def down do
+    alter table(:users) do
+      remove(:accepts_chat_messages)
+    end
+  end
+end
index 7cc3fee40875f7d8be2c92896da7acf7e4d76fcd..e7722cf726fe2a3b0b3fd3236c04e80808252c38 100644 (file)
@@ -13,6 +13,7 @@
             },
             "discoverable": "toot:discoverable",
             "manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
+            "capabilities": "litepub:capabilities",
             "ostatus": "http://ostatus.org#",
             "schema": "http://schema.org#",
             "toot": "http://joinmastodon.org/ns#",
index 9fdd6557ca26095df858b17b4dd7e6adf58d8aaf..a911b979a21650e3cdff58c4e2bcdc525c3859f1 100644 (file)
@@ -26,6 +26,9 @@
   "summary": "\u003cp\u003e\u003c/p\u003e",
   "url": "http://mastodon.example.org/@admin",
   "manuallyApprovesFollowers": false,
+  "capabilities": {
+    "acceptsChatMessages": true
+  },
   "publicKey": {
     "id": "http://mastodon.example.org/users/admin#main-key",
     "owner": "http://mastodon.example.org/users/admin",
index 7126bb539adbbdd78abe84e3010e7ffcf6f83d3b..9788e09d9b24f5d74bace103993e53c6c6d5797a 100644 (file)
@@ -486,6 +486,15 @@ defmodule Pleroma.UserTest do
     }
     setup do: clear_config([:instance, :account_activation_required], true)
 
+    test "it sets the 'accepts_chat_messages' set to true" do
+      changeset = User.register_changeset(%User{}, @full_user_data)
+      assert changeset.valid?
+
+      {:ok, user} = Repo.insert(changeset)
+
+      assert user.accepts_chat_messages
+    end
+
     test "it creates unconfirmed user" do
       changeset = User.register_changeset(%User{}, @full_user_data)
       assert changeset.valid?
index 38c98f658004ed8e35655890b28d47e921e88cd2..b988e44371de28edc38e6fb4bd2f7945c9711840 100644 (file)
@@ -184,36 +184,43 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
       assert User.invisible?(user)
     end
 
-    test "it fetches the appropriate tag-restricted posts" do
-      user = insert(:user)
+    test "it returns a user that accepts chat messages" do
+      user_id = "http://mastodon.example.org/users/admin"
+      {:ok, user} = ActivityPub.make_user_from_ap_id(user_id)
 
-      {:ok, status_one} = CommonAPI.post(user, %{status: ". #test"})
-      {:ok, status_two} = CommonAPI.post(user, %{status: ". #essais"})
-      {:ok, status_three} = CommonAPI.post(user, %{status: ". #test #reject"})
+      assert user.accepts_chat_messages
+    end
+  end
 
-      fetch_one = ActivityPub.fetch_activities([], %{type: "Create", tag: "test"})
+  test "it fetches the appropriate tag-restricted posts" do
+    user = insert(:user)
 
-      fetch_two = ActivityPub.fetch_activities([], %{type: "Create", tag: ["test", "essais"]})
+    {:ok, status_one} = CommonAPI.post(user, %{status: ". #test"})
+    {:ok, status_two} = CommonAPI.post(user, %{status: ". #essais"})
+    {:ok, status_three} = CommonAPI.post(user, %{status: ". #test #reject"})
 
-      fetch_three =
-        ActivityPub.fetch_activities([], %{
-          type: "Create",
-          tag: ["test", "essais"],
-          tag_reject: ["reject"]
-        })
+    fetch_one = ActivityPub.fetch_activities([], %{type: "Create", tag: "test"})
 
-      fetch_four =
-        ActivityPub.fetch_activities([], %{
-          type: "Create",
-          tag: ["test"],
-          tag_all: ["test", "reject"]
-        })
+    fetch_two = ActivityPub.fetch_activities([], %{type: "Create", tag: ["test", "essais"]})
 
-      assert fetch_one == [status_one, status_three]
-      assert fetch_two == [status_one, status_two, status_three]
-      assert fetch_three == [status_one, status_two]
-      assert fetch_four == [status_three]
-    end
+    fetch_three =
+      ActivityPub.fetch_activities([], %{
+        type: "Create",
+        tag: ["test", "essais"],
+        tag_reject: ["reject"]
+      })
+
+    fetch_four =
+      ActivityPub.fetch_activities([], %{
+        type: "Create",
+        tag: ["test"],
+        tag_all: ["test", "reject"]
+      })
+
+    assert fetch_one == [status_one, status_three]
+    assert fetch_two == [status_one, status_two, status_three]
+    assert fetch_three == [status_one, status_two]
+    assert fetch_four == [status_three]
   end
 
   describe "insertion" do
index ec1e497fa32e59547d67d41217ce88776a5aba85..50bf03515dff31ab30905a920d1bc606e1b655c3 100644 (file)
@@ -161,6 +161,17 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ChatValidationTest do
       refute match?({:ok, _object, _meta}, ObjectValidator.validate(valid_chat_message, []))
     end
 
+    test "does not validate if the recipient is not accepting chat messages", %{
+      valid_chat_message: valid_chat_message,
+      recipient: recipient
+    } do
+      recipient
+      |> Ecto.Changeset.change(%{accepts_chat_messages: false})
+      |> Pleroma.Repo.update!()
+
+      refute match?({:ok, _object, _meta}, ObjectValidator.validate(valid_chat_message, []))
+    end
+
     test "does not validate if the actor or the recipient is not in our system", %{
       valid_chat_message: valid_chat_message
     } do
index bec15a996fd256ba56300402679a1cef60820b41..98c7c9d0966ab5c60b766b9c9dbeeb9863cf60d2 100644 (file)
@@ -158,4 +158,23 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do
       assert %{"totalItems" => 1} = UserView.render("following.json", %{user: user})
     end
   end
+
+  describe "acceptsChatMessages" do
+    test "it returns this value if it is set" do
+      true_user = insert(:user, accepts_chat_messages: true)
+      false_user = insert(:user, accepts_chat_messages: false)
+      nil_user = insert(:user, accepts_chat_messages: nil)
+
+      assert %{"capabilities" => %{"acceptsChatMessages" => true}} =
+               UserView.render("user.json", user: true_user)
+
+      assert %{"capabilities" => %{"acceptsChatMessages" => false}} =
+               UserView.render("user.json", user: false_user)
+
+      refute Map.has_key?(
+               UserView.render("user.json", user: nil_user)["capabilities"],
+               "acceptsChatMessages"
+             )
+    end
+  end
 end
index b55bb76a75bf5bff82ffeb4cdc9078662233000b..638626b45d0e480587696cf205f13ef040e27867 100644 (file)
@@ -108,6 +108,13 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do
       assert user_data["locked"] == true
     end
 
+    test "updates the user's chat acceptance status", %{conn: conn} do
+      conn = patch(conn, "/api/v1/accounts/update_credentials", %{accepts_chat_messages: "false"})
+
+      assert user_data = json_response_and_validate_schema(conn, 200)
+      assert user_data["pleroma"]["accepts_chat_messages"] == false
+    end
+
     test "updates the user's allow_following_move", %{user: user, conn: conn} do
       assert user.allow_following_move == true
 
index f5bfc9c678c6aeb50af32d331ca0dea3d6bd443e..17f035add993fa88603a8846cacf621b4aa62de2 100644 (file)
@@ -90,7 +90,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
         hide_followers_count: false,
         hide_follows_count: false,
         relationship: %{},
-        skip_thread_containment: false
+        skip_thread_containment: false,
+        accepts_chat_messages: nil
       }
     }
 
@@ -186,7 +187,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
         hide_followers_count: false,
         hide_follows_count: false,
         relationship: %{},
-        skip_thread_containment: false
+        skip_thread_containment: false,
+        accepts_chat_messages: nil
       }
     }