/*.ez
/test/uploads
/.elixir_ls
+/test/fixtures/DSCN0010_tmp.jpg
/test/fixtures/test_tmp.txt
/test/fixtures/image_tmp.jpg
/test/tmp/
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
<<: *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
- 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>
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 &&\
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,
},
%{
key: :webhook_url,
+ label: "Webhook URL",
type: :string,
description: "Configure the Slack incoming webhook",
suggestions: ["https://hooks.slack.com/services/YOUR-KEY-HERE"]
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,
children: [
%{
key: :match_actor,
- type: :map,
+ type: {:map, {:list, :string}},
description: "Matches a series of regular expressions against the actor field",
suggestions: [
%{
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"}]
},
%{
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}
+ }
+ ]
}
]
},
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"
+ ]
}
]
},
%{
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",
},
%{
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.",
- `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.
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:
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
### 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
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)
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.
--- /dev/null
+# 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.
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
: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
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
# 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()
Pleroma.ApplicationRequirements.verify!()
setup_instrumenters()
load_custom_modules()
+ Pleroma.Docs.JSON.compile()
adapter = Application.get_env(:tesla, :adapter)
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 """
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
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
{: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
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
defmodule Pleroma.Plugs.AdminSecretAuthenticationPlug do
import Plug.Conn
+
+ alias Pleroma.Plugs.OAuthScopesPlug
+ alias Pleroma.Plugs.RateLimiter
alias Pleroma.User
def init(options) 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
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
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
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
--- /dev/null
+# 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
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
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
|> 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()
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
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)
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
}
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"),
%Schema{type: :boolean, default: false},
"Get only saved in database settings"
)
+ | admin_api_params()
],
security: [%{"oAuth" => ["read"]}],
responses: %{
summary: "Update config settings",
operationId: "AdminAPI.ConfigController.update",
security: [%{"oAuth" => ["write"]}],
+ parameters: admin_api_params(),
requestBody:
request_body("Parameters", %Schema{
type: :object,
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{
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{
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,
summary: "Revoke invite by token",
operationId: "AdminAPI.InviteController.revoke",
security: [%{"oAuth" => ["write:invites"]}],
+ parameters: admin_api_params(),
requestBody:
request_body(
"Parameters",
summary: "Sends registration invite via email",
operationId: "AdminAPI.InviteController.email",
security: [%{"oAuth" => ["write:invites"]}],
+ parameters: admin_api_params(),
requestBody:
request_body(
"Parameters",
%Schema{type: :integer, default: 50},
"Number of statuses to return"
)
+ | admin_api_params()
],
responses: %{
200 => success_response()
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",
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",
%Schema{type: :integer, default: 50},
"Number of apps to return"
)
+ | admin_api_params()
],
responses: %{
200 =>
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()),
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: %{
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(),
summary: "List Relays",
operationId: "AdminAPI.RelayController.index",
security: [%{"oAuth" => ["read"]}],
+ parameters: admin_api_params(),
responses: %{
200 =>
Operation.response("Response", "application/json", %Schema{
summary: "Follow a Relay",
operationId: "AdminAPI.RelayController.follow",
security: [%{"oAuth" => ["write:follows"]}],
+ parameters: admin_api_params(),
requestBody:
request_body("Parameters", %Schema{
type: :object,
summary: "Unfollow a Relay",
operationId: "AdminAPI.RelayController.unfollow",
security: [%{"oAuth" => ["write:follows"]}],
+ parameters: admin_api_params(),
requestBody:
request_body("Parameters", %Schema{
type: :object,
%Schema{type: :integer, default: 50},
"Number number of log entries per page"
)
+ | admin_api_params()
],
responses: %{
200 =>
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()),
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(),
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,
parameters: [
Operation.parameter(:report_id, :path, :string, "Report ID"),
Operation.parameter(:id, :path, :string, "Note ID")
+ | admin_api_params()
],
security: [%{"oAuth" => ["write:reports"]}],
responses: %{
%Schema{type: :integer, default: 50},
"Number of statuses to return"
)
+ | admin_api_params()
],
responses: %{
200 =>
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()),
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: %{
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(),
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.
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 ""
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 ""
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 ""
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 ""
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 ""
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 ""
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 ""
msgstr ""
#, elixir-format
-#: lib/pleroma/plugs/uploaded_media.ex:94
+#: lib/pleroma/plugs/uploaded_media.ex:104
msgid "Internal Error"
msgstr ""
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 ""
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 ""
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 ""
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
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 ""
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
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
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
--- /dev/null
+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
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,
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)
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")
|> 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
|> AdminSecretAuthenticationPlug.call(%{})
refute conn.assigns[:user]
+ assert called(RateLimiter.call(conn, name: :authentication))
conn =
conn
|> AdminSecretAuthenticationPlug.call(%{})
assert conn.assigns[:user].is_admin
+ assert PlugHelper.plug_skipped?(conn, OAuthScopesPlug)
end
end
end
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
--- /dev/null
+# 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
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
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)
{: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)
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
|> 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
]
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 =
[