Merge branch 'feature/1922-media-proxy-whitelist' into 'develop'
authorfeld <feld@feld.me>
Tue, 14 Jul 2020 18:07:44 +0000 (18:07 +0000)
committerfeld <feld@feld.me>
Tue, 14 Jul 2020 18:07:44 +0000 (18:07 +0000)
Support for hosts with scheme in MediaProxy whitelist setting

Closes #1922

See merge request pleroma/pleroma!2754

45 files changed:
.gitignore
.gitlab-ci.yml
CHANGELOG.md
Dockerfile
config/description.exs
docs/API/differences_in_mastoapi_responses.md
docs/configuration/cheatsheet.md
docs/configuration/howto_database_config.md [new file with mode: 0644]
lib/mix/tasks/pleroma/config.ex
lib/pleroma/application.ex
lib/pleroma/docs/generator.ex
lib/pleroma/docs/json.ex
lib/pleroma/docs/markdown.ex
lib/pleroma/plugs/admin_secret_authentication_plug.ex
lib/pleroma/plugs/user_is_admin_plug.ex
lib/pleroma/upload/filter/exiftool.ex [new file with mode: 0644]
lib/pleroma/user.ex
lib/pleroma/web/activity_pub/activity_pub.ex
lib/pleroma/web/activity_pub/transmogrifier.ex
lib/pleroma/web/admin_api/controllers/config_controller.ex
lib/pleroma/web/api_spec/helpers.ex
lib/pleroma/web/api_spec/operations/admin/config_operation.ex
lib/pleroma/web/api_spec/operations/admin/invite_operation.ex
lib/pleroma/web/api_spec/operations/admin/media_proxy_cache_operation.ex
lib/pleroma/web/api_spec/operations/admin/oauth_app_operation.ex
lib/pleroma/web/api_spec/operations/admin/relay_operation.ex
lib/pleroma/web/api_spec/operations/admin/report_operation.ex
lib/pleroma/web/api_spec/operations/admin/status_operation.ex
mix.exs
priv/gettext/errors.pot
priv/gettext/it/LC_MESSAGES/errors.po
priv/gettext/nl/LC_MESSAGES/errors.po
priv/gettext/pl/LC_MESSAGES/errors.po
priv/repo/migrations/20200714081657_oban_2_0_config_changes.exs [new file with mode: 0644]
test/docs/generator_test.exs
test/fixtures/DSCN0010.jpg [new file with mode: 0644]
test/plugs/admin_secret_authentication_plug_test.exs
test/plugs/user_is_admin_plug_test.exs
test/upload/filter/exiftool_test.exs [new file with mode: 0644]
test/web/activity_pub/activity_pub_test.exs
test/web/activity_pub/transmogrifier_test.exs
test/web/admin_api/controllers/admin_api_controller_test.exs
test/web/admin_api/controllers/config_controller_test.exs
test/web/admin_api/controllers/report_controller_test.exs
test/web/mastodon_api/controllers/account_controller/update_credentials_test.exs

index 198e801399757dda814145f3458d194f88bb363d..599b52b9e0b8f2084a889bb9aca56ca0dec76e01 100644 (file)
@@ -5,6 +5,7 @@
 /*.ez
 /test/uploads
 /.elixir_ls
+/test/fixtures/DSCN0010_tmp.jpg
 /test/fixtures/test_tmp.txt
 /test/fixtures/image_tmp.jpg
 /test/tmp/
index 6a2be879e6c803c6bac4076f62976a9cef73b209..c9ab848926f21debe460cee18509380aefdc3ffb 100644 (file)
@@ -58,6 +58,7 @@ unit-testing:
     alias: postgres
     command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
   script:
+    - apt-get update && apt-get install -y libimage-exiftool-perl
     - mix deps.get
     - mix ecto.create
     - mix ecto.migrate
@@ -89,6 +90,7 @@ unit-testing-rum:
     <<: *global_variables
     RUM_ENABLED: "true"
   script:
+    - apt-get update && apt-get install -y libimage-exiftool-perl
     - mix deps.get
     - mix ecto.create
     - mix ecto.migrate
index 42149a2d2969b386cc7057abd0bae33339b91d15..b1a420b49058c76d1cbbaed299f80abb6a425bbc 100644 (file)
@@ -58,6 +58,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - MRF (`EmojiStealPolicy`): New MRF Policy which allows to automatically download emojis from remote instances
 - Support pagination in emoji packs API (for packs and for files in pack)
 - Support for viewing instances favicons next to posts and accounts
+- Added Pleroma.Upload.Filter.Exiftool as an alternate EXIF stripping mechanism targeting GPS/location metadata.
 
 <details>
   <summary>API Changes</summary>
index 29931a5e3a398cd2fef9cb0e244da7db5725ce1f..0f4fcd0bb271c18fde7a9dbb30a319fc18e7f423 100644 (file)
@@ -33,7 +33,7 @@ ARG DATA=/var/lib/pleroma
 
 RUN echo "http://nl.alpinelinux.org/alpine/latest-stable/community" >> /etc/apk/repositories &&\
        apk update &&\
-       apk add imagemagick ncurses postgresql-client &&\
+       apk add exiftool imagemagick ncurses postgresql-client &&\
        adduser --system --shell /bin/false --home ${HOME} pleroma &&\
        mkdir -p ${DATA}/uploads &&\
        mkdir -p ${DATA}/static &&\
index 432705307704b6330214da8e66f56d18da71cc3d..afc4dcd79b79301cfbe113468d081dd63f1cd4a7 100644 (file)
@@ -23,18 +23,14 @@ config :pleroma, :config_description, [
         key: :uploader,
         type: :module,
         description: "Module which will be used for uploads",
-        suggestions: [Pleroma.Uploaders.Local, Pleroma.Uploaders.S3]
+        suggestions: {:list_behaviour_implementations, Pleroma.Uploaders.Uploader}
       },
       %{
         key: :filters,
         type: {:list, :module},
         description:
           "List of filter modules for uploads. Module names are shortened (removed leading `Pleroma.Upload.Filter.` part), but on adding custom module you need to use full name.",
-        suggestions:
-          Generator.list_modules_in_dir(
-            "lib/pleroma/upload/filter",
-            "Elixir.Pleroma.Upload.Filter."
-          )
+        suggestions: {:list_behaviour_implementations, Pleroma.Upload.Filter}
       },
       %{
         key: :link_name,
@@ -1071,6 +1067,7 @@ config :pleroma, :config_description, [
       },
       %{
         key: :webhook_url,
+        label: "Webhook URL",
         type: :string,
         description: "Configure the Slack incoming webhook",
         suggestions: ["https://hooks.slack.com/services/YOUR-KEY-HERE"]
@@ -1404,11 +1401,7 @@ config :pleroma, :config_description, [
         type: [:module, {:list, :module}],
         description:
           "A list of MRF policies enabled. Module names are shortened (removed leading `Pleroma.Web.ActivityPub.MRF.` part), but on adding custom module you need to use full name.",
-        suggestions:
-          Generator.list_modules_in_dir(
-            "lib/pleroma/web/activity_pub/mrf",
-            "Elixir.Pleroma.Web.ActivityPub.MRF."
-          )
+        suggestions: {:list_behaviour_implementations, Pleroma.Web.ActivityPub.MRF}
       },
       %{
         key: :transparency,
@@ -1523,7 +1516,7 @@ config :pleroma, :config_description, [
     children: [
       %{
         key: :match_actor,
-        type: :map,
+        type: {:map, {:list, :string}},
         description: "Matches a series of regular expressions against the actor field",
         suggestions: [
           %{
@@ -1589,21 +1582,21 @@ config :pleroma, :config_description, [
     children: [
       %{
         key: :reject,
-        type: [:string, :regex],
+        type: {:list, :string},
         description:
           "A list of patterns which result in message being rejected. Each pattern can be a string or a regular expression.",
         suggestions: ["foo", ~r/foo/iu]
       },
       %{
         key: :federated_timeline_removal,
-        type: [:string, :regex],
+        type: {:list, :string},
         description:
           "A list of patterns which result in message being removed from federated timelines (a.k.a unlisted). Each pattern can be a string or a regular expression.",
         suggestions: ["foo", ~r/foo/iu]
       },
       %{
         key: :replace,
-        type: [{:tuple, :string, :string}, {:tuple, :regex, :string}],
+        type: {:list, :tuple},
         description:
           "A list of tuples containing {pattern, replacement}. Each pattern can be a string or a regular expression.",
         suggestions: [{"foo", "bar"}, {~r/foo/iu, "bar"}]
@@ -1793,15 +1786,20 @@ config :pleroma, :config_description, [
       },
       %{
         key: :headers,
-        type: {:list, :tuple},
-        description: "HTTP headers of request.",
+        type: {:keyword, :string},
+        description: "HTTP headers of request",
         suggestions: [{"x-refresh", 1}]
       },
       %{
         key: :options,
         type: :keyword,
-        description: "Request options.",
-        suggestions: [params: %{ts: "xxx"}]
+        description: "Request options",
+        children: [
+          %{
+            key: :params,
+            type: {:map, :string}
+          }
+        ]
       }
     ]
   },
@@ -2010,13 +2008,15 @@ config :pleroma, :config_description, [
     label: "Pleroma Admin Token",
     type: :group,
     description:
-      "Allows to set a token that can be used to authenticate with the admin api without using an actual user by giving it as the `admin_token` parameter",
+      "Allows setting a token that can be used to authenticate requests with admin privileges without a normal user account token. Append the `admin_token` parameter to requests to utilize it. (Please reconsider using HTTP Basic Auth or OAuth-based authentication if possible)",
     children: [
       %{
         key: :admin_token,
         type: :string,
         description: "Admin token",
-        suggestions: ["We recommend a secure random string or UUID"]
+        suggestions: [
+          "Please use a high entropy string or UUID"
+        ]
       }
     ]
   },
@@ -2517,7 +2517,7 @@ config :pleroma, :config_description, [
       %{
         key: :styling,
         type: :map,
-        description: "a map with color settings for email templates.",
+        description: "A map with color settings for email templates.",
         suggestions: [
           %{
             link_color: "#d8a070",
@@ -2622,7 +2622,7 @@ config :pleroma, :config_description, [
       },
       %{
         key: :groups,
-        type: {:keyword, :string, {:list, :string}},
+        type: {:keyword, {:list, :string}},
         description:
           "Emojis are ordered in groups (tags). This is an array of key-value pairs where the key is the group name" <>
             " and the value is the location or array of locations. * can be used as a wildcard.",
index 65f9f1aefc52d4be68300188089508e75f5800bf..c4a9c6dade9aeadfe12140475e359091151623ce 100644 (file)
@@ -64,8 +64,8 @@ Has these additional fields under the `pleroma` object:
 - `hide_follows`: boolean, true when the user has follow hiding enabled
 - `hide_followers_count`: boolean, true when the user has follower stat hiding enabled
 - `hide_follows_count`: boolean, true when the user has follow stat hiding enabled
-- `settings_store`: A generic map of settings for frontends. Opaque to the backend. Only returned in `verify_credentials` and `update_credentials`
-- `chat_token`: The token needed for Pleroma chat. Only returned in `verify_credentials`
+- `settings_store`: A generic map of settings for frontends. Opaque to the backend. Only returned in `/api/v1/accounts/verify_credentials` and `/api/v1/accounts/update_credentials`
+- `chat_token`: The token needed for Pleroma chat. Only returned in `/api/v1/accounts/verify_credentials`
 - `deactivated`: boolean, true when the user is deactivated
 - `allow_following_move`: boolean, true when the user allows automatically follow moved following accounts
 - `unread_conversation_count`: The count of unread conversations. Only returned to the account owner.
@@ -169,7 +169,7 @@ Returns: array of Status.
 
 The maximum number of statuses is limited to 100 per request.
 
-## PATCH `/api/v1/update_credentials`
+## PATCH `/api/v1/accounts/update_credentials`
 
 Additional parameters can be added to the JSON body/Form data:
 
@@ -197,7 +197,7 @@ Pleroma has mechanism that allows frontends to save blobs of json for each user
 
 The parameter should have a form of `{frontend_name: {...}}`, with `frontend_name` identifying your type of client, e.g. `pleroma_fe`. It will overwrite everything under this property, but will not overwrite other frontend's settings.
 
-This information is returned in the `verify_credentials` endpoint.
+This information is returned in the `/api/v1/accounts/verify_credentials` endpoint.
 
 ## Authentication
 
index f7885c11d4bb7fcf04d29802de3060ebd32e78e5..ba62a721e1acdc07fbf9cd39668693b4ddf90506 100644 (file)
@@ -551,20 +551,26 @@ config :ex_aws, :s3,
 
 ### Upload filters
 
-#### Pleroma.Upload.Filter.Mogrify
+#### Pleroma.Upload.Filter.AnonymizeFilename
 
-* `args`: List of actions for the `mogrify` command like `"strip"` or `["strip", "auto-orient", {"implode", "1"}]`.
+This filter replaces the filename (not the path) of an upload. For complete obfuscation, add
+`Pleroma.Upload.Filter.Dedupe` before AnonymizeFilename.
+
+* `text`: Text to replace filenames in links. If empty, `{random}.extension` will be used. You can get the original filename extension by using `{extension}`, for example `custom-file-name.{extension}`.
 
 #### Pleroma.Upload.Filter.Dedupe
 
 No specific configuration.
 
-#### Pleroma.Upload.Filter.AnonymizeFilename
+#### Pleroma.Upload.Filter.Exiftool
 
-This filter replaces the filename (not the path) of an upload. For complete obfuscation, add
-`Pleroma.Upload.Filter.Dedupe` before AnonymizeFilename.
+This filter only strips the GPS and location metadata with Exiftool leaving color profiles and attributes intact.
 
-* `text`: Text to replace filenames in links. If empty, `{random}.extension` will be used. You can get the original filename extension by using `{extension}`, for example `custom-file-name.{extension}`.
+No specific configuration.
+
+#### Pleroma.Upload.Filter.Mogrify
+
+* `args`: List of actions for the `mogrify` command like `"strip"` or `["strip", "auto-orient", {"implode", "1"}]`.
 
 ## Email
 
@@ -626,8 +632,7 @@ Email notifications settings.
 Configuration options described in [Oban readme](https://github.com/sorentwo/oban#usage):
 
 * `repo` - app's Ecto repo (`Pleroma.Repo`)
-* `verbose` - logs verbosity
-* `prune` - non-retryable jobs [pruning settings](https://github.com/sorentwo/oban#pruning) (`:disabled` / `{:maxlen, value}` / `{:maxage, value}`)
+* `log` - logs verbosity
 * `queues` - job queues (see below)
 * `crontab` - periodic jobs, see [`Oban.Cron`](#obancron)
 
@@ -812,6 +817,8 @@ or
 curl -H "X-Admin-Token: somerandomtoken" "http://localhost:4000/api/pleroma/admin/users/invites"
 ```
 
+Warning: it's discouraged to use this feature because of the associated security risk: static / rarely changed instance-wide token is much weaker compared to email-password pair of a real admin user; consider using HTTP Basic Auth or OAuth-based authentication instead.
+
 ### :auth
 
 * `Pleroma.Web.Auth.PleromaAuthenticator`: default database authenticator.
diff --git a/docs/configuration/howto_database_config.md b/docs/configuration/howto_database_config.md
new file mode 100644 (file)
index 0000000..ded9a2e
--- /dev/null
@@ -0,0 +1,151 @@
+# How to activate Pleroma in-database configuration
+## Explanation
+
+The configuration of Pleroma has traditionally been managed with a config file, e.g. `config/prod.secret.exs`. This method requires a restart of the application for any configuration changes to take effect. We have made it possible to control most settings in the AdminFE interface after running a migration script.
+
+## Migration to database config
+
+1. Stop your Pleroma instance and edit your Pleroma config to enable database configuration: 
+
+  ```
+  config :pleroma, configurable_from_database: true
+  ```
+
+2. Run the mix task to migrate to the database. You'll receive some debugging output and a few messages informing you of what happened.
+
+  **Source:**
+  
+  ```
+  $ mix pleroma.config migrate_to_db
+  ```
+  
+  or
+  
+  **OTP:**
+  
+  ```
+  $ ./bin/pleroma_ctl config migrate_to_db
+  ```
+
+  ```  
+  10:04:34.155 [debug] QUERY OK source="config" db=1.6ms decode=2.0ms queue=33.5ms idle=0.0ms
+SELECT c0."id", c0."key", c0."group", c0."value", c0."inserted_at", c0."updated_at" FROM "config" AS c0 []
+Migrating settings from file: /home/pleroma/config/dev.secret.exs
+  
+  10:04:34.240 [debug] QUERY OK db=4.5ms queue=0.3ms idle=92.2ms
+TRUNCATE config; []
+  
+  10:04:34.244 [debug] QUERY OK db=2.8ms queue=0.3ms idle=97.2ms
+ALTER SEQUENCE config_id_seq RESTART; []
+  
+  10:04:34.256 [debug] QUERY OK source="config" db=0.8ms queue=1.4ms idle=109.8ms
+SELECT c0."id", c0."key", c0."group", c0."value", c0."inserted_at", c0."updated_at" FROM "config" AS c0 WHERE ((c0."group" = $1) AND (c0."key" = $2)) [":pleroma", ":instance"]
+  
+  10:04:34.292 [debug] QUERY OK db=2.6ms queue=1.7ms idle=137.7ms
+INSERT INTO "config" ("group","key","value","inserted_at","updated_at") VALUES ($1,$2,$3,$4,$5) RETURNING "id" [":pleroma", ":instance", <<131, 108, 0, 0, 0, 1, 104, 2, 100, 0, 4, 110, 97, 109, 101, 109, 0, 0, 0, 7, 66, 108, 101, 114, 111, 109, 97, 106>>, ~N[2020-07-12 15:04:34], ~N[2020-07-12 15:04:34]]
+  Settings for key instance migrated.
+  Settings for group :pleroma migrated.
+  ```
+  
+3. It is recommended to backup your config file now.
+  ```
+  cp config/dev.secret.exs config/dev.secret.exs.orig
+  ```
+  
+4. Now you can edit your config file and strip it down to the only settings which are not possible to control in the database. e.g., the Postgres and webserver (Endpoint) settings cannot be controlled in the database because the application needs the settings to start up and access the database.
+
+ ⚠️ **THIS IS NOT REQUIRED**
+ Any settings in the database will override those in the config file, but you may find it less confusing if the setting is only declared in one place.
+
+ A non-exhaustive list of settings that are only possible in the config file include the following:
+
+* config :pleroma, Pleroma.Web.Endpoint
+* config :pleroma, Pleroma.Repo
+* config :pleroma, configurable_from_database
+* config :pleroma, :database, rum_enabled
+* config :pleroma, :connections_pool
+
+Here is an example of a server config stripped down after migration:
+
+```
+use Mix.Config
+
+config :pleroma, Pleroma.Web.Endpoint,
+  url: [host: "cool.pleroma.site", scheme: "https", port: 443]
+
+
+config :pleroma, Pleroma.Repo,
+  adapter: Ecto.Adapters.Postgres,
+  username: "pleroma",
+  password: "MySecretPassword",
+  database: "pleroma_prod",
+  hostname: "localhost"
+
+config :pleroma, configurable_from_database: true
+```
+
+5. Start your instance back up and you can now access the Settings tab in AdminFE.
+
+
+## Reverting back from database config
+
+1. Stop your Pleroma instance.
+
+2. Run the mix task to migrate back from the database. You'll receive some debugging output and a few messages informing you of what happened.
+
+  **Source:**
+  
+  ```
+  $ mix pleroma.config migrate_from_db
+  ```
+  
+  or
+  
+  **OTP:**
+  
+  ```
+  $ ./bin/pleroma_ctl config migrate_from_db
+  ```
+
+  ```
+  10:26:30.593 [debug] QUERY OK source="config" db=9.8ms decode=1.2ms queue=26.0ms idle=0.0ms
+SELECT c0."id", c0."key", c0."group", c0."value", c0."inserted_at", c0."updated_at" FROM "config" AS c0 []
+
+  10:26:30.659 [debug] QUERY OK source="config" db=1.1ms idle=80.7ms
+SELECT c0."id", c0."key", c0."group", c0."value", c0."inserted_at", c0."updated_at" FROM "config" AS c0 []
+Database configuration settings have been saved to config/dev.exported_from_db.secret.exs
+```
+
+3. The in-database configuration still exists, but it will not be used if you remove `config :pleroma, configurable_from_database: true` from your config.
+
+## Debugging
+
+### Clearing database config
+You can clear the database config by truncating the `config` table in the database. e.g.,
+
+```
+psql -d pleroma_dev
+pleroma_dev=# TRUNCATE config;
+TRUNCATE TABLE
+```
+
+Additionally, every time you migrate the configuration to the database the config table is automatically truncated to ensure a clean migration.
+
+### Manually removing a setting
+If you encounter a situation where the server cannot run properly because of an invalid setting in the database and this is preventing you from accessing AdminFE, you can manually remove the offending setting if you know which one it is.
+
+e.g., here is an example showing a minimal configuration in the database. Only the `config :pleroma, :instance` settings are in the table:
+
+```
+psql -d pleroma_dev
+pleroma_dev=# select * from config;
+ id |    key    |                           value                            |     inserted_at     |     updated_at      |  group
+----+-----------+------------------------------------------------------------+---------------------+---------------------+----------
+  1 | :instance | \x836c0000000168026400046e616d656d00000007426c65726f6d616a | 2020-07-12 15:33:29 | 2020-07-12 15:33:29 | :pleroma
+(1 row)
+pleroma_dev=# delete from config where key = ':instance' and group = ':pleroma';
+DELETE 1
+```
+
+Now the `config :pleroma, :instance` settings have been removed from the database.
index d5129d410b8c9cde9cc5b38208344b4e2a334c0a..904c5a74b731e68a9f2a20560716d35a58ae01c4 100644 (file)
@@ -83,7 +83,7 @@ defmodule Mix.Tasks.Pleroma.Config do
 
   defp migrate_from_db(opts) do
     if Pleroma.Config.get([:configurable_from_database]) do
-      env = opts[:env] || "prod"
+      env = opts[:env] || Pleroma.Config.get(:env)
 
       config_path =
         if Pleroma.Config.get(:release) do
@@ -105,6 +105,10 @@ defmodule Mix.Tasks.Pleroma.Config do
 
       :ok = File.close(file)
       System.cmd("mix", ["format", config_path])
+
+      shell_info(
+        "Database configuration settings have been exported to config/#{env}.exported_from_db.secret.exs"
+      )
     else
       migration_error()
     end
@@ -112,7 +116,7 @@ defmodule Mix.Tasks.Pleroma.Config do
 
   defp migration_error do
     shell_error(
-      "Migration is not allowed in config. You can change this behavior by setting `configurable_from_database` to true."
+      "Migration is not allowed in config. You can change this behavior by setting `config :pleroma, configurable_from_database: true`"
     )
   end
 
index 84f3aa82deca12658ab12f67f7b83a706bf32935..3282c6882104c9e14fafe004fa0a675b34959ddd 100644 (file)
@@ -35,6 +35,10 @@ defmodule Pleroma.Application do
   # See http://elixir-lang.org/docs/stable/elixir/Application.html
   # for more information on OTP Applications
   def start(_type, _args) do
+    # Scrubbers are compiled at runtime and therefore will cause a conflict
+    # every time the application is restarted, so we disable module
+    # conflicts at runtime
+    Code.compiler_options(ignore_module_conflict: true)
     Config.Holder.save_default()
     Pleroma.HTML.compile_scrubbers()
     Config.DeprecationWarnings.warn()
@@ -42,6 +46,7 @@ defmodule Pleroma.Application do
     Pleroma.ApplicationRequirements.verify!()
     setup_instrumenters()
     load_custom_modules()
+    Pleroma.Docs.JSON.compile()
 
     adapter = Application.get_env(:tesla, :adapter)
 
index e0fc8cd02144de31766dbb22a204d1e53e7aa5d3..a671a62787cc33df4359012985c2c0b8b89fb729 100644 (file)
@@ -6,16 +6,21 @@ defmodule Pleroma.Docs.Generator do
     implementation.process(descriptions)
   end
 
-  @spec list_modules_in_dir(String.t(), String.t()) :: [module()]
-  def list_modules_in_dir(dir, start) do
-    with {:ok, files} <- File.ls(dir) do
-      files
-      |> Enum.filter(&String.ends_with?(&1, ".ex"))
-      |> Enum.map(fn filename ->
-        module = filename |> String.trim_trailing(".ex") |> Macro.camelize()
-        String.to_atom(start <> module)
-      end)
-    end
+  @spec list_behaviour_implementations(behaviour :: module()) :: [module()]
+  def list_behaviour_implementations(behaviour) do
+    :code.all_loaded()
+    |> Enum.filter(fn {module, _} ->
+      # This shouldn't be needed as all modules are expected to have module_info/1,
+      # but in test enviroments some transient modules `:elixir_compiler_XX`
+      # are loaded for some reason (where XX is a random integer).
+      if function_exported?(module, :module_info, 1) do
+        module.module_info(:attributes)
+        |> Keyword.get_values(:behaviour)
+        |> List.flatten()
+        |> Enum.member?(behaviour)
+      end
+    end)
+    |> Enum.map(fn {module, _} -> module end)
   end
 
   @doc """
@@ -87,6 +92,12 @@ defmodule Pleroma.Docs.Generator do
       else: string
   end
 
+  defp format_suggestions({:list_behaviour_implementations, behaviour}) do
+    behaviour
+    |> list_behaviour_implementations()
+    |> format_suggestions()
+  end
+
   defp format_suggestions([]), do: []
 
   defp format_suggestions([suggestion | tail]) do
index d1cf1f487b447ae994f982dd5359c1211402a1d4..feeb4320e62b10ed56b9160eea548fe83fe9c77d 100644 (file)
@@ -1,5 +1,19 @@
 defmodule Pleroma.Docs.JSON do
   @behaviour Pleroma.Docs.Generator
+  @external_resource "config/description.exs"
+  @raw_config Pleroma.Config.Loader.read("config/description.exs")
+  @raw_descriptions @raw_config[:pleroma][:config_description]
+  @term __MODULE__.Compiled
+
+  @spec compile :: :ok
+  def compile do
+    :persistent_term.put(@term, Pleroma.Docs.Generator.convert_to_strings(@raw_descriptions))
+  end
+
+  @spec compiled_descriptions :: Map.t()
+  def compiled_descriptions do
+    :persistent_term.get(@term)
+  end
 
   @spec process(keyword()) :: {:ok, String.t()}
   def process(descriptions) do
@@ -13,11 +27,4 @@ defmodule Pleroma.Docs.JSON do
       {:ok, path}
     end
   end
-
-  def compile do
-    with config <- Pleroma.Config.Loader.read("config/description.exs") do
-      config[:pleroma][:config_description]
-      |> Pleroma.Docs.Generator.convert_to_strings()
-    end
-  end
 end
index 68b10649955ab2ab4a7cfc4a6adee1c36b9611e3..da3f20f4341ae46b0cd29e10acd94e1092f081f5 100644 (file)
@@ -68,6 +68,11 @@ defmodule Pleroma.Docs.Markdown do
     IO.write(file, "  #{list_mark}`#{inspect(suggestion)}`\n")
   end
 
+  defp print_suggestions(file, {:list_behaviour_implementations, behaviour}) do
+    suggestions = Pleroma.Docs.Generator.list_behaviour_implementations(behaviour)
+    print_suggestions(file, suggestions)
+  end
+
   defp print_suggestions(_file, nil), do: nil
 
   defp print_suggestions(_file, ""), do: nil
index b4b47a31f2ffaff3d1095610f64e1f3499cb1454..2e54df47a386bb6e02eb402981e09c5567d08cdc 100644 (file)
@@ -4,6 +4,9 @@
 
 defmodule Pleroma.Plugs.AdminSecretAuthenticationPlug do
   import Plug.Conn
+
+  alias Pleroma.Plugs.OAuthScopesPlug
+  alias Pleroma.Plugs.RateLimiter
   alias Pleroma.User
 
   def init(options) do
@@ -11,7 +14,10 @@ defmodule Pleroma.Plugs.AdminSecretAuthenticationPlug do
   end
 
   def secret_token do
-    Pleroma.Config.get(:admin_token)
+    case Pleroma.Config.get(:admin_token) do
+      blank when blank in [nil, ""] -> nil
+      token -> token
+    end
   end
 
   def call(%{assigns: %{user: %User{}}} = conn, _), do: conn
@@ -26,9 +32,9 @@ defmodule Pleroma.Plugs.AdminSecretAuthenticationPlug do
 
   def authenticate(%{params: %{"admin_token" => admin_token}} = conn) do
     if admin_token == secret_token() do
-      assign(conn, :user, %User{is_admin: true})
+      assign_admin_user(conn)
     else
-      conn
+      handle_bad_token(conn)
     end
   end
 
@@ -36,8 +42,19 @@ defmodule Pleroma.Plugs.AdminSecretAuthenticationPlug do
     token = secret_token()
 
     case get_req_header(conn, "x-admin-token") do
-      [^token] -> assign(conn, :user, %User{is_admin: true})
-      _ -> conn
+      blank when blank in [[], [""]] -> conn
+      [^token] -> assign_admin_user(conn)
+      _ -> handle_bad_token(conn)
     end
   end
+
+  defp assign_admin_user(conn) do
+    conn
+    |> assign(:user, %User{is_admin: true})
+    |> OAuthScopesPlug.skip_plug()
+  end
+
+  defp handle_bad_token(conn) do
+    RateLimiter.call(conn, name: :authentication)
+  end
 end
index 2748102dff6e90bcdc21e9a9c4051b2d79cbe965..488a61d1d4940304423694a94c8127a43a8ca139 100644 (file)
@@ -7,37 +7,18 @@ defmodule Pleroma.Plugs.UserIsAdminPlug do
   import Plug.Conn
 
   alias Pleroma.User
-  alias Pleroma.Web.OAuth
 
   def init(options) do
     options
   end
 
-  def call(%{assigns: %{user: %User{is_admin: true}} = assigns} = conn, _) do
-    token = assigns[:token]
-
-    cond do
-      not Pleroma.Config.enforce_oauth_admin_scope_usage?() ->
-        conn
-
-      token && OAuth.Scopes.contains_admin_scopes?(token.scopes) ->
-        # Note: checking for _any_ admin scope presence, not necessarily fitting requested action.
-        #   Thus, controller must explicitly invoke OAuthScopesPlug to verify scope requirements.
-        #   Admin might opt out of admin scope for some apps to block any admin actions from them.
-        conn
-
-      true ->
-        fail(conn)
-    end
+  def call(%{assigns: %{user: %User{is_admin: true}}} = conn, _) do
+    conn
   end
 
   def call(conn, _) do
-    fail(conn)
-  end
-
-  defp fail(conn) do
     conn
-    |> render_error(:forbidden, "User is not an admin or OAuth admin scope is not granted.")
+    |> render_error(:forbidden, "User is not an admin.")
     |> halt()
   end
 end
diff --git a/lib/pleroma/upload/filter/exiftool.ex b/lib/pleroma/upload/filter/exiftool.ex
new file mode 100644 (file)
index 0000000..c7fb6ae
--- /dev/null
@@ -0,0 +1,18 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Upload.Filter.Exiftool do
+  @moduledoc """
+  Strips GPS related EXIF tags and overwrites the file in place.
+  Also strips or replaces filesystem metadata e.g., timestamps.
+  """
+  @behaviour Pleroma.Upload.Filter
+
+  def filter(%Pleroma.Upload{tempfile: file, content_type: "image" <> _}) do
+    System.cmd("exiftool", ["-overwrite_original", "-gps:all=", file], parallelism: true)
+    :ok
+  end
+
+  def filter(_), do: :ok
+end
index b9989f9018194122f2499918afedf2f88d73ad4f..9240e912d9a3db676b6f19a2a95e2b932a4bf661 100644 (file)
@@ -530,11 +530,21 @@ defmodule Pleroma.User do
   end
 
   defp put_emoji(changeset) do
-    bio = get_change(changeset, :bio)
-    name = get_change(changeset, :name)
+    emojified_fields = [:bio, :name, :raw_fields]
+
+    if Enum.any?(changeset.changes, fn {k, _} -> k in emojified_fields end) do
+      bio = Emoji.Formatter.get_emoji_map(get_field(changeset, :bio))
+      name = Emoji.Formatter.get_emoji_map(get_field(changeset, :name))
+
+      emoji = Map.merge(bio, name)
+
+      emoji =
+        changeset
+        |> get_field(:raw_fields)
+        |> Enum.reduce(emoji, fn x, acc ->
+          Map.merge(acc, Emoji.Formatter.get_emoji_map(x["name"] <> x["value"]))
+        end)
 
-    if bio || name do
-      emoji = Map.merge(Emoji.Formatter.get_emoji_map(bio), Emoji.Formatter.get_emoji_map(name))
       put_change(changeset, :emoji, emoji)
     else
       changeset
index 8da5cf938be96a3a12d5ab6130a9fb11757f173f..bc7b5d95a6925e98d5f67237d832f6be50322785 100644 (file)
@@ -1376,13 +1376,28 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
     end
   end
 
-  def maybe_handle_clashing_nickname(nickname) do
-    with %User{} = old_user <- User.get_by_nickname(nickname) do
-      Logger.info("Found an old user for #{nickname}, ap id is #{old_user.ap_id}, renaming.")
+  def maybe_handle_clashing_nickname(data) do
+    nickname = data[:nickname]
+
+    with %User{} = old_user <- User.get_by_nickname(nickname),
+         {_, false} <- {:ap_id_comparison, data[:ap_id] == old_user.ap_id} do
+      Logger.info(
+        "Found an old user for #{nickname}, the old ap id is #{old_user.ap_id}, new one is #{
+          data[:ap_id]
+        }, renaming."
+      )
 
       old_user
       |> User.remote_user_changeset(%{nickname: "#{old_user.id}.#{old_user.nickname}"})
       |> User.update_and_set_cache()
+    else
+      {:ap_id_comparison, true} ->
+        Logger.info(
+          "Found an old user for #{nickname}, but the ap id #{data[:ap_id]} is the same as the new user. Race condition? Not changing anything."
+        )
+
+      _ ->
+        nil
     end
   end
 
@@ -1398,7 +1413,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
           |> User.remote_user_changeset(data)
           |> User.update_and_set_cache()
         else
-          maybe_handle_clashing_nickname(data[:nickname])
+          maybe_handle_clashing_nickname(data)
 
           data
           |> User.remote_user_changeset()
index 884646cebf98ce45e85a342523e16ac41e2c28f5..f37bcab3e752493f090c671ef2955ed3ae2159c3 100644 (file)
@@ -62,15 +62,17 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
   def fix_summary(object), do: Map.put(object, "summary", "")
 
   def fix_addressing_list(map, field) do
+    addrs = map[field]
+
     cond do
-      is_binary(map[field]) ->
-        Map.put(map, field, [map[field]])
+      is_list(addrs) ->
+        Map.put(map, field, Enum.filter(addrs, &is_binary/1))
 
-      is_nil(map[field]) ->
-        Map.put(map, field, [])
+      is_binary(addrs) ->
+        Map.put(map, field, [addrs])
 
       true ->
-        map
+        Map.put(map, field, [])
     end
   end
 
index 7f60470cbb7a4a237131f4688df9e3a1ba0a2650..0df13007f67ccd01fc78ff5da859c0fd6b963924 100644 (file)
@@ -9,8 +9,6 @@ defmodule Pleroma.Web.AdminAPI.ConfigController do
   alias Pleroma.ConfigDB
   alias Pleroma.Plugs.OAuthScopesPlug
 
-  @descriptions Pleroma.Docs.JSON.compile()
-
   plug(Pleroma.Web.ApiSpec.CastAndValidate)
   plug(OAuthScopesPlug, %{scopes: ["write"], admin: true} when action == :update)
 
@@ -25,7 +23,7 @@ defmodule Pleroma.Web.AdminAPI.ConfigController do
   defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.Admin.ConfigOperation
 
   def descriptions(conn, _params) do
-    descriptions = Enum.filter(@descriptions, &whitelisted_config?/1)
+    descriptions = Enum.filter(Pleroma.Docs.JSON.compiled_descriptions(), &whitelisted_config?/1)
 
     json(conn, descriptions)
   end
index a258e8421c6e6877145ba85e76bec2e0094bd629..2a7f1a706bc737d1ed829fb6bf7d1c3bf015acf1 100644 (file)
@@ -29,6 +29,10 @@ defmodule Pleroma.Web.ApiSpec.Helpers do
     }
   end
 
+  def admin_api_params do
+    [Operation.parameter(:admin_token, :query, :string, "Allows authorization via admin token.")]
+  end
+
   def pagination_params do
     [
       Operation.parameter(:max_id, :query, :string, "Return items older than this ID"),
index 7b38a2ef4e053da0310de366676fb0f8b3a79b86..3a8380797dd5373d58fa78834f522b9fa7d839bc 100644 (file)
@@ -26,6 +26,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.ConfigOperation do
           %Schema{type: :boolean, default: false},
           "Get only saved in database settings"
         )
+        | admin_api_params()
       ],
       security: [%{"oAuth" => ["read"]}],
       responses: %{
@@ -41,6 +42,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.ConfigOperation do
       summary: "Update config settings",
       operationId: "AdminAPI.ConfigController.update",
       security: [%{"oAuth" => ["write"]}],
+      parameters: admin_api_params(),
       requestBody:
         request_body("Parameters", %Schema{
           type: :object,
@@ -73,6 +75,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.ConfigOperation do
       summary: "Get JSON with config descriptions.",
       operationId: "AdminAPI.ConfigController.descriptions",
       security: [%{"oAuth" => ["read"]}],
+      parameters: admin_api_params(),
       responses: %{
         200 =>
           Operation.response("Config Descriptions", "application/json", %Schema{
index d3af9db49213b712f65269efe5dd3626211b8651..801024d75f5eb63d823bb9f2f6af71c7d1c8fa23 100644 (file)
@@ -20,6 +20,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.InviteOperation do
       summary: "Get a list of generated invites",
       operationId: "AdminAPI.InviteController.index",
       security: [%{"oAuth" => ["read:invites"]}],
+      parameters: admin_api_params(),
       responses: %{
         200 =>
           Operation.response("Invites", "application/json", %Schema{
@@ -51,6 +52,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.InviteOperation do
       summary: "Create an account registration invite token",
       operationId: "AdminAPI.InviteController.create",
       security: [%{"oAuth" => ["write:invites"]}],
+      parameters: admin_api_params(),
       requestBody:
         request_body("Parameters", %Schema{
           type: :object,
@@ -71,6 +73,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.InviteOperation do
       summary: "Revoke invite by token",
       operationId: "AdminAPI.InviteController.revoke",
       security: [%{"oAuth" => ["write:invites"]}],
+      parameters: admin_api_params(),
       requestBody:
         request_body(
           "Parameters",
@@ -97,6 +100,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.InviteOperation do
       summary: "Sends registration invite via email",
       operationId: "AdminAPI.InviteController.email",
       security: [%{"oAuth" => ["write:invites"]}],
+      parameters: admin_api_params(),
       requestBody:
         request_body(
           "Parameters",
index 0358cfbad8ae095d5583eef03161987fbec4f2c7..20d033f66f9f88c4863c72af71d3c133e9f2a045 100644 (file)
@@ -33,6 +33,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.MediaProxyCacheOperation do
           %Schema{type: :integer, default: 50},
           "Number of statuses to return"
         )
+        | admin_api_params()
       ],
       responses: %{
         200 => success_response()
@@ -46,6 +47,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.MediaProxyCacheOperation do
       summary: "Remove a banned MediaProxy URL from Cachex",
       operationId: "AdminAPI.MediaProxyCacheController.delete",
       security: [%{"oAuth" => ["write:media_proxy_caches"]}],
+      parameters: admin_api_params(),
       requestBody:
         request_body(
           "Parameters",
@@ -71,6 +73,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.MediaProxyCacheOperation do
       summary: "Purge and optionally ban a MediaProxy URL",
       operationId: "AdminAPI.MediaProxyCacheController.purge",
       security: [%{"oAuth" => ["write:media_proxy_caches"]}],
+      parameters: admin_api_params(),
       requestBody:
         request_body(
           "Parameters",
index fbc9f80d7809615847744f837e85ab11af6323bf..a75f3e6229442fd0023708ea8bad824103eb0903 100644 (file)
@@ -36,6 +36,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.OAuthAppOperation do
           %Schema{type: :integer, default: 50},
           "Number of apps to return"
         )
+        | admin_api_params()
       ],
       responses: %{
         200 =>
@@ -72,6 +73,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.OAuthAppOperation do
       summary: "Create OAuth App",
       operationId: "AdminAPI.OAuthAppController.create",
       requestBody: request_body("Parameters", create_request()),
+      parameters: admin_api_params(),
       security: [%{"oAuth" => ["write"]}],
       responses: %{
         200 => Operation.response("App", "application/json", oauth_app()),
@@ -85,7 +87,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.OAuthAppOperation do
       tags: ["Admin", "oAuth Apps"],
       summary: "Update OAuth App",
       operationId: "AdminAPI.OAuthAppController.update",
-      parameters: [id_param()],
+      parameters: [id_param() | admin_api_params()],
       security: [%{"oAuth" => ["write"]}],
       requestBody: request_body("Parameters", update_request()),
       responses: %{
@@ -103,7 +105,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.OAuthAppOperation do
       tags: ["Admin", "oAuth Apps"],
       summary: "Delete OAuth App",
       operationId: "AdminAPI.OAuthAppController.delete",
-      parameters: [id_param()],
+      parameters: [id_param() | admin_api_params()],
       security: [%{"oAuth" => ["write"]}],
       responses: %{
         204 => no_content_response(),
index 7672cb467a4d490cc1c1c173c8a87e567e647077..67ee5eee02900b0a0a7db09985b902c43653edd3 100644 (file)
@@ -19,6 +19,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.RelayOperation do
       summary: "List Relays",
       operationId: "AdminAPI.RelayController.index",
       security: [%{"oAuth" => ["read"]}],
+      parameters: admin_api_params(),
       responses: %{
         200 =>
           Operation.response("Response", "application/json", %Schema{
@@ -41,6 +42,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.RelayOperation do
       summary: "Follow a Relay",
       operationId: "AdminAPI.RelayController.follow",
       security: [%{"oAuth" => ["write:follows"]}],
+      parameters: admin_api_params(),
       requestBody:
         request_body("Parameters", %Schema{
           type: :object,
@@ -64,6 +66,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.RelayOperation do
       summary: "Unfollow a Relay",
       operationId: "AdminAPI.RelayController.unfollow",
       security: [%{"oAuth" => ["write:follows"]}],
+      parameters: admin_api_params(),
       requestBody:
         request_body("Parameters", %Schema{
           type: :object,
index 15e78bfafe714c8ce6cd4b3048d50944ed7ab0c1..3bb7ec49ec7a28e7ad7d33a520cd3694d5e47ae8 100644 (file)
@@ -48,6 +48,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.ReportOperation do
           %Schema{type: :integer, default: 50},
           "Number number of log entries per page"
         )
+        | admin_api_params()
       ],
       responses: %{
         200 =>
@@ -71,7 +72,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.ReportOperation do
       tags: ["Admin", "Reports"],
       summary: "Get an individual report",
       operationId: "AdminAPI.ReportController.show",
-      parameters: [id_param()],
+      parameters: [id_param() | admin_api_params()],
       security: [%{"oAuth" => ["read:reports"]}],
       responses: %{
         200 => Operation.response("Report", "application/json", report()),
@@ -86,6 +87,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.ReportOperation do
       summary: "Change the state of one or multiple reports",
       operationId: "AdminAPI.ReportController.update",
       security: [%{"oAuth" => ["write:reports"]}],
+      parameters: admin_api_params(),
       requestBody: request_body("Parameters", update_request(), required: true),
       responses: %{
         204 => no_content_response(),
@@ -100,7 +102,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.ReportOperation do
       tags: ["Admin", "Reports"],
       summary: "Create report note",
       operationId: "AdminAPI.ReportController.notes_create",
-      parameters: [id_param()],
+      parameters: [id_param() | admin_api_params()],
       requestBody:
         request_body("Parameters", %Schema{
           type: :object,
@@ -124,6 +126,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.ReportOperation do
       parameters: [
         Operation.parameter(:report_id, :path, :string, "Report ID"),
         Operation.parameter(:id, :path, :string, "Note ID")
+        | admin_api_params()
       ],
       security: [%{"oAuth" => ["write:reports"]}],
       responses: %{
index 745399b4b08bcda7472e9589f28e46b3c5038472..c105838a4ee41f5b7d038eb7d6f6d3a912733eba 100644 (file)
@@ -55,6 +55,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.StatusOperation do
           %Schema{type: :integer, default: 50},
           "Number of statuses to return"
         )
+        | admin_api_params()
       ],
       responses: %{
         200 =>
@@ -71,7 +72,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.StatusOperation do
       tags: ["Admin", "Statuses"],
       summary: "Show Status",
       operationId: "AdminAPI.StatusController.show",
-      parameters: [id_param()],
+      parameters: [id_param() | admin_api_params()],
       security: [%{"oAuth" => ["read:statuses"]}],
       responses: %{
         200 => Operation.response("Status", "application/json", status()),
@@ -85,7 +86,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.StatusOperation do
       tags: ["Admin", "Statuses"],
       summary: "Change the scope of an individual reported status",
       operationId: "AdminAPI.StatusController.update",
-      parameters: [id_param()],
+      parameters: [id_param() | admin_api_params()],
       security: [%{"oAuth" => ["write:statuses"]}],
       requestBody: request_body("Parameters", update_request(), required: true),
       responses: %{
@@ -100,7 +101,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.StatusOperation do
       tags: ["Admin", "Statuses"],
       summary: "Delete an individual reported status",
       operationId: "AdminAPI.StatusController.delete",
-      parameters: [id_param()],
+      parameters: [id_param() | admin_api_params()],
       security: [%{"oAuth" => ["write:statuses"]}],
       responses: %{
         200 => empty_object_response(),
diff --git a/mix.exs b/mix.exs
index d7992ee37181180cfbe687e92e70193df87e4364..741f917e68fd7b7e4b81ee365d9b166457d77b10 100644 (file)
--- a/mix.exs
+++ b/mix.exs
@@ -90,8 +90,6 @@ defmodule Pleroma.Mixfile do
   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.
index 0e1cf37ebde3a9a49b8c911d9092ec5334e39995..e337226a736c63c0e7d5d319c9b3ebc7f42b5ee9 100644 (file)
@@ -90,110 +90,100 @@ msgid "must be equal to %{number}"
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/common_api/common_api.ex:421
+#: lib/pleroma/web/common_api/common_api.ex:505
 msgid "Account not found"
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/common_api/common_api.ex:249
+#: lib/pleroma/web/common_api/common_api.ex:339
 msgid "Already voted"
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/oauth/oauth_controller.ex:360
+#: lib/pleroma/web/oauth/oauth_controller.ex:359
 msgid "Bad request"
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:425
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:426
 msgid "Can't delete object"
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/mastodon_api/controllers/status_controller.ex:196
-msgid "Can't delete this post"
-msgstr ""
-
-#, elixir-format
-#: lib/pleroma/web/controller_helper.ex:95
-#: lib/pleroma/web/controller_helper.ex:101
+#: lib/pleroma/web/controller_helper.ex:105
+#: lib/pleroma/web/controller_helper.ex:111
 msgid "Can't display this activity"
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:227
-#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:254
+#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:285
 msgid "Can't find user"
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/pleroma_api/controllers/account_controller.ex:114
+#: lib/pleroma/web/pleroma_api/controllers/account_controller.ex:61
 msgid "Can't get favorites"
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:437
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:438
 msgid "Can't like object"
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/common_api/utils.ex:556
+#: lib/pleroma/web/common_api/utils.ex:563
 msgid "Cannot post an empty status without attachments"
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/common_api/utils.ex:504
+#: lib/pleroma/web/common_api/utils.ex:511
 msgid "Comment must be up to %{max_size} characters"
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/config/config_db.ex:222
+#: lib/pleroma/config/config_db.ex:191
 msgid "Config with params %{params} not found"
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/common_api/common_api.ex:95
+#: lib/pleroma/web/common_api/common_api.ex:181
+#: lib/pleroma/web/common_api/common_api.ex:185
 msgid "Could not delete"
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/common_api/common_api.ex:141
+#: lib/pleroma/web/common_api/common_api.ex:231
 msgid "Could not favorite"
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/common_api/common_api.ex:370
+#: lib/pleroma/web/common_api/common_api.ex:453
 msgid "Could not pin"
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/common_api/common_api.ex:112
-msgid "Could not repeat"
-msgstr ""
-
-#, elixir-format
-#: lib/pleroma/web/common_api/common_api.ex:188
+#: lib/pleroma/web/common_api/common_api.ex:278
 msgid "Could not unfavorite"
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/common_api/common_api.ex:380
+#: lib/pleroma/web/common_api/common_api.ex:463
 msgid "Could not unpin"
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/common_api/common_api.ex:126
+#: lib/pleroma/web/common_api/common_api.ex:216
 msgid "Could not unrepeat"
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/common_api/common_api.ex:428
-#: lib/pleroma/web/common_api/common_api.ex:437
+#: lib/pleroma/web/common_api/common_api.ex:512
+#: lib/pleroma/web/common_api/common_api.ex:521
 msgid "Could not update state"
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex:202
+#: lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex:207
 msgid "Error."
 msgstr ""
 
@@ -203,8 +193,8 @@ msgid "Invalid CAPTCHA"
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:117
-#: lib/pleroma/web/oauth/oauth_controller.ex:569
+#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:116
+#: lib/pleroma/web/oauth/oauth_controller.ex:568
 msgid "Invalid credentials"
 msgstr ""
 
@@ -214,22 +204,22 @@ msgid "Invalid credentials."
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/common_api/common_api.ex:265
+#: lib/pleroma/web/common_api/common_api.ex:355
 msgid "Invalid indices"
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/admin_api/admin_api_controller.ex:1147
+#: lib/pleroma/web/admin_api/controllers/fallback_controller.ex:29
 msgid "Invalid parameters"
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/common_api/utils.ex:411
+#: lib/pleroma/web/common_api/utils.ex:414
 msgid "Invalid password."
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:187
+#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:220
 msgid "Invalid request"
 msgstr ""
 
@@ -239,44 +229,44 @@ msgid "Kocaptcha service unavailable"
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:113
+#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:112
 msgid "Missing parameters"
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/common_api/utils.ex:540
+#: lib/pleroma/web/common_api/utils.ex:547
 msgid "No such conversation"
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/admin_api/admin_api_controller.ex:439
-#: lib/pleroma/web/admin_api/admin_api_controller.ex:465 lib/pleroma/web/admin_api/admin_api_controller.ex:507
+#: lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:388
+#: lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:414 lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:456
 msgid "No such permission_group"
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/plugs/uploaded_media.ex:74
-#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:485 lib/pleroma/web/admin_api/admin_api_controller.ex:1135
-#: lib/pleroma/web/feed/user_controller.ex:73 lib/pleroma/web/ostatus/ostatus_controller.ex:143
+#: lib/pleroma/plugs/uploaded_media.ex:84
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:486 lib/pleroma/web/admin_api/controllers/fallback_controller.ex:11
+#: lib/pleroma/web/feed/user_controller.ex:71 lib/pleroma/web/ostatus/ostatus_controller.ex:143
 msgid "Not found"
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/common_api/common_api.ex:241
+#: lib/pleroma/web/common_api/common_api.ex:331
 msgid "Poll's author can't vote"
 msgstr ""
 
 #, elixir-format
 #: lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex:20
 #: lib/pleroma/web/mastodon_api/controllers/poll_controller.ex:37 lib/pleroma/web/mastodon_api/controllers/poll_controller.ex:49
-#: lib/pleroma/web/mastodon_api/controllers/poll_controller.ex:50 lib/pleroma/web/mastodon_api/controllers/status_controller.ex:290
+#: lib/pleroma/web/mastodon_api/controllers/poll_controller.ex:50 lib/pleroma/web/mastodon_api/controllers/status_controller.ex:306
 #: lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex:71
 msgid "Record not found"
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/admin_api/admin_api_controller.ex:1153
-#: lib/pleroma/web/feed/user_controller.ex:79 lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex:32
+#: lib/pleroma/web/admin_api/controllers/fallback_controller.ex:35
+#: lib/pleroma/web/feed/user_controller.ex:77 lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex:36
 #: lib/pleroma/web/ostatus/ostatus_controller.ex:149
 msgid "Something went wrong"
 msgstr ""
@@ -287,7 +277,7 @@ msgid "The message visibility must be direct"
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/common_api/utils.ex:566
+#: lib/pleroma/web/common_api/utils.ex:573
 msgid "The status is over the character limit"
 msgstr ""
 
@@ -302,65 +292,65 @@ msgid "Throttled"
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/common_api/common_api.ex:266
+#: lib/pleroma/web/common_api/common_api.ex:356
 msgid "Too many choices"
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:442
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:443
 msgid "Unhandled activity type"
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/admin_api/admin_api_controller.ex:536
+#: lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:485
 msgid "You can't revoke your own admin status."
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/oauth/oauth_controller.ex:218
-#: lib/pleroma/web/oauth/oauth_controller.ex:309
+#: lib/pleroma/web/oauth/oauth_controller.ex:221
+#: lib/pleroma/web/oauth/oauth_controller.ex:308
 msgid "Your account is currently disabled"
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/oauth/oauth_controller.ex:180
-#: lib/pleroma/web/oauth/oauth_controller.ex:332
+#: lib/pleroma/web/oauth/oauth_controller.ex:183
+#: lib/pleroma/web/oauth/oauth_controller.ex:331
 msgid "Your login is missing a confirmed e-mail address"
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:389
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:390
 msgid "can't read inbox of %{nickname} as %{as_nickname}"
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:472
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:473
 msgid "can't update outbox of %{nickname} as %{as_nickname}"
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/common_api/common_api.ex:388
+#: lib/pleroma/web/common_api/common_api.ex:471
 msgid "conversation is already muted"
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:316
-#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:491
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:314
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:492
 msgid "error"
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/pleroma_api/controllers/mascot_controller.ex:29
+#: lib/pleroma/web/pleroma_api/controllers/mascot_controller.ex:32
 msgid "mascots can only be images"
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:60
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:62
 msgid "not found"
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/oauth/oauth_controller.ex:395
+#: lib/pleroma/web/oauth/oauth_controller.ex:394
 msgid "Bad OAuth request."
 msgstr ""
 
@@ -375,17 +365,17 @@ msgid "CAPTCHA expired"
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/plugs/uploaded_media.ex:55
+#: lib/pleroma/plugs/uploaded_media.ex:57
 msgid "Failed"
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/oauth/oauth_controller.ex:411
+#: lib/pleroma/web/oauth/oauth_controller.ex:410
 msgid "Failed to authenticate: %{message}."
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/oauth/oauth_controller.ex:442
+#: lib/pleroma/web/oauth/oauth_controller.ex:441
 msgid "Failed to set up user account."
 msgstr ""
 
@@ -395,7 +385,7 @@ msgid "Insufficient permissions: %{permissions}."
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/plugs/uploaded_media.ex:94
+#: lib/pleroma/plugs/uploaded_media.ex:104
 msgid "Internal Error"
 msgstr ""
 
@@ -411,12 +401,12 @@ msgid "Invalid answer data"
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/nodeinfo/nodeinfo_controller.ex:128
+#: lib/pleroma/web/nodeinfo/nodeinfo_controller.ex:33
 msgid "Nodeinfo schema version not handled"
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/oauth/oauth_controller.ex:169
+#: lib/pleroma/web/oauth/oauth_controller.ex:172
 msgid "This action is outside the authorized scopes"
 msgstr ""
 
@@ -426,13 +416,13 @@ msgid "Unknown error, please check the details and try again."
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/oauth/oauth_controller.ex:116
-#: lib/pleroma/web/oauth/oauth_controller.ex:155
+#: lib/pleroma/web/oauth/oauth_controller.ex:119
+#: lib/pleroma/web/oauth/oauth_controller.ex:158
 msgid "Unlisted redirect_uri."
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/oauth/oauth_controller.ex:391
+#: lib/pleroma/web/oauth/oauth_controller.ex:390
 msgid "Unsupported OAuth provider: %{provider}."
 msgstr ""
 
@@ -452,12 +442,12 @@ msgid "CAPTCHA Error"
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/common_api/common_api.ex:200
+#: lib/pleroma/web/common_api/common_api.ex:290
 msgid "Could not add reaction emoji"
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/common_api/common_api.ex:211
+#: lib/pleroma/web/common_api/common_api.ex:301
 msgid "Could not remove reaction emoji"
 msgstr ""
 
@@ -472,39 +462,45 @@ msgid "List not found"
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:124
+#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:123
 msgid "Missing parameter: %{name}"
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/oauth/oauth_controller.ex:207
-#: lib/pleroma/web/oauth/oauth_controller.ex:322
+#: lib/pleroma/web/oauth/oauth_controller.ex:210
+#: lib/pleroma/web/oauth/oauth_controller.ex:321
 msgid "Password reset is required"
 msgstr ""
 
 #, elixir-format
 #: lib/pleroma/tests/auth_test_controller.ex:9
-#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:6 lib/pleroma/web/admin_api/admin_api_controller.ex:6
-#: lib/pleroma/web/controller_helper.ex:6 lib/pleroma/web/fallback_redirect_controller.ex:6
-#: lib/pleroma/web/feed/tag_controller.ex:6 lib/pleroma/web/feed/user_controller.ex:6
-#: lib/pleroma/web/mailer/subscription_controller.ex:2 lib/pleroma/web/masto_fe_controller.ex:6
-#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/app_controller.ex:6
-#: lib/pleroma/web/mastodon_api/controllers/auth_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/conversation_controller.ex:6
-#: lib/pleroma/web/mastodon_api/controllers/custom_emoji_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/domain_block_controller.ex:6
-#: lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/filter_controller.ex:6
-#: lib/pleroma/web/mastodon_api/controllers/follow_request_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/instance_controller.ex:6
-#: lib/pleroma/web/mastodon_api/controllers/list_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/marker_controller.ex:6
-#: lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex:14 lib/pleroma/web/mastodon_api/controllers/media_controller.ex:6
-#: lib/pleroma/web/mastodon_api/controllers/notification_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/poll_controller.ex:6
-#: lib/pleroma/web/mastodon_api/controllers/report_controller.ex:8 lib/pleroma/web/mastodon_api/controllers/scheduled_activity_controller.ex:6
-#: lib/pleroma/web/mastodon_api/controllers/search_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/status_controller.ex:6
-#: lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex:7 lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex:6
-#: lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex:6 lib/pleroma/web/media_proxy/media_proxy_controller.ex:6
-#: lib/pleroma/web/mongooseim/mongoose_im_controller.ex:6 lib/pleroma/web/nodeinfo/nodeinfo_controller.ex:6
-#: lib/pleroma/web/oauth/fallback_controller.ex:6 lib/pleroma/web/oauth/mfa_controller.ex:10
-#: lib/pleroma/web/oauth/oauth_controller.ex:6 lib/pleroma/web/ostatus/ostatus_controller.ex:6
-#: lib/pleroma/web/pleroma_api/controllers/account_controller.ex:6 lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex:2
-#: lib/pleroma/web/pleroma_api/controllers/mascot_controller.ex:6 lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex:6
+#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:6 lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:6
+#: lib/pleroma/web/admin_api/controllers/config_controller.ex:6 lib/pleroma/web/admin_api/controllers/fallback_controller.ex:6
+#: lib/pleroma/web/admin_api/controllers/invite_controller.ex:6 lib/pleroma/web/admin_api/controllers/media_proxy_cache_controller.ex:6
+#: lib/pleroma/web/admin_api/controllers/oauth_app_controller.ex:6 lib/pleroma/web/admin_api/controllers/relay_controller.ex:6
+#: lib/pleroma/web/admin_api/controllers/report_controller.ex:6 lib/pleroma/web/admin_api/controllers/status_controller.ex:6
+#: lib/pleroma/web/controller_helper.ex:6 lib/pleroma/web/embed_controller.ex:6
+#: lib/pleroma/web/fallback_redirect_controller.ex:6 lib/pleroma/web/feed/tag_controller.ex:6
+#: lib/pleroma/web/feed/user_controller.ex:6 lib/pleroma/web/mailer/subscription_controller.ex:2
+#: lib/pleroma/web/masto_fe_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/account_controller.ex:6
+#: lib/pleroma/web/mastodon_api/controllers/app_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/auth_controller.ex:6
+#: lib/pleroma/web/mastodon_api/controllers/conversation_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/custom_emoji_controller.ex:6
+#: lib/pleroma/web/mastodon_api/controllers/domain_block_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex:6
+#: lib/pleroma/web/mastodon_api/controllers/filter_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/follow_request_controller.ex:6
+#: lib/pleroma/web/mastodon_api/controllers/instance_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/list_controller.ex:6
+#: lib/pleroma/web/mastodon_api/controllers/marker_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex:14
+#: lib/pleroma/web/mastodon_api/controllers/media_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/notification_controller.ex:6
+#: lib/pleroma/web/mastodon_api/controllers/poll_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/report_controller.ex:8
+#: lib/pleroma/web/mastodon_api/controllers/scheduled_activity_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/search_controller.ex:6
+#: lib/pleroma/web/mastodon_api/controllers/status_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex:7
+#: lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex:6
+#: lib/pleroma/web/media_proxy/media_proxy_controller.ex:6 lib/pleroma/web/mongooseim/mongoose_im_controller.ex:6
+#: lib/pleroma/web/nodeinfo/nodeinfo_controller.ex:6 lib/pleroma/web/oauth/fallback_controller.ex:6
+#: lib/pleroma/web/oauth/mfa_controller.ex:10 lib/pleroma/web/oauth/oauth_controller.ex:6
+#: lib/pleroma/web/ostatus/ostatus_controller.ex:6 lib/pleroma/web/pleroma_api/controllers/account_controller.ex:6
+#: lib/pleroma/web/pleroma_api/controllers/chat_controller.ex:5 lib/pleroma/web/pleroma_api/controllers/conversation_controller.ex:6
+#: lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex:2 lib/pleroma/web/pleroma_api/controllers/emoji_reaction_controller.ex:6
+#: lib/pleroma/web/pleroma_api/controllers/mascot_controller.ex:6 lib/pleroma/web/pleroma_api/controllers/notification_controller.ex:6
 #: lib/pleroma/web/pleroma_api/controllers/scrobble_controller.ex:6
 #: lib/pleroma/web/pleroma_api/controllers/two_factor_authentication_controller.ex:7 lib/pleroma/web/static_fe/static_fe_controller.ex:6
 #: lib/pleroma/web/twitter_api/controllers/password_controller.ex:10 lib/pleroma/web/twitter_api/controllers/remote_follow_controller.ex:6
@@ -519,46 +515,56 @@ msgid "Two-factor authentication enabled, you must use a access token."
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex:210
+#: lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex:210
 msgid "Unexpected error occurred while adding file to pack."
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex:138
+#: lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex:138
 msgid "Unexpected error occurred while creating pack."
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex:278
+#: lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex:278
 msgid "Unexpected error occurred while removing file from pack."
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex:250
+#: lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex:250
 msgid "Unexpected error occurred while updating file in pack."
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex:179
+#: lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex:179
 msgid "Unexpected error occurred while updating pack metadata."
 msgstr ""
 
-#, elixir-format
-#: lib/pleroma/plugs/user_is_admin_plug.ex:40
-msgid "User is not an admin or OAuth admin scope is not granted."
-msgstr ""
-
 #, elixir-format
 #: lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex:61
 msgid "Web push subscription is disabled on this Pleroma instance"
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/admin_api/admin_api_controller.ex:502
+#: lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:451
 msgid "You can't revoke your own admin/moderator status."
 msgstr ""
 
 #, elixir-format
-#: lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex:105
+#: lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex:126
 msgid "authorization required for timeline view"
 msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex:24
+msgid "Access denied"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:282
+msgid "This API requires an authenticated user"
+msgstr ""
+
+#, elixir-format
+#: lib/pleroma/plugs/user_is_admin_plug.ex:21
+msgid "User is not an admin."
+msgstr ""
index 406a297d1b1d1f8dc550c1fef1cc9151539c01dc..cd0cd6c658b1b7af72b2165374213ef9afe1bbdb 100644 (file)
@@ -562,11 +562,11 @@ msgstr "Errore inaspettato durante l'aggiornamento del file nel pacchetto."
 msgid "Unexpected error occurred while updating pack metadata."
 msgstr "Errore inaspettato durante l'aggiornamento dei metadati del pacchetto."
 
-#: lib/pleroma/plugs/user_is_admin_plug.ex:40
+#: lib/pleroma/plugs/user_is_admin_plug.ex:21
 #, elixir-format
-msgid "User is not an admin or OAuth admin scope is not granted."
+msgid "User is not an admin."
 msgstr ""
-"L'utente non è un amministratore o non ha ricevuto questa autorizzazione "
+"L'utente non è un amministratore."
 "OAuth."
 
 #: lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex:61
index 3118f6b5d9eb749145d1c870954d8467be006459..cfcb05fe67a56dc508954f936db29cec6ec0dacf 100644 (file)
@@ -559,9 +559,9 @@ msgstr ""
 msgid "Unexpected error occurred while updating pack metadata."
 msgstr ""
 
-#: lib/pleroma/plugs/user_is_admin_plug.ex:40
+#: lib/pleroma/plugs/user_is_admin_plug.ex:21
 #, elixir-format
-msgid "User is not an admin or OAuth admin scope is not granted."
+msgid "User is not an admin."
 msgstr ""
 
 #: lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex:61
index 7241d8a0a95b42300f5fd8ce13877557b1fd4257..653ea00a162077982760468a31587ba1826711f4 100644 (file)
@@ -566,9 +566,9 @@ msgstr "Nieoczekiwany błąd podczas zmieniania pliku w paczce."
 msgid "Unexpected error occurred while updating pack metadata."
 msgstr "Nieoczekiwany błąd podczas zmieniania metadanych paczki."
 
-#: lib/pleroma/plugs/user_is_admin_plug.ex:40
+#: lib/pleroma/plugs/user_is_admin_plug.ex:21
 #, elixir-format
-msgid "User is not an admin or OAuth admin scope is not granted."
+msgid "User is not an admin."
 msgstr ""
 
 #: lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex:61
diff --git a/priv/repo/migrations/20200714081657_oban_2_0_config_changes.exs b/priv/repo/migrations/20200714081657_oban_2_0_config_changes.exs
new file mode 100644 (file)
index 0000000..c54bb25
--- /dev/null
@@ -0,0 +1,27 @@
+defmodule Elixir.Pleroma.Repo.Migrations.Oban20ConfigChanges do
+  use Ecto.Migration
+  import Ecto.Query
+  alias Pleroma.ConfigDB
+  alias Pleroma.Repo
+
+  def change do
+    config_entry =
+      from(c in ConfigDB, where: c.group == ^":pleroma" and c.key == ^"Oban")
+      |> select([c], struct(c, [:value, :id]))
+      |> Repo.one()
+
+    if config_entry do
+      %{value: value} = config_entry
+
+      value =
+        case Keyword.fetch(value, :verbose) do
+          {:ok, log} -> Keyword.put_new(value, :log, log)
+          _ -> value
+        end
+        |> Keyword.drop([:verbose, :prune])
+
+      Ecto.Changeset.change(config_entry, %{value: value})
+      |> Repo.update()
+    end
+  end
+end
index 9c9f4357b2836a116d84c0d05d528e4aee2484ab..b32918a69339dd166e3644f1bc11751c69256b90 100644 (file)
@@ -13,21 +13,13 @@ defmodule Pleroma.Docs.GeneratorTest do
           key: :uploader,
           type: :module,
           description: "",
-          suggestions:
-            Generator.list_modules_in_dir(
-              "lib/pleroma/upload/filter",
-              "Elixir.Pleroma.Upload.Filter."
-            )
+          suggestions: {:list_behaviour_implementations, Pleroma.Upload.Filter}
         },
         %{
           key: :filters,
           type: {:list, :module},
           description: "",
-          suggestions:
-            Generator.list_modules_in_dir(
-              "lib/pleroma/web/activity_pub/mrf",
-              "Elixir.Pleroma.Web.ActivityPub.MRF."
-            )
+          suggestions: {:list_behaviour_implementations, Pleroma.Web.ActivityPub.MRF}
         },
         %{
           key: Pleroma.Upload,
diff --git a/test/fixtures/DSCN0010.jpg b/test/fixtures/DSCN0010.jpg
new file mode 100644 (file)
index 0000000..4a2c155
Binary files /dev/null and b/test/fixtures/DSCN0010.jpg differ
index 100016c62842461e987b0c04f82efa71fe6a45a2..89df03c4b719bd56c33fa11e44dd0060fd91f0e4 100644 (file)
@@ -4,9 +4,14 @@
 
 defmodule Pleroma.Plugs.AdminSecretAuthenticationPlugTest do
   use Pleroma.Web.ConnCase, async: true
+
+  import Mock
   import Pleroma.Factory
 
   alias Pleroma.Plugs.AdminSecretAuthenticationPlug
+  alias Pleroma.Plugs.OAuthScopesPlug
+  alias Pleroma.Plugs.PlugHelper
+  alias Pleroma.Plugs.RateLimiter
 
   test "does nothing if a user is assigned", %{conn: conn} do
     user = insert(:user)
@@ -25,6 +30,10 @@ defmodule Pleroma.Plugs.AdminSecretAuthenticationPlugTest do
   describe "when secret set it assigns an admin user" do
     setup do: clear_config([:admin_token])
 
+    setup_with_mocks([{RateLimiter, [:passthrough], []}]) do
+      :ok
+    end
+
     test "with `admin_token` query parameter", %{conn: conn} do
       Pleroma.Config.put(:admin_token, "password123")
 
@@ -33,12 +42,14 @@ defmodule Pleroma.Plugs.AdminSecretAuthenticationPlugTest do
         |> AdminSecretAuthenticationPlug.call(%{})
 
       refute conn.assigns[:user]
+      assert called(RateLimiter.call(conn, name: :authentication))
 
       conn =
         %{conn | params: %{"admin_token" => "password123"}}
         |> AdminSecretAuthenticationPlug.call(%{})
 
       assert conn.assigns[:user].is_admin
+      assert PlugHelper.plug_skipped?(conn, OAuthScopesPlug)
     end
 
     test "with `x-admin-token` HTTP header", %{conn: conn} do
@@ -50,6 +61,7 @@ defmodule Pleroma.Plugs.AdminSecretAuthenticationPlugTest do
         |> AdminSecretAuthenticationPlug.call(%{})
 
       refute conn.assigns[:user]
+      assert called(RateLimiter.call(conn, name: :authentication))
 
       conn =
         conn
@@ -57,6 +69,7 @@ defmodule Pleroma.Plugs.AdminSecretAuthenticationPlugTest do
         |> AdminSecretAuthenticationPlug.call(%{})
 
       assert conn.assigns[:user].is_admin
+      assert PlugHelper.plug_skipped?(conn, OAuthScopesPlug)
     end
   end
 end
index fd6a50e534e62079d33993470f000322fac93792..8bc00e44497a58dafcdb48600fe07ca88880955f 100644 (file)
@@ -8,112 +8,30 @@ defmodule Pleroma.Plugs.UserIsAdminPlugTest do
   alias Pleroma.Plugs.UserIsAdminPlug
   import Pleroma.Factory
 
-  describe "unless [:auth, :enforce_oauth_admin_scope_usage]," do
-    setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage], false)
+  test "accepts a user that is an admin" do
+    user = insert(:user, is_admin: true)
 
-    test "accepts a user that is an admin" do
-      user = insert(:user, is_admin: true)
+    conn = assign(build_conn(), :user, user)
 
-      conn = assign(build_conn(), :user, user)
+    ret_conn = UserIsAdminPlug.call(conn, %{})
 
-      ret_conn = UserIsAdminPlug.call(conn, %{})
-
-      assert conn == ret_conn
-    end
-
-    test "denies a user that isn't an admin" do
-      user = insert(:user)
-
-      conn =
-        build_conn()
-        |> assign(:user, user)
-        |> UserIsAdminPlug.call(%{})
-
-      assert conn.status == 403
-    end
-
-    test "denies when a user isn't set" do
-      conn = UserIsAdminPlug.call(build_conn(), %{})
-
-      assert conn.status == 403
-    end
+    assert conn == ret_conn
   end
 
-  describe "with [:auth, :enforce_oauth_admin_scope_usage]," do
-    setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage], true)
-
-    setup do
-      admin_user = insert(:user, is_admin: true)
-      non_admin_user = insert(:user, is_admin: false)
-      blank_user = nil
-
-      {:ok, %{users: [admin_user, non_admin_user, blank_user]}}
-    end
-
-    test "if token has any of admin scopes, accepts a user that is an admin", %{conn: conn} do
-      user = insert(:user, is_admin: true)
-      token = insert(:oauth_token, user: user, scopes: ["admin:something"])
-
-      conn =
-        conn
-        |> assign(:user, user)
-        |> assign(:token, token)
+  test "denies a user that isn't an admin" do
+    user = insert(:user)
 
-      ret_conn = UserIsAdminPlug.call(conn, %{})
+    conn =
+      build_conn()
+      |> assign(:user, user)
+      |> UserIsAdminPlug.call(%{})
 
-      assert conn == ret_conn
-    end
-
-    test "if token has any of admin scopes, denies a user that isn't an admin", %{conn: conn} do
-      user = insert(:user, is_admin: false)
-      token = insert(:oauth_token, user: user, scopes: ["admin:something"])
-
-      conn =
-        conn
-        |> assign(:user, user)
-        |> assign(:token, token)
-        |> UserIsAdminPlug.call(%{})
-
-      assert conn.status == 403
-    end
-
-    test "if token has any of admin scopes, denies when a user isn't set", %{conn: conn} do
-      token = insert(:oauth_token, scopes: ["admin:something"])
-
-      conn =
-        conn
-        |> assign(:user, nil)
-        |> assign(:token, token)
-        |> UserIsAdminPlug.call(%{})
-
-      assert conn.status == 403
-    end
-
-    test "if token lacks admin scopes, denies users regardless of is_admin flag",
-         %{users: users} do
-      for user <- users do
-        token = insert(:oauth_token, user: user)
-
-        conn =
-          build_conn()
-          |> assign(:user, user)
-          |> assign(:token, token)
-          |> UserIsAdminPlug.call(%{})
-
-        assert conn.status == 403
-      end
-    end
+    assert conn.status == 403
+  end
 
-    test "if token is missing, denies users regardless of is_admin flag", %{users: users} do
-      for user <- users do
-        conn =
-          build_conn()
-          |> assign(:user, user)
-          |> assign(:token, nil)
-          |> UserIsAdminPlug.call(%{})
+  test "denies when a user isn't set" do
+    conn = UserIsAdminPlug.call(build_conn(), %{})
 
-        assert conn.status == 403
-      end
-    end
+    assert conn.status == 403
   end
 end
diff --git a/test/upload/filter/exiftool_test.exs b/test/upload/filter/exiftool_test.exs
new file mode 100644 (file)
index 0000000..a1b7e46
--- /dev/null
@@ -0,0 +1,31 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Upload.Filter.ExiftoolTest do
+  use Pleroma.DataCase
+  alias Pleroma.Upload.Filter
+
+  test "apply exiftool filter" do
+    File.cp!(
+      "test/fixtures/DSCN0010.jpg",
+      "test/fixtures/DSCN0010_tmp.jpg"
+    )
+
+    upload = %Pleroma.Upload{
+      name: "image_with_GPS_data.jpg",
+      content_type: "image/jpg",
+      path: Path.absname("test/fixtures/DSCN0010.jpg"),
+      tempfile: Path.absname("test/fixtures/DSCN0010_tmp.jpg")
+    }
+
+    assert Filter.Exiftool.filter(upload) == :ok
+
+    {exif_original, 0} = System.cmd("exiftool", ["test/fixtures/DSCN0010.jpg"])
+    {exif_filtered, 0} = System.cmd("exiftool", ["test/fixtures/DSCN0010_tmp.jpg"])
+
+    refute exif_original == exif_filtered
+    assert String.match?(exif_original, ~r/GPS/)
+    refute String.match?(exif_filtered, ~r/GPS/)
+  end
+end
index 1520ffc4b154d6cf93c875620fcdf6cc38281ba9..f3951462f8d459c4e6fcafeeaf07f778ae9d4bba 100644 (file)
@@ -2056,4 +2056,46 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
       assert [%{activity_id: ^id_create}] = Pleroma.ActivityExpiration |> Repo.all()
     end
   end
+
+  describe "handling of clashing nicknames" do
+    test "renames an existing user with a clashing nickname and a different ap id" do
+      orig_user =
+        insert(
+          :user,
+          local: false,
+          nickname: "admin@mastodon.example.org",
+          ap_id: "http://mastodon.example.org/users/harinezumigari"
+        )
+
+      %{
+        nickname: orig_user.nickname,
+        ap_id: orig_user.ap_id <> "part_2"
+      }
+      |> ActivityPub.maybe_handle_clashing_nickname()
+
+      user = User.get_by_id(orig_user.id)
+
+      assert user.nickname == "#{orig_user.id}.admin@mastodon.example.org"
+    end
+
+    test "does nothing with a clashing nickname and the same ap id" do
+      orig_user =
+        insert(
+          :user,
+          local: false,
+          nickname: "admin@mastodon.example.org",
+          ap_id: "http://mastodon.example.org/users/harinezumigari"
+        )
+
+      %{
+        nickname: orig_user.nickname,
+        ap_id: orig_user.ap_id
+      }
+      |> ActivityPub.maybe_handle_clashing_nickname()
+
+      user = User.get_by_id(orig_user.id)
+
+      assert user.nickname == orig_user.nickname
+    end
+  end
 end
index f7b7d1a9f2b89063495a584705ff0293da8a495e..248b410c641cebb290746f5279d932922102a3a3 100644 (file)
@@ -774,6 +774,29 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
       assert [user.follower_address] == activity.data["to"]
     end
 
+    test "it correctly processes messages with weirdness in address fields" do
+      user = insert(:user)
+
+      message = %{
+        "@context" => "https://www.w3.org/ns/activitystreams",
+        "to" => [nil, user.follower_address],
+        "cc" => ["https://www.w3.org/ns/activitystreams#Public", ["¿"]],
+        "type" => "Create",
+        "object" => %{
+          "content" => "…",
+          "type" => "Note",
+          "attributedTo" => user.ap_id,
+          "inReplyTo" => nil
+        },
+        "actor" => user.ap_id
+      }
+
+      assert {:ok, activity} = Transmogrifier.handle_incoming(message)
+
+      assert ["https://www.w3.org/ns/activitystreams#Public"] == activity.data["cc"]
+      assert [user.follower_address] == activity.data["to"]
+    end
+
     test "it accepts Move activities" do
       old_user = insert(:user)
       new_user = insert(:user)
index c2433f23c1480a69d216b901cdec9b3cc3bd46f9..da91cd552afb8552695baa21f861c84d3c3d9666 100644 (file)
@@ -41,6 +41,16 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
     {:ok, %{admin: admin, token: token, conn: conn}}
   end
 
+  test "with valid `admin_token` query parameter, skips OAuth scopes check" do
+    clear_config([:admin_token], "password123")
+
+    user = insert(:user)
+
+    conn = get(build_conn(), "/api/pleroma/admin/users/#{user.nickname}?admin_token=password123")
+
+    assert json_response(conn, 200)
+  end
+
   describe "with [:auth, :enforce_oauth_admin_scope_usage]," do
     setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage], true)
 
index 064ef9bc7cb82224d6b4b5188602d5b2e9f21348..61bc9fd39093616ca0cd4db2361b7950cd03f87d 100644 (file)
@@ -152,6 +152,14 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do
       assert emoji_val[:groups] == [a: 1, b: 2]
       assert assets_val[:mascots] == [a: 1, b: 2]
     end
+
+    test "with valid `admin_token` query parameter, skips OAuth scopes check" do
+      clear_config([:admin_token], "password123")
+
+      build_conn()
+      |> get("/api/pleroma/admin/config?admin_token=password123")
+      |> json_response_and_validate_schema(200)
+    end
   end
 
   test "POST /api/pleroma/admin/config error", %{conn: conn} do
index 940bce340d2f26082ecb343e24e0fb70f76063f6..f30dc895629835cd9569f98dc134f75beda67e00 100644 (file)
@@ -297,7 +297,7 @@ defmodule Pleroma.Web.AdminAPI.ReportControllerTest do
         |> get("/api/pleroma/admin/reports")
 
       assert json_response(conn, :forbidden) ==
-               %{"error" => "User is not an admin or OAuth admin scope is not granted."}
+               %{"error" => "User is not an admin."}
     end
 
     test "returns 403 when requested by anonymous" do
index 638626b45d0e480587696cf205f13ef040e27867..b888e4c7110100e8135c1ef60d403c4355e47d05 100644 (file)
@@ -351,6 +351,30 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do
              ]
     end
 
+    test "emojis in fields labels", %{conn: conn} do
+      fields = [
+        %{"name" => ":firefox:", "value" => "is best 2hu"},
+        %{"name" => "they wins", "value" => ":blank:"}
+      ]
+
+      account_data =
+        conn
+        |> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields})
+        |> json_response_and_validate_schema(200)
+
+      assert account_data["fields"] == [
+               %{"name" => ":firefox:", "value" => "is best 2hu"},
+               %{"name" => "they wins", "value" => ":blank:"}
+             ]
+
+      assert account_data["source"]["fields"] == [
+               %{"name" => ":firefox:", "value" => "is best 2hu"},
+               %{"name" => "they wins", "value" => ":blank:"}
+             ]
+
+      assert [%{"shortcode" => "blank"}, %{"shortcode" => "firefox"}] = account_data["emojis"]
+    end
+
     test "update fields via x-www-form-urlencoded", %{conn: conn} do
       fields =
         [