Merge branch 'mastoapi-non-html-strings' into 'develop'
authorfeld <feld@feld.me>
Thu, 6 Feb 2020 16:08:23 +0000 (16:08 +0000)
committerfeld <feld@feld.me>
Thu, 6 Feb 2020 16:08:23 +0000 (16:08 +0000)
mastodon API: do not sanitize html in non-html fields

See merge request pleroma/pleroma!2167

41 files changed:
CHANGELOG.md
config/config.exs
config/description.exs
docs/API/admin_api.md
docs/API/differences_in_mastoapi_responses.md
docs/API/pleroma_api.md
docs/admin/config.md [deleted file]
docs/administration/CLI_tasks/config.md
docs/configuration/cheatsheet.md
lib/pleroma/application.ex
lib/pleroma/config/loader.ex
lib/pleroma/config/transfer_task.ex
lib/pleroma/formatter.ex
lib/pleroma/object.ex
lib/pleroma/plugs/http_security_plug.ex
lib/pleroma/plugs/rate_limiter/rate_limiter.ex
lib/pleroma/plugs/remote_ip.ex
lib/pleroma/web/activity_pub/activity_pub.ex
lib/pleroma/web/admin_api/admin_api_controller.ex
lib/pleroma/web/admin_api/views/config_view.ex
lib/pleroma/web/common_api/common_api.ex
lib/pleroma/web/mastodon_api/views/status_view.ex
lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex
lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex
lib/pleroma/web/router.ex
mix.exs
restarter/lib/pleroma.ex [new file with mode: 0644]
restarter/lib/restarter.ex [new file with mode: 0644]
restarter/mix.exs [new file with mode: 0644]
test/config/transfer_task_test.exs
test/fixtures/emoji-reaction-no-emoji.json [new file with mode: 0644]
test/fixtures/emoji-reaction-too-long.json [new file with mode: 0644]
test/object_test.exs
test/plugs/rate_limiter_test.exs
test/web/activity_pub/transmogrifier_test.exs
test/web/admin_api/admin_api_controller_test.exs
test/web/common_api/common_api_test.exs
test/web/mastodon_api/controllers/account_controller_test.exs
test/web/mastodon_api/views/status_view_test.exs
test/web/pleroma_api/controllers/emoji_api_controller_test.exs
test/web/pleroma_api/controllers/pleroma_api_controller_test.exs

index 917e5d4ef26baf0e9427fc38de303013a36a97b3..b470b74ed12b11f37d9c5d3c73203a896d2eeace 100644 (file)
@@ -9,11 +9,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - **Breaking**: OStatus protocol support
 - **Breaking**: MDII uploader
 - **Breaking**: Using third party engines for user recommendation
+<details>
+  <summary>API Changes</summary>
+- **Breaking**: AdminAPI: migrate_from_db endpoint
+</details>
 
 ### Changed
 - **Breaking:** Pleroma won't start if it detects unapplied migrations
-- **Breaking:** attachments are removed along with statuses. Does not affect duplicate files and attachments without status.
 - **Breaking:** Elixir >=1.8 is now required (was >= 1.7)
+- **Breaking:** `Pleroma.Plugs.RemoteIp` and `:rate_limiter` enabled by default. Please ensure your reverse proxy forwards the real IP!
 - **Breaking:** attachment links (`config :pleroma, :instance, no_attachment_links` and `config :pleroma, Pleroma.Upload, link_name`) disabled by default
 - **Breaking:** OAuth: defaulted `[:auth, :enforce_oauth_admin_scope_usage]` setting to `true` which demands `admin` OAuth scope to perform admin actions (in addition to `is_admin` flag on User); make sure to use bundled or newer versions of AdminFE & PleromaFE to access admin / moderator features.
 - **Breaking:** Dynamic configuration has been rearchitected. The `:pleroma, :instance, dynamic_configuration` setting has been replaced with `config :pleroma, configurable_from_database`. Please backup your configuration to a file and run the migration task to ensure consistency with the new schema.
@@ -27,6 +31,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - Deprecated `User.Info` embedded schema (fields moved to `User`)
 - Store status data inside Flag activity
 - Deprecated (reorganized as `UserRelationship` entity) User fields with user AP IDs (`blocks`, `mutes`, `muted_reblogs`, `muted_notifications`, `subscribers`).
+- Rate limiter is now disabled for localhost/socket (unless remoteip plug is enabled)
 - Logger: default log level changed from `warn` to `info`.
 - Config mix task `migrate_to_db` truncates `config` table before migrating the config file.
 <details>
@@ -53,6 +58,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 
 ### Added
 - `:chat_limit` option to limit chat characters.
+- `cleanup_attachments` option to remove attachments along with statuses. Does not affect duplicate files and attachments without status. Enabling this will increase load to database when deleting statuses on larger instances.
 - Refreshing poll results for remote polls
 - Authentication: Added rate limit for password-authorized actions / login existence checks
 - Static Frontend: Add the ability to render user profiles and notices server-side without requiring JS app.
@@ -105,6 +111,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - Mastodon API: Change emoji reaction reply format once more
 - Configuration: `feed.logo` option for tag feed.
 - Tag feed: `/tags/:tag.rss` - list public statuses by hashtag.
+- Mastodon API: Add `reacted` property to `emoji_reactions`
 </details>
 
 ### Fixed
index f4e307e183ad680b822b919dff9a5dd5748376eb..34716cf37fd32268cb83055b4714be0ce5a1f732 100644 (file)
@@ -271,7 +271,8 @@ config :pleroma, :instance,
   account_field_name_length: 512,
   account_field_value_length: 2048,
   external_user_synchronization: true,
-  extended_nickname_format: true
+  extended_nickname_format: true,
+  cleanup_attachments: false
 
 config :pleroma, :feed,
   post_title: %{
@@ -508,7 +509,6 @@ config :pleroma, :fetch_initial_posts,
 
 config :auto_linker,
   opts: [
-    scheme: true,
     extra: true,
     # TODO: Set to :no_scheme when it works properly
     validate_tld: true,
@@ -596,11 +596,21 @@ config :pleroma, :env, Mix.env()
 config :http_signatures,
   adapter: Pleroma.Signature
 
-config :pleroma, :rate_limit, authentication: {60_000, 15}
+config :pleroma, :rate_limit,
+  authentication: {60_000, 15},
+  search: [{1000, 10}, {1000, 30}],
+  app_account_creation: {1_800_000, 25},
+  relations_actions: {10_000, 10},
+  relation_id_action: {60_000, 2},
+  statuses_actions: {10_000, 15},
+  status_id_action: {60_000, 3},
+  password_reset: {1_800_000, 5},
+  account_confirmation_resend: {8_640_000, 5},
+  ap_routes: {60_000, 15}
 
 config :pleroma, Pleroma.ActivityExpiration, enabled: true
 
-config :pleroma, Pleroma.Plugs.RemoteIp, enabled: false
+config :pleroma, Pleroma.Plugs.RemoteIp, enabled: true
 
 config :pleroma, :static_fe, enabled: false
 
index 5f3c58b08dbc4bbc62f99cf351e69a0091c7f137..6b912a07e3e190ebed40b1567f53d7fa705d104e 100644 (file)
@@ -764,6 +764,15 @@ config :pleroma, :config_description, [
           "Set to `true` to use extended local nicknames format (allows underscores/dashes)." <>
             " This will break federation with older software for theses nicknames."
       },
+      %{
+        key: :cleanup_attachments,
+        type: :boolean,
+        description: """
+        "Set to `true` to remove associated attachments when status is removed.
+        This will not affect duplicates and attachments without status.
+        Enabling this will increase load to database when deleting statuses on larger instances.
+        """
+      },
       %{
         key: :max_pinned_statuses,
         type: :integer,
@@ -862,7 +871,7 @@ config :pleroma, :config_description, [
       },
       %{
         key: :limit_to_local_content,
-        type: [:atom, false],
+        type: {:dropdown, :atom},
         description:
           "Limit unauthenticated users to search for local statutes and users only. Default: `:unauthenticated`.",
         suggestions: [
@@ -933,7 +942,7 @@ config :pleroma, :config_description, [
     children: [
       %{
         key: :level,
-        type: :atom,
+        type: {:dropdown, :atom},
         description: "Log level",
         suggestions: [:debug, :info, :warn, :error]
       },
@@ -965,7 +974,7 @@ config :pleroma, :config_description, [
     children: [
       %{
         key: :level,
-        type: :atom,
+        type: {:dropdown, :atom},
         description: "Log level",
         suggestions: [:debug, :info, :warn, :error]
       },
@@ -989,7 +998,7 @@ config :pleroma, :config_description, [
     children: [
       %{
         key: :level,
-        type: :atom,
+        type: {:dropdown, :atom},
         description: "Log level",
         suggestions: [:debug, :info, :warn, :error]
       },
@@ -1960,7 +1969,7 @@ config :pleroma, :config_description, [
       },
       %{
         key: :verbose,
-        type: [:atom, false],
+        type: {:dropdown, :atom},
         description: "Logs verbose mode",
         suggestions: [false, :error, :warn, :info, :debug]
       },
@@ -2169,12 +2178,7 @@ config :pleroma, :config_description, [
       %{
         key: :new_window,
         type: :boolean,
-        description: "Set to `false` to remove target='_blank' attribute"
-      },
-      %{
-        key: :scheme,
-        type: :boolean,
-        description: "Set to `true` to link urls with schema http://google.com"
+        description: "Link urls will open in new window/tab"
       },
       %{
         key: :truncate,
index 07aa7ec3f56f22cd37b0e43de9656e7a7393e38a..fb6dfcb087d48383bcd8c90b80a0c8257a653bd8 100644 (file)
@@ -665,11 +665,9 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
     - 404 Not Found `"Not found"`
   - On success: 200 OK `{}`
 
-## `GET /api/pleroma/admin/config/migrate_from_db`
+## `GET /api/pleroma/admin/restart`
 
-### Run mix task pleroma.config migrate_from_db
-
-Copies all settings from database to `config/{env}.exported_from_db.secret.exs` with deletion from the table. Where `{env}` is the environment in which `pleroma` is running.
+### Restarts pleroma application
 
 - Params: none
 - Response:
@@ -691,7 +689,6 @@ Copies all settings from database to `config/{env}.exported_from_db.secret.exs`
 - Response:
   - On failure:
     - 400 Bad Request `"To use this endpoint you need to enable configuration from database."`
-    - 400 Bad Request `"To use configuration from database migrate your settings to database."`
 
 ```json
 {
index 030660b346b401c84317bb1840a1313b2b6242d6..82d967e4de475d04f84a36927e12742d639bf575 100644 (file)
@@ -29,7 +29,7 @@ Has these additional fields under the `pleroma` object:
 - `spoiler_text`: a map consisting of alternate representations of the `spoiler_text` property with the key being it's mimetype. Currently the only alternate representation supported is `text/plain`
 - `expires_at`: a datetime (iso8601) that states when the post will expire (be deleted automatically), or empty if the post won't expire
 - `thread_muted`: true if the thread the post belongs to is muted
-- `emoji_reactions`: A list with emoji / reaction maps. The format is {emoji: "☕", count: 1}. Contains no information about the reacting users, for that use the `emoji_reactions_by` endpoint.
+- `emoji_reactions`: A list with emoji / reaction maps. The format is `{emoji: "☕", count: 1, reacted: true}`. Contains no information about the reacting users, for that use the `emoji_reactions_by` endpoint.
 
 ## Attachments
 
index 9f5cafe5a0145f002d615524d769cceaf7eb4093..c7125c1cd8d44b8302e1da98cd6870f09bd364ff 100644 (file)
@@ -455,7 +455,7 @@ Emoji reactions work a lot like favourites do. They make it possible to react to
 * Example Response:
 ```json
 [
-  {"emoji": "😀", "count": 2, "accounts": [{"id" => "xyz.."...}, {"id" => "zyx..."}]},
-  {"emoji": "☕", "count": 1, "accounts": [{"id" => "abc..."}]}
+  {"emoji": "😀", "count": 2, "reacted": true, "accounts": [{"id" => "xyz.."...}, {"id" => "zyx..."}]},
+  {"emoji": "☕", "count": 1, "reacted": false, "accounts": [{"id" => "abc..."}]}
 ]
 ```
diff --git a/docs/admin/config.md b/docs/admin/config.md
deleted file mode 100644 (file)
index 35e43b6..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-# Configuring instance
-You can configure your instance from admin interface. You need account with admin rights and little change in config file, which will allow settings configuration from database.
-
-```elixir
-config :pleroma, configurable_from_database: true
-```
-
-## How it works
-Settings are stored in database and are applied in `runtime` after each change. Most of the settings take effect immediately, except some, which need instance reboot. These settings are needed in `compile time`, that's why settings are duplicated to the file.
-
-File with duplicated settings is located in `config/{env}.exported_from_db.exs` if pleroma is runned from source. For prod env it will be `config/prod.exported_from_db.exs`.
-
-For releases: `/etc/pleroma/prod.exported_from_db.secret.exs` or `PLEROMA_CONFIG_PATH/prod.exported_from_db.exs`.
-
-## How to set it up
-You need to migrate your existing settings to the database. This task will migrate only added by user settings.
-For example you add settings to `prod.secret.exs` file, only these settings will be migrated to database. For release it will be `/etc/pleroma/config.exs` or `PLEROMA_CONFIG_PATH`.
-You can do this with mix task (all config files will remain untouched):
-
-```sh tab="OTP"
- ./bin/pleroma_ctl config migrate_to_db
-```
-
-```sh tab="From Source"
-mix pleroma.config migrate_to_db
-```
-
-Now you can change settings in admin interface. After each save, settings from database are duplicated to the `config/{env}.exported_from_db.exs` file.
-
-<span style="color:red">**ATTENTION**</span>
-
-**<span style="color:red">Be careful while changing the settings. Every inaccurate configuration change can break the federation or the instance load.</span>**
-
-*Compile time settings, which require instance reboot and can break instance loading:*
-- all settings inside these keys:
-  - `:hackney_pools`
-  - `:chat`
-- partially settings inside these keys:
-  - `:seconds_valid` in `Pleroma.Captcha`
-  - `:proxy_remote` in `Pleroma.Upload`
-  - `:upload_limit` in `:instance`
-
-## How to dump settings from database to file
-
-*Adding `-d` flag will delete migrated settings from database table.*
-
-```sh tab="OTP"
- ./bin/pleroma_ctl config migrate_from_db [-d]
-```
-
-```sh tab="From Source"
-mix pleroma.config migrate_from_db [-d]
-```
-
-
-## How to completely remove it
-
-1. Truncate or delete all values from `config` table
-```sql
-TRUNCATE TABLE config;
-```
-2. Delete `config/{env}.exported_from_db.exs`.
-
-For `prod` env:
-```bash
-cd /opt/pleroma
-cp config/prod.exported_from_db.exs config/exported_from_db.back
-rm -rf config/prod.exported_from_db.exs
-```
-*If you don't want to backup settings, you can skip step with `cp` command.*
-
-3. Set configurable_from_database to `false`.
-```elixir
-config :pleroma, configurable_from_database: false
-```
-4. Restart pleroma instance
-```bash
-sudo service pleroma restart
-```
index 2af51c247957ff45f668053065e554ad6e302853..873775962faf1c22ca191928576fb48aea7f0725 100644 (file)
@@ -1,12 +1,16 @@
 # Transfering the config to/from the database
 
-!!! danger
-    This is a Work In Progress, not usable just yet.
-
 {! backend/administration/CLI_tasks/general_cli_task_info.include !}
 
 ## Transfer config from file to DB.
 
+!!! note
+    You need to add the following to your config before executing this command:
+
+    ```elixir
+    config :pleroma, configurable_from_database: true
+    ```
+
 ```sh tab="OTP"
  ./bin/pleroma_ctl config migrate_to_db
 ```
@@ -18,7 +22,15 @@ mix pleroma.config migrate_to_db
 
 ## Transfer config from DB to `config/env.exported_from_db.secret.exs`
 
+!!! note
+    In-Database configuration will still be applied after executing this command unless you set the following in your config:
+
+    ```elixir
+    config :pleroma, configurable_from_database: false
+    ```
+
 To delete transfered settings from database optional flag `-d` can be used. <env> is `prod` by default.
+
 ```sh tab="OTP"
  ./bin/pleroma_ctl config migrate_from_db [--env=<env>] [-d]
 ```
index 30d673ebad156d30b8e06cd3ec15b86bc0f5cb04..f30aedc01ee832c7a79236f9ce644381b363661b 100644 (file)
@@ -69,6 +69,7 @@ You shouldn't edit the base config directly to avoid breakages and merge conflic
 * `account_field_name_length`: An account field name maximum length (default: `512`).
 * `account_field_value_length`: An account field value maximum length (default: `2048`).
 * `external_user_synchronization`: Enabling following/followers counters synchronization for external users.
+* `cleanup_attachments`: Remove attachments along with statuses. Does not affect duplicate files and attachments without status. Enabling this will increase load to database when deleting statuses on larger instances.
 
 ## Federation
 ### MRF policies
@@ -308,16 +309,15 @@ This will make Pleroma listen on `127.0.0.1` port `8080` and generate urls start
 Available options:
 
 * `enabled` - Enable/disable the plug. Defaults to `false`.
-* `headers` - A list of strings naming the `req_headers` to use when deriving the `remote_ip`. Order does not matter. Defaults to `~w[forwarded x-forwarded-for x-client-ip x-real-ip]`.
+* `headers` - A list of strings naming the `req_headers` to use when deriving the `remote_ip`. Order does not matter. Defaults to `["x-forwarded-for"]`.
 * `proxies` - A list of strings in [CIDR](https://en.wikipedia.org/wiki/CIDR) notation specifying the IPs of known proxies. Defaults to `[]`.
 * `reserved` - Defaults to [localhost](https://en.wikipedia.org/wiki/Localhost) and [private network](https://en.wikipedia.org/wiki/Private_network).
 
 
 ### :rate_limit
 
-This is an advanced feature and disabled by default.
-
-If your instance is behind a reverse proxy you must enable and configure [`Pleroma.Plugs.RemoteIp`](#pleroma-plugs-remoteip).
+!!! note
+   If your instance is behind a reverse proxy ensure [`Pleroma.Plugs.RemoteIp`](#pleroma-plugs-remoteip) is enabled (it is enabled by default).
 
 A keyword list of rate limiters where a key is a limiter name and value is the limiter configuration. The basic configuration is a tuple where:
 
@@ -326,14 +326,31 @@ A keyword list of rate limiters where a key is a limiter name and value is the l
 
 It is also possible to have different limits for unauthenticated and authenticated users: the keyword value must be a list of two tuples where the first one is a config for unauthenticated users and the second one is for authenticated.
 
+For example:
+
+```elixir
+config :pleroma, :rate_limit,
+  authentication: {60_000, 15},
+  search: [{1000, 10}, {1000, 30}]
+```
+
+Means that:
+
+1. In 60 seconds, 15 authentication attempts can be performed from the same IP address.
+2. In 1 second, 10 search requests can be performed from the same IP adress by unauthenticated users, while authenticated users can perform 30 search requests per second.
+
 Supported rate limiters:
 
-* `:search` for the search requests (account & status search etc.)
-* `:app_account_creation` for registering user accounts from the same IP address
-* `:relations_actions` for actions on relations with all users (follow, unfollow)
-* `:relation_id_action` for actions on relation with a specific user (follow, unfollow)
-* `:statuses_actions` for create / delete / fav / unfav / reblog / unreblog actions on any statuses
-* `:status_id_action` for fav / unfav or reblog / unreblog actions on the same status by the same user
+* `:search` - Account/Status search.
+* `:app_account_creation` - Account registration from the API.
+* `:relations_actions` - Following/Unfollowing in general.
+* `:relation_id_action` - Following/Unfollowing for a specific user.
+* `:statuses_actions` - Status actions such as: (un)repeating, (un)favouriting, creating, deleting.
+* `:status_id_action` - (un)Repeating/(un)Favouriting a particular status.
+* `:authentication` - Authentication actions, i.e getting an OAuth token.
+* `:password_reset` - Requesting password reset emails.
+* `:account_confirmation_resend` - Requesting resending account confirmation emails.
+* `:ap_routes` - Requesting statuses via ActivityPub.
 
 ### :web_cache_ttl
 
@@ -839,4 +856,5 @@ config :auto_linker,
 
 
 ## :configurable_from_database
-Enable/disable configuration from database.
+
+Boolean, enables/disables in-database configuration. Read [Transfering the config to/from the database](../administration/CLI_tasks/config.md) for more information.
index e1706887671fab475c6cd8517e05071c9091cc37..2c8889ce58886fa502c04af9248aca4c42817219 100644 (file)
@@ -33,6 +33,7 @@ defmodule Pleroma.Application do
   def start(_type, _args) do
     Pleroma.HTML.compile_scrubbers()
     Pleroma.Config.DeprecationWarnings.warn()
+    Pleroma.Plugs.HTTPSecurityPlug.warn_if_disabled()
     Pleroma.Repo.check_migrations_applied!()
     setup_instrumenters()
     load_custom_modules()
index 68b247381d9680c00e876c83d07bc63a66263923..b8787cb49ad44213e20e4d0eca7c11ed0d4e3ce6 100644 (file)
@@ -3,8 +3,6 @@
 # SPDX-License-Identifier: AGPL-3.0-only
 
 defmodule Pleroma.Config.Loader do
-  @paths ["config/config.exs", "config/#{Mix.env()}.exs"]
-
   @reject_keys [
     Pleroma.Repo,
     Pleroma.Web.Endpoint,
@@ -35,8 +33,8 @@ defmodule Pleroma.Config.Loader do
   def load_and_merge do
     all_paths =
       if Pleroma.Config.get(:release),
-        do: @paths ++ ["config/releases.exs"],
-        else: @paths
+        do: ["config/config.exs", "config/releases.exs"],
+        else: ["config/config.exs"]
 
     all_paths
     |> Enum.map(&load(&1))
index d54f38ee4c022db43703b638a3de24317294474f..6c5ba1f95ca040107bcf026c904ed6467e530e3d 100644 (file)
@@ -10,6 +10,30 @@ defmodule Pleroma.Config.TransferTask do
 
   require Logger
 
+  @type env() :: :test | :benchmark | :dev | :prod
+
+  @reboot_time_keys [
+    {:pleroma, :hackney_pools},
+    {:pleroma, :chat},
+    {:pleroma, Oban},
+    {:pleroma, :rate_limit},
+    {:pleroma, :markup},
+    {:plerome, :streamer}
+  ]
+
+  @reboot_time_subkeys [
+    {:pleroma, Pleroma.Captcha, [:seconds_valid]},
+    {:pleroma, Pleroma.Upload, [:proxy_remote]},
+    {:pleroma, :instance, [:upload_limit]},
+    {:pleroma, :email_notifications, [:digest]},
+    {:pleroma, :oauth2, [:clean_expired_tokens]},
+    {:pleroma, Pleroma.ActivityExpiration, [:enabled]},
+    {:pleroma, Pleroma.ScheduledActivity, [:enabled]},
+    {:pleroma, :gopher, [:enabled]}
+  ]
+
+  @reject [nil, :prometheus]
+
   def start_link(_) do
     load_and_update_env()
     if Pleroma.Config.get(:env) == :test, do: Ecto.Adapters.SQL.Sandbox.checkin(Repo)
@@ -17,21 +41,34 @@ defmodule Pleroma.Config.TransferTask do
   end
 
   @spec load_and_update_env([ConfigDB.t()]) :: :ok | false
-  def load_and_update_env(deleted \\ []) do
+  def load_and_update_env(deleted \\ [], restart_pleroma? \\ true) do
     with true <- Pleroma.Config.get(:configurable_from_database),
          true <- Ecto.Adapters.SQL.table_exists?(Repo, "config"),
          started_applications <- Application.started_applications() do
       # We need to restart applications for loaded settings take effect
+
       in_db = Repo.all(ConfigDB)
 
       with_deleted = in_db ++ deleted
 
-      with_deleted
-      |> Enum.map(&merge_and_update(&1))
-      |> Enum.uniq()
-      # TODO: some problem with prometheus after restart!
-      |> Enum.reject(&(&1 in [:pleroma, nil, :prometheus]))
-      |> Enum.each(&restart(started_applications, &1))
+      reject_for_restart = if restart_pleroma?, do: @reject, else: [:pleroma | @reject]
+
+      applications =
+        with_deleted
+        |> Enum.map(&merge_and_update(&1))
+        |> Enum.uniq()
+        # TODO: some problem with prometheus after restart!
+        |> Enum.reject(&(&1 in reject_for_restart))
+
+      # to be ensured that pleroma will be restarted last
+      applications =
+        if :pleroma in applications do
+          List.delete(applications, :pleroma) ++ [:pleroma]
+        else
+          applications
+        end
+
+      Enum.each(applications, &restart(started_applications, &1, Pleroma.Config.get(:env)))
 
       :ok
     end
@@ -43,12 +80,25 @@ defmodule Pleroma.Config.TransferTask do
       group = ConfigDB.from_string(setting.group)
 
       default = Pleroma.Config.Holder.config(group, key)
-      merged_value = merge_value(setting, default, group, key)
+      value = ConfigDB.from_binary(setting.value)
+
+      merged_value =
+        if Ecto.get_meta(setting, :state) == :deleted do
+          default
+        else
+          if can_be_merged?(default, value) do
+            ConfigDB.merge_group(group, key, default, value)
+          else
+            value
+          end
+        end
 
       :ok = update_env(group, key, merged_value)
 
       if group != :logger do
-        group
+        if group != :pleroma or pleroma_need_restart?(group, key, value) do
+          group
+        end
       else
         # change logger configuration in runtime, without restart
         if Keyword.keyword?(merged_value) and
@@ -76,22 +126,31 @@ defmodule Pleroma.Config.TransferTask do
     end
   end
 
-  defp merge_value(%{__meta__: %{state: :deleted}}, default, _group, _key), do: default
+  @spec pleroma_need_restart?(atom(), atom(), any()) :: boolean()
+  def pleroma_need_restart?(group, key, value) do
+    group_and_key_need_reboot?(group, key) or group_and_subkey_need_reboot?(group, key, value)
+  end
 
-  defp merge_value(setting, default, group, key) do
-    value = ConfigDB.from_binary(setting.value)
+  defp group_and_key_need_reboot?(group, key) do
+    Enum.any?(@reboot_time_keys, fn {g, k} -> g == group and k == key end)
+  end
 
-    if can_be_merged?(default, value) do
-      ConfigDB.merge_group(group, key, default, value)
-    else
-      value
-    end
+  defp group_and_subkey_need_reboot?(group, key, value) do
+    Keyword.keyword?(value) and
+      Enum.any?(@reboot_time_subkeys, fn {g, k, subkeys} ->
+        g == group and k == key and
+          Enum.any?(Keyword.keys(value), &(&1 in subkeys))
+      end)
   end
 
   defp update_env(group, key, nil), do: Application.delete_env(group, key)
   defp update_env(group, key, value), do: Application.put_env(group, key, value)
 
-  defp restart(started_applications, app) do
+  defp restart(_, :pleroma, :test), do: Logger.warn("pleroma restarted")
+
+  defp restart(_, :pleroma, _), do: send(Restarter.Pleroma, :after_boot)
+
+  defp restart(started_applications, app, _) do
     with {^app, _, _} <- List.keyfind(started_applications, app, 0),
          :ok <- Application.stop(app) do
       :ok = Application.start(app)
index 19b9af46cc3bb48613c3e5e52b1d9f32f591c041..90895374daa9509ab52766671b0814bb851c9b08 100644 (file)
@@ -13,7 +13,8 @@ defmodule Pleroma.Formatter do
   @auto_linker_config hashtag: true,
                       hashtag_handler: &Pleroma.Formatter.hashtag_handler/4,
                       mention: true,
-                      mention_handler: &Pleroma.Formatter.mention_handler/4
+                      mention_handler: &Pleroma.Formatter.mention_handler/4,
+                      scheme: true
 
   def escape_mention_handler("@" <> nickname = mention, buffer, _, _) do
     case User.get_cached_by_nickname(nickname) do
index 38e372f6ddf6817dc04615a0f014d279ddc90eaa..52556bf31546a3ff74f02663d6d2a39a6d2261f9 100644 (file)
@@ -184,11 +184,14 @@ defmodule Pleroma.Object do
     with {:ok, _obj} = swap_object_with_tombstone(object),
          deleted_activity = Activity.delete_all_by_object_ap_id(id),
          {:ok, true} <- Cachex.del(:object_cache, "object:#{id}"),
-         {:ok, _} <- Cachex.del(:web_resp_cache, URI.parse(id).path),
-         {:ok, _} <-
-           Pleroma.Workers.AttachmentsCleanupWorker.enqueue("cleanup_attachments", %{
-             "object" => object
-           }) do
+         {:ok, _} <- Cachex.del(:web_resp_cache, URI.parse(id).path) do
+      with true <- Pleroma.Config.get([:instance, :cleanup_attachments]) do
+        {:ok, _} =
+          Pleroma.Workers.AttachmentsCleanupWorker.enqueue("cleanup_attachments", %{
+            "object" => object
+          })
+      end
+
       {:ok, object, deleted_activity}
     end
   end
index a7cc228318af06514ae7a74ca15d3478d56816c7..b042739799d78933702f30c36842ec2ce69c37b4 100644 (file)
@@ -6,6 +6,8 @@ defmodule Pleroma.Plugs.HTTPSecurityPlug do
   alias Pleroma.Config
   import Plug.Conn
 
+  require Logger
+
   def init(opts), do: opts
 
   def call(conn, _options) do
@@ -90,6 +92,51 @@ defmodule Pleroma.Plugs.HTTPSecurityPlug do
     |> Enum.join("; ")
   end
 
+  def warn_if_disabled do
+    unless Config.get([:http_security, :enabled]) do
+      Logger.warn("
+                                 .i;;;;i.
+                               iYcviii;vXY:
+                             .YXi       .i1c.
+                            .YC.     .    in7.
+                           .vc.   ......   ;1c.
+                           i7,   ..        .;1;
+                          i7,   .. ...      .Y1i
+                         ,7v     .6MMM@;     .YX,
+                        .7;.   ..IMMMMMM1     :t7.
+                       .;Y.     ;$MMMMMM9.     :tc.
+                       vY.   .. .nMMM@MMU.      ;1v.
+                      i7i   ...  .#MM@M@C. .....:71i
+                     it:   ....   $MMM@9;.,i;;;i,;tti
+                    :t7.  .....   0MMMWv.,iii:::,,;St.
+                   .nC.   .....   IMMMQ..,::::::,.,czX.
+                  .ct:   ....... .ZMMMI..,:::::::,,:76Y.
+                  c2:   ......,i..Y$M@t..:::::::,,..inZY
+                 vov   ......:ii..c$MBc..,,,,,,,,,,..iI9i
+                i9Y   ......iii:..7@MA,..,,,,,,,,,....;AA:
+               iIS.  ......:ii::..;@MI....,............;Ez.
+              .I9.  ......:i::::...8M1..................C0z.
+             .z9;  ......:i::::,.. .i:...................zWX.
+             vbv  ......,i::::,,.      ................. :AQY
+            c6Y.  .,...,::::,,..:t0@@QY. ................ :8bi
+           :6S. ..,,...,:::,,,..EMMMMMMI. ............... .;bZ,
+          :6o,  .,,,,..:::,,,..i#MMMMMM#v.................  YW2.
+         .n8i ..,,,,,,,::,,,,.. tMMMMM@C:.................. .1Wn
+         7Uc. .:::,,,,,::,,,,..   i1t;,..................... .UEi
+         7C...::::::::::::,,,,..        ....................  vSi.
+         ;1;...,,::::::,.........       ..................    Yz:
+          v97,.........                                     .voC.
+           izAotX7777777777777777777777777777777777777777Y7n92:
+             .;CoIIIIIUAA666666699999ZZZZZZZZZZZZZZZZZZZZ6ov.
+
+HTTP Security is disabled. Please re-enable it to prevent users from attacking
+your instance and your users via malicious posts:
+
+      config :pleroma, :http_security, enabled: true
+      ")
+    end
+  end
+
   defp maybe_send_sts_header(conn, true) do
     max_age_sts = Config.get([:http_security, :sts_max_age])
     max_age_ct = Config.get([:http_security, :ct_max_age])
index d720508c8aafa12bdd14776a81b4be9f4598cc9e..7fb92489c4ff2822dea813337ee61e147a0105f1 100644 (file)
@@ -67,6 +67,8 @@ defmodule Pleroma.Plugs.RateLimiter do
   alias Pleroma.Plugs.RateLimiter.LimiterSupervisor
   alias Pleroma.User
 
+  require Logger
+
   def init(opts) do
     limiter_name = Keyword.get(opts, :name)
 
@@ -89,18 +91,39 @@ defmodule Pleroma.Plugs.RateLimiter do
   def call(conn, nil), do: conn
 
   def call(conn, settings) do
-    settings
-    |> incorporate_conn_info(conn)
-    |> check_rate()
-    |> case do
-      {:ok, _count} ->
+    case disabled?() do
+      true ->
+        if Pleroma.Config.get(:env) == :prod,
+          do: Logger.warn("Rate limiter is disabled for localhost/socket")
+
         conn
 
-      {:error, _count} ->
-        render_throttled_error(conn)
+      false ->
+        settings
+        |> incorporate_conn_info(conn)
+        |> check_rate()
+        |> case do
+          {:ok, _count} ->
+            conn
+
+          {:error, _count} ->
+            render_throttled_error(conn)
+        end
     end
   end
 
+  def disabled? do
+    localhost_or_socket =
+      Pleroma.Config.get([Pleroma.Web.Endpoint, :http, :ip])
+      |> Tuple.to_list()
+      |> Enum.join(".")
+      |> String.match?(~r/^local|^127.0.0.1/)
+
+    remote_ip_disabled = not Pleroma.Config.get([Pleroma.Plugs.RemoteIp, :enabled])
+
+    localhost_or_socket and remote_ip_disabled
+  end
+
   def inspect_bucket(conn, name_root, settings) do
     settings =
       settings
index fdedc27eed6dcf8b2bf7aa7cb5d2bc4235a7bff4..1cd5af48adeecf7f9a5f09edb90f08ee507d48cd 100644 (file)
@@ -10,10 +10,7 @@ defmodule Pleroma.Plugs.RemoteIp do
   @behaviour Plug
 
   @headers ~w[
-    forwarded
     x-forwarded-for
-    x-client-ip
-    x-real-ip
   ]
 
   # https://en.wikipedia.org/wiki/Localhost
index 1ac67b618c84fc8675efcf591bb7fbb686ddcd87..5c436941ae7aa8f6eea72cdb42256298bca69bfb 100644 (file)
@@ -325,12 +325,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
   def react_with_emoji(user, object, emoji, options \\ []) do
     with local <- Keyword.get(options, :local, true),
          activity_id <- Keyword.get(options, :activity_id, nil),
-         Pleroma.Emoji.is_unicode_emoji?(emoji),
+         true <- Pleroma.Emoji.is_unicode_emoji?(emoji),
          reaction_data <- make_emoji_reaction_data(user, object, emoji, activity_id),
          {:ok, activity} <- insert(reaction_data, local),
          {:ok, object} <- add_emoji_reaction_to_object(activity, object),
          :ok <- maybe_federate(activity) do
       {:ok, activity, object}
+    else
+      e -> {:error, e}
     end
   end
 
@@ -345,6 +347,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
          {:ok, object} <- remove_emoji_reaction_from_object(reaction_activity, object),
          :ok <- maybe_federate(activity) do
       {:ok, activity, object}
+    else
+      e -> {:error, e}
     end
   end
 
index 2314d32741fe0c42dbc6d979b364aa0dd103e5cd..c95cd182dddbc79a1f199477e21b506799c26cf3 100644 (file)
@@ -97,7 +97,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
   plug(
     OAuthScopesPlug,
     %{scopes: ["read"], admin: true}
-    when action in [:config_show, :migrate_from_db, :list_log]
+    when action in [:config_show, :list_log]
   )
 
   plug(
@@ -793,33 +793,13 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
     |> Plug.Conn.send_resp(200, @descriptions_json)
   end
 
-  def migrate_from_db(conn, _params) do
-    with :ok <- configurable_from_database(conn) do
-      Mix.Tasks.Pleroma.Config.run([
-        "migrate_from_db",
-        "--env",
-        to_string(Pleroma.Config.get(:env)),
-        "-d"
-      ])
-
-      json(conn, %{})
-    end
-  end
-
   def config_show(conn, %{"only_db" => true}) do
     with :ok <- configurable_from_database(conn) do
       configs = Pleroma.Repo.all(ConfigDB)
 
-      if configs == [] do
-        errors(
-          conn,
-          {:error, "To use configuration from database migrate your settings to database."}
-        )
-      else
-        conn
-        |> put_view(ConfigView)
-        |> render("index.json", %{configs: configs})
-      end
+      conn
+      |> put_view(ConfigView)
+      |> render("index.json", %{configs: configs})
     end
   end
 
@@ -827,45 +807,38 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
     with :ok <- configurable_from_database(conn) do
       configs = ConfigDB.get_all_as_keyword()
 
-      if configs == [] do
-        errors(
-          conn,
-          {:error, "To use configuration from database migrate your settings to database."}
-        )
-      else
-        merged =
-          Pleroma.Config.Holder.config()
-          |> ConfigDB.merge(configs)
-          |> Enum.map(fn {group, values} ->
-            Enum.map(values, fn {key, value} ->
-              db =
-                if configs[group][key] do
-                  ConfigDB.get_db_keys(configs[group][key], key)
-                end
-
-              db_value = configs[group][key]
-
-              merged_value =
-                if !is_nil(db_value) and Keyword.keyword?(db_value) and
-                     ConfigDB.sub_key_full_update?(group, key, Keyword.keys(db_value)) do
-                  ConfigDB.merge_group(group, key, value, db_value)
-                else
-                  value
-                end
-
-              setting = %{
-                group: ConfigDB.convert(group),
-                key: ConfigDB.convert(key),
-                value: ConfigDB.convert(merged_value)
-              }
-
-              if db, do: Map.put(setting, :db, db), else: setting
-            end)
+      merged =
+        Pleroma.Config.Holder.config()
+        |> ConfigDB.merge(configs)
+        |> Enum.map(fn {group, values} ->
+          Enum.map(values, fn {key, value} ->
+            db =
+              if configs[group][key] do
+                ConfigDB.get_db_keys(configs[group][key], key)
+              end
+
+            db_value = configs[group][key]
+
+            merged_value =
+              if !is_nil(db_value) and Keyword.keyword?(db_value) and
+                   ConfigDB.sub_key_full_update?(group, key, Keyword.keys(db_value)) do
+                ConfigDB.merge_group(group, key, value, db_value)
+              else
+                value
+              end
+
+            setting = %{
+              group: ConfigDB.convert(group),
+              key: ConfigDB.convert(key),
+              value: ConfigDB.convert(merged_value)
+            }
+
+            if db, do: Map.put(setting, :db, db), else: setting
           end)
-          |> List.flatten()
+        end)
+        |> List.flatten()
 
-        json(conn, %{configs: merged})
-      end
+      json(conn, %{configs: merged})
     end
   end
 
@@ -890,17 +863,36 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
           Ecto.get_meta(config, :state) == :deleted
         end)
 
-      Pleroma.Config.TransferTask.load_and_update_env(deleted)
+      Pleroma.Config.TransferTask.load_and_update_env(deleted, false)
+
+      need_reboot? =
+        Enum.any?(updated, fn config ->
+          group = ConfigDB.from_string(config.group)
+          key = ConfigDB.from_string(config.key)
+          value = ConfigDB.from_binary(config.value)
+          Pleroma.Config.TransferTask.pleroma_need_restart?(group, key, value)
+        end)
+
+      response = %{configs: updated}
 
-      Mix.Tasks.Pleroma.Config.run([
-        "migrate_from_db",
-        "--env",
-        to_string(Pleroma.Config.get(:env))
-      ])
+      response =
+        if need_reboot?, do: Map.put(response, :need_reboot, need_reboot?), else: response
 
       conn
       |> put_view(ConfigView)
-      |> render("index.json", %{configs: updated})
+      |> render("index.json", response)
+    end
+  end
+
+  def restart(conn, _params) do
+    with :ok <- configurable_from_database(conn) do
+      if Pleroma.Config.get(:env) == :test do
+        Logger.warn("pleroma restarted")
+      else
+        send(Restarter.Pleroma, {:restart, 50})
+      end
+
+      json(conn, %{})
     end
   end
 
index 23d97e847bb6f826104dd4a85c499bcbcc0c089a..bbb53efcd6f3ad214c8e8000718afddf7f1a7b75 100644 (file)
@@ -5,10 +5,16 @@
 defmodule Pleroma.Web.AdminAPI.ConfigView do
   use Pleroma.Web, :view
 
-  def render("index.json", %{configs: configs}) do
-    %{
+  def render("index.json", %{configs: configs} = params) do
+    map = %{
       configs: render_many(configs, __MODULE__, "show.json", as: :config)
     }
+
+    if params[:need_reboot] do
+      Map.put(map, :need_reboot, true)
+    else
+      map
+    end
   end
 
   def render("show.json", %{config: config}) do
index c05a6c544f3f3eb2ff7578ceda844224c8289c9e..2a348dcf6a9459593faeb7aa2beb6009ba8d8ca5 100644 (file)
@@ -315,8 +315,9 @@ defmodule Pleroma.Web.CommonAPI do
     with %Activity{
            actor: ^user_ap_id,
            data: %{"type" => "Create"},
-           object: %Object{data: %{"type" => "Note"}}
+           object: %Object{data: %{"type" => object_type}}
          } = activity <- get_by_id_or_ap_id(id_or_ap_id),
+         true <- object_type in ["Note", "Article", "Question"],
          true <- Visibility.is_public?(activity),
          {:ok, _user} <- User.add_pinnned_activity(user, activity) do
       {:ok, activity}
index 721e9f5660cb3299116f989f54645d5416b8f27f..6cb158bbfedaec737e1ede58975a053dd4b116fb 100644 (file)
@@ -241,7 +241,11 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
     emoji_reactions =
       with %{data: %{"reactions" => emoji_reactions}} <- object do
         Enum.map(emoji_reactions, fn [emoji, users] ->
-          %{emoji: emoji, count: length(users)}
+          %{
+            emoji: emoji,
+            count: length(users),
+            reacted: !!(opts[:for] && opts[:for].ap_id in users)
+          }
         end)
       else
         _ -> []
index 0bbf84fd3706d2dc9c904af8a0ae86afc5442eda..a2f6d2287f5a4f2966f2977532f979e0c22ba9d0 100644 (file)
@@ -573,11 +573,14 @@ keeping it in cache for #{div(cache_ms, 1000)}s")
   assumed to be emojis and stored in the new `pack.json` file.
   """
   def import_from_fs(conn, _params) do
-    with {:ok, results} <- File.ls(emoji_dir_path()) do
+    emoji_path = emoji_dir_path()
+
+    with {:ok, %{access: :read_write}} <- File.stat(emoji_path),
+         {:ok, results} <- File.ls(emoji_path) do
       imported_pack_names =
         results
         |> Enum.filter(fn file ->
-          dir_path = Path.join(emoji_dir_path(), file)
+          dir_path = Path.join(emoji_path, file)
           # Find the directories that do NOT have pack.json
           File.dir?(dir_path) and not File.exists?(Path.join(dir_path, "pack.json"))
         end)
@@ -585,6 +588,11 @@ keeping it in cache for #{div(cache_ms, 1000)}s")
 
       json(conn, imported_pack_names)
     else
+      {:ok, %{access: _}} ->
+        conn
+        |> put_status(:internal_server_error)
+        |> json(%{error: "Error: emoji pack directory must be writable"})
+
       {:error, _} ->
         conn
         |> put_status(:internal_server_error)
index cd1c0764f6954f9bed89bba6591b634fa1f18f2f..d76e39795a84d706b066dd8bac2f2924e05970c6 100644 (file)
@@ -47,13 +47,16 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do
            Object.normalize(activity) do
       reactions =
         emoji_reactions
-        |> Enum.map(fn [emoji, users] ->
-          users = Enum.map(users, &User.get_cached_by_ap_id/1)
+        |> Enum.map(fn [emoji, user_ap_ids] ->
+          users =
+            Enum.map(user_ap_ids, &User.get_cached_by_ap_id/1)
+            |> Enum.filter(& &1)
 
           %{
             emoji: emoji,
             count: length(users),
-            accounts: AccountView.render("index.json", %{users: users, for: user, as: :user})
+            accounts: AccountView.render("index.json", %{users: users, for: user, as: :user}),
+            reacted: !!(user && user.ap_id in user_ap_ids)
           }
         end)
 
index b5c1d85c700a4a4843cf358173d1c108b409f024..e86bc3cc3c82882132ff4f0dc47399033493672c 100644 (file)
@@ -196,7 +196,7 @@ defmodule Pleroma.Web.Router do
     get("/config", AdminAPIController, :config_show)
     post("/config", AdminAPIController, :config_update)
     get("/config/descriptions", AdminAPIController, :config_descriptions)
-    get("/config/migrate_from_db", AdminAPIController, :migrate_from_db)
+    get("/restart", AdminAPIController, :restart)
 
     get("/moderation_log", AdminAPIController, :list_log)
 
diff --git a/mix.exs b/mix.exs
index ea6b29f57d1f9aa22513499add2efc4cee6c29a3..f6794f12671d38b85e98b5c1c06b5ddb3a08870a 100644 (file)
--- a/mix.exs
+++ b/mix.exs
@@ -8,7 +8,7 @@ defmodule Pleroma.Mixfile do
       elixir: "~> 1.8",
       elixirc_paths: elixirc_paths(Mix.env()),
       compilers: [:phoenix, :gettext] ++ Mix.compilers(),
-      elixirc_options: [warnings_as_errors: true],
+      elixirc_options: [warnings_as_errors: warnings_as_errors(Mix.env())],
       xref: [exclude: [:eldap]],
       start_permanent: Mix.env() == :prod,
       aliases: aliases(),
@@ -73,6 +73,11 @@ defmodule Pleroma.Mixfile do
   defp elixirc_paths(:test), do: ["lib", "test/support"]
   defp elixirc_paths(_), do: ["lib"]
 
+  defp warnings_as_errors(:prod), do: false
+  # Uncomment this if you need testing configurable_from_database logic
+  # defp warnings_as_errors(:dev), do: false
+  defp warnings_as_errors(_), do: true
+
   # Specifies OAuth dependencies.
   defp oauth_deps do
     oauth_strategy_packages =
@@ -166,7 +171,8 @@ defmodule Pleroma.Mixfile do
       {:captcha,
        git: "https://git.pleroma.social/pleroma/elixir-libraries/elixir-captcha.git",
        ref: "e0f16822d578866e186a0974d65ad58cddc1e2ab"},
-      {:mox, "~> 0.5", only: :test}
+      {:mox, "~> 0.5", only: :test},
+      {:restarter, path: "./restarter"}
     ] ++ oauth_deps()
   end
 
diff --git a/restarter/lib/pleroma.ex b/restarter/lib/pleroma.ex
new file mode 100644 (file)
index 0000000..da71465
--- /dev/null
@@ -0,0 +1,28 @@
+defmodule Restarter.Pleroma do
+  use GenServer
+
+  def start_link(_) do
+    GenServer.start_link(__MODULE__, [], name: __MODULE__)
+  end
+
+  def init(_), do: {:ok, %{}}
+
+  def handle_info(:after_boot, %{after_boot: true} = state), do: {:noreply, state}
+
+  def handle_info(:after_boot, state) do
+    restart(:pleroma)
+    {:noreply, Map.put(state, :after_boot, true)}
+  end
+
+  def handle_info({:restart, delay}, state) do
+    Process.sleep(delay)
+    restart(:pleroma)
+    {:noreply, state}
+  end
+
+  defp restart(app) do
+    :ok = Application.ensure_started(app)
+    :ok = Application.stop(app)
+    :ok = Application.start(app)
+  end
+end
diff --git a/restarter/lib/restarter.ex b/restarter/lib/restarter.ex
new file mode 100644 (file)
index 0000000..eadd86f
--- /dev/null
@@ -0,0 +1,8 @@
+defmodule Restarter do
+  use Application
+
+  def start(_, _) do
+    opts = [strategy: :one_for_one, name: Restarter.Supervisor]
+    Supervisor.start_link([Restarter.Pleroma], opts)
+  end
+end
diff --git a/restarter/mix.exs b/restarter/mix.exs
new file mode 100644 (file)
index 0000000..b0908ae
--- /dev/null
@@ -0,0 +1,21 @@
+defmodule Restarter.MixProject do
+  use Mix.Project
+
+  def project do
+    [
+      app: :restarter,
+      version: "0.1.0",
+      elixir: "~> 1.8",
+      start_permanent: Mix.env() == :prod,
+      deps: deps()
+    ]
+  end
+
+  def application do
+    [
+      mod: {Restarter, []}
+    ]
+  end
+
+  defp deps, do: []
+end
index 53e8703fd6f05193208192fdeba5ed8a899cbde5..ebdc951cf71576e4052bc1da498ac1b79db435f7 100644 (file)
@@ -5,6 +5,8 @@
 defmodule Pleroma.Config.TransferTaskTest do
   use Pleroma.DataCase
 
+  import ExUnit.CaptureLog
+
   alias Pleroma.Config.TransferTask
   alias Pleroma.ConfigDB
 
@@ -105,4 +107,75 @@ defmodule Pleroma.Config.TransferTaskTest do
       Application.put_env(:pleroma, :assets, assets)
     end)
   end
+
+  describe "pleroma restart" do
+    test "don't restart if no reboot time settings were changed" do
+      emoji = Application.get_env(:pleroma, :emoji)
+      on_exit(fn -> Application.put_env(:pleroma, :emoji, emoji) end)
+
+      ConfigDB.create(%{
+        group: ":pleroma",
+        key: ":emoji",
+        value: [groups: [a: 1, b: 2]]
+      })
+
+      refute String.contains?(
+               capture_log(fn -> TransferTask.start_link([]) end),
+               "pleroma restarted"
+             )
+    end
+
+    test "restart pleroma on reboot time key" do
+      chat = Application.get_env(:pleroma, :chat)
+      on_exit(fn -> Application.put_env(:pleroma, :chat, chat) end)
+
+      ConfigDB.create(%{
+        group: ":pleroma",
+        key: ":chat",
+        value: [enabled: false]
+      })
+
+      assert capture_log(fn -> TransferTask.start_link([]) end) =~ "pleroma restarted"
+    end
+
+    test "restart pleroma on reboot time subkey" do
+      captcha = Application.get_env(:pleroma, Pleroma.Captcha)
+      on_exit(fn -> Application.put_env(:pleroma, Pleroma.Captcha, captcha) end)
+
+      ConfigDB.create(%{
+        group: ":pleroma",
+        key: "Pleroma.Captcha",
+        value: [seconds_valid: 60]
+      })
+
+      assert capture_log(fn -> TransferTask.start_link([]) end) =~ "pleroma restarted"
+    end
+
+    test "don't restart pleroma on reboot time key and subkey if there is false flag" do
+      chat = Application.get_env(:pleroma, :chat)
+      captcha = Application.get_env(:pleroma, Pleroma.Captcha)
+
+      on_exit(fn ->
+        Application.put_env(:pleroma, :chat, chat)
+        Application.put_env(:pleroma, Pleroma.Captcha, captcha)
+      end)
+
+      ConfigDB.create(%{
+        group: ":pleroma",
+        key: ":chat",
+        value: [enabled: false]
+      })
+
+      ConfigDB.create(%{
+        group: ":pleroma",
+        key: "Pleroma.Captcha",
+        value: [seconds_valid: 60]
+      })
+
+      refute String.contains?(
+               capture_log(fn -> TransferTask.load_and_update_env([], false) end),
+               "pleroma restarted"
+             )
+    end
+  end
 end
diff --git a/test/fixtures/emoji-reaction-no-emoji.json b/test/fixtures/emoji-reaction-no-emoji.json
new file mode 100644 (file)
index 0000000..fff77b2
--- /dev/null
@@ -0,0 +1,30 @@
+{
+  "type": "EmojiReaction",
+  "signature": {
+    "type": "RsaSignature2017",
+    "signatureValue": "fdxMfQSMwbC6wP6sh6neS/vM5879K67yQkHTbiT5Npr5wAac0y6+o3Ij+41tN3rL6wfuGTosSBTHOtta6R4GCOOhCaCSLMZKypnp1VltCzLDoyrZELnYQIC8gpUXVmIycZbREk22qWUe/w7DAFaKK4UscBlHDzeDVcA0K3Se5Sluqi9/Zh+ldAnEzj/rSEPDjrtvf5wGNf3fHxbKSRKFt90JvKK6hS+vxKUhlRFDf6/SMETw+EhwJSNW4d10yMUakqUWsFv4Acq5LW7l+HpYMvlYY1FZhNde1+uonnCyuQDyvzkff8zwtEJmAXC4RivO/VVLa17SmqheJZfI8oluVg==",
+    "creator": "http://mastodon.example.org/users/admin#main-key",
+    "created": "2018-02-17T18:57:49Z"
+  },
+  "object": "http://localtesting.pleroma.lol/objects/eb92579d-3417-42a8-8652-2492c2d4f454",
+  "content": "~",
+  "nickname": "lain",
+  "id": "http://mastodon.example.org/users/admin#reactions/2",
+  "actor": "http://mastodon.example.org/users/admin",
+  "@context": [
+    "https://www.w3.org/ns/activitystreams",
+    "https://w3id.org/security/v1",
+    {
+      "toot": "http://joinmastodon.org/ns#",
+      "sensitive": "as:sensitive",
+      "ostatus": "http://ostatus.org#",
+      "movedTo": "as:movedTo",
+      "manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
+      "inReplyToAtomUri": "ostatus:inReplyToAtomUri",
+      "conversation": "ostatus:conversation",
+      "atomUri": "ostatus:atomUri",
+      "Hashtag": "as:Hashtag",
+      "Emoji": "toot:Emoji"
+    }
+  ]
+}
diff --git a/test/fixtures/emoji-reaction-too-long.json b/test/fixtures/emoji-reaction-too-long.json
new file mode 100644 (file)
index 0000000..31830d9
--- /dev/null
@@ -0,0 +1,30 @@
+{
+  "type": "EmojiReaction",
+  "signature": {
+    "type": "RsaSignature2017",
+    "signatureValue": "fdxMfQSMwbC6wP6sh6neS/vM5879K67yQkHTbiT5Npr5wAac0y6+o3Ij+41tN3rL6wfuGTosSBTHOtta6R4GCOOhCaCSLMZKypnp1VltCzLDoyrZELnYQIC8gpUXVmIycZbREk22qWUe/w7DAFaKK4UscBlHDzeDVcA0K3Se5Sluqi9/Zh+ldAnEzj/rSEPDjrtvf5wGNf3fHxbKSRKFt90JvKK6hS+vxKUhlRFDf6/SMETw+EhwJSNW4d10yMUakqUWsFv4Acq5LW7l+HpYMvlYY1FZhNde1+uonnCyuQDyvzkff8zwtEJmAXC4RivO/VVLa17SmqheJZfI8oluVg==",
+    "creator": "http://mastodon.example.org/users/admin#main-key",
+    "created": "2018-02-17T18:57:49Z"
+  },
+  "object": "http://localtesting.pleroma.lol/objects/eb92579d-3417-42a8-8652-2492c2d4f454",
+  "content": "👌👌",
+  "nickname": "lain",
+  "id": "http://mastodon.example.org/users/admin#reactions/2",
+  "actor": "http://mastodon.example.org/users/admin",
+  "@context": [
+    "https://www.w3.org/ns/activitystreams",
+    "https://w3id.org/security/v1",
+    {
+      "toot": "http://joinmastodon.org/ns#",
+      "sensitive": "as:sensitive",
+      "ostatus": "http://ostatus.org#",
+      "movedTo": "as:movedTo",
+      "manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
+      "inReplyToAtomUri": "ostatus:inReplyToAtomUri",
+      "conversation": "ostatus:conversation",
+      "atomUri": "ostatus:atomUri",
+      "Hashtag": "as:Hashtag",
+      "Emoji": "toot:Emoji"
+    }
+  ]
+}
index c6b2bc399cc7e5748c5ce35ab6df4cff64ef04a1..5690bedece2261874a39adc5bd9322394e49e3e7 100644 (file)
@@ -76,8 +76,43 @@ defmodule Pleroma.ObjectTest do
   describe "delete attachments" do
     clear_config([Pleroma.Upload])
 
+    test "Disabled via config" do
+      Pleroma.Config.put([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local)
+      Pleroma.Config.put([:instance, :cleanup_attachments], false)
+
+      file = %Plug.Upload{
+        content_type: "image/jpg",
+        path: Path.absname("test/fixtures/image.jpg"),
+        filename: "an_image.jpg"
+      }
+
+      user = insert(:user)
+
+      {:ok, %Object{} = attachment} =
+        Pleroma.Web.ActivityPub.ActivityPub.upload(file, actor: user.ap_id)
+
+      %{data: %{"attachment" => [%{"url" => [%{"href" => href}]}]}} =
+        note = insert(:note, %{user: user, data: %{"attachment" => [attachment.data]}})
+
+      uploads_dir = Pleroma.Config.get!([Pleroma.Uploaders.Local, :uploads])
+
+      path = href |> Path.dirname() |> Path.basename()
+
+      assert {:ok, ["an_image.jpg"]} == File.ls("#{uploads_dir}/#{path}")
+
+      Object.delete(note)
+
+      ObanHelpers.perform(all_enqueued(worker: Pleroma.Workers.AttachmentsCleanupWorker))
+
+      assert Object.get_by_id(note.id).data["deleted"]
+      refute Object.get_by_id(attachment.id) == nil
+
+      assert {:ok, ["an_image.jpg"]} == File.ls("#{uploads_dir}/#{path}")
+    end
+
     test "in subdirectories" do
       Pleroma.Config.put([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local)
+      Pleroma.Config.put([:instance, :cleanup_attachments], true)
 
       file = %Plug.Upload{
         content_type: "image/jpg",
@@ -103,6 +138,7 @@ defmodule Pleroma.ObjectTest do
 
       ObanHelpers.perform(all_enqueued(worker: Pleroma.Workers.AttachmentsCleanupWorker))
 
+      assert Object.get_by_id(note.id).data["deleted"]
       assert Object.get_by_id(attachment.id) == nil
 
       assert {:ok, []} == File.ls("#{uploads_dir}/#{path}")
@@ -111,6 +147,7 @@ defmodule Pleroma.ObjectTest do
     test "with dedupe enabled" do
       Pleroma.Config.put([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local)
       Pleroma.Config.put([Pleroma.Upload, :filters], [Pleroma.Upload.Filter.Dedupe])
+      Pleroma.Config.put([:instance, :cleanup_attachments], true)
 
       uploads_dir = Pleroma.Config.get!([Pleroma.Uploaders.Local, :uploads])
 
@@ -139,6 +176,7 @@ defmodule Pleroma.ObjectTest do
 
       ObanHelpers.perform(all_enqueued(worker: Pleroma.Workers.AttachmentsCleanupWorker))
 
+      assert Object.get_by_id(note.id).data["deleted"]
       assert Object.get_by_id(attachment.id) == nil
       assert {:ok, files} = File.ls(uploads_dir)
       refute filename in files
@@ -146,6 +184,7 @@ defmodule Pleroma.ObjectTest do
 
     test "with objects that have legacy data.url attribute" do
       Pleroma.Config.put([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local)
+      Pleroma.Config.put([:instance, :cleanup_attachments], true)
 
       file = %Plug.Upload{
         content_type: "image/jpg",
@@ -173,6 +212,7 @@ defmodule Pleroma.ObjectTest do
 
       ObanHelpers.perform(all_enqueued(worker: Pleroma.Workers.AttachmentsCleanupWorker))
 
+      assert Object.get_by_id(note.id).data["deleted"]
       assert Object.get_by_id(attachment.id) == nil
 
       assert {:ok, []} == File.ls("#{uploads_dir}/#{path}")
@@ -181,6 +221,7 @@ defmodule Pleroma.ObjectTest do
     test "With custom base_url" do
       Pleroma.Config.put([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local)
       Pleroma.Config.put([Pleroma.Upload, :base_url], "https://sub.domain.tld/dir/")
+      Pleroma.Config.put([:instance, :cleanup_attachments], true)
 
       file = %Plug.Upload{
         content_type: "image/jpg",
@@ -206,6 +247,7 @@ defmodule Pleroma.ObjectTest do
 
       ObanHelpers.perform(all_enqueued(worker: Pleroma.Workers.AttachmentsCleanupWorker))
 
+      assert Object.get_by_id(note.id).data["deleted"]
       assert Object.get_by_id(attachment.id) == nil
 
       assert {:ok, []} == File.ls("#{uploads_dir}/#{path}")
index 78f1ea9e46c83079c59667a7cf058cb9846ee0d5..06ffa7b7037476dae35822526982748855714354 100644 (file)
@@ -16,6 +16,7 @@ defmodule Pleroma.Plugs.RateLimiterTest do
     test "config is required for plug to work" do
       limiter_name = :test_init
       Pleroma.Config.put([:rate_limit, limiter_name], {1, 1})
+      Pleroma.Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8})
 
       assert %{limits: {1, 1}, name: :test_init, opts: [name: :test_init]} ==
                RateLimiter.init(name: limiter_name)
@@ -23,11 +24,39 @@ defmodule Pleroma.Plugs.RateLimiterTest do
       assert nil == RateLimiter.init(name: :foo)
     end
 
+    test "it is disabled for localhost" do
+      limiter_name = :test_init
+      Pleroma.Config.put([:rate_limit, limiter_name], {1, 1})
+      Pleroma.Config.put([Pleroma.Web.Endpoint, :http, :ip], {127, 0, 0, 1})
+      Pleroma.Config.put([Pleroma.Plugs.RemoteIp, :enabled], false)
+
+      assert RateLimiter.disabled?() == true
+    end
+
+    test "it is disabled for socket" do
+      limiter_name = :test_init
+      Pleroma.Config.put([:rate_limit, limiter_name], {1, 1})
+      Pleroma.Config.put([Pleroma.Web.Endpoint, :http, :ip], {:local, "/path/to/pleroma.sock"})
+      Pleroma.Config.put([Pleroma.Plugs.RemoteIp, :enabled], false)
+
+      assert RateLimiter.disabled?() == true
+    end
+
+    test "it is enabled for socket when remote ip is enabled" do
+      limiter_name = :test_init
+      Pleroma.Config.put([:rate_limit, limiter_name], {1, 1})
+      Pleroma.Config.put([Pleroma.Web.Endpoint, :http, :ip], {:local, "/path/to/pleroma.sock"})
+      Pleroma.Config.put([Pleroma.Plugs.RemoteIp, :enabled], true)
+
+      assert RateLimiter.disabled?() == false
+    end
+
     test "it restricts based on config values" do
       limiter_name = :test_opts
       scale = 80
       limit = 5
 
+      Pleroma.Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8})
       Pleroma.Config.put([:rate_limit, limiter_name], {scale, limit})
 
       opts = RateLimiter.init(name: limiter_name)
@@ -61,6 +90,7 @@ defmodule Pleroma.Plugs.RateLimiterTest do
       limiter_name = :test_bucket_name
 
       Pleroma.Config.put([:rate_limit, limiter_name], {1000, 5})
+      Pleroma.Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8})
 
       base_bucket_name = "#{limiter_name}:group1"
       opts = RateLimiter.init(name: limiter_name, bucket_name: base_bucket_name)
@@ -75,6 +105,7 @@ defmodule Pleroma.Plugs.RateLimiterTest do
     test "`params` option allows different queries to be tracked independently" do
       limiter_name = :test_params
       Pleroma.Config.put([:rate_limit, limiter_name], {1000, 5})
+      Pleroma.Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8})
 
       opts = RateLimiter.init(name: limiter_name, params: ["id"])
 
@@ -90,6 +121,7 @@ defmodule Pleroma.Plugs.RateLimiterTest do
     test "it supports combination of options modifying bucket name" do
       limiter_name = :test_options_combo
       Pleroma.Config.put([:rate_limit, limiter_name], {1000, 5})
+      Pleroma.Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8})
 
       base_bucket_name = "#{limiter_name}:group1"
       opts = RateLimiter.init(name: limiter_name, bucket_name: base_bucket_name, params: ["id"])
@@ -109,6 +141,7 @@ defmodule Pleroma.Plugs.RateLimiterTest do
     test "are restricted based on remote IP" do
       limiter_name = :test_unauthenticated
       Pleroma.Config.put([:rate_limit, limiter_name], [{1000, 5}, {1, 10}])
+      Pleroma.Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8})
 
       opts = RateLimiter.init(name: limiter_name)
 
@@ -147,6 +180,7 @@ defmodule Pleroma.Plugs.RateLimiterTest do
 
       scale = 50
       limit = 5
+      Pleroma.Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8})
       Pleroma.Config.put([:rate_limit, limiter_name], [{1000, 1}, {scale, limit}])
 
       opts = RateLimiter.init(name: limiter_name)
@@ -169,6 +203,7 @@ defmodule Pleroma.Plugs.RateLimiterTest do
     test "diffrerent users are counted independently" do
       limiter_name = :test_authenticated
       Pleroma.Config.put([:rate_limit, limiter_name], [{1, 10}, {1000, 5}])
+      Pleroma.Config.put([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8})
 
       opts = RateLimiter.init(name: limiter_name)
 
index 5da358c43636e10fa0556dfbb3d4b72393c43bfe..0829a6ec2d1e2f47064a99ab1fcaceedeb87db12 100644 (file)
@@ -395,6 +395,25 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
       assert data["content"] == "👌"
     end
 
+    test "it reject invalid emoji reactions" do
+      user = insert(:user)
+      {:ok, activity} = CommonAPI.post(user, %{"status" => "hello"})
+
+      data =
+        File.read!("test/fixtures/emoji-reaction-too-long.json")
+        |> Poison.decode!()
+        |> Map.put("object", activity.data["object"])
+
+      assert :error = Transmogrifier.handle_incoming(data)
+
+      data =
+        File.read!("test/fixtures/emoji-reaction-no-emoji.json")
+        |> Poison.decode!()
+        |> Map.put("object", activity.data["object"])
+
+      assert :error = Transmogrifier.handle_incoming(data)
+    end
+
     test "it works for incoming emoji reaction undos" do
       user = insert(:user)
 
index 5c767219ac39d72b02a0eaf2c824a2ed359b018d..5fbdf96f6072af4238472ea128fb98e88a6d5bc3 100644 (file)
@@ -1899,13 +1899,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
                "To use this endpoint you need to enable configuration from database."
     end
 
-    test "without any settings in db", %{conn: conn} do
-      conn = get(conn, "/api/pleroma/admin/config")
-
-      assert json_response(conn, 400) ==
-               "To use configuration from database migrate your settings to database."
-    end
-
     test "with settings only in db", %{conn: conn} do
       config1 = insert(:config)
       config2 = insert(:config)
@@ -2043,7 +2036,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
         Application.delete_env(:pleroma, Pleroma.Captcha.NotReal)
         Application.put_env(:pleroma, :http, http)
         Application.put_env(:tesla, :adapter, Tesla.Mock)
-        :ok = File.rm("config/test.exported_from_db.secret.exs")
       end)
     end
 
@@ -2170,7 +2162,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
       assert Application.get_env(:idna, :key5) == {"string", Pleroma.Captcha.NotReal, []}
     end
 
-    test "save config setting without key", %{conn: conn} do
+    test "save configs setting without explicit key", %{conn: conn} do
       level = Application.get_env(:quack, :level)
       meta = Application.get_env(:quack, :meta)
       webhook_url = Application.get_env(:quack, :webhook_url)
@@ -2256,6 +2248,34 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
              }
     end
 
+    test "saving config which need pleroma reboot", %{conn: conn} do
+      chat = Pleroma.Config.get(:chat)
+      on_exit(fn -> Pleroma.Config.put(:chat, chat) end)
+
+      conn =
+        post(
+          conn,
+          "/api/pleroma/admin/config",
+          %{
+            configs: [
+              %{group: ":pleroma", key: ":chat", value: [%{"tuple" => [":enabled", true]}]}
+            ]
+          }
+        )
+
+      assert json_response(conn, 200) == %{
+               "configs" => [
+                 %{
+                   "db" => [":enabled"],
+                   "group" => ":pleroma",
+                   "key" => ":chat",
+                   "value" => [%{"tuple" => [":enabled", true]}]
+                 }
+               ],
+               "need_reboot" => true
+             }
+    end
+
     test "saving config with nested merge", %{conn: conn} do
       config =
         insert(:config, key: ":key1", value: :erlang.term_to_binary(key1: 1, key2: [k1: 1, k2: 2]))
@@ -2957,47 +2977,15 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
     end
   end
 
-  describe "config mix tasks run" do
-    setup do
-      Mix.shell(Mix.Shell.Quiet)
-
-      on_exit(fn ->
-        Mix.shell(Mix.Shell.IO)
-      end)
-
-      :ok
-    end
-
+  describe "GET /api/pleroma/admin/restart" do
     clear_config(:configurable_from_database) do
       Pleroma.Config.put(:configurable_from_database, true)
     end
 
-    clear_config([:feed, :post_title]) do
-      Pleroma.Config.put([:feed, :post_title], %{max_length: 100, omission: "…"})
-    end
-
-    test "transfer settings to DB and to file", %{conn: conn} do
-      assert Repo.all(Pleroma.ConfigDB) == []
-      Mix.Tasks.Pleroma.Config.migrate_to_db("test/fixtures/config/temp.secret.exs")
-      assert Repo.aggregate(Pleroma.ConfigDB, :count, :id) > 0
-
-      conn = get(conn, "/api/pleroma/admin/config/migrate_from_db")
-
-      assert json_response(conn, 200) == %{}
-      assert Repo.all(Pleroma.ConfigDB) == []
-    end
-
-    test "returns error if configuration from database is off", %{conn: conn} do
-      initial = Pleroma.Config.get(:configurable_from_database)
-      on_exit(fn -> Pleroma.Config.put(:configurable_from_database, initial) end)
-      Pleroma.Config.put(:configurable_from_database, false)
-
-      conn = get(conn, "/api/pleroma/admin/config/migrate_from_db")
-
-      assert json_response(conn, 400) ==
-               "To use this endpoint you need to enable configuration from database."
-
-      assert Repo.all(Pleroma.ConfigDB) == []
+    test "pleroma restarts", %{conn: conn} do
+      ExUnit.CaptureLog.capture_log(fn ->
+        assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) == %{}
+      end) =~ "pleroma restarted"
     end
   end
 
index f8963e42e90a9c41666fc78f4f0905b27e736310..214cbdd7cee31423970fb1ba904dc88cf65bcb8a 100644 (file)
@@ -238,7 +238,9 @@ defmodule Pleroma.Web.CommonAPITest do
       assert reaction.data["actor"] == user.ap_id
       assert reaction.data["content"] == "👍"
 
-      # TODO: test error case.
+      {:ok, activity} = CommonAPI.post(other_user, %{"status" => "cofe"})
+
+      {:error, _} = CommonAPI.react_with_emoji(activity.id, user, ".")
     end
 
     test "unreacting to a status with an emoji" do
@@ -322,6 +324,21 @@ defmodule Pleroma.Web.CommonAPITest do
       assert %User{pinned_activities: [^id]} = user
     end
 
+    test "pin poll", %{user: user} do
+      {:ok, activity} =
+        CommonAPI.post(user, %{
+          "status" => "How is fediverse today?",
+          "poll" => %{"options" => ["Absolutely outstanding", "Not good"], "expires_in" => 20}
+        })
+
+      assert {:ok, ^activity} = CommonAPI.pin(activity.id, user)
+
+      id = activity.id
+      user = refresh_record(user)
+
+      assert %User{pinned_activities: [^id]} = user
+    end
+
     test "unlisted statuses can be pinned", %{user: user} do
       {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!", "visibility" => "unlisted"})
       assert {:ok, ^activity} = CommonAPI.pin(activity.id, user)
index 0d4860a4299fb3de210216856f653367338a34bf..ec1e180025c03afc0aec36c782386cb2aa2b37a4 100644 (file)
@@ -668,6 +668,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
     end
 
     test "rate limit", %{conn: conn} do
+      Pleroma.Config.put([Pleroma.Plugs.RemoteIp, :enabled], true)
       app_token = insert(:oauth_token, user: nil)
 
       conn =
index 25777b0115a89ead183ad11a0ddff7b0a05203c9..fc110417cd35d6d52e15caa75df1c1ef2a9f7b60 100644 (file)
@@ -37,8 +37,15 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do
     status = StatusView.render("show.json", activity: activity)
 
     assert status[:pleroma][:emoji_reactions] == [
-             %{emoji: "☕", count: 2},
-             %{emoji: "🍵", count: 1}
+             %{emoji: "☕", count: 2, reacted: false},
+             %{emoji: "🍵", count: 1, reacted: false}
+           ]
+
+    status = StatusView.render("show.json", activity: activity, for: user)
+
+    assert status[:pleroma][:emoji_reactions] == [
+             %{emoji: "☕", count: 2, reacted: true},
+             %{emoji: "🍵", count: 1, reacted: false}
            ]
   end
 
index 8e76f2f3dbc5cff90a3102b2ee11136b57ba1218..6f1ea78eced1631c1f74be53ab6ad5950fb799d1 100644 (file)
@@ -6,7 +6,6 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIControllerTest do
   use Pleroma.Web.ConnCase
 
   import Tesla.Mock
-
   import Pleroma.Factory
 
   @emoji_dir_path Path.join(
index 3978c2ec5587d734ac857ca87a9047a6ffef9b6d..be5007de52b00acbaa8a256b9a84693ec207a2fc 100644 (file)
@@ -25,9 +25,14 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIControllerTest do
       |> assign(:user, other_user)
       |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["write:statuses"]))
       |> post("/api/v1/pleroma/statuses/#{activity.id}/react_with_emoji", %{"emoji" => "☕"})
+      |> json_response(200)
 
-    assert %{"id" => id} = json_response(result, 200)
+    assert %{"id" => id} = result
     assert to_string(activity.id) == id
+
+    assert result["pleroma"]["emoji_reactions"] == [
+             %{"emoji" => "☕", "count" => 1, "reacted" => true}
+           ]
   end
 
   test "POST /api/v1/pleroma/statuses/:id/unreact_with_emoji", %{conn: conn} do
@@ -54,6 +59,7 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIControllerTest do
   test "GET /api/v1/pleroma/statuses/:id/emoji_reactions_by", %{conn: conn} do
     user = insert(:user)
     other_user = insert(:user)
+    doomed_user = insert(:user)
 
     {:ok, activity} = CommonAPI.post(user, %{"status" => "#cofe"})
 
@@ -65,14 +71,29 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIControllerTest do
     assert result == []
 
     {:ok, _, _} = CommonAPI.react_with_emoji(activity.id, other_user, "🎅")
+    {:ok, _, _} = CommonAPI.react_with_emoji(activity.id, doomed_user, "🎅")
+
+    User.perform(:delete, doomed_user)
 
     result =
       conn
       |> get("/api/v1/pleroma/statuses/#{activity.id}/emoji_reactions_by")
       |> json_response(200)
 
-    [%{"emoji" => "🎅", "count" => 1, "accounts" => [represented_user]}] = result
+    [%{"emoji" => "🎅", "count" => 1, "accounts" => [represented_user], "reacted" => false}] =
+      result
+
     assert represented_user["id"] == other_user.id
+
+    result =
+      conn
+      |> assign(:user, other_user)
+      |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["read:statuses"]))
+      |> get("/api/v1/pleroma/statuses/#{activity.id}/emoji_reactions_by")
+      |> json_response(200)
+
+    assert [%{"emoji" => "🎅", "count" => 1, "accounts" => [_represented_user], "reacted" => true}] =
+             result
   end
 
   test "/api/v1/pleroma/conversations/:id" do