- MFR policy to set global expiration for all local Create activities
- OGP rich media parser merged with TwitterCard
- Configuration: `:instance, rewrite_policy` moved to `:mrf, policies`, `:instance, :mrf_transparency` moved to `:mrf, :transparency`, `:instance, :mrf_transparency_exclusions` moved to `:mrf, :transparency_exclusions`. Old config namespace is deprecated.
+- Configuration: `:media_proxy, whitelist` format changed to host with scheme (e.g. `http://example.com` instead of `example.com`). Domain format is deprecated.
<details>
<summary>API Changes</summary>
attachments_cleanup: 5,
new_users_digest: 1
],
+ plugins: [Oban.Plugins.Pruner],
crontab: [
{"0 0 * * *", Pleroma.Workers.Cron.ClearOauthTokenWorker},
{"0 * * * *", Pleroma.Workers.Cron.StatsWorker},
%{
key: :whitelist,
type: {:list, :string},
- description: "List of domains to bypass the mediaproxy",
- suggestions: ["example.com"]
+ description: "List of hosts with scheme to bypass the mediaproxy",
+ suggestions: ["http://example.com"]
}
]
},
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"
+ ]
}
]
},
config :pleroma, :instances_favicons, enabled: true
+config :pleroma, Pleroma.Uploaders.S3,
+ bucket: nil,
+ streaming_enabled: true,
+ public_endpoint: nil
+
if File.exists?("./config/test.secret.exs") do
import_config "test.secret.exs"
else
* `background_color`: Describe the background color of the app. (Example: `"#191b22"`, `"aliceblue"`).
## :emoji
+
* `shortcode_globs`: Location of custom emoji files. `*` can be used as a wildcard. Example `["/emoji/custom/**/*.png"]`
* `pack_extensions`: A list of file extensions for emojis, when no emoji.txt for a pack is present. Example `[".png", ".gif"]`
* `groups`: Emojis are ordered in groups (tags). This is an array of key-value pairs where the key is the groupname and the value the location or array of locations. `*` can be used as a wildcard. Example `[Custom: ["/emoji/*.png", "/emoji/custom/*.png"]]`
memory for this amount of seconds multiplied by the number of files.
## :media_proxy
+
* `enabled`: Enables proxying of remote media to the instance’s proxy
* `base_url`: The base URL to access a user-uploaded file. Useful when you want to proxy the media files via another host/CDN fronts.
* `proxy_opts`: All options defined in `Pleroma.ReverseProxy` documentation, defaults to `[max_body_length: (25*1_048_576)]`.
-* `whitelist`: List of domains to bypass the mediaproxy
+* `whitelist`: List of hosts with scheme to bypass the mediaproxy (e.g. `https://example.com`)
* `invalidation`: options for remove media from cache after delete object:
- * `enabled`: Enables purge cache
- * `provider`: Which one of the [purge cache strategy](#purge-cache-strategy) to use.
+ * `enabled`: Enables purge cache
+ * `provider`: Which one of the [purge cache strategy](#purge-cache-strategy) to use.
### Purge cache strategy
* `script_path`: path to external script.
Example:
+
```elixir
config :pleroma, Pleroma.Web.MediaProxy.Invalidation.Script,
script_path: "./installation/nginx-cache-purge.example"
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.
# 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()
check_hellthread_threshold()
mrf_user_allowlist()
check_old_mrf_config()
+ check_media_proxy_whitelist_config()
end
def check_old_mrf_config do
move_namespace_and_warn(@mrf_config_map, warning_preface)
end
- @spec move_namespace_and_warn([config_map()], String.t()) :: :ok
+ @spec move_namespace_and_warn([config_map()], String.t()) :: :ok | nil
def move_namespace_and_warn(config_map, warning_preface) do
warning =
Enum.reduce(config_map, "", fn
Logger.warn(warning_preface <> warning)
end
end
+
+ @spec check_media_proxy_whitelist_config() :: :ok | nil
+ def check_media_proxy_whitelist_config do
+ whitelist = Config.get([:media_proxy, :whitelist])
+
+ if Enum.any?(whitelist, &(not String.starts_with?(&1, "http"))) do
+ Logger.warn("""
+ !!!DEPRECATION WARNING!!!
+ Your config is using old format (only domain) for MediaProxy whitelist option. Setting should work for now, but you are advised to change format to scheme with port to prevent possible issues later.
+ """)
+ end
+ end
end
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
|> :erlang.iolist_to_binary()
end
- defp build_csp_multimedia_source_list do
- media_proxy_whitelist =
- Enum.reduce(Config.get([:media_proxy, :whitelist]), [], fn host, acc ->
- add_source(acc, host)
- end)
+ defp build_csp_from_whitelist([], acc), do: acc
- media_proxy_base_url = build_csp_param(Config.get([:media_proxy, :base_url]))
+ defp build_csp_from_whitelist([last], acc) do
+ [build_csp_param_from_whitelist(last) | acc]
+ end
- upload_base_url = build_csp_param(Config.get([Pleroma.Upload, :base_url]))
+ defp build_csp_from_whitelist([head | tail], acc) do
+ build_csp_from_whitelist(tail, [[?\s, build_csp_param_from_whitelist(head)] | acc])
+ end
- s3_endpoint = build_csp_param(Config.get([Pleroma.Uploaders.S3, :public_endpoint]))
+ # TODO: use `build_csp_param/1` after removing support bare domains for media proxy whitelist
+ defp build_csp_param_from_whitelist("http" <> _ = url) do
+ build_csp_param(url)
+ end
- captcha_method = Config.get([Pleroma.Captcha, :method])
+ defp build_csp_param_from_whitelist(url), do: url
- captcha_endpoint = build_csp_param(Config.get([captcha_method, :endpoint]))
+ defp build_csp_multimedia_source_list do
+ media_proxy_whitelist =
+ [:media_proxy, :whitelist]
+ |> Config.get()
+ |> build_csp_from_whitelist([])
- []
- |> add_source(media_proxy_base_url)
- |> add_source(upload_base_url)
- |> add_source(s3_endpoint)
+ captcha_method = Config.get([Pleroma.Captcha, :method])
+ captcha_endpoint = Config.get([captcha_method, :endpoint])
+
+ base_endpoints =
+ [
+ [:media_proxy, :base_url],
+ [Pleroma.Upload, :base_url],
+ [Pleroma.Uploaders.S3, :public_endpoint]
+ ]
+ |> Enum.map(&Config.get/1)
+
+ [captcha_endpoint | base_endpoints]
+ |> Enum.map(&build_csp_param/1)
+ |> Enum.reduce([], &add_source(&2, &1))
|> add_source(media_proxy_whitelist)
- |> add_source(captcha_endpoint)
end
defp add_source(iodata, nil), do: iodata
+ defp add_source(iodata, []), do: iodata
defp add_source(iodata, source), do: [[?\s, source] | iodata]
defp add_csp_param(csp_iodata, nil), do: csp_iodata
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
if score < 0.8 do
{:ok, message}
else
- {:reject, nil}
+ {:reject, "[AntiFollowbotPolicy] Scored #{actor_id} as #{score}"}
end
end
{:ok, message}
{:old_user, false} ->
- {:reject, nil}
+ {:reject, "[AntiLinkSpamPolicy] User has no posts nor followers"}
{:error, _} ->
- {:reject, nil}
+ {:reject, "[AntiLinkSpamPolicy] Failed to get or fetch user by ap_id"}
e ->
- Logger.warn("[MRF anti-link-spam] WTF: unhandled error #{inspect(e)}")
- {:reject, nil}
+ {:reject, "[AntiLinkSpamPolicy] Unhandled error #{inspect(e)}"}
end
end
defp reject_message(message, threshold) when threshold > 0 do
with {_, recipients} <- get_recipient_count(message) do
if recipients > threshold do
- {:reject, nil}
+ {:reject, "[HellthreadPolicy] #{recipients} recipients is over the limit of #{threshold}"}
else
{:ok, message}
end
{:ok, message} <- delist_message(message, delist_threshold) do
{:ok, message}
else
- _e -> {:reject, nil}
+ e -> e
end
end
if Enum.any?(Pleroma.Config.get([:mrf_keyword, :reject]), fn pattern ->
string_matches?(content, pattern) or string_matches?(summary, pattern)
end) do
- {:reject, nil}
+ {:reject, "[KeywordPolicy] Matches with rejected keyword"}
else
{:ok, message}
end
{:ok, message} <- check_replace(message) do
{:ok, message}
else
- _e ->
- {:reject, nil}
+ {:reject, nil} -> {:reject, "[KeywordPolicy] "}
+ {:reject, _} = e -> e
+ _e -> {:reject, "[KeywordPolicy] "}
end
end
reject_actors = Pleroma.Config.get([:mrf_mention, :actors], [])
recipients = (message["to"] || []) ++ (message["cc"] || [])
- if Enum.any?(recipients, fn recipient -> Enum.member?(reject_actors, recipient) end) do
- {:reject, nil}
+ if rejected_mention =
+ Enum.find(recipients, fn recipient -> Enum.member?(reject_actors, recipient) end) do
+ {:reject, "[MentionPolicy] Rejected for mention of #{rejected_mention}"}
else
{:ok, message}
end
defp check_reject(message, actions) do
if :reject in actions do
- {:reject, nil}
+ {:reject, "[ObjectAgePolicy]"}
else
{:ok, message}
end
{:ok, message}
else
- # Unhandleable error: somebody is messing around, just drop the message.
_e ->
- {:reject, nil}
+ {:reject, "[ObjectAgePolicy] Unhandled error"}
end
else
{:ok, message}
{:ok, message}
else
- # Unhandleable error: somebody is messing around, just drop the message.
_e ->
- {:reject, nil}
+ {:reject, "[ObjectAgePolicy] Unhandled error"}
end
else
{:ok, message}
{:ok, object}
true ->
- {:reject, nil}
+ {:reject, "[RejectNonPublic] visibility: #{visibility}"}
end
end
accepts == [] -> {:ok, object}
actor_host == Config.get([Pleroma.Web.Endpoint, :url, :host]) -> {:ok, object}
MRF.subdomain_match?(accepts, actor_host) -> {:ok, object}
- true -> {:reject, nil}
+ true -> {:reject, "[SimplePolicy] host not in accept list"}
end
end
|> MRF.subdomains_regex()
if MRF.subdomain_match?(rejects, actor_host) do
- {:reject, nil}
+ {:reject, "[SimplePolicy] host in reject list"}
else
{:ok, object}
end
|> MRF.subdomains_regex()
if MRF.subdomain_match?(report_removal, actor_host) do
- {:reject, nil}
+ {:reject, "[SimplePolicy] host in report_removal list"}
else
{:ok, object}
end
|> MRF.subdomains_regex()
if MRF.subdomain_match?(reject_deletes, actor_host) do
- {:reject, nil}
+ {:reject, "[SimplePolicy] host in reject_deletes list"}
else
{:ok, object}
end
{:ok, object} <- check_report_removal(actor_info, object) do
{:ok, object}
else
- _e -> {:reject, nil}
+ {:reject, nil} -> {:reject, "[SimplePolicy]"}
+ {:reject, _} = e -> e
+ _ -> {:reject, "[SimplePolicy]"}
end
end
{:ok, object} <- check_banner_removal(actor_info, object) do
{:ok, object}
else
- _e -> {:reject, nil}
+ {:reject, nil} -> {:reject, "[SimplePolicy]"}
+ {:reject, _} = e -> e
+ _ -> {:reject, "[SimplePolicy]"}
end
end
if user.local == true do
{:ok, message}
else
- {:reject, nil}
+ {:reject,
+ "[TagPolicy] Follow from #{actor} tagged with mrf_tag:disable-remote-subscription"}
end
end
- defp process_tag("mrf_tag:disable-any-subscription", %{"type" => "Follow"}),
- do: {:reject, nil}
+ defp process_tag("mrf_tag:disable-any-subscription", %{"type" => "Follow", "actor" => actor}),
+ do: {:reject, "[TagPolicy] Follow from #{actor} tagged with mrf_tag:disable-any-subscription"}
defp process_tag(_, message), do: {:ok, message}
if actor in allow_list do
{:ok, object}
else
- {:reject, nil}
+ {:reject, "[UserAllowListPolicy] #{actor} not in the list"}
end
end
with {:ok, _} <- filter(child_message) do
{:ok, message}
else
- {:reject, nil} ->
- {:reject, nil}
+ {:reject, _} = e -> e
end
end
def filter(%{"type" => message_type} = message) do
with accepted_vocabulary <- Pleroma.Config.get([:mrf_vocabulary, :accept]),
rejected_vocabulary <- Pleroma.Config.get([:mrf_vocabulary, :reject]),
- true <-
- Enum.empty?(accepted_vocabulary) || Enum.member?(accepted_vocabulary, message_type),
- false <-
- length(rejected_vocabulary) > 0 && Enum.member?(rejected_vocabulary, message_type),
+ {_, true} <-
+ {:accepted,
+ Enum.empty?(accepted_vocabulary) || Enum.member?(accepted_vocabulary, message_type)},
+ {_, false} <-
+ {:rejected,
+ length(rejected_vocabulary) > 0 && Enum.member?(rejected_vocabulary, message_type)},
{:ok, _} <- filter(message["object"]) do
{:ok, message}
else
- _ -> {:reject, nil}
+ {:reject, _} = e -> e
+ {:accepted, _} -> {:reject, "[VocabularyPolicy] #{message_type} not in accept list"}
+ {:rejected, _} -> {:reject, "[VocabularyPolicy] #{message_type} in reject list"}
+ _ -> {:reject, "[VocabularyPolicy]"}
end
end
"""
def publish_one(%{inbox: inbox, json: json, actor: %User{} = actor, id: id} = params) do
Logger.debug("Federating #{id} to #{inbox}")
- %{host: host, path: path} = URI.parse(inbox)
+
+ uri = URI.parse(inbox)
digest = "SHA-256=" <> (:crypto.hash(:sha256, json) |> Base.encode64())
signature =
Pleroma.Signature.sign(actor, %{
- "(request-target)": "post #{path}",
- host: host,
+ "(request-target)": "post #{uri.path}",
+ host: signature_host(uri),
"content-length": byte_size(json),
digest: digest,
date: date
{"digest", digest}
]
) do
- if !Map.has_key?(params, :unreachable_since) || params[:unreachable_since],
- do: Instances.set_reachable(inbox)
+ if not Map.has_key?(params, :unreachable_since) || params[:unreachable_since] do
+ Instances.set_reachable(inbox)
+ end
result
else
|> publish_one()
end
+ defp signature_host(%URI{port: port, scheme: scheme, host: host}) do
+ if port == URI.default_port(scheme) do
+ host
+ else
+ "#{host}:#{port}"
+ end
+ end
+
defp should_federate?(inbox, public) do
if public do
true
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
}
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(),
with_direct_conversation_id: true
)
else
+ {:error, {:reject, message}} ->
+ conn
+ |> put_status(:unprocessable_entity)
+ |> json(%{error: message})
+
{:error, message} ->
conn
|> put_status(:unprocessable_entity)
defp whitelisted?(url) do
%{host: domain} = URI.parse(url)
- mediaproxy_whitelist = Config.get([:media_proxy, :whitelist])
-
- upload_base_url_domain =
- if !is_nil(Config.get([Upload, :base_url])) do
- [URI.parse(Config.get([Upload, :base_url])).host]
+ mediaproxy_whitelist_domains =
+ [:media_proxy, :whitelist]
+ |> Config.get()
+ |> Enum.map(&maybe_get_domain_from_url/1)
+
+ whitelist_domains =
+ if base_url = Config.get([Upload, :base_url]) do
+ %{host: base_domain} = URI.parse(base_url)
+ [base_domain | mediaproxy_whitelist_domains]
else
- []
+ mediaproxy_whitelist_domains
end
- whitelist = mediaproxy_whitelist ++ upload_base_url_domain
+ domain in whitelist_domains
+ end
- Enum.any?(whitelist, fn pattern ->
- String.equivalent?(domain, pattern)
- end)
+ defp maybe_get_domain_from_url("http" <> _ = url) do
+ URI.parse(url).host
end
+ defp maybe_get_domain_from_url(domain), do: domain
+
def encode_url(url) do
base64 = Base.url_encode64(url, @base64_opts)
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
assert Pleroma.Config.get(new_group2) == 2
assert Pleroma.Config.get(new_group3) == 3
end
+
+ test "check_media_proxy_whitelist_config/0" do
+ clear_config([:media_proxy, :whitelist], ["https://example.com", "example2.com"])
+
+ assert capture_log(fn ->
+ Pleroma.Config.DeprecationWarnings.check_media_proxy_whitelist_config()
+ end) =~ "Your config is using old format (only domain) for MediaProxy whitelist option"
+ end
end
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
defmodule Pleroma.Web.Plugs.HTTPSecurityPlugTest do
use Pleroma.Web.ConnCase
+
alias Pleroma.Config
alias Plug.Conn
- setup do: clear_config([:http_securiy, :enabled])
- setup do: clear_config([:http_security, :sts])
- setup do: clear_config([:http_security, :referrer_policy])
-
describe "http security enabled" do
- setup do
- Config.put([:http_security, :enabled], true)
- end
+ setup do: clear_config([:http_security, :enabled], true)
test "it sends CSP headers when enabled", %{conn: conn} do
conn = get(conn, "/api/v1/instance")
end
test "it sends STS headers when enabled", %{conn: conn} do
- Config.put([:http_security, :sts], true)
+ clear_config([:http_security, :sts], true)
conn = get(conn, "/api/v1/instance")
end
test "it does not send STS headers when disabled", %{conn: conn} do
- Config.put([:http_security, :sts], false)
+ clear_config([:http_security, :sts], false)
conn = get(conn, "/api/v1/instance")
end
test "referrer-policy header reflects configured value", %{conn: conn} do
- conn = get(conn, "/api/v1/instance")
+ resp = get(conn, "/api/v1/instance")
- assert Conn.get_resp_header(conn, "referrer-policy") == ["same-origin"]
+ assert Conn.get_resp_header(resp, "referrer-policy") == ["same-origin"]
- Config.put([:http_security, :referrer_policy], "no-referrer")
+ clear_config([:http_security, :referrer_policy], "no-referrer")
- conn =
- build_conn()
- |> get("/api/v1/instance")
+ resp = get(conn, "/api/v1/instance")
- assert Conn.get_resp_header(conn, "referrer-policy") == ["no-referrer"]
+ assert Conn.get_resp_header(resp, "referrer-policy") == ["no-referrer"]
end
- test "it sends `report-to` & `report-uri` CSP response headers" do
- conn =
- build_conn()
- |> get("/api/v1/instance")
+ test "it sends `report-to` & `report-uri` CSP response headers", %{conn: conn} do
+ conn = get(conn, "/api/v1/instance")
[csp] = Conn.get_resp_header(conn, "content-security-policy")
assert reply_to ==
"{\"endpoints\":[{\"url\":\"https://endpoint.com\"}],\"group\":\"csp-endpoint\",\"max-age\":10886400}"
end
+
+ test "default values for img-src and media-src with disabled media proxy", %{conn: conn} do
+ conn = get(conn, "/api/v1/instance")
+
+ [csp] = Conn.get_resp_header(conn, "content-security-policy")
+ assert csp =~ "media-src 'self' https:;"
+ assert csp =~ "img-src 'self' data: blob: https:;"
+ end
+ end
+
+ describe "img-src and media-src" do
+ setup do
+ clear_config([:http_security, :enabled], true)
+ clear_config([:media_proxy, :enabled], true)
+ clear_config([:media_proxy, :proxy_opts, :redirect_on_failure], false)
+ end
+
+ test "media_proxy with base_url", %{conn: conn} do
+ url = "https://example.com"
+ clear_config([:media_proxy, :base_url], url)
+ assert_media_img_src(conn, url)
+ end
+
+ test "upload with base url", %{conn: conn} do
+ url = "https://example2.com"
+ clear_config([Pleroma.Upload, :base_url], url)
+ assert_media_img_src(conn, url)
+ end
+
+ test "with S3 public endpoint", %{conn: conn} do
+ url = "https://example3.com"
+ clear_config([Pleroma.Uploaders.S3, :public_endpoint], url)
+ assert_media_img_src(conn, url)
+ end
+
+ test "with captcha endpoint", %{conn: conn} do
+ clear_config([Pleroma.Captcha.Mock, :endpoint], "https://captcha.com")
+ assert_media_img_src(conn, "https://captcha.com")
+ end
+
+ test "with media_proxy whitelist", %{conn: conn} do
+ clear_config([:media_proxy, :whitelist], ["https://example6.com", "https://example7.com"])
+ assert_media_img_src(conn, "https://example7.com https://example6.com")
+ end
+
+ # TODO: delete after removing support bare domains for media proxy whitelist
+ test "with media_proxy bare domains whitelist (deprecated)", %{conn: conn} do
+ clear_config([:media_proxy, :whitelist], ["example4.com", "example5.com"])
+ assert_media_img_src(conn, "example5.com example4.com")
+ end
+ end
+
+ defp assert_media_img_src(conn, url) do
+ conn = get(conn, "/api/v1/instance")
+ [csp] = Conn.get_resp_header(conn, "content-security-policy")
+ assert csp =~ "media-src 'self' #{url};"
+ assert csp =~ "img-src 'self' data: blob: #{url};"
end
test "it does not send CSP headers when disabled", %{conn: conn} do
- Config.put([:http_security, :enabled], false)
+ clear_config([:http_security, :enabled], false)
conn = get(conn, "/api/v1/instance")
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
assert object = Object.get_by_ap_id(note_object.data["id"])
assert object.data["like_count"] == 1
end
+
+ test "it doesn't spreads faulty attributedTo or actor fields", %{
+ conn: conn,
+ activity: activity
+ } do
+ reimu = insert(:user, nickname: "reimu")
+ cirno = insert(:user, nickname: "cirno")
+
+ assert reimu.ap_id
+ assert cirno.ap_id
+
+ activity =
+ activity
+ |> put_in(["object", "actor"], reimu.ap_id)
+ |> put_in(["object", "attributedTo"], reimu.ap_id)
+ |> put_in(["actor"], reimu.ap_id)
+ |> put_in(["attributedTo"], reimu.ap_id)
+
+ _reimu_outbox =
+ conn
+ |> assign(:user, cirno)
+ |> put_req_header("content-type", "application/activity+json")
+ |> post("/users/#{reimu.nickname}/outbox", activity)
+ |> json_response(403)
+
+ cirno_outbox =
+ conn
+ |> assign(:user, cirno)
+ |> put_req_header("content-type", "application/activity+json")
+ |> post("/users/#{cirno.nickname}/outbox", activity)
+ |> json_response(201)
+
+ assert cirno_outbox["attributedTo"] == nil
+ assert cirno_outbox["actor"] == cirno.ap_id
+
+ assert cirno_object = Object.normalize(cirno_outbox["object"])
+ assert cirno_object.data["actor"] == cirno.ap_id
+ assert cirno_object.data["attributedTo"] == cirno.ap_id
+ end
end
describe "/relay/followers" do
"id" => "https://example.com/activities/1234"
}
- {:reject, nil} = AntiFollowbotPolicy.filter(message)
+ assert {:reject, "[AntiFollowbotPolicy]" <> _} = AntiFollowbotPolicy.filter(message)
end
test "matches followbots by display name" do
"id" => "https://example.com/activities/1234"
}
- {:reject, nil} = AntiFollowbotPolicy.filter(message)
+ assert {:reject, "[AntiFollowbotPolicy]" <> _} = AntiFollowbotPolicy.filter(message)
end
end
} do
Pleroma.Config.put([:mrf_hellthread], %{delist_threshold: 0, reject_threshold: 2})
- {:reject, nil} = filter(message)
+ assert {:reject, "[HellthreadPolicy] 3 recipients is over the limit of 2"} ==
+ filter(message)
end
test "does not reject the message if the recipient count is below reject_threshold", %{
}
}
- assert {:reject, nil} == KeywordPolicy.filter(message)
+ assert {:reject, "[KeywordPolicy] Matches with rejected keyword"} =
+ KeywordPolicy.filter(message)
end
test "rejects if string matches in summary" do
}
}
- assert {:reject, nil} == KeywordPolicy.filter(message)
+ assert {:reject, "[KeywordPolicy] Matches with rejected keyword"} =
+ KeywordPolicy.filter(message)
end
test "rejects if regex matches in content" do
}
}
- {:reject, nil} == KeywordPolicy.filter(message)
+ {:reject, "[KeywordPolicy] Matches with rejected keyword"} ==
+ KeywordPolicy.filter(message)
end)
end
}
}
- {:reject, nil} == KeywordPolicy.filter(message)
+ {:reject, "[KeywordPolicy] Matches with rejected keyword"} ==
+ KeywordPolicy.filter(message)
end)
end
end
"to" => ["https://example.com/blocked"]
}
- assert MentionPolicy.filter(message) == {:reject, nil}
+ assert MentionPolicy.filter(message) ==
+ {:reject, "[MentionPolicy] Rejected for mention of https://example.com/blocked"}
end
test "cc" do
"cc" => ["https://example.com/blocked"]
}
- assert MentionPolicy.filter(message) == {:reject, nil}
+ assert MentionPolicy.filter(message) ==
+ {:reject, "[MentionPolicy] Rejected for mention of https://example.com/blocked"}
end
end
end
}
Pleroma.Config.put([:mrf_rejectnonpublic, :allow_followersonly], false)
- assert {:reject, nil} = RejectNonPublic.filter(message)
+ assert {:reject, _} = RejectNonPublic.filter(message)
end
end
}
Pleroma.Config.put([:mrf_rejectnonpublic, :allow_direct], false)
- assert {:reject, nil} = RejectNonPublic.filter(message)
+ assert {:reject, _} = RejectNonPublic.filter(message)
end
end
end
report_message = build_report_message()
local_message = build_local_message()
- assert SimplePolicy.filter(report_message) == {:reject, nil}
+ assert {:reject, _} = SimplePolicy.filter(report_message)
assert SimplePolicy.filter(local_message) == {:ok, local_message}
end
report_message = build_report_message()
local_message = build_local_message()
- assert SimplePolicy.filter(report_message) == {:reject, nil}
+ assert {:reject, _} = SimplePolicy.filter(report_message)
assert SimplePolicy.filter(local_message) == {:ok, local_message}
end
end
remote_message = build_remote_message()
- assert SimplePolicy.filter(remote_message) == {:reject, nil}
+ assert {:reject, _} = SimplePolicy.filter(remote_message)
end
test "activity matches with wildcard domain" do
remote_message = build_remote_message()
- assert SimplePolicy.filter(remote_message) == {:reject, nil}
+ assert {:reject, _} = SimplePolicy.filter(remote_message)
end
test "actor has a matching host" do
remote_user = build_remote_user()
- assert SimplePolicy.filter(remote_user) == {:reject, nil}
+ assert {:reject, _} = SimplePolicy.filter(remote_user)
end
end
remote_message = build_remote_message()
assert SimplePolicy.filter(local_message) == {:ok, local_message}
- assert SimplePolicy.filter(remote_message) == {:reject, nil}
+ assert {:reject, _} = SimplePolicy.filter(remote_message)
end
test "activity has a matching host" do
test "it rejects the deletion" do
deletion_message = build_remote_deletion_message()
- assert SimplePolicy.filter(deletion_message) == {:reject, nil}
+ assert {:reject, _} = SimplePolicy.filter(deletion_message)
end
end
test "it rejects the deletion" do
deletion_message = build_remote_deletion_message()
- assert SimplePolicy.filter(deletion_message) == {:reject, nil}
+ assert {:reject, _} = SimplePolicy.filter(deletion_message)
end
end
describe "mrf_tag:disable-any-subscription" do
test "rejects message" do
actor = insert(:user, tags: ["mrf_tag:disable-any-subscription"])
- message = %{"object" => actor.ap_id, "type" => "Follow"}
- assert {:reject, nil} = TagPolicy.filter(message)
+ message = %{"object" => actor.ap_id, "type" => "Follow", "actor" => actor.ap_id}
+ assert {:reject, _} = TagPolicy.filter(message)
end
end
actor = insert(:user, tags: ["mrf_tag:disable-remote-subscription"])
follower = insert(:user, tags: ["mrf_tag:disable-remote-subscription"], local: false)
message = %{"object" => actor.ap_id, "type" => "Follow", "actor" => follower.ap_id}
- assert {:reject, nil} = TagPolicy.filter(message)
+ assert {:reject, _} = TagPolicy.filter(message)
end
test "allows non-local follow requests" do
actor = insert(:user)
Pleroma.Config.put([:mrf_user_allowlist], %{"localhost" => ["test-ap-id"]})
message = %{"actor" => actor.ap_id}
- assert UserAllowListPolicy.filter(message) == {:reject, nil}
+ assert {:reject, _} = UserAllowListPolicy.filter(message)
end
end
}
}
- {:reject, nil} = VocabularyPolicy.filter(message)
+ {:reject, _} = VocabularyPolicy.filter(message)
end
test "it does not accept disallowed parent types" do
}
}
- {:reject, nil} = VocabularyPolicy.filter(message)
+ {:reject, _} = VocabularyPolicy.filter(message)
end
end
"object" => "whatever"
}
- {:reject, nil} = VocabularyPolicy.filter(message)
+ {:reject, _} = VocabularyPolicy.filter(message)
end
test "it rejects based on child object type" do
}
}
- {:reject, nil} = VocabularyPolicy.filter(message)
+ {:reject, _} = VocabularyPolicy.filter(message)
end
test "it passes through objects that aren't disallowed" do
end
describe "publish_one/1" do
+ test "publish to url with with different ports" do
+ inbox80 = "http://42.site/users/nick1/inbox"
+ inbox42 = "http://42.site:42/users/nick1/inbox"
+
+ mock(fn
+ %{method: :post, url: "http://42.site:42/users/nick1/inbox"} ->
+ {:ok, %Tesla.Env{status: 200, body: "port 42"}}
+
+ %{method: :post, url: "http://42.site/users/nick1/inbox"} ->
+ {:ok, %Tesla.Env{status: 200, body: "port 80"}}
+ end)
+
+ actor = insert(:user)
+
+ assert {:ok, %{body: "port 42"}} =
+ Publisher.publish_one(%{
+ inbox: inbox42,
+ json: "{}",
+ actor: actor,
+ id: 1,
+ unreachable_since: true
+ })
+
+ assert {:ok, %{body: "port 80"}} =
+ Publisher.publish_one(%{
+ inbox: inbox80,
+ json: "{}",
+ actor: actor,
+ id: 1,
+ unreachable_since: true
+ })
+ end
+
test_with_mock "calls `Instances.set_reachable` on successful federation if `unreachable_since` is not specified",
Instances,
[:passthrough],
inbox = "http://200.site/users/nick1/inbox"
assert {:ok, _} = Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1})
-
assert called(Instances.set_reachable(inbox))
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
setup do: clear_config([:instance, :federating])
setup do: clear_config([:instance, :allow_relay])
setup do: clear_config([:rich_media, :enabled])
+ setup do: clear_config([:mrf, :policies])
+ setup do: clear_config([:mrf_keyword, :reject])
describe "posting statuses" do
setup do: oauth_access(["write:statuses"])
|> json_response_and_validate_schema(422)
end
+ test "Get MRF reason when posting a status is rejected by one", %{conn: conn} do
+ Pleroma.Config.put([:mrf_keyword, :reject], ["GNO"])
+ Pleroma.Config.put([:mrf, :policies], [Pleroma.Web.ActivityPub.MRF.KeywordPolicy])
+
+ assert %{"error" => "[KeywordPolicy] Matches with rejected keyword"} =
+ conn
+ |> put_req_header("content-type", "application/json")
+ |> post("api/v1/statuses", %{"status" => "GNO/Linux"})
+ |> json_response_and_validate_schema(422)
+ end
+
test "posting an undefined status with an attachment", %{user: user, conn: conn} do
file = %Plug.Upload{
content_type: "image/jpg",
defmodule Pleroma.Web.MediaProxy.MediaProxyControllerTest do
use Pleroma.Web.ConnCase
+
import Mock
- alias Pleroma.Config
- setup do: clear_config(:media_proxy)
- setup do: clear_config([Pleroma.Web.Endpoint, :secret_key_base])
+ alias Pleroma.Web.MediaProxy
+ alias Pleroma.Web.MediaProxy.MediaProxyController
+ alias Plug.Conn
setup do
on_exit(fn -> Cachex.clear(:banned_urls_cache) end)
end
test "it returns 404 when MediaProxy disabled", %{conn: conn} do
- Config.put([:media_proxy, :enabled], false)
+ clear_config([:media_proxy, :enabled], false)
- assert %Plug.Conn{
+ assert %Conn{
status: 404,
resp_body: "Not Found"
} = get(conn, "/proxy/hhgfh/eeeee")
- assert %Plug.Conn{
+ assert %Conn{
status: 404,
resp_body: "Not Found"
} = get(conn, "/proxy/hhgfh/eeee/fff")
end
- test "it returns 403 when signature invalidated", %{conn: conn} do
- Config.put([:media_proxy, :enabled], true)
- Config.put([Pleroma.Web.Endpoint, :secret_key_base], "00000000000")
- path = URI.parse(Pleroma.Web.MediaProxy.encode_url("https://google.fn")).path
- Config.put([Pleroma.Web.Endpoint, :secret_key_base], "000")
-
- assert %Plug.Conn{
- status: 403,
- resp_body: "Forbidden"
- } = get(conn, path)
-
- assert %Plug.Conn{
- status: 403,
- resp_body: "Forbidden"
- } = get(conn, "/proxy/hhgfh/eeee")
-
- assert %Plug.Conn{
- status: 403,
- resp_body: "Forbidden"
- } = get(conn, "/proxy/hhgfh/eeee/fff")
- end
+ describe "" do
+ setup do
+ clear_config([:media_proxy, :enabled], true)
+ clear_config([Pleroma.Web.Endpoint, :secret_key_base], "00000000000")
+ [url: MediaProxy.encode_url("https://google.fn/test.png")]
+ end
- test "redirects on valid url when filename invalidated", %{conn: conn} do
- Config.put([:media_proxy, :enabled], true)
- Config.put([Pleroma.Web.Endpoint, :secret_key_base], "00000000000")
- url = Pleroma.Web.MediaProxy.encode_url("https://google.fn/test.png")
- invalid_url = String.replace(url, "test.png", "test-file.png")
- response = get(conn, invalid_url)
- assert response.status == 302
- assert redirected_to(response) == url
- end
+ test "it returns 403 for invalid signature", %{conn: conn, url: url} do
+ Pleroma.Config.put([Pleroma.Web.Endpoint, :secret_key_base], "000")
+ %{path: path} = URI.parse(url)
+
+ assert %Conn{
+ status: 403,
+ resp_body: "Forbidden"
+ } = get(conn, path)
+
+ assert %Conn{
+ status: 403,
+ resp_body: "Forbidden"
+ } = get(conn, "/proxy/hhgfh/eeee")
+
+ assert %Conn{
+ status: 403,
+ resp_body: "Forbidden"
+ } = get(conn, "/proxy/hhgfh/eeee/fff")
+ end
- test "it performs ReverseProxy.call when signature valid", %{conn: conn} do
- Config.put([:media_proxy, :enabled], true)
- Config.put([Pleroma.Web.Endpoint, :secret_key_base], "00000000000")
- url = Pleroma.Web.MediaProxy.encode_url("https://google.fn/test.png")
+ test "redirects on valid url when filename is invalidated", %{conn: conn, url: url} do
+ invalid_url = String.replace(url, "test.png", "test-file.png")
+ response = get(conn, invalid_url)
+ assert response.status == 302
+ assert redirected_to(response) == url
+ end
- with_mock Pleroma.ReverseProxy,
- call: fn _conn, _url, _opts -> %Plug.Conn{status: :success} end do
- assert %Plug.Conn{status: :success} = get(conn, url)
+ test "it performs ReverseProxy.call with valid signature", %{conn: conn, url: url} do
+ with_mock Pleroma.ReverseProxy,
+ call: fn _conn, _url, _opts -> %Conn{status: :success} end do
+ assert %Conn{status: :success} = get(conn, url)
+ end
+ end
+
+ test "it returns 404 when url is in banned_urls cache", %{conn: conn, url: url} do
+ MediaProxy.put_in_banned_urls("https://google.fn/test.png")
+
+ with_mock Pleroma.ReverseProxy,
+ call: fn _conn, _url, _opts -> %Conn{status: :success} end do
+ assert %Conn{status: 404, resp_body: "Not Found"} = get(conn, url)
+ end
end
end
- test "it returns 404 when url contains in banned_urls cache", %{conn: conn} do
- Config.put([:media_proxy, :enabled], true)
- Config.put([Pleroma.Web.Endpoint, :secret_key_base], "00000000000")
- url = Pleroma.Web.MediaProxy.encode_url("https://google.fn/test.png")
- Pleroma.Web.MediaProxy.put_in_banned_urls("https://google.fn/test.png")
+ describe "filename_matches/3" do
+ test "preserves the encoded or decoded path" do
+ assert MediaProxyController.filename_matches(
+ %{"filename" => "/Hello world.jpg"},
+ "/Hello world.jpg",
+ "http://pleroma.social/Hello world.jpg"
+ ) == :ok
+
+ assert MediaProxyController.filename_matches(
+ %{"filename" => "/Hello%20world.jpg"},
+ "/Hello%20world.jpg",
+ "http://pleroma.social/Hello%20world.jpg"
+ ) == :ok
+
+ assert MediaProxyController.filename_matches(
+ %{"filename" => "/my%2Flong%2Furl%2F2019%2F07%2FS.jpg"},
+ "/my%2Flong%2Furl%2F2019%2F07%2FS.jpg",
+ "http://pleroma.social/my%2Flong%2Furl%2F2019%2F07%2FS.jpg"
+ ) == :ok
+
+ assert MediaProxyController.filename_matches(
+ %{"filename" => "/my%2Flong%2Furl%2F2019%2F07%2FS.jp"},
+ "/my%2Flong%2Furl%2F2019%2F07%2FS.jp",
+ "http://pleroma.social/my%2Flong%2Furl%2F2019%2F07%2FS.jpg"
+ ) == {:wrong_filename, "my%2Flong%2Furl%2F2019%2F07%2FS.jpg"}
+ end
+
+ test "encoded url are tried to match for proxy as `conn.request_path` encodes the url" do
+ # conn.request_path will return encoded url
+ request_path = "/ANALYSE-DAI-_-LE-STABLECOIN-100-D%C3%89CENTRALIS%C3%89-BQ.jpg"
- with_mock Pleroma.ReverseProxy,
- call: fn _conn, _url, _opts -> %Plug.Conn{status: :success} end do
- assert %Plug.Conn{status: 404, resp_body: "Not Found"} = get(conn, url)
+ assert MediaProxyController.filename_matches(
+ true,
+ request_path,
+ "https://mydomain.com/uploads/2019/07/ANALYSE-DAI-_-LE-STABLECOIN-100-DÉCENTRALISÉ-BQ.jpg"
+ ) == :ok
end
end
end
defmodule Pleroma.Web.MediaProxyTest do
use ExUnit.Case
use Pleroma.Tests.Helpers
- import Pleroma.Web.MediaProxy
- alias Pleroma.Web.MediaProxy.MediaProxyController
- setup do: clear_config([:media_proxy, :enabled])
- setup do: clear_config(Pleroma.Upload)
+ alias Pleroma.Web.Endpoint
+ alias Pleroma.Web.MediaProxy
describe "when enabled" do
- setup do
- Pleroma.Config.put([:media_proxy, :enabled], true)
- :ok
- end
+ setup do: clear_config([:media_proxy, :enabled], true)
test "ignores invalid url" do
- assert url(nil) == nil
- assert url("") == nil
+ assert MediaProxy.url(nil) == nil
+ assert MediaProxy.url("") == nil
end
test "ignores relative url" do
- assert url("/local") == "/local"
- assert url("/") == "/"
+ assert MediaProxy.url("/local") == "/local"
+ assert MediaProxy.url("/") == "/"
end
test "ignores local url" do
- local_url = Pleroma.Web.Endpoint.url() <> "/hello"
- local_root = Pleroma.Web.Endpoint.url()
- assert url(local_url) == local_url
- assert url(local_root) == local_root
+ local_url = Endpoint.url() <> "/hello"
+ local_root = Endpoint.url()
+ assert MediaProxy.url(local_url) == local_url
+ assert MediaProxy.url(local_root) == local_root
end
test "encodes and decodes URL" do
url = "https://pleroma.soykaf.com/static/logo.png"
- encoded = url(url)
+ encoded = MediaProxy.url(url)
assert String.starts_with?(
encoded,
test "encodes and decodes URL without a path" do
url = "https://pleroma.soykaf.com"
- encoded = url(url)
+ encoded = MediaProxy.url(url)
assert decode_result(encoded) == url
end
test "encodes and decodes URL without an extension" do
url = "https://pleroma.soykaf.com/path/"
- encoded = url(url)
+ encoded = MediaProxy.url(url)
assert String.ends_with?(encoded, "/path")
assert decode_result(encoded) == url
end
test "encodes and decodes URL and ignores query params for the path" do
url = "https://pleroma.soykaf.com/static/logo.png?93939393939&bunny=true"
- encoded = url(url)
+ encoded = MediaProxy.url(url)
assert String.ends_with?(encoded, "/logo.png")
assert decode_result(encoded) == url
end
test "validates signature" do
- secret_key_base = Pleroma.Config.get([Pleroma.Web.Endpoint, :secret_key_base])
-
- on_exit(fn ->
- Pleroma.Config.put([Pleroma.Web.Endpoint, :secret_key_base], secret_key_base)
- end)
-
- encoded = url("https://pleroma.social")
+ encoded = MediaProxy.url("https://pleroma.social")
- Pleroma.Config.put(
- [Pleroma.Web.Endpoint, :secret_key_base],
+ clear_config(
+ [Endpoint, :secret_key_base],
"00000000000000000000000000000000000000000000000"
)
[_, "proxy", sig, base64 | _] = URI.parse(encoded).path |> String.split("/")
- assert decode_url(sig, base64) == {:error, :invalid_signature}
- end
-
- test "filename_matches preserves the encoded or decoded path" do
- assert MediaProxyController.filename_matches(
- %{"filename" => "/Hello world.jpg"},
- "/Hello world.jpg",
- "http://pleroma.social/Hello world.jpg"
- ) == :ok
-
- assert MediaProxyController.filename_matches(
- %{"filename" => "/Hello%20world.jpg"},
- "/Hello%20world.jpg",
- "http://pleroma.social/Hello%20world.jpg"
- ) == :ok
-
- assert MediaProxyController.filename_matches(
- %{"filename" => "/my%2Flong%2Furl%2F2019%2F07%2FS.jpg"},
- "/my%2Flong%2Furl%2F2019%2F07%2FS.jpg",
- "http://pleroma.social/my%2Flong%2Furl%2F2019%2F07%2FS.jpg"
- ) == :ok
-
- assert MediaProxyController.filename_matches(
- %{"filename" => "/my%2Flong%2Furl%2F2019%2F07%2FS.jp"},
- "/my%2Flong%2Furl%2F2019%2F07%2FS.jp",
- "http://pleroma.social/my%2Flong%2Furl%2F2019%2F07%2FS.jpg"
- ) == {:wrong_filename, "my%2Flong%2Furl%2F2019%2F07%2FS.jpg"}
- end
-
- test "encoded url are tried to match for proxy as `conn.request_path` encodes the url" do
- # conn.request_path will return encoded url
- request_path = "/ANALYSE-DAI-_-LE-STABLECOIN-100-D%C3%89CENTRALIS%C3%89-BQ.jpg"
-
- assert MediaProxyController.filename_matches(
- true,
- request_path,
- "https://mydomain.com/uploads/2019/07/ANALYSE-DAI-_-LE-STABLECOIN-100-DÉCENTRALISÉ-BQ.jpg"
- ) == :ok
+ assert MediaProxy.decode_url(sig, base64) == {:error, :invalid_signature}
end
test "uses the configured base_url" do
- clear_config([:media_proxy, :base_url], "https://cache.pleroma.social")
+ base_url = "https://cache.pleroma.social"
+ clear_config([:media_proxy, :base_url], base_url)
url = "https://pleroma.soykaf.com/static/logo.png"
- encoded = url(url)
+ encoded = MediaProxy.url(url)
- assert String.starts_with?(encoded, Pleroma.Config.get([:media_proxy, :base_url]))
+ assert String.starts_with?(encoded, base_url)
end
# Some sites expect ASCII encoded characters in the URL to be preserved even if
url =
"https://pleroma.com/%20/%21/%22/%23/%24/%25/%26/%27/%28/%29/%2A/%2B/%2C/%2D/%2E/%2F/%30/%31/%32/%33/%34/%35/%36/%37/%38/%39/%3A/%3B/%3C/%3D/%3E/%3F/%40/%41/%42/%43/%44/%45/%46/%47/%48/%49/%4A/%4B/%4C/%4D/%4E/%4F/%50/%51/%52/%53/%54/%55/%56/%57/%58/%59/%5A/%5B/%5C/%5D/%5E/%5F/%60/%61/%62/%63/%64/%65/%66/%67/%68/%69/%6A/%6B/%6C/%6D/%6E/%6F/%70/%71/%72/%73/%74/%75/%76/%77/%78/%79/%7A/%7B/%7C/%7D/%7E/%7F/%80/%81/%82/%83/%84/%85/%86/%87/%88/%89/%8A/%8B/%8C/%8D/%8E/%8F/%90/%91/%92/%93/%94/%95/%96/%97/%98/%99/%9A/%9B/%9C/%9D/%9E/%9F/%C2%A0/%A1/%A2/%A3/%A4/%A5/%A6/%A7/%A8/%A9/%AA/%AB/%AC/%C2%AD/%AE/%AF/%B0/%B1/%B2/%B3/%B4/%B5/%B6/%B7/%B8/%B9/%BA/%BB/%BC/%BD/%BE/%BF/%C0/%C1/%C2/%C3/%C4/%C5/%C6/%C7/%C8/%C9/%CA/%CB/%CC/%CD/%CE/%CF/%D0/%D1/%D2/%D3/%D4/%D5/%D6/%D7/%D8/%D9/%DA/%DB/%DC/%DD/%DE/%DF/%E0/%E1/%E2/%E3/%E4/%E5/%E6/%E7/%E8/%E9/%EA/%EB/%EC/%ED/%EE/%EF/%F0/%F1/%F2/%F3/%F4/%F5/%F6/%F7/%F8/%F9/%FA/%FB/%FC/%FD/%FE/%FF"
- encoded = url(url)
+ encoded = MediaProxy.url(url)
assert decode_result(encoded) == url
end
url =
"https://pleroma.com/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890-._~:/?#[]@!$&'()*+,;=|^`{}"
- encoded = url(url)
+ encoded = MediaProxy.url(url)
assert decode_result(encoded) == url
end
test "preserve unicode characters" do
url = "https://ko.wikipedia.org/wiki/위키백과:대문"
- encoded = url(url)
+ encoded = MediaProxy.url(url)
assert decode_result(encoded) == url
end
end
describe "when disabled" do
- setup do
- enabled = Pleroma.Config.get([:media_proxy, :enabled])
-
- if enabled do
- Pleroma.Config.put([:media_proxy, :enabled], false)
-
- on_exit(fn ->
- Pleroma.Config.put([:media_proxy, :enabled], enabled)
- :ok
- end)
- end
-
- :ok
- end
+ setup do: clear_config([:media_proxy, :enabled], false)
test "does not encode remote urls" do
- assert url("https://google.fr") == "https://google.fr"
+ assert MediaProxy.url("https://google.fr") == "https://google.fr"
end
end
defp decode_result(encoded) do
[_, "proxy", sig, base64 | _] = URI.parse(encoded).path |> String.split("/")
- {:ok, decoded} = decode_url(sig, base64)
+ {:ok, decoded} = MediaProxy.decode_url(sig, base64)
decoded
end
describe "whitelist" do
- setup do
- Pleroma.Config.put([:media_proxy, :enabled], true)
- :ok
- end
+ setup do: clear_config([:media_proxy, :enabled], true)
test "mediaproxy whitelist" do
- Pleroma.Config.put([:media_proxy, :whitelist], ["google.com", "feld.me"])
+ clear_config([:media_proxy, :whitelist], ["https://google.com", "https://feld.me"])
+ url = "https://feld.me/foo.png"
+
+ unencoded = MediaProxy.url(url)
+ assert unencoded == url
+ end
+
+ # TODO: delete after removing support bare domains for media proxy whitelist
+ test "mediaproxy whitelist bare domains whitelist (deprecated)" do
+ clear_config([:media_proxy, :whitelist], ["google.com", "feld.me"])
url = "https://feld.me/foo.png"
- unencoded = url(url)
+ unencoded = MediaProxy.url(url)
assert unencoded == url
end
media_url = "https://mycdn.akamai.com"
url = "#{media_url}/static/logo.png"
- encoded = url(url)
+ encoded = MediaProxy.url(url)
assert String.starts_with?(encoded, media_url)
end
test "ensure Pleroma.Upload base_url is always whitelisted" do
media_url = "https://media.pleroma.social"
- Pleroma.Config.put([Pleroma.Upload, :base_url], media_url)
+ clear_config([Pleroma.Upload, :base_url], media_url)
url = "#{media_url}/static/logo.png"
- encoded = url(url)
+ encoded = MediaProxy.url(url)
assert String.starts_with?(encoded, media_url)
end