Merge remote-tracking branch 'upstream/develop' into by-approval
This commit is contained in:
commit
48983e9421
|
@ -12,6 +12,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- MFR policy to set global expiration for all local Create activities
|
- MFR policy to set global expiration for all local Create activities
|
||||||
- OGP rich media parser merged with TwitterCard
|
- 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: `: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>
|
<details>
|
||||||
<summary>API Changes</summary>
|
<summary>API Changes</summary>
|
||||||
|
|
|
@ -23,18 +23,14 @@
|
||||||
key: :uploader,
|
key: :uploader,
|
||||||
type: :module,
|
type: :module,
|
||||||
description: "Module which will be used for uploads",
|
description: "Module which will be used for uploads",
|
||||||
suggestions: [Pleroma.Uploaders.Local, Pleroma.Uploaders.S3]
|
suggestions: {:list_behaviour_implementations, Pleroma.Uploaders.Uploader}
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
key: :filters,
|
key: :filters,
|
||||||
type: {:list, :module},
|
type: {:list, :module},
|
||||||
description:
|
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.",
|
"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:
|
suggestions: {:list_behaviour_implementations, Pleroma.Upload.Filter}
|
||||||
Generator.list_modules_in_dir(
|
|
||||||
"lib/pleroma/upload/filter",
|
|
||||||
"Elixir.Pleroma.Upload.Filter."
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
key: :link_name,
|
key: :link_name,
|
||||||
|
@ -1076,6 +1072,7 @@
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
key: :webhook_url,
|
key: :webhook_url,
|
||||||
|
label: "Webhook URL",
|
||||||
type: :string,
|
type: :string,
|
||||||
description: "Configure the Slack incoming webhook",
|
description: "Configure the Slack incoming webhook",
|
||||||
suggestions: ["https://hooks.slack.com/services/YOUR-KEY-HERE"]
|
suggestions: ["https://hooks.slack.com/services/YOUR-KEY-HERE"]
|
||||||
|
@ -1409,11 +1406,7 @@
|
||||||
type: [:module, {:list, :module}],
|
type: [:module, {:list, :module}],
|
||||||
description:
|
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.",
|
"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:
|
suggestions: {:list_behaviour_implementations, Pleroma.Web.ActivityPub.MRF}
|
||||||
Generator.list_modules_in_dir(
|
|
||||||
"lib/pleroma/web/activity_pub/mrf",
|
|
||||||
"Elixir.Pleroma.Web.ActivityPub.MRF."
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
key: :transparency,
|
key: :transparency,
|
||||||
|
@ -1528,7 +1521,7 @@
|
||||||
children: [
|
children: [
|
||||||
%{
|
%{
|
||||||
key: :match_actor,
|
key: :match_actor,
|
||||||
type: :map,
|
type: {:map, {:list, :string}},
|
||||||
description: "Matches a series of regular expressions against the actor field",
|
description: "Matches a series of regular expressions against the actor field",
|
||||||
suggestions: [
|
suggestions: [
|
||||||
%{
|
%{
|
||||||
|
@ -1594,21 +1587,21 @@
|
||||||
children: [
|
children: [
|
||||||
%{
|
%{
|
||||||
key: :reject,
|
key: :reject,
|
||||||
type: [:string, :regex],
|
type: {:list, :string},
|
||||||
description:
|
description:
|
||||||
"A list of patterns which result in message being rejected. Each pattern can be a string or a regular expression.",
|
"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]
|
suggestions: ["foo", ~r/foo/iu]
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
key: :federated_timeline_removal,
|
key: :federated_timeline_removal,
|
||||||
type: [:string, :regex],
|
type: {:list, :string},
|
||||||
description:
|
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.",
|
"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]
|
suggestions: ["foo", ~r/foo/iu]
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
key: :replace,
|
key: :replace,
|
||||||
type: [{:tuple, :string, :string}, {:tuple, :regex, :string}],
|
type: {:list, :tuple},
|
||||||
description:
|
description:
|
||||||
"A list of tuples containing {pattern, replacement}. Each pattern can be a string or a regular expression.",
|
"A list of tuples containing {pattern, replacement}. Each pattern can be a string or a regular expression.",
|
||||||
suggestions: [{"foo", "bar"}, {~r/foo/iu, "bar"}]
|
suggestions: [{"foo", "bar"}, {~r/foo/iu, "bar"}]
|
||||||
|
@ -1780,8 +1773,8 @@
|
||||||
%{
|
%{
|
||||||
key: :whitelist,
|
key: :whitelist,
|
||||||
type: {:list, :string},
|
type: {:list, :string},
|
||||||
description: "List of domains to bypass the mediaproxy",
|
description: "List of hosts with scheme to bypass the mediaproxy",
|
||||||
suggestions: ["example.com"]
|
suggestions: ["http://example.com"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -1798,15 +1791,20 @@
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
key: :headers,
|
key: :headers,
|
||||||
type: {:list, :tuple},
|
type: {:keyword, :string},
|
||||||
description: "HTTP headers of request.",
|
description: "HTTP headers of request",
|
||||||
suggestions: [{"x-refresh", 1}]
|
suggestions: [{"x-refresh", 1}]
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
key: :options,
|
key: :options,
|
||||||
type: :keyword,
|
type: :keyword,
|
||||||
description: "Request options.",
|
description: "Request options",
|
||||||
suggestions: [params: %{ts: "xxx"}]
|
children: [
|
||||||
|
%{
|
||||||
|
key: :params,
|
||||||
|
type: {:map, :string}
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -2015,13 +2013,15 @@
|
||||||
label: "Pleroma Admin Token",
|
label: "Pleroma Admin Token",
|
||||||
type: :group,
|
type: :group,
|
||||||
description:
|
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: [
|
children: [
|
||||||
%{
|
%{
|
||||||
key: :admin_token,
|
key: :admin_token,
|
||||||
type: :string,
|
type: :string,
|
||||||
description: "Admin token",
|
description: "Admin token",
|
||||||
suggestions: ["We recommend a secure random string or UUID"]
|
suggestions: [
|
||||||
|
"Please use a high entropy string or UUID"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -2522,7 +2522,7 @@
|
||||||
%{
|
%{
|
||||||
key: :styling,
|
key: :styling,
|
||||||
type: :map,
|
type: :map,
|
||||||
description: "a map with color settings for email templates.",
|
description: "A map with color settings for email templates.",
|
||||||
suggestions: [
|
suggestions: [
|
||||||
%{
|
%{
|
||||||
link_color: "#d8a070",
|
link_color: "#d8a070",
|
||||||
|
@ -2627,7 +2627,7 @@
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
key: :groups,
|
key: :groups,
|
||||||
type: {:keyword, :string, {:list, :string}},
|
type: {:keyword, {:list, :string}},
|
||||||
description:
|
description:
|
||||||
"Emojis are ordered in groups (tags). This is an array of key-value pairs where the key is the group name" <>
|
"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.",
|
" and the value is the location or array of locations. * can be used as a wildcard.",
|
||||||
|
|
|
@ -113,6 +113,11 @@
|
||||||
|
|
||||||
config :pleroma, :instances_favicons, enabled: true
|
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
|
if File.exists?("./config/test.secret.exs") do
|
||||||
import_config "test.secret.exs"
|
import_config "test.secret.exs"
|
||||||
else
|
else
|
||||||
|
|
|
@ -253,6 +253,7 @@ This section describe PWA manifest instance-specific values. Currently this opti
|
||||||
* `background_color`: Describe the background color of the app. (Example: `"#191b22"`, `"aliceblue"`).
|
* `background_color`: Describe the background color of the app. (Example: `"#191b22"`, `"aliceblue"`).
|
||||||
|
|
||||||
## :emoji
|
## :emoji
|
||||||
|
|
||||||
* `shortcode_globs`: Location of custom emoji files. `*` can be used as a wildcard. Example `["/emoji/custom/**/*.png"]`
|
* `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"]`
|
* `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"]]`
|
* `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"]]`
|
||||||
|
@ -261,13 +262,14 @@ This section describe PWA manifest instance-specific values. Currently this opti
|
||||||
memory for this amount of seconds multiplied by the number of files.
|
memory for this amount of seconds multiplied by the number of files.
|
||||||
|
|
||||||
## :media_proxy
|
## :media_proxy
|
||||||
|
|
||||||
* `enabled`: Enables proxying of remote media to the instance’s 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.
|
* `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)]`.
|
* `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:
|
* `invalidation`: options for remove media from cache after delete object:
|
||||||
* `enabled`: Enables purge cache
|
* `enabled`: Enables purge cache
|
||||||
* `provider`: Which one of the [purge cache strategy](#purge-cache-strategy) to use.
|
* `provider`: Which one of the [purge cache strategy](#purge-cache-strategy) to use.
|
||||||
|
|
||||||
### Purge cache strategy
|
### Purge cache strategy
|
||||||
|
|
||||||
|
@ -279,6 +281,7 @@ Urls of attachments pass to script as arguments.
|
||||||
* `script_path`: path to external script.
|
* `script_path`: path to external script.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
```elixir
|
```elixir
|
||||||
config :pleroma, Pleroma.Web.MediaProxy.Invalidation.Script,
|
config :pleroma, Pleroma.Web.MediaProxy.Invalidation.Script,
|
||||||
script_path: "./installation/nginx-cache-purge.example"
|
script_path: "./installation/nginx-cache-purge.example"
|
||||||
|
@ -630,8 +633,7 @@ Email notifications settings.
|
||||||
Configuration options described in [Oban readme](https://github.com/sorentwo/oban#usage):
|
Configuration options described in [Oban readme](https://github.com/sorentwo/oban#usage):
|
||||||
|
|
||||||
* `repo` - app's Ecto repo (`Pleroma.Repo`)
|
* `repo` - app's Ecto repo (`Pleroma.Repo`)
|
||||||
* `verbose` - logs verbosity
|
* `log` - logs verbosity
|
||||||
* `prune` - non-retryable jobs [pruning settings](https://github.com/sorentwo/oban#pruning) (`:disabled` / `{:maxlen, value}` / `{:maxage, value}`)
|
|
||||||
* `queues` - job queues (see below)
|
* `queues` - job queues (see below)
|
||||||
* `crontab` - periodic jobs, see [`Oban.Cron`](#obancron)
|
* `crontab` - periodic jobs, see [`Oban.Cron`](#obancron)
|
||||||
|
|
||||||
|
@ -816,6 +818,8 @@ or
|
||||||
curl -H "X-Admin-Token: somerandomtoken" "http://localhost:4000/api/pleroma/admin/users/invites"
|
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
|
### :auth
|
||||||
|
|
||||||
* `Pleroma.Web.Auth.PleromaAuthenticator`: default database authenticator.
|
* `Pleroma.Web.Auth.PleromaAuthenticator`: default database authenticator.
|
||||||
|
|
|
@ -0,0 +1,151 @@
|
||||||
|
# How to activate Pleroma in-database configuration
|
||||||
|
## Explanation
|
||||||
|
|
||||||
|
The configuration of Pleroma has traditionally been managed with a config file, e.g. `config/prod.secret.exs`. This method requires a restart of the application for any configuration changes to take effect. We have made it possible to control most settings in the AdminFE interface after running a migration script.
|
||||||
|
|
||||||
|
## Migration to database config
|
||||||
|
|
||||||
|
1. Stop your Pleroma instance and edit your Pleroma config to enable database configuration:
|
||||||
|
|
||||||
|
```
|
||||||
|
config :pleroma, configurable_from_database: true
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Run the mix task to migrate to the database. You'll receive some debugging output and a few messages informing you of what happened.
|
||||||
|
|
||||||
|
**Source:**
|
||||||
|
|
||||||
|
```
|
||||||
|
$ mix pleroma.config migrate_to_db
|
||||||
|
```
|
||||||
|
|
||||||
|
or
|
||||||
|
|
||||||
|
**OTP:**
|
||||||
|
|
||||||
|
```
|
||||||
|
$ ./bin/pleroma_ctl config migrate_to_db
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
10:04:34.155 [debug] QUERY OK source="config" db=1.6ms decode=2.0ms queue=33.5ms idle=0.0ms
|
||||||
|
SELECT c0."id", c0."key", c0."group", c0."value", c0."inserted_at", c0."updated_at" FROM "config" AS c0 []
|
||||||
|
Migrating settings from file: /home/pleroma/config/dev.secret.exs
|
||||||
|
|
||||||
|
10:04:34.240 [debug] QUERY OK db=4.5ms queue=0.3ms idle=92.2ms
|
||||||
|
TRUNCATE config; []
|
||||||
|
|
||||||
|
10:04:34.244 [debug] QUERY OK db=2.8ms queue=0.3ms idle=97.2ms
|
||||||
|
ALTER SEQUENCE config_id_seq RESTART; []
|
||||||
|
|
||||||
|
10:04:34.256 [debug] QUERY OK source="config" db=0.8ms queue=1.4ms idle=109.8ms
|
||||||
|
SELECT c0."id", c0."key", c0."group", c0."value", c0."inserted_at", c0."updated_at" FROM "config" AS c0 WHERE ((c0."group" = $1) AND (c0."key" = $2)) [":pleroma", ":instance"]
|
||||||
|
|
||||||
|
10:04:34.292 [debug] QUERY OK db=2.6ms queue=1.7ms idle=137.7ms
|
||||||
|
INSERT INTO "config" ("group","key","value","inserted_at","updated_at") VALUES ($1,$2,$3,$4,$5) RETURNING "id" [":pleroma", ":instance", <<131, 108, 0, 0, 0, 1, 104, 2, 100, 0, 4, 110, 97, 109, 101, 109, 0, 0, 0, 7, 66, 108, 101, 114, 111, 109, 97, 106>>, ~N[2020-07-12 15:04:34], ~N[2020-07-12 15:04:34]]
|
||||||
|
Settings for key instance migrated.
|
||||||
|
Settings for group :pleroma migrated.
|
||||||
|
```
|
||||||
|
|
||||||
|
3. It is recommended to backup your config file now.
|
||||||
|
```
|
||||||
|
cp config/dev.secret.exs config/dev.secret.exs.orig
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Now you can edit your config file and strip it down to the only settings which are not possible to control in the database. e.g., the Postgres and webserver (Endpoint) settings cannot be controlled in the database because the application needs the settings to start up and access the database.
|
||||||
|
|
||||||
|
⚠️ **THIS IS NOT REQUIRED**
|
||||||
|
|
||||||
|
Any settings in the database will override those in the config file, but you may find it less confusing if the setting is only declared in one place.
|
||||||
|
|
||||||
|
A non-exhaustive list of settings that are only possible in the config file include the following:
|
||||||
|
|
||||||
|
* config :pleroma, Pleroma.Web.Endpoint
|
||||||
|
* config :pleroma, Pleroma.Repo
|
||||||
|
* config :pleroma, configurable_from_database
|
||||||
|
* config :pleroma, :database, rum_enabled
|
||||||
|
* config :pleroma, :connections_pool
|
||||||
|
|
||||||
|
Here is an example of a server config stripped down after migration:
|
||||||
|
|
||||||
|
```
|
||||||
|
use Mix.Config
|
||||||
|
|
||||||
|
config :pleroma, Pleroma.Web.Endpoint,
|
||||||
|
url: [host: "cool.pleroma.site", scheme: "https", port: 443]
|
||||||
|
|
||||||
|
|
||||||
|
config :pleroma, Pleroma.Repo,
|
||||||
|
adapter: Ecto.Adapters.Postgres,
|
||||||
|
username: "pleroma",
|
||||||
|
password: "MySecretPassword",
|
||||||
|
database: "pleroma_prod",
|
||||||
|
hostname: "localhost"
|
||||||
|
|
||||||
|
config :pleroma, configurable_from_database: true
|
||||||
|
```
|
||||||
|
|
||||||
|
5. Start your instance back up and you can now access the Settings tab in AdminFE.
|
||||||
|
|
||||||
|
|
||||||
|
## Reverting back from database config
|
||||||
|
|
||||||
|
1. Stop your Pleroma instance.
|
||||||
|
|
||||||
|
2. Run the mix task to migrate back from the database. You'll receive some debugging output and a few messages informing you of what happened.
|
||||||
|
|
||||||
|
**Source:**
|
||||||
|
|
||||||
|
```
|
||||||
|
$ mix pleroma.config migrate_from_db
|
||||||
|
```
|
||||||
|
|
||||||
|
or
|
||||||
|
|
||||||
|
**OTP:**
|
||||||
|
|
||||||
|
```
|
||||||
|
$ ./bin/pleroma_ctl config migrate_from_db
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
10:26:30.593 [debug] QUERY OK source="config" db=9.8ms decode=1.2ms queue=26.0ms idle=0.0ms
|
||||||
|
SELECT c0."id", c0."key", c0."group", c0."value", c0."inserted_at", c0."updated_at" FROM "config" AS c0 []
|
||||||
|
|
||||||
|
10:26:30.659 [debug] QUERY OK source="config" db=1.1ms idle=80.7ms
|
||||||
|
SELECT c0."id", c0."key", c0."group", c0."value", c0."inserted_at", c0."updated_at" FROM "config" AS c0 []
|
||||||
|
Database configuration settings have been saved to config/dev.exported_from_db.secret.exs
|
||||||
|
```
|
||||||
|
|
||||||
|
3. The in-database configuration still exists, but it will not be used if you remove `config :pleroma, configurable_from_database: true` from your config.
|
||||||
|
|
||||||
|
## Debugging
|
||||||
|
|
||||||
|
### Clearing database config
|
||||||
|
You can clear the database config by truncating the `config` table in the database. e.g.,
|
||||||
|
|
||||||
|
```
|
||||||
|
psql -d pleroma_dev
|
||||||
|
pleroma_dev=# TRUNCATE config;
|
||||||
|
TRUNCATE TABLE
|
||||||
|
```
|
||||||
|
|
||||||
|
Additionally, every time you migrate the configuration to the database the config table is automatically truncated to ensure a clean migration.
|
||||||
|
|
||||||
|
### Manually removing a setting
|
||||||
|
If you encounter a situation where the server cannot run properly because of an invalid setting in the database and this is preventing you from accessing AdminFE, you can manually remove the offending setting if you know which one it is.
|
||||||
|
|
||||||
|
e.g., here is an example showing a minimal configuration in the database. Only the `config :pleroma, :instance` settings are in the table:
|
||||||
|
|
||||||
|
```
|
||||||
|
psql -d pleroma_dev
|
||||||
|
pleroma_dev=# select * from config;
|
||||||
|
id | key | value | inserted_at | updated_at | group
|
||||||
|
----+-----------+------------------------------------------------------------+---------------------+---------------------+----------
|
||||||
|
1 | :instance | \x836c0000000168026400046e616d656d00000007426c65726f6d616a | 2020-07-12 15:33:29 | 2020-07-12 15:33:29 | :pleroma
|
||||||
|
(1 row)
|
||||||
|
pleroma_dev=# delete from config where key = ':instance' and group = ':pleroma';
|
||||||
|
DELETE 1
|
||||||
|
```
|
||||||
|
|
||||||
|
Now the `config :pleroma, :instance` settings have been removed from the database.
|
|
@ -83,7 +83,7 @@ defp create(group, settings) do
|
||||||
|
|
||||||
defp migrate_from_db(opts) do
|
defp migrate_from_db(opts) do
|
||||||
if Pleroma.Config.get([:configurable_from_database]) do
|
if Pleroma.Config.get([:configurable_from_database]) do
|
||||||
env = opts[:env] || "prod"
|
env = opts[:env] || Pleroma.Config.get(:env)
|
||||||
|
|
||||||
config_path =
|
config_path =
|
||||||
if Pleroma.Config.get(:release) do
|
if Pleroma.Config.get(:release) do
|
||||||
|
@ -105,6 +105,10 @@ defp migrate_from_db(opts) do
|
||||||
|
|
||||||
:ok = File.close(file)
|
:ok = File.close(file)
|
||||||
System.cmd("mix", ["format", config_path])
|
System.cmd("mix", ["format", config_path])
|
||||||
|
|
||||||
|
shell_info(
|
||||||
|
"Database configuration settings have been exported to config/#{env}.exported_from_db.secret.exs"
|
||||||
|
)
|
||||||
else
|
else
|
||||||
migration_error()
|
migration_error()
|
||||||
end
|
end
|
||||||
|
@ -112,7 +116,7 @@ defp migrate_from_db(opts) do
|
||||||
|
|
||||||
defp migration_error do
|
defp migration_error do
|
||||||
shell_error(
|
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
|
end
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,10 @@ def user_agent do
|
||||||
# See http://elixir-lang.org/docs/stable/elixir/Application.html
|
# See http://elixir-lang.org/docs/stable/elixir/Application.html
|
||||||
# for more information on OTP Applications
|
# for more information on OTP Applications
|
||||||
def start(_type, _args) do
|
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()
|
Config.Holder.save_default()
|
||||||
Pleroma.HTML.compile_scrubbers()
|
Pleroma.HTML.compile_scrubbers()
|
||||||
Config.DeprecationWarnings.warn()
|
Config.DeprecationWarnings.warn()
|
||||||
|
@ -42,6 +46,7 @@ def start(_type, _args) do
|
||||||
Pleroma.ApplicationRequirements.verify!()
|
Pleroma.ApplicationRequirements.verify!()
|
||||||
setup_instrumenters()
|
setup_instrumenters()
|
||||||
load_custom_modules()
|
load_custom_modules()
|
||||||
|
Pleroma.Docs.JSON.compile()
|
||||||
|
|
||||||
adapter = Application.get_env(:tesla, :adapter)
|
adapter = Application.get_env(:tesla, :adapter)
|
||||||
|
|
||||||
|
|
|
@ -54,6 +54,7 @@ def warn do
|
||||||
check_hellthread_threshold()
|
check_hellthread_threshold()
|
||||||
mrf_user_allowlist()
|
mrf_user_allowlist()
|
||||||
check_old_mrf_config()
|
check_old_mrf_config()
|
||||||
|
check_media_proxy_whitelist_config()
|
||||||
end
|
end
|
||||||
|
|
||||||
def check_old_mrf_config do
|
def check_old_mrf_config do
|
||||||
|
@ -65,7 +66,7 @@ def check_old_mrf_config do
|
||||||
move_namespace_and_warn(@mrf_config_map, warning_preface)
|
move_namespace_and_warn(@mrf_config_map, warning_preface)
|
||||||
end
|
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
|
def move_namespace_and_warn(config_map, warning_preface) do
|
||||||
warning =
|
warning =
|
||||||
Enum.reduce(config_map, "", fn
|
Enum.reduce(config_map, "", fn
|
||||||
|
@ -84,4 +85,16 @@ def move_namespace_and_warn(config_map, warning_preface) do
|
||||||
Logger.warn(warning_preface <> warning)
|
Logger.warn(warning_preface <> warning)
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|
|
@ -6,16 +6,21 @@ def process(implementation, descriptions) do
|
||||||
implementation.process(descriptions)
|
implementation.process(descriptions)
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec list_modules_in_dir(String.t(), String.t()) :: [module()]
|
@spec list_behaviour_implementations(behaviour :: module()) :: [module()]
|
||||||
def list_modules_in_dir(dir, start) do
|
def list_behaviour_implementations(behaviour) do
|
||||||
with {:ok, files} <- File.ls(dir) do
|
:code.all_loaded()
|
||||||
files
|
|> Enum.filter(fn {module, _} ->
|
||||||
|> Enum.filter(&String.ends_with?(&1, ".ex"))
|
# This shouldn't be needed as all modules are expected to have module_info/1,
|
||||||
|> Enum.map(fn filename ->
|
# but in test enviroments some transient modules `:elixir_compiler_XX`
|
||||||
module = filename |> String.trim_trailing(".ex") |> Macro.camelize()
|
# are loaded for some reason (where XX is a random integer).
|
||||||
String.to_atom(start <> module)
|
if function_exported?(module, :module_info, 1) do
|
||||||
end)
|
module.module_info(:attributes)
|
||||||
end
|
|> Keyword.get_values(:behaviour)
|
||||||
|
|> List.flatten()
|
||||||
|
|> Enum.member?(behaviour)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|> Enum.map(fn {module, _} -> module end)
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
|
@ -87,6 +92,12 @@ defp humanize(entity) do
|
||||||
else: string
|
else: string
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp format_suggestions({:list_behaviour_implementations, behaviour}) do
|
||||||
|
behaviour
|
||||||
|
|> list_behaviour_implementations()
|
||||||
|
|> format_suggestions()
|
||||||
|
end
|
||||||
|
|
||||||
defp format_suggestions([]), do: []
|
defp format_suggestions([]), do: []
|
||||||
|
|
||||||
defp format_suggestions([suggestion | tail]) do
|
defp format_suggestions([suggestion | tail]) do
|
||||||
|
|
|
@ -1,5 +1,19 @@
|
||||||
defmodule Pleroma.Docs.JSON do
|
defmodule Pleroma.Docs.JSON do
|
||||||
@behaviour Pleroma.Docs.Generator
|
@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()}
|
@spec process(keyword()) :: {:ok, String.t()}
|
||||||
def process(descriptions) do
|
def process(descriptions) do
|
||||||
|
@ -13,11 +27,4 @@ def process(descriptions) do
|
||||||
{:ok, path}
|
{:ok, path}
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|
|
@ -68,6 +68,11 @@ defp print_suggestion(file, suggestion, as_list \\ false) do
|
||||||
IO.write(file, " #{list_mark}`#{inspect(suggestion)}`\n")
|
IO.write(file, " #{list_mark}`#{inspect(suggestion)}`\n")
|
||||||
end
|
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, nil), do: nil
|
||||||
|
|
||||||
defp print_suggestions(_file, ""), do: nil
|
defp print_suggestions(_file, ""), do: nil
|
||||||
|
|
|
@ -4,6 +4,9 @@
|
||||||
|
|
||||||
defmodule Pleroma.Plugs.AdminSecretAuthenticationPlug do
|
defmodule Pleroma.Plugs.AdminSecretAuthenticationPlug do
|
||||||
import Plug.Conn
|
import Plug.Conn
|
||||||
|
|
||||||
|
alias Pleroma.Plugs.OAuthScopesPlug
|
||||||
|
alias Pleroma.Plugs.RateLimiter
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
|
||||||
def init(options) do
|
def init(options) do
|
||||||
|
@ -11,7 +14,10 @@ def init(options) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def secret_token do
|
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
|
end
|
||||||
|
|
||||||
def call(%{assigns: %{user: %User{}}} = conn, _), do: conn
|
def call(%{assigns: %{user: %User{}}} = conn, _), do: conn
|
||||||
|
@ -26,9 +32,9 @@ def call(conn, _) do
|
||||||
|
|
||||||
def authenticate(%{params: %{"admin_token" => admin_token}} = conn) do
|
def authenticate(%{params: %{"admin_token" => admin_token}} = conn) do
|
||||||
if admin_token == secret_token() do
|
if admin_token == secret_token() do
|
||||||
assign(conn, :user, %User{is_admin: true})
|
assign_admin_user(conn)
|
||||||
else
|
else
|
||||||
conn
|
handle_bad_token(conn)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -36,8 +42,19 @@ def authenticate(conn) do
|
||||||
token = secret_token()
|
token = secret_token()
|
||||||
|
|
||||||
case get_req_header(conn, "x-admin-token") do
|
case get_req_header(conn, "x-admin-token") do
|
||||||
[^token] -> assign(conn, :user, %User{is_admin: true})
|
blank when blank in [[], [""]] -> conn
|
||||||
_ -> conn
|
[^token] -> assign_admin_user(conn)
|
||||||
|
_ -> handle_bad_token(conn)
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|
|
@ -108,31 +108,48 @@ defp csp_string do
|
||||||
|> :erlang.iolist_to_binary()
|
|> :erlang.iolist_to_binary()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp build_csp_from_whitelist([], acc), do: acc
|
||||||
|
|
||||||
|
defp build_csp_from_whitelist([last], acc) do
|
||||||
|
[build_csp_param_from_whitelist(last) | acc]
|
||||||
|
end
|
||||||
|
|
||||||
|
defp build_csp_from_whitelist([head | tail], acc) do
|
||||||
|
build_csp_from_whitelist(tail, [[?\s, build_csp_param_from_whitelist(head)] | acc])
|
||||||
|
end
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
defp build_csp_param_from_whitelist(url), do: url
|
||||||
|
|
||||||
defp build_csp_multimedia_source_list do
|
defp build_csp_multimedia_source_list do
|
||||||
media_proxy_whitelist =
|
media_proxy_whitelist =
|
||||||
Enum.reduce(Config.get([:media_proxy, :whitelist]), [], fn host, acc ->
|
[:media_proxy, :whitelist]
|
||||||
add_source(acc, host)
|
|> Config.get()
|
||||||
end)
|
|> build_csp_from_whitelist([])
|
||||||
|
|
||||||
media_proxy_base_url = build_csp_param(Config.get([:media_proxy, :base_url]))
|
|
||||||
|
|
||||||
upload_base_url = build_csp_param(Config.get([Pleroma.Upload, :base_url]))
|
|
||||||
|
|
||||||
s3_endpoint = build_csp_param(Config.get([Pleroma.Uploaders.S3, :public_endpoint]))
|
|
||||||
|
|
||||||
captcha_method = Config.get([Pleroma.Captcha, :method])
|
captcha_method = Config.get([Pleroma.Captcha, :method])
|
||||||
|
captcha_endpoint = Config.get([captcha_method, :endpoint])
|
||||||
|
|
||||||
captcha_endpoint = build_csp_param(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]
|
||||||
|> add_source(media_proxy_base_url)
|
|> Enum.map(&build_csp_param/1)
|
||||||
|> add_source(upload_base_url)
|
|> Enum.reduce([], &add_source(&2, &1))
|
||||||
|> add_source(s3_endpoint)
|
|
||||||
|> add_source(media_proxy_whitelist)
|
|> add_source(media_proxy_whitelist)
|
||||||
|> add_source(captcha_endpoint)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
defp add_source(iodata, nil), do: iodata
|
defp add_source(iodata, nil), do: iodata
|
||||||
|
defp add_source(iodata, []), do: iodata
|
||||||
defp add_source(iodata, source), do: [[?\s, source] | iodata]
|
defp add_source(iodata, source), do: [[?\s, source] | iodata]
|
||||||
|
|
||||||
defp add_csp_param(csp_iodata, nil), do: csp_iodata
|
defp add_csp_param(csp_iodata, nil), do: csp_iodata
|
||||||
|
|
|
@ -7,37 +7,18 @@ defmodule Pleroma.Plugs.UserIsAdminPlug do
|
||||||
import Plug.Conn
|
import Plug.Conn
|
||||||
|
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.OAuth
|
|
||||||
|
|
||||||
def init(options) do
|
def init(options) do
|
||||||
options
|
options
|
||||||
end
|
end
|
||||||
|
|
||||||
def call(%{assigns: %{user: %User{is_admin: true}} = assigns} = conn, _) do
|
def call(%{assigns: %{user: %User{is_admin: true}}} = conn, _) do
|
||||||
token = assigns[:token]
|
conn
|
||||||
|
|
||||||
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
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def call(conn, _) do
|
def call(conn, _) do
|
||||||
fail(conn)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp fail(conn) do
|
|
||||||
conn
|
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()
|
|> halt()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -538,11 +538,21 @@ defp parse_fields(value) do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp put_emoji(changeset) do
|
defp put_emoji(changeset) do
|
||||||
bio = get_change(changeset, :bio)
|
emojified_fields = [:bio, :name, :raw_fields]
|
||||||
name = get_change(changeset, :name)
|
|
||||||
|
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)
|
put_change(changeset, :emoji, emoji)
|
||||||
else
|
else
|
||||||
changeset
|
changeset
|
||||||
|
|
|
@ -1376,13 +1376,28 @@ def fetch_and_prepare_user_from_ap_id(ap_id) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def maybe_handle_clashing_nickname(nickname) do
|
def maybe_handle_clashing_nickname(data) do
|
||||||
with %User{} = old_user <- User.get_by_nickname(nickname) do
|
nickname = data[:nickname]
|
||||||
Logger.info("Found an old user for #{nickname}, ap id is #{old_user.ap_id}, renaming.")
|
|
||||||
|
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
|
old_user
|
||||||
|> User.remote_user_changeset(%{nickname: "#{old_user.id}.#{old_user.nickname}"})
|
|> User.remote_user_changeset(%{nickname: "#{old_user.id}.#{old_user.nickname}"})
|
||||||
|> User.update_and_set_cache()
|
|> 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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1398,7 +1413,7 @@ def make_user_from_ap_id(ap_id) do
|
||||||
|> User.remote_user_changeset(data)
|
|> User.remote_user_changeset(data)
|
||||||
|> User.update_and_set_cache()
|
|> User.update_and_set_cache()
|
||||||
else
|
else
|
||||||
maybe_handle_clashing_nickname(data[:nickname])
|
maybe_handle_clashing_nickname(data)
|
||||||
|
|
||||||
data
|
data
|
||||||
|> User.remote_user_changeset()
|
|> User.remote_user_changeset()
|
||||||
|
|
|
@ -62,15 +62,17 @@ def fix_summary(%{"summary" => _} = object) do
|
||||||
def fix_summary(object), do: Map.put(object, "summary", "")
|
def fix_summary(object), do: Map.put(object, "summary", "")
|
||||||
|
|
||||||
def fix_addressing_list(map, field) do
|
def fix_addressing_list(map, field) do
|
||||||
cond do
|
addrs = map[field]
|
||||||
is_binary(map[field]) ->
|
|
||||||
Map.put(map, field, [map[field]])
|
|
||||||
|
|
||||||
is_nil(map[field]) ->
|
cond do
|
||||||
Map.put(map, field, [])
|
is_list(addrs) ->
|
||||||
|
Map.put(map, field, Enum.filter(addrs, &is_binary/1))
|
||||||
|
|
||||||
|
is_binary(addrs) ->
|
||||||
|
Map.put(map, field, [addrs])
|
||||||
|
|
||||||
true ->
|
true ->
|
||||||
map
|
Map.put(map, field, [])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -9,8 +9,6 @@ defmodule Pleroma.Web.AdminAPI.ConfigController do
|
||||||
alias Pleroma.ConfigDB
|
alias Pleroma.ConfigDB
|
||||||
alias Pleroma.Plugs.OAuthScopesPlug
|
alias Pleroma.Plugs.OAuthScopesPlug
|
||||||
|
|
||||||
@descriptions Pleroma.Docs.JSON.compile()
|
|
||||||
|
|
||||||
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||||
plug(OAuthScopesPlug, %{scopes: ["write"], admin: true} when action == :update)
|
plug(OAuthScopesPlug, %{scopes: ["write"], admin: true} when action == :update)
|
||||||
|
|
||||||
|
@ -25,7 +23,7 @@ defmodule Pleroma.Web.AdminAPI.ConfigController do
|
||||||
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.Admin.ConfigOperation
|
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.Admin.ConfigOperation
|
||||||
|
|
||||||
def descriptions(conn, _params) do
|
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)
|
json(conn, descriptions)
|
||||||
end
|
end
|
||||||
|
|
|
@ -29,6 +29,10 @@ def request_body(description, schema_ref, opts \\ []) do
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def admin_api_params do
|
||||||
|
[Operation.parameter(:admin_token, :query, :string, "Allows authorization via admin token.")]
|
||||||
|
end
|
||||||
|
|
||||||
def pagination_params do
|
def pagination_params do
|
||||||
[
|
[
|
||||||
Operation.parameter(:max_id, :query, :string, "Return items older than this ID"),
|
Operation.parameter(:max_id, :query, :string, "Return items older than this ID"),
|
||||||
|
|
|
@ -26,6 +26,7 @@ def show_operation do
|
||||||
%Schema{type: :boolean, default: false},
|
%Schema{type: :boolean, default: false},
|
||||||
"Get only saved in database settings"
|
"Get only saved in database settings"
|
||||||
)
|
)
|
||||||
|
| admin_api_params()
|
||||||
],
|
],
|
||||||
security: [%{"oAuth" => ["read"]}],
|
security: [%{"oAuth" => ["read"]}],
|
||||||
responses: %{
|
responses: %{
|
||||||
|
@ -41,6 +42,7 @@ def update_operation do
|
||||||
summary: "Update config settings",
|
summary: "Update config settings",
|
||||||
operationId: "AdminAPI.ConfigController.update",
|
operationId: "AdminAPI.ConfigController.update",
|
||||||
security: [%{"oAuth" => ["write"]}],
|
security: [%{"oAuth" => ["write"]}],
|
||||||
|
parameters: admin_api_params(),
|
||||||
requestBody:
|
requestBody:
|
||||||
request_body("Parameters", %Schema{
|
request_body("Parameters", %Schema{
|
||||||
type: :object,
|
type: :object,
|
||||||
|
@ -73,6 +75,7 @@ def descriptions_operation do
|
||||||
summary: "Get JSON with config descriptions.",
|
summary: "Get JSON with config descriptions.",
|
||||||
operationId: "AdminAPI.ConfigController.descriptions",
|
operationId: "AdminAPI.ConfigController.descriptions",
|
||||||
security: [%{"oAuth" => ["read"]}],
|
security: [%{"oAuth" => ["read"]}],
|
||||||
|
parameters: admin_api_params(),
|
||||||
responses: %{
|
responses: %{
|
||||||
200 =>
|
200 =>
|
||||||
Operation.response("Config Descriptions", "application/json", %Schema{
|
Operation.response("Config Descriptions", "application/json", %Schema{
|
||||||
|
|
|
@ -20,6 +20,7 @@ def index_operation do
|
||||||
summary: "Get a list of generated invites",
|
summary: "Get a list of generated invites",
|
||||||
operationId: "AdminAPI.InviteController.index",
|
operationId: "AdminAPI.InviteController.index",
|
||||||
security: [%{"oAuth" => ["read:invites"]}],
|
security: [%{"oAuth" => ["read:invites"]}],
|
||||||
|
parameters: admin_api_params(),
|
||||||
responses: %{
|
responses: %{
|
||||||
200 =>
|
200 =>
|
||||||
Operation.response("Invites", "application/json", %Schema{
|
Operation.response("Invites", "application/json", %Schema{
|
||||||
|
@ -51,6 +52,7 @@ def create_operation do
|
||||||
summary: "Create an account registration invite token",
|
summary: "Create an account registration invite token",
|
||||||
operationId: "AdminAPI.InviteController.create",
|
operationId: "AdminAPI.InviteController.create",
|
||||||
security: [%{"oAuth" => ["write:invites"]}],
|
security: [%{"oAuth" => ["write:invites"]}],
|
||||||
|
parameters: admin_api_params(),
|
||||||
requestBody:
|
requestBody:
|
||||||
request_body("Parameters", %Schema{
|
request_body("Parameters", %Schema{
|
||||||
type: :object,
|
type: :object,
|
||||||
|
@ -71,6 +73,7 @@ def revoke_operation do
|
||||||
summary: "Revoke invite by token",
|
summary: "Revoke invite by token",
|
||||||
operationId: "AdminAPI.InviteController.revoke",
|
operationId: "AdminAPI.InviteController.revoke",
|
||||||
security: [%{"oAuth" => ["write:invites"]}],
|
security: [%{"oAuth" => ["write:invites"]}],
|
||||||
|
parameters: admin_api_params(),
|
||||||
requestBody:
|
requestBody:
|
||||||
request_body(
|
request_body(
|
||||||
"Parameters",
|
"Parameters",
|
||||||
|
@ -97,6 +100,7 @@ def email_operation do
|
||||||
summary: "Sends registration invite via email",
|
summary: "Sends registration invite via email",
|
||||||
operationId: "AdminAPI.InviteController.email",
|
operationId: "AdminAPI.InviteController.email",
|
||||||
security: [%{"oAuth" => ["write:invites"]}],
|
security: [%{"oAuth" => ["write:invites"]}],
|
||||||
|
parameters: admin_api_params(),
|
||||||
requestBody:
|
requestBody:
|
||||||
request_body(
|
request_body(
|
||||||
"Parameters",
|
"Parameters",
|
||||||
|
|
|
@ -33,6 +33,7 @@ def index_operation do
|
||||||
%Schema{type: :integer, default: 50},
|
%Schema{type: :integer, default: 50},
|
||||||
"Number of statuses to return"
|
"Number of statuses to return"
|
||||||
)
|
)
|
||||||
|
| admin_api_params()
|
||||||
],
|
],
|
||||||
responses: %{
|
responses: %{
|
||||||
200 => success_response()
|
200 => success_response()
|
||||||
|
@ -46,6 +47,7 @@ def delete_operation do
|
||||||
summary: "Remove a banned MediaProxy URL from Cachex",
|
summary: "Remove a banned MediaProxy URL from Cachex",
|
||||||
operationId: "AdminAPI.MediaProxyCacheController.delete",
|
operationId: "AdminAPI.MediaProxyCacheController.delete",
|
||||||
security: [%{"oAuth" => ["write:media_proxy_caches"]}],
|
security: [%{"oAuth" => ["write:media_proxy_caches"]}],
|
||||||
|
parameters: admin_api_params(),
|
||||||
requestBody:
|
requestBody:
|
||||||
request_body(
|
request_body(
|
||||||
"Parameters",
|
"Parameters",
|
||||||
|
@ -71,6 +73,7 @@ def purge_operation do
|
||||||
summary: "Purge and optionally ban a MediaProxy URL",
|
summary: "Purge and optionally ban a MediaProxy URL",
|
||||||
operationId: "AdminAPI.MediaProxyCacheController.purge",
|
operationId: "AdminAPI.MediaProxyCacheController.purge",
|
||||||
security: [%{"oAuth" => ["write:media_proxy_caches"]}],
|
security: [%{"oAuth" => ["write:media_proxy_caches"]}],
|
||||||
|
parameters: admin_api_params(),
|
||||||
requestBody:
|
requestBody:
|
||||||
request_body(
|
request_body(
|
||||||
"Parameters",
|
"Parameters",
|
||||||
|
|
|
@ -36,6 +36,7 @@ def index_operation do
|
||||||
%Schema{type: :integer, default: 50},
|
%Schema{type: :integer, default: 50},
|
||||||
"Number of apps to return"
|
"Number of apps to return"
|
||||||
)
|
)
|
||||||
|
| admin_api_params()
|
||||||
],
|
],
|
||||||
responses: %{
|
responses: %{
|
||||||
200 =>
|
200 =>
|
||||||
|
@ -72,6 +73,7 @@ def create_operation do
|
||||||
summary: "Create OAuth App",
|
summary: "Create OAuth App",
|
||||||
operationId: "AdminAPI.OAuthAppController.create",
|
operationId: "AdminAPI.OAuthAppController.create",
|
||||||
requestBody: request_body("Parameters", create_request()),
|
requestBody: request_body("Parameters", create_request()),
|
||||||
|
parameters: admin_api_params(),
|
||||||
security: [%{"oAuth" => ["write"]}],
|
security: [%{"oAuth" => ["write"]}],
|
||||||
responses: %{
|
responses: %{
|
||||||
200 => Operation.response("App", "application/json", oauth_app()),
|
200 => Operation.response("App", "application/json", oauth_app()),
|
||||||
|
@ -85,7 +87,7 @@ def update_operation do
|
||||||
tags: ["Admin", "oAuth Apps"],
|
tags: ["Admin", "oAuth Apps"],
|
||||||
summary: "Update OAuth App",
|
summary: "Update OAuth App",
|
||||||
operationId: "AdminAPI.OAuthAppController.update",
|
operationId: "AdminAPI.OAuthAppController.update",
|
||||||
parameters: [id_param()],
|
parameters: [id_param() | admin_api_params()],
|
||||||
security: [%{"oAuth" => ["write"]}],
|
security: [%{"oAuth" => ["write"]}],
|
||||||
requestBody: request_body("Parameters", update_request()),
|
requestBody: request_body("Parameters", update_request()),
|
||||||
responses: %{
|
responses: %{
|
||||||
|
@ -103,7 +105,7 @@ def delete_operation do
|
||||||
tags: ["Admin", "oAuth Apps"],
|
tags: ["Admin", "oAuth Apps"],
|
||||||
summary: "Delete OAuth App",
|
summary: "Delete OAuth App",
|
||||||
operationId: "AdminAPI.OAuthAppController.delete",
|
operationId: "AdminAPI.OAuthAppController.delete",
|
||||||
parameters: [id_param()],
|
parameters: [id_param() | admin_api_params()],
|
||||||
security: [%{"oAuth" => ["write"]}],
|
security: [%{"oAuth" => ["write"]}],
|
||||||
responses: %{
|
responses: %{
|
||||||
204 => no_content_response(),
|
204 => no_content_response(),
|
||||||
|
|
|
@ -19,6 +19,7 @@ def index_operation do
|
||||||
summary: "List Relays",
|
summary: "List Relays",
|
||||||
operationId: "AdminAPI.RelayController.index",
|
operationId: "AdminAPI.RelayController.index",
|
||||||
security: [%{"oAuth" => ["read"]}],
|
security: [%{"oAuth" => ["read"]}],
|
||||||
|
parameters: admin_api_params(),
|
||||||
responses: %{
|
responses: %{
|
||||||
200 =>
|
200 =>
|
||||||
Operation.response("Response", "application/json", %Schema{
|
Operation.response("Response", "application/json", %Schema{
|
||||||
|
@ -41,6 +42,7 @@ def follow_operation do
|
||||||
summary: "Follow a Relay",
|
summary: "Follow a Relay",
|
||||||
operationId: "AdminAPI.RelayController.follow",
|
operationId: "AdminAPI.RelayController.follow",
|
||||||
security: [%{"oAuth" => ["write:follows"]}],
|
security: [%{"oAuth" => ["write:follows"]}],
|
||||||
|
parameters: admin_api_params(),
|
||||||
requestBody:
|
requestBody:
|
||||||
request_body("Parameters", %Schema{
|
request_body("Parameters", %Schema{
|
||||||
type: :object,
|
type: :object,
|
||||||
|
@ -64,6 +66,7 @@ def unfollow_operation do
|
||||||
summary: "Unfollow a Relay",
|
summary: "Unfollow a Relay",
|
||||||
operationId: "AdminAPI.RelayController.unfollow",
|
operationId: "AdminAPI.RelayController.unfollow",
|
||||||
security: [%{"oAuth" => ["write:follows"]}],
|
security: [%{"oAuth" => ["write:follows"]}],
|
||||||
|
parameters: admin_api_params(),
|
||||||
requestBody:
|
requestBody:
|
||||||
request_body("Parameters", %Schema{
|
request_body("Parameters", %Schema{
|
||||||
type: :object,
|
type: :object,
|
||||||
|
|
|
@ -48,6 +48,7 @@ def index_operation do
|
||||||
%Schema{type: :integer, default: 50},
|
%Schema{type: :integer, default: 50},
|
||||||
"Number number of log entries per page"
|
"Number number of log entries per page"
|
||||||
)
|
)
|
||||||
|
| admin_api_params()
|
||||||
],
|
],
|
||||||
responses: %{
|
responses: %{
|
||||||
200 =>
|
200 =>
|
||||||
|
@ -71,7 +72,7 @@ def show_operation do
|
||||||
tags: ["Admin", "Reports"],
|
tags: ["Admin", "Reports"],
|
||||||
summary: "Get an individual report",
|
summary: "Get an individual report",
|
||||||
operationId: "AdminAPI.ReportController.show",
|
operationId: "AdminAPI.ReportController.show",
|
||||||
parameters: [id_param()],
|
parameters: [id_param() | admin_api_params()],
|
||||||
security: [%{"oAuth" => ["read:reports"]}],
|
security: [%{"oAuth" => ["read:reports"]}],
|
||||||
responses: %{
|
responses: %{
|
||||||
200 => Operation.response("Report", "application/json", report()),
|
200 => Operation.response("Report", "application/json", report()),
|
||||||
|
@ -86,6 +87,7 @@ def update_operation do
|
||||||
summary: "Change the state of one or multiple reports",
|
summary: "Change the state of one or multiple reports",
|
||||||
operationId: "AdminAPI.ReportController.update",
|
operationId: "AdminAPI.ReportController.update",
|
||||||
security: [%{"oAuth" => ["write:reports"]}],
|
security: [%{"oAuth" => ["write:reports"]}],
|
||||||
|
parameters: admin_api_params(),
|
||||||
requestBody: request_body("Parameters", update_request(), required: true),
|
requestBody: request_body("Parameters", update_request(), required: true),
|
||||||
responses: %{
|
responses: %{
|
||||||
204 => no_content_response(),
|
204 => no_content_response(),
|
||||||
|
@ -100,7 +102,7 @@ def notes_create_operation do
|
||||||
tags: ["Admin", "Reports"],
|
tags: ["Admin", "Reports"],
|
||||||
summary: "Create report note",
|
summary: "Create report note",
|
||||||
operationId: "AdminAPI.ReportController.notes_create",
|
operationId: "AdminAPI.ReportController.notes_create",
|
||||||
parameters: [id_param()],
|
parameters: [id_param() | admin_api_params()],
|
||||||
requestBody:
|
requestBody:
|
||||||
request_body("Parameters", %Schema{
|
request_body("Parameters", %Schema{
|
||||||
type: :object,
|
type: :object,
|
||||||
|
@ -124,6 +126,7 @@ def notes_delete_operation do
|
||||||
parameters: [
|
parameters: [
|
||||||
Operation.parameter(:report_id, :path, :string, "Report ID"),
|
Operation.parameter(:report_id, :path, :string, "Report ID"),
|
||||||
Operation.parameter(:id, :path, :string, "Note ID")
|
Operation.parameter(:id, :path, :string, "Note ID")
|
||||||
|
| admin_api_params()
|
||||||
],
|
],
|
||||||
security: [%{"oAuth" => ["write:reports"]}],
|
security: [%{"oAuth" => ["write:reports"]}],
|
||||||
responses: %{
|
responses: %{
|
||||||
|
|
|
@ -55,6 +55,7 @@ def index_operation do
|
||||||
%Schema{type: :integer, default: 50},
|
%Schema{type: :integer, default: 50},
|
||||||
"Number of statuses to return"
|
"Number of statuses to return"
|
||||||
)
|
)
|
||||||
|
| admin_api_params()
|
||||||
],
|
],
|
||||||
responses: %{
|
responses: %{
|
||||||
200 =>
|
200 =>
|
||||||
|
@ -71,7 +72,7 @@ def show_operation do
|
||||||
tags: ["Admin", "Statuses"],
|
tags: ["Admin", "Statuses"],
|
||||||
summary: "Show Status",
|
summary: "Show Status",
|
||||||
operationId: "AdminAPI.StatusController.show",
|
operationId: "AdminAPI.StatusController.show",
|
||||||
parameters: [id_param()],
|
parameters: [id_param() | admin_api_params()],
|
||||||
security: [%{"oAuth" => ["read:statuses"]}],
|
security: [%{"oAuth" => ["read:statuses"]}],
|
||||||
responses: %{
|
responses: %{
|
||||||
200 => Operation.response("Status", "application/json", status()),
|
200 => Operation.response("Status", "application/json", status()),
|
||||||
|
@ -85,7 +86,7 @@ def update_operation do
|
||||||
tags: ["Admin", "Statuses"],
|
tags: ["Admin", "Statuses"],
|
||||||
summary: "Change the scope of an individual reported status",
|
summary: "Change the scope of an individual reported status",
|
||||||
operationId: "AdminAPI.StatusController.update",
|
operationId: "AdminAPI.StatusController.update",
|
||||||
parameters: [id_param()],
|
parameters: [id_param() | admin_api_params()],
|
||||||
security: [%{"oAuth" => ["write:statuses"]}],
|
security: [%{"oAuth" => ["write:statuses"]}],
|
||||||
requestBody: request_body("Parameters", update_request(), required: true),
|
requestBody: request_body("Parameters", update_request(), required: true),
|
||||||
responses: %{
|
responses: %{
|
||||||
|
@ -100,7 +101,7 @@ def delete_operation do
|
||||||
tags: ["Admin", "Statuses"],
|
tags: ["Admin", "Statuses"],
|
||||||
summary: "Delete an individual reported status",
|
summary: "Delete an individual reported status",
|
||||||
operationId: "AdminAPI.StatusController.delete",
|
operationId: "AdminAPI.StatusController.delete",
|
||||||
parameters: [id_param()],
|
parameters: [id_param() | admin_api_params()],
|
||||||
security: [%{"oAuth" => ["write:statuses"]}],
|
security: [%{"oAuth" => ["write:statuses"]}],
|
||||||
responses: %{
|
responses: %{
|
||||||
200 => empty_object_response(),
|
200 => empty_object_response(),
|
||||||
|
|
|
@ -60,22 +60,28 @@ defp local?(url), do: String.starts_with?(url, Pleroma.Web.base_url())
|
||||||
defp whitelisted?(url) do
|
defp whitelisted?(url) do
|
||||||
%{host: domain} = URI.parse(url)
|
%{host: domain} = URI.parse(url)
|
||||||
|
|
||||||
mediaproxy_whitelist = Config.get([:media_proxy, :whitelist])
|
mediaproxy_whitelist_domains =
|
||||||
|
[:media_proxy, :whitelist]
|
||||||
|
|> Config.get()
|
||||||
|
|> Enum.map(&maybe_get_domain_from_url/1)
|
||||||
|
|
||||||
upload_base_url_domain =
|
whitelist_domains =
|
||||||
if !is_nil(Config.get([Upload, :base_url])) do
|
if base_url = Config.get([Upload, :base_url]) do
|
||||||
[URI.parse(Config.get([Upload, :base_url])).host]
|
%{host: base_domain} = URI.parse(base_url)
|
||||||
|
[base_domain | mediaproxy_whitelist_domains]
|
||||||
else
|
else
|
||||||
[]
|
mediaproxy_whitelist_domains
|
||||||
end
|
end
|
||||||
|
|
||||||
whitelist = mediaproxy_whitelist ++ upload_base_url_domain
|
domain in whitelist_domains
|
||||||
|
|
||||||
Enum.any?(whitelist, fn pattern ->
|
|
||||||
String.equivalent?(domain, pattern)
|
|
||||||
end)
|
|
||||||
end
|
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
|
def encode_url(url) do
|
||||||
base64 = Base.url_encode64(url, @base64_opts)
|
base64 = Base.url_encode64(url, @base64_opts)
|
||||||
|
|
||||||
|
|
2
mix.exs
2
mix.exs
|
@ -90,8 +90,6 @@ defp elixirc_paths(:test), do: ["lib", "test/support"]
|
||||||
defp elixirc_paths(_), do: ["lib"]
|
defp elixirc_paths(_), do: ["lib"]
|
||||||
|
|
||||||
defp warnings_as_errors(:prod), do: false
|
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
|
defp warnings_as_errors(_), do: true
|
||||||
|
|
||||||
# Specifies OAuth dependencies.
|
# Specifies OAuth dependencies.
|
||||||
|
|
|
@ -90,110 +90,100 @@ msgid "must be equal to %{number}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
#: lib/pleroma/web/common_api/common_api.ex:421
|
#: lib/pleroma/web/common_api/common_api.ex:505
|
||||||
msgid "Account not found"
|
msgid "Account not found"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
#: lib/pleroma/web/common_api/common_api.ex:249
|
#: lib/pleroma/web/common_api/common_api.ex:339
|
||||||
msgid "Already voted"
|
msgid "Already voted"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
#: lib/pleroma/web/oauth/oauth_controller.ex:360
|
#: lib/pleroma/web/oauth/oauth_controller.ex:359
|
||||||
msgid "Bad request"
|
msgid "Bad request"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, 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"
|
msgid "Can't delete object"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
#: lib/pleroma/web/mastodon_api/controllers/status_controller.ex:196
|
#: lib/pleroma/web/controller_helper.ex:105
|
||||||
msgid "Can't delete this post"
|
#: lib/pleroma/web/controller_helper.ex:111
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#, elixir-format
|
|
||||||
#: lib/pleroma/web/controller_helper.ex:95
|
|
||||||
#: lib/pleroma/web/controller_helper.ex:101
|
|
||||||
msgid "Can't display this activity"
|
msgid "Can't display this activity"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:227
|
#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:285
|
||||||
#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:254
|
|
||||||
msgid "Can't find user"
|
msgid "Can't find user"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, 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"
|
msgid "Can't get favorites"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, 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"
|
msgid "Can't like object"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, 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"
|
msgid "Cannot post an empty status without attachments"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, 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"
|
msgid "Comment must be up to %{max_size} characters"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
#: lib/pleroma/config/config_db.ex:222
|
#: lib/pleroma/config/config_db.ex:191
|
||||||
msgid "Config with params %{params} not found"
|
msgid "Config with params %{params} not found"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, 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"
|
msgid "Could not delete"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
#: lib/pleroma/web/common_api/common_api.ex:141
|
#: lib/pleroma/web/common_api/common_api.ex:231
|
||||||
msgid "Could not favorite"
|
msgid "Could not favorite"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
#: lib/pleroma/web/common_api/common_api.ex:370
|
#: lib/pleroma/web/common_api/common_api.ex:453
|
||||||
msgid "Could not pin"
|
msgid "Could not pin"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
#: lib/pleroma/web/common_api/common_api.ex:112
|
#: lib/pleroma/web/common_api/common_api.ex:278
|
||||||
msgid "Could not repeat"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#, elixir-format
|
|
||||||
#: lib/pleroma/web/common_api/common_api.ex:188
|
|
||||||
msgid "Could not unfavorite"
|
msgid "Could not unfavorite"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
#: lib/pleroma/web/common_api/common_api.ex:380
|
#: lib/pleroma/web/common_api/common_api.ex:463
|
||||||
msgid "Could not unpin"
|
msgid "Could not unpin"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
#: lib/pleroma/web/common_api/common_api.ex:126
|
#: lib/pleroma/web/common_api/common_api.ex:216
|
||||||
msgid "Could not unrepeat"
|
msgid "Could not unrepeat"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
#: lib/pleroma/web/common_api/common_api.ex:428
|
#: lib/pleroma/web/common_api/common_api.ex:512
|
||||||
#: lib/pleroma/web/common_api/common_api.ex:437
|
#: lib/pleroma/web/common_api/common_api.ex:521
|
||||||
msgid "Could not update state"
|
msgid "Could not update state"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
#: lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex:202
|
#: lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex:207
|
||||||
msgid "Error."
|
msgid "Error."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -203,8 +193,8 @@ msgid "Invalid CAPTCHA"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:117
|
#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:116
|
||||||
#: lib/pleroma/web/oauth/oauth_controller.ex:569
|
#: lib/pleroma/web/oauth/oauth_controller.ex:568
|
||||||
msgid "Invalid credentials"
|
msgid "Invalid credentials"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -214,22 +204,22 @@ msgid "Invalid credentials."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
#: lib/pleroma/web/common_api/common_api.ex:265
|
#: lib/pleroma/web/common_api/common_api.ex:355
|
||||||
msgid "Invalid indices"
|
msgid "Invalid indices"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, 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"
|
msgid "Invalid parameters"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
#: lib/pleroma/web/common_api/utils.ex:411
|
#: lib/pleroma/web/common_api/utils.ex:414
|
||||||
msgid "Invalid password."
|
msgid "Invalid password."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, 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"
|
msgid "Invalid request"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -239,44 +229,44 @@ msgid "Kocaptcha service unavailable"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, 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"
|
msgid "Missing parameters"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
#: lib/pleroma/web/common_api/utils.ex:540
|
#: lib/pleroma/web/common_api/utils.ex:547
|
||||||
msgid "No such conversation"
|
msgid "No such conversation"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
#: lib/pleroma/web/admin_api/admin_api_controller.ex:439
|
#: lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:388
|
||||||
#: 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:414 lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:456
|
||||||
msgid "No such permission_group"
|
msgid "No such permission_group"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
#: lib/pleroma/plugs/uploaded_media.ex:74
|
#: lib/pleroma/plugs/uploaded_media.ex:84
|
||||||
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:485 lib/pleroma/web/admin_api/admin_api_controller.ex:1135
|
#: 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:73 lib/pleroma/web/ostatus/ostatus_controller.ex:143
|
#: lib/pleroma/web/feed/user_controller.ex:71 lib/pleroma/web/ostatus/ostatus_controller.ex:143
|
||||||
msgid "Not found"
|
msgid "Not found"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, 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"
|
msgid "Poll's author can't vote"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
#: lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex:20
|
#: 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: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
|
#: lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex:71
|
||||||
msgid "Record not found"
|
msgid "Record not found"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
#: lib/pleroma/web/admin_api/admin_api_controller.ex:1153
|
#: lib/pleroma/web/admin_api/controllers/fallback_controller.ex:35
|
||||||
#: lib/pleroma/web/feed/user_controller.ex:79 lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex:32
|
#: 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
|
#: lib/pleroma/web/ostatus/ostatus_controller.ex:149
|
||||||
msgid "Something went wrong"
|
msgid "Something went wrong"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
@ -287,7 +277,7 @@ msgid "The message visibility must be direct"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, 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"
|
msgid "The status is over the character limit"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -302,65 +292,65 @@ msgid "Throttled"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
#: lib/pleroma/web/common_api/common_api.ex:266
|
#: lib/pleroma/web/common_api/common_api.ex:356
|
||||||
msgid "Too many choices"
|
msgid "Too many choices"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, 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"
|
msgid "Unhandled activity type"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, 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."
|
msgid "You can't revoke your own admin status."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
#: lib/pleroma/web/oauth/oauth_controller.ex:218
|
#: lib/pleroma/web/oauth/oauth_controller.ex:221
|
||||||
#: lib/pleroma/web/oauth/oauth_controller.ex:309
|
#: lib/pleroma/web/oauth/oauth_controller.ex:308
|
||||||
msgid "Your account is currently disabled"
|
msgid "Your account is currently disabled"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
#: lib/pleroma/web/oauth/oauth_controller.ex:180
|
#: lib/pleroma/web/oauth/oauth_controller.ex:183
|
||||||
#: lib/pleroma/web/oauth/oauth_controller.ex:332
|
#: lib/pleroma/web/oauth/oauth_controller.ex:331
|
||||||
msgid "Your login is missing a confirmed e-mail address"
|
msgid "Your login is missing a confirmed e-mail address"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, 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}"
|
msgid "can't read inbox of %{nickname} as %{as_nickname}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, 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}"
|
msgid "can't update outbox of %{nickname} as %{as_nickname}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, 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"
|
msgid "conversation is already muted"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:316
|
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:314
|
||||||
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:491
|
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:492
|
||||||
msgid "error"
|
msgid "error"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, 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"
|
msgid "mascots can only be images"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, 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"
|
msgid "not found"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
#: lib/pleroma/web/oauth/oauth_controller.ex:395
|
#: lib/pleroma/web/oauth/oauth_controller.ex:394
|
||||||
msgid "Bad OAuth request."
|
msgid "Bad OAuth request."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -375,17 +365,17 @@ msgid "CAPTCHA expired"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
#: lib/pleroma/plugs/uploaded_media.ex:55
|
#: lib/pleroma/plugs/uploaded_media.ex:57
|
||||||
msgid "Failed"
|
msgid "Failed"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
#: lib/pleroma/web/oauth/oauth_controller.ex:411
|
#: lib/pleroma/web/oauth/oauth_controller.ex:410
|
||||||
msgid "Failed to authenticate: %{message}."
|
msgid "Failed to authenticate: %{message}."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, 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."
|
msgid "Failed to set up user account."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -395,7 +385,7 @@ msgid "Insufficient permissions: %{permissions}."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
#: lib/pleroma/plugs/uploaded_media.ex:94
|
#: lib/pleroma/plugs/uploaded_media.ex:104
|
||||||
msgid "Internal Error"
|
msgid "Internal Error"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -411,12 +401,12 @@ msgid "Invalid answer data"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
#: lib/pleroma/web/nodeinfo/nodeinfo_controller.ex:128
|
#: lib/pleroma/web/nodeinfo/nodeinfo_controller.ex:33
|
||||||
msgid "Nodeinfo schema version not handled"
|
msgid "Nodeinfo schema version not handled"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, 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"
|
msgid "This action is outside the authorized scopes"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -426,13 +416,13 @@ msgid "Unknown error, please check the details and try again."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
#: lib/pleroma/web/oauth/oauth_controller.ex:116
|
#: lib/pleroma/web/oauth/oauth_controller.ex:119
|
||||||
#: lib/pleroma/web/oauth/oauth_controller.ex:155
|
#: lib/pleroma/web/oauth/oauth_controller.ex:158
|
||||||
msgid "Unlisted redirect_uri."
|
msgid "Unlisted redirect_uri."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
#: lib/pleroma/web/oauth/oauth_controller.ex:391
|
#: lib/pleroma/web/oauth/oauth_controller.ex:390
|
||||||
msgid "Unsupported OAuth provider: %{provider}."
|
msgid "Unsupported OAuth provider: %{provider}."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -452,12 +442,12 @@ msgid "CAPTCHA Error"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, 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"
|
msgid "Could not add reaction emoji"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, 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"
|
msgid "Could not remove reaction emoji"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -472,39 +462,45 @@ msgid "List not found"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, 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}"
|
msgid "Missing parameter: %{name}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
#: lib/pleroma/web/oauth/oauth_controller.ex:207
|
#: lib/pleroma/web/oauth/oauth_controller.ex:210
|
||||||
#: lib/pleroma/web/oauth/oauth_controller.ex:322
|
#: lib/pleroma/web/oauth/oauth_controller.ex:321
|
||||||
msgid "Password reset is required"
|
msgid "Password reset is required"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
#: lib/pleroma/tests/auth_test_controller.ex:9
|
#: 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/activity_pub/activity_pub_controller.ex:6 lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:6
|
||||||
#: lib/pleroma/web/controller_helper.ex:6 lib/pleroma/web/fallback_redirect_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/feed/tag_controller.ex:6 lib/pleroma/web/feed/user_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/mailer/subscription_controller.ex:2 lib/pleroma/web/masto_fe_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/mastodon_api/controllers/account_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/app_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/mastodon_api/controllers/auth_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/conversation_controller.ex:6
|
#: lib/pleroma/web/controller_helper.ex:6 lib/pleroma/web/embed_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/fallback_redirect_controller.ex:6 lib/pleroma/web/feed/tag_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/feed/user_controller.ex:6 lib/pleroma/web/mailer/subscription_controller.ex:2
|
||||||
#: lib/pleroma/web/mastodon_api/controllers/follow_request_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/instance_controller.ex:6
|
#: lib/pleroma/web/masto_fe_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/account_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/app_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/auth_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/conversation_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/custom_emoji_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/domain_block_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/fallback_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/filter_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/follow_request_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/instance_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/list_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/marker_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex:14
|
||||||
#: lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex:6 lib/pleroma/web/media_proxy/media_proxy_controller.ex:6
|
#: lib/pleroma/web/mastodon_api/controllers/media_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/notification_controller.ex:6
|
||||||
#: lib/pleroma/web/mongooseim/mongoose_im_controller.ex:6 lib/pleroma/web/nodeinfo/nodeinfo_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/oauth/fallback_controller.ex:6 lib/pleroma/web/oauth/mfa_controller.ex:10
|
#: lib/pleroma/web/mastodon_api/controllers/scheduled_activity_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/search_controller.ex:6
|
||||||
#: lib/pleroma/web/oauth/oauth_controller.ex:6 lib/pleroma/web/ostatus/ostatus_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/pleroma_api/controllers/account_controller.ex:6 lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex:2
|
#: lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex:6
|
||||||
#: lib/pleroma/web/pleroma_api/controllers/mascot_controller.ex:6 lib/pleroma/web/pleroma_api/controllers/pleroma_api_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/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/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
|
#: lib/pleroma/web/twitter_api/controllers/password_controller.ex:10 lib/pleroma/web/twitter_api/controllers/remote_follow_controller.ex:6
|
||||||
|
@ -519,46 +515,56 @@ msgid "Two-factor authentication enabled, you must use a access token."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, 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."
|
msgid "Unexpected error occurred while adding file to pack."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, 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."
|
msgid "Unexpected error occurred while creating pack."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, 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."
|
msgid "Unexpected error occurred while removing file from pack."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, 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."
|
msgid "Unexpected error occurred while updating file in pack."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, 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."
|
msgid "Unexpected error occurred while updating pack metadata."
|
||||||
msgstr ""
|
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
|
#, elixir-format
|
||||||
#: lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex:61
|
#: lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex:61
|
||||||
msgid "Web push subscription is disabled on this Pleroma instance"
|
msgid "Web push subscription is disabled on this Pleroma instance"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, 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."
|
msgid "You can't revoke your own admin/moderator status."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#, elixir-format
|
#, 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"
|
msgid "authorization required for timeline view"
|
||||||
msgstr ""
|
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 ""
|
||||||
|
|
|
@ -562,11 +562,11 @@ msgstr "Errore inaspettato durante l'aggiornamento del file nel pacchetto."
|
||||||
msgid "Unexpected error occurred while updating pack metadata."
|
msgid "Unexpected error occurred while updating pack metadata."
|
||||||
msgstr "Errore inaspettato durante l'aggiornamento dei metadati del pacchetto."
|
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
|
#, elixir-format
|
||||||
msgid "User is not an admin or OAuth admin scope is not granted."
|
msgid "User is not an admin."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"L'utente non è un amministratore o non ha ricevuto questa autorizzazione "
|
"L'utente non è un amministratore."
|
||||||
"OAuth."
|
"OAuth."
|
||||||
|
|
||||||
#: lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex:61
|
#: lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex:61
|
||||||
|
|
|
@ -559,9 +559,9 @@ msgstr ""
|
||||||
msgid "Unexpected error occurred while updating pack metadata."
|
msgid "Unexpected error occurred while updating pack metadata."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/pleroma/plugs/user_is_admin_plug.ex:40
|
#: lib/pleroma/plugs/user_is_admin_plug.ex:21
|
||||||
#, elixir-format
|
#, elixir-format
|
||||||
msgid "User is not an admin or OAuth admin scope is not granted."
|
msgid "User is not an admin."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex:61
|
#: lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex:61
|
||||||
|
|
|
@ -566,9 +566,9 @@ msgstr "Nieoczekiwany błąd podczas zmieniania pliku w paczce."
|
||||||
msgid "Unexpected error occurred while updating pack metadata."
|
msgid "Unexpected error occurred while updating pack metadata."
|
||||||
msgstr "Nieoczekiwany błąd podczas zmieniania metadanych paczki."
|
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
|
#, elixir-format
|
||||||
msgid "User is not an admin or OAuth admin scope is not granted."
|
msgid "User is not an admin."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex:61
|
#: lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex:61
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
defmodule Elixir.Pleroma.Repo.Migrations.Oban20ConfigChanges do
|
||||||
|
use Ecto.Migration
|
||||||
|
import Ecto.Query
|
||||||
|
alias Pleroma.ConfigDB
|
||||||
|
alias Pleroma.Repo
|
||||||
|
|
||||||
|
def change do
|
||||||
|
config_entry =
|
||||||
|
from(c in ConfigDB, where: c.group == ^":pleroma" and c.key == ^"Oban")
|
||||||
|
|> select([c], struct(c, [:value, :id]))
|
||||||
|
|> Repo.one()
|
||||||
|
|
||||||
|
if config_entry do
|
||||||
|
%{value: value} = config_entry
|
||||||
|
|
||||||
|
value =
|
||||||
|
case Keyword.fetch(value, :verbose) do
|
||||||
|
{:ok, log} -> Keyword.put_new(value, :log, log)
|
||||||
|
_ -> value
|
||||||
|
end
|
||||||
|
|> Keyword.drop([:verbose, :prune])
|
||||||
|
|
||||||
|
Ecto.Changeset.change(config_entry, %{value: value})
|
||||||
|
|> Repo.update()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -54,4 +54,12 @@ test "move_namespace_and_warn/2" do
|
||||||
assert Pleroma.Config.get(new_group2) == 2
|
assert Pleroma.Config.get(new_group2) == 2
|
||||||
assert Pleroma.Config.get(new_group3) == 3
|
assert Pleroma.Config.get(new_group3) == 3
|
||||||
end
|
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
|
end
|
||||||
|
|
|
@ -13,21 +13,13 @@ defmodule Pleroma.Docs.GeneratorTest do
|
||||||
key: :uploader,
|
key: :uploader,
|
||||||
type: :module,
|
type: :module,
|
||||||
description: "",
|
description: "",
|
||||||
suggestions:
|
suggestions: {:list_behaviour_implementations, Pleroma.Upload.Filter}
|
||||||
Generator.list_modules_in_dir(
|
|
||||||
"lib/pleroma/upload/filter",
|
|
||||||
"Elixir.Pleroma.Upload.Filter."
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
key: :filters,
|
key: :filters,
|
||||||
type: {:list, :module},
|
type: {:list, :module},
|
||||||
description: "",
|
description: "",
|
||||||
suggestions:
|
suggestions: {:list_behaviour_implementations, Pleroma.Web.ActivityPub.MRF}
|
||||||
Generator.list_modules_in_dir(
|
|
||||||
"lib/pleroma/web/activity_pub/mrf",
|
|
||||||
"Elixir.Pleroma.Web.ActivityPub.MRF."
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
key: Pleroma.Upload,
|
key: Pleroma.Upload,
|
||||||
|
|
|
@ -4,9 +4,14 @@
|
||||||
|
|
||||||
defmodule Pleroma.Plugs.AdminSecretAuthenticationPlugTest do
|
defmodule Pleroma.Plugs.AdminSecretAuthenticationPlugTest do
|
||||||
use Pleroma.Web.ConnCase, async: true
|
use Pleroma.Web.ConnCase, async: true
|
||||||
|
|
||||||
|
import Mock
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
|
|
||||||
alias Pleroma.Plugs.AdminSecretAuthenticationPlug
|
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
|
test "does nothing if a user is assigned", %{conn: conn} do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
@ -25,6 +30,10 @@ test "does nothing if a user is assigned", %{conn: conn} do
|
||||||
describe "when secret set it assigns an admin user" do
|
describe "when secret set it assigns an admin user" do
|
||||||
setup do: clear_config([:admin_token])
|
setup do: clear_config([:admin_token])
|
||||||
|
|
||||||
|
setup_with_mocks([{RateLimiter, [:passthrough], []}]) do
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
|
||||||
test "with `admin_token` query parameter", %{conn: conn} do
|
test "with `admin_token` query parameter", %{conn: conn} do
|
||||||
Pleroma.Config.put(:admin_token, "password123")
|
Pleroma.Config.put(:admin_token, "password123")
|
||||||
|
|
||||||
|
@ -33,12 +42,14 @@ test "with `admin_token` query parameter", %{conn: conn} do
|
||||||
|> AdminSecretAuthenticationPlug.call(%{})
|
|> AdminSecretAuthenticationPlug.call(%{})
|
||||||
|
|
||||||
refute conn.assigns[:user]
|
refute conn.assigns[:user]
|
||||||
|
assert called(RateLimiter.call(conn, name: :authentication))
|
||||||
|
|
||||||
conn =
|
conn =
|
||||||
%{conn | params: %{"admin_token" => "password123"}}
|
%{conn | params: %{"admin_token" => "password123"}}
|
||||||
|> AdminSecretAuthenticationPlug.call(%{})
|
|> AdminSecretAuthenticationPlug.call(%{})
|
||||||
|
|
||||||
assert conn.assigns[:user].is_admin
|
assert conn.assigns[:user].is_admin
|
||||||
|
assert PlugHelper.plug_skipped?(conn, OAuthScopesPlug)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "with `x-admin-token` HTTP header", %{conn: conn} do
|
test "with `x-admin-token` HTTP header", %{conn: conn} do
|
||||||
|
@ -50,6 +61,7 @@ test "with `x-admin-token` HTTP header", %{conn: conn} do
|
||||||
|> AdminSecretAuthenticationPlug.call(%{})
|
|> AdminSecretAuthenticationPlug.call(%{})
|
||||||
|
|
||||||
refute conn.assigns[:user]
|
refute conn.assigns[:user]
|
||||||
|
assert called(RateLimiter.call(conn, name: :authentication))
|
||||||
|
|
||||||
conn =
|
conn =
|
||||||
conn
|
conn
|
||||||
|
@ -57,6 +69,7 @@ test "with `x-admin-token` HTTP header", %{conn: conn} do
|
||||||
|> AdminSecretAuthenticationPlug.call(%{})
|
|> AdminSecretAuthenticationPlug.call(%{})
|
||||||
|
|
||||||
assert conn.assigns[:user].is_admin
|
assert conn.assigns[:user].is_admin
|
||||||
|
assert PlugHelper.plug_skipped?(conn, OAuthScopesPlug)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,17 +4,12 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.Plugs.HTTPSecurityPlugTest do
|
defmodule Pleroma.Web.Plugs.HTTPSecurityPlugTest do
|
||||||
use Pleroma.Web.ConnCase
|
use Pleroma.Web.ConnCase
|
||||||
|
|
||||||
alias Pleroma.Config
|
alias Pleroma.Config
|
||||||
alias Plug.Conn
|
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
|
describe "http security enabled" do
|
||||||
setup do
|
setup do: clear_config([:http_security, :enabled], true)
|
||||||
Config.put([:http_security, :enabled], true)
|
|
||||||
end
|
|
||||||
|
|
||||||
test "it sends CSP headers when enabled", %{conn: conn} do
|
test "it sends CSP headers when enabled", %{conn: conn} do
|
||||||
conn = get(conn, "/api/v1/instance")
|
conn = get(conn, "/api/v1/instance")
|
||||||
|
@ -29,7 +24,7 @@ test "it sends CSP headers when enabled", %{conn: conn} do
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it sends STS headers when enabled", %{conn: conn} do
|
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")
|
conn = get(conn, "/api/v1/instance")
|
||||||
|
|
||||||
|
@ -38,7 +33,7 @@ test "it sends STS headers when enabled", %{conn: conn} do
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it does not send STS headers when disabled", %{conn: conn} do
|
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")
|
conn = get(conn, "/api/v1/instance")
|
||||||
|
|
||||||
|
@ -47,23 +42,19 @@ test "it does not send STS headers when disabled", %{conn: conn} do
|
||||||
end
|
end
|
||||||
|
|
||||||
test "referrer-policy header reflects configured value", %{conn: conn} do
|
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 =
|
resp = get(conn, "/api/v1/instance")
|
||||||
build_conn()
|
|
||||||
|> get("/api/v1/instance")
|
|
||||||
|
|
||||||
assert Conn.get_resp_header(conn, "referrer-policy") == ["no-referrer"]
|
assert Conn.get_resp_header(resp, "referrer-policy") == ["no-referrer"]
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it sends `report-to` & `report-uri` CSP response headers" do
|
test "it sends `report-to` & `report-uri` CSP response headers", %{conn: conn} do
|
||||||
conn =
|
conn = get(conn, "/api/v1/instance")
|
||||||
build_conn()
|
|
||||||
|> get("/api/v1/instance")
|
|
||||||
|
|
||||||
[csp] = Conn.get_resp_header(conn, "content-security-policy")
|
[csp] = Conn.get_resp_header(conn, "content-security-policy")
|
||||||
|
|
||||||
|
@ -74,10 +65,67 @@ test "it sends `report-to` & `report-uri` CSP response headers" do
|
||||||
assert reply_to ==
|
assert reply_to ==
|
||||||
"{\"endpoints\":[{\"url\":\"https://endpoint.com\"}],\"group\":\"csp-endpoint\",\"max-age\":10886400}"
|
"{\"endpoints\":[{\"url\":\"https://endpoint.com\"}],\"group\":\"csp-endpoint\",\"max-age\":10886400}"
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
test "it does not send CSP headers when disabled", %{conn: conn} do
|
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")
|
conn = get(conn, "/api/v1/instance")
|
||||||
|
|
||||||
|
|
|
@ -8,112 +8,30 @@ defmodule Pleroma.Plugs.UserIsAdminPlugTest do
|
||||||
alias Pleroma.Plugs.UserIsAdminPlug
|
alias Pleroma.Plugs.UserIsAdminPlug
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
|
|
||||||
describe "unless [:auth, :enforce_oauth_admin_scope_usage]," do
|
test "accepts a user that is an admin" do
|
||||||
setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage], false)
|
user = insert(:user, is_admin: true)
|
||||||
|
|
||||||
test "accepts a user that is an admin" do
|
conn = assign(build_conn(), :user, user)
|
||||||
user = insert(:user, is_admin: true)
|
|
||||||
|
|
||||||
conn = assign(build_conn(), :user, user)
|
ret_conn = UserIsAdminPlug.call(conn, %{})
|
||||||
|
|
||||||
ret_conn = UserIsAdminPlug.call(conn, %{})
|
assert conn == ret_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
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "with [:auth, :enforce_oauth_admin_scope_usage]," do
|
test "denies a user that isn't an admin" do
|
||||||
setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage], true)
|
user = insert(:user)
|
||||||
|
|
||||||
setup do
|
conn =
|
||||||
admin_user = insert(:user, is_admin: true)
|
build_conn()
|
||||||
non_admin_user = insert(:user, is_admin: false)
|
|> assign(:user, user)
|
||||||
blank_user = nil
|
|> UserIsAdminPlug.call(%{})
|
||||||
|
|
||||||
{:ok, %{users: [admin_user, non_admin_user, blank_user]}}
|
assert conn.status == 403
|
||||||
end
|
end
|
||||||
|
|
||||||
test "if token has any of admin scopes, accepts a user that is an admin", %{conn: conn} do
|
test "denies when a user isn't set" do
|
||||||
user = insert(:user, is_admin: true)
|
conn = UserIsAdminPlug.call(build_conn(), %{})
|
||||||
token = insert(:oauth_token, user: user, scopes: ["admin:something"])
|
|
||||||
|
|
||||||
conn =
|
assert conn.status == 403
|
||||||
conn
|
|
||||||
|> assign(:user, user)
|
|
||||||
|> assign(:token, token)
|
|
||||||
|
|
||||||
ret_conn = UserIsAdminPlug.call(conn, %{})
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
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(%{})
|
|
||||||
|
|
||||||
assert conn.status == 403
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2056,4 +2056,46 @@ test "creates an activity expiration for local Create activities" do
|
||||||
assert [%{activity_id: ^id_create}] = Pleroma.ActivityExpiration |> Repo.all()
|
assert [%{activity_id: ^id_create}] = Pleroma.ActivityExpiration |> Repo.all()
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|
|
@ -774,6 +774,29 @@ test "it correctly processes messages with non-array cc field" do
|
||||||
assert [user.follower_address] == activity.data["to"]
|
assert [user.follower_address] == activity.data["to"]
|
||||||
end
|
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
|
test "it accepts Move activities" do
|
||||||
old_user = insert(:user)
|
old_user = insert(:user)
|
||||||
new_user = insert(:user)
|
new_user = insert(:user)
|
||||||
|
|
|
@ -41,6 +41,16 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
||||||
{:ok, %{admin: admin, token: token, conn: conn}}
|
{:ok, %{admin: admin, token: token, conn: conn}}
|
||||||
end
|
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
|
describe "with [:auth, :enforce_oauth_admin_scope_usage]," do
|
||||||
setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage], true)
|
setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage], true)
|
||||||
|
|
||||||
|
|
|
@ -152,6 +152,14 @@ test "subkeys with full update right merge", %{conn: conn} do
|
||||||
assert emoji_val[:groups] == [a: 1, b: 2]
|
assert emoji_val[:groups] == [a: 1, b: 2]
|
||||||
assert assets_val[:mascots] == [a: 1, b: 2]
|
assert assets_val[:mascots] == [a: 1, b: 2]
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
test "POST /api/pleroma/admin/config error", %{conn: conn} do
|
test "POST /api/pleroma/admin/config error", %{conn: conn} do
|
||||||
|
|
|
@ -297,7 +297,7 @@ test "returns 403 when requested by a non-admin" do
|
||||||
|> get("/api/pleroma/admin/reports")
|
|> get("/api/pleroma/admin/reports")
|
||||||
|
|
||||||
assert json_response(conn, :forbidden) ==
|
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
|
end
|
||||||
|
|
||||||
test "returns 403 when requested by anonymous" do
|
test "returns 403 when requested by anonymous" do
|
||||||
|
|
|
@ -351,6 +351,30 @@ test "update fields", %{conn: conn} do
|
||||||
]
|
]
|
||||||
end
|
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
|
test "update fields via x-www-form-urlencoded", %{conn: conn} do
|
||||||
fields =
|
fields =
|
||||||
[
|
[
|
||||||
|
|
|
@ -4,82 +4,118 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.MediaProxy.MediaProxyControllerTest do
|
defmodule Pleroma.Web.MediaProxy.MediaProxyControllerTest do
|
||||||
use Pleroma.Web.ConnCase
|
use Pleroma.Web.ConnCase
|
||||||
import Mock
|
|
||||||
alias Pleroma.Config
|
|
||||||
|
|
||||||
setup do: clear_config(:media_proxy)
|
import Mock
|
||||||
setup do: clear_config([Pleroma.Web.Endpoint, :secret_key_base])
|
|
||||||
|
alias Pleroma.Web.MediaProxy
|
||||||
|
alias Pleroma.Web.MediaProxy.MediaProxyController
|
||||||
|
alias Plug.Conn
|
||||||
|
|
||||||
setup do
|
setup do
|
||||||
on_exit(fn -> Cachex.clear(:banned_urls_cache) end)
|
on_exit(fn -> Cachex.clear(:banned_urls_cache) end)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it returns 404 when MediaProxy disabled", %{conn: conn} do
|
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,
|
status: 404,
|
||||||
resp_body: "Not Found"
|
resp_body: "Not Found"
|
||||||
} = get(conn, "/proxy/hhgfh/eeeee")
|
} = get(conn, "/proxy/hhgfh/eeeee")
|
||||||
|
|
||||||
assert %Plug.Conn{
|
assert %Conn{
|
||||||
status: 404,
|
status: 404,
|
||||||
resp_body: "Not Found"
|
resp_body: "Not Found"
|
||||||
} = get(conn, "/proxy/hhgfh/eeee/fff")
|
} = get(conn, "/proxy/hhgfh/eeee/fff")
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it returns 403 when signature invalidated", %{conn: conn} do
|
describe "" do
|
||||||
Config.put([:media_proxy, :enabled], true)
|
setup do
|
||||||
Config.put([Pleroma.Web.Endpoint, :secret_key_base], "00000000000")
|
clear_config([:media_proxy, :enabled], true)
|
||||||
path = URI.parse(Pleroma.Web.MediaProxy.encode_url("https://google.fn")).path
|
clear_config([Pleroma.Web.Endpoint, :secret_key_base], "00000000000")
|
||||||
Config.put([Pleroma.Web.Endpoint, :secret_key_base], "000")
|
[url: MediaProxy.encode_url("https://google.fn/test.png")]
|
||||||
|
end
|
||||||
|
|
||||||
assert %Plug.Conn{
|
test "it returns 403 for invalid signature", %{conn: conn, url: url} do
|
||||||
status: 403,
|
Pleroma.Config.put([Pleroma.Web.Endpoint, :secret_key_base], "000")
|
||||||
resp_body: "Forbidden"
|
%{path: path} = URI.parse(url)
|
||||||
} = get(conn, path)
|
|
||||||
|
|
||||||
assert %Plug.Conn{
|
assert %Conn{
|
||||||
status: 403,
|
status: 403,
|
||||||
resp_body: "Forbidden"
|
resp_body: "Forbidden"
|
||||||
} = get(conn, "/proxy/hhgfh/eeee")
|
} = get(conn, path)
|
||||||
|
|
||||||
assert %Plug.Conn{
|
assert %Conn{
|
||||||
status: 403,
|
status: 403,
|
||||||
resp_body: "Forbidden"
|
resp_body: "Forbidden"
|
||||||
} = get(conn, "/proxy/hhgfh/eeee/fff")
|
} = get(conn, "/proxy/hhgfh/eeee")
|
||||||
end
|
|
||||||
|
|
||||||
test "redirects on valid url when filename invalidated", %{conn: conn} do
|
assert %Conn{
|
||||||
Config.put([:media_proxy, :enabled], true)
|
status: 403,
|
||||||
Config.put([Pleroma.Web.Endpoint, :secret_key_base], "00000000000")
|
resp_body: "Forbidden"
|
||||||
url = Pleroma.Web.MediaProxy.encode_url("https://google.fn/test.png")
|
} = get(conn, "/proxy/hhgfh/eeee/fff")
|
||||||
invalid_url = String.replace(url, "test.png", "test-file.png")
|
end
|
||||||
response = get(conn, invalid_url)
|
|
||||||
assert response.status == 302
|
|
||||||
assert redirected_to(response) == url
|
|
||||||
end
|
|
||||||
|
|
||||||
test "it performs ReverseProxy.call when signature valid", %{conn: conn} do
|
test "redirects on valid url when filename is invalidated", %{conn: conn, url: url} do
|
||||||
Config.put([:media_proxy, :enabled], true)
|
invalid_url = String.replace(url, "test.png", "test-file.png")
|
||||||
Config.put([Pleroma.Web.Endpoint, :secret_key_base], "00000000000")
|
response = get(conn, invalid_url)
|
||||||
url = Pleroma.Web.MediaProxy.encode_url("https://google.fn/test.png")
|
assert response.status == 302
|
||||||
|
assert redirected_to(response) == url
|
||||||
|
end
|
||||||
|
|
||||||
with_mock Pleroma.ReverseProxy,
|
test "it performs ReverseProxy.call with valid signature", %{conn: conn, url: url} do
|
||||||
call: fn _conn, _url, _opts -> %Plug.Conn{status: :success} end do
|
with_mock Pleroma.ReverseProxy,
|
||||||
assert %Plug.Conn{status: :success} = get(conn, url)
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it returns 404 when url contains in banned_urls cache", %{conn: conn} do
|
describe "filename_matches/3" do
|
||||||
Config.put([:media_proxy, :enabled], true)
|
test "preserves the encoded or decoded path" do
|
||||||
Config.put([Pleroma.Web.Endpoint, :secret_key_base], "00000000000")
|
assert MediaProxyController.filename_matches(
|
||||||
url = Pleroma.Web.MediaProxy.encode_url("https://google.fn/test.png")
|
%{"filename" => "/Hello world.jpg"},
|
||||||
Pleroma.Web.MediaProxy.put_in_banned_urls("https://google.fn/test.png")
|
"/Hello world.jpg",
|
||||||
|
"http://pleroma.social/Hello world.jpg"
|
||||||
|
) == :ok
|
||||||
|
|
||||||
with_mock Pleroma.ReverseProxy,
|
assert MediaProxyController.filename_matches(
|
||||||
call: fn _conn, _url, _opts -> %Plug.Conn{status: :success} end do
|
%{"filename" => "/Hello%20world.jpg"},
|
||||||
assert %Plug.Conn{status: 404, resp_body: "Not Found"} = get(conn, url)
|
"/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
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,38 +5,33 @@
|
||||||
defmodule Pleroma.Web.MediaProxyTest do
|
defmodule Pleroma.Web.MediaProxyTest do
|
||||||
use ExUnit.Case
|
use ExUnit.Case
|
||||||
use Pleroma.Tests.Helpers
|
use Pleroma.Tests.Helpers
|
||||||
import Pleroma.Web.MediaProxy
|
|
||||||
alias Pleroma.Web.MediaProxy.MediaProxyController
|
|
||||||
|
|
||||||
setup do: clear_config([:media_proxy, :enabled])
|
alias Pleroma.Web.Endpoint
|
||||||
setup do: clear_config(Pleroma.Upload)
|
alias Pleroma.Web.MediaProxy
|
||||||
|
|
||||||
describe "when enabled" do
|
describe "when enabled" do
|
||||||
setup do
|
setup do: clear_config([:media_proxy, :enabled], true)
|
||||||
Pleroma.Config.put([:media_proxy, :enabled], true)
|
|
||||||
:ok
|
|
||||||
end
|
|
||||||
|
|
||||||
test "ignores invalid url" do
|
test "ignores invalid url" do
|
||||||
assert url(nil) == nil
|
assert MediaProxy.url(nil) == nil
|
||||||
assert url("") == nil
|
assert MediaProxy.url("") == nil
|
||||||
end
|
end
|
||||||
|
|
||||||
test "ignores relative url" do
|
test "ignores relative url" do
|
||||||
assert url("/local") == "/local"
|
assert MediaProxy.url("/local") == "/local"
|
||||||
assert url("/") == "/"
|
assert MediaProxy.url("/") == "/"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "ignores local url" do
|
test "ignores local url" do
|
||||||
local_url = Pleroma.Web.Endpoint.url() <> "/hello"
|
local_url = Endpoint.url() <> "/hello"
|
||||||
local_root = Pleroma.Web.Endpoint.url()
|
local_root = Endpoint.url()
|
||||||
assert url(local_url) == local_url
|
assert MediaProxy.url(local_url) == local_url
|
||||||
assert url(local_root) == local_root
|
assert MediaProxy.url(local_root) == local_root
|
||||||
end
|
end
|
||||||
|
|
||||||
test "encodes and decodes URL" do
|
test "encodes and decodes URL" do
|
||||||
url = "https://pleroma.soykaf.com/static/logo.png"
|
url = "https://pleroma.soykaf.com/static/logo.png"
|
||||||
encoded = url(url)
|
encoded = MediaProxy.url(url)
|
||||||
|
|
||||||
assert String.starts_with?(
|
assert String.starts_with?(
|
||||||
encoded,
|
encoded,
|
||||||
|
@ -50,86 +45,44 @@ test "encodes and decodes URL" do
|
||||||
|
|
||||||
test "encodes and decodes URL without a path" do
|
test "encodes and decodes URL without a path" do
|
||||||
url = "https://pleroma.soykaf.com"
|
url = "https://pleroma.soykaf.com"
|
||||||
encoded = url(url)
|
encoded = MediaProxy.url(url)
|
||||||
assert decode_result(encoded) == url
|
assert decode_result(encoded) == url
|
||||||
end
|
end
|
||||||
|
|
||||||
test "encodes and decodes URL without an extension" do
|
test "encodes and decodes URL without an extension" do
|
||||||
url = "https://pleroma.soykaf.com/path/"
|
url = "https://pleroma.soykaf.com/path/"
|
||||||
encoded = url(url)
|
encoded = MediaProxy.url(url)
|
||||||
assert String.ends_with?(encoded, "/path")
|
assert String.ends_with?(encoded, "/path")
|
||||||
assert decode_result(encoded) == url
|
assert decode_result(encoded) == url
|
||||||
end
|
end
|
||||||
|
|
||||||
test "encodes and decodes URL and ignores query params for the path" do
|
test "encodes and decodes URL and ignores query params for the path" do
|
||||||
url = "https://pleroma.soykaf.com/static/logo.png?93939393939&bunny=true"
|
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 String.ends_with?(encoded, "/logo.png")
|
||||||
assert decode_result(encoded) == url
|
assert decode_result(encoded) == url
|
||||||
end
|
end
|
||||||
|
|
||||||
test "validates signature" do
|
test "validates signature" do
|
||||||
secret_key_base = Pleroma.Config.get([Pleroma.Web.Endpoint, :secret_key_base])
|
encoded = MediaProxy.url("https://pleroma.social")
|
||||||
|
|
||||||
on_exit(fn ->
|
clear_config(
|
||||||
Pleroma.Config.put([Pleroma.Web.Endpoint, :secret_key_base], secret_key_base)
|
[Endpoint, :secret_key_base],
|
||||||
end)
|
|
||||||
|
|
||||||
encoded = url("https://pleroma.social")
|
|
||||||
|
|
||||||
Pleroma.Config.put(
|
|
||||||
[Pleroma.Web.Endpoint, :secret_key_base],
|
|
||||||
"00000000000000000000000000000000000000000000000"
|
"00000000000000000000000000000000000000000000000"
|
||||||
)
|
)
|
||||||
|
|
||||||
[_, "proxy", sig, base64 | _] = URI.parse(encoded).path |> String.split("/")
|
[_, "proxy", sig, base64 | _] = URI.parse(encoded).path |> String.split("/")
|
||||||
assert decode_url(sig, base64) == {:error, :invalid_signature}
|
assert MediaProxy.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
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test "uses the configured base_url" do
|
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"
|
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
|
end
|
||||||
|
|
||||||
# Some sites expect ASCII encoded characters in the URL to be preserved even if
|
# Some sites expect ASCII encoded characters in the URL to be preserved even if
|
||||||
|
@ -140,7 +93,7 @@ test "preserve ASCII encoding" do
|
||||||
url =
|
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"
|
"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
|
assert decode_result(encoded) == url
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -151,56 +104,49 @@ test "preserve non-unicode characters per RFC3986" do
|
||||||
url =
|
url =
|
||||||
"https://pleroma.com/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890-._~:/?#[]@!$&'()*+,;=|^`{}"
|
"https://pleroma.com/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890-._~:/?#[]@!$&'()*+,;=|^`{}"
|
||||||
|
|
||||||
encoded = url(url)
|
encoded = MediaProxy.url(url)
|
||||||
assert decode_result(encoded) == url
|
assert decode_result(encoded) == url
|
||||||
end
|
end
|
||||||
|
|
||||||
test "preserve unicode characters" do
|
test "preserve unicode characters" do
|
||||||
url = "https://ko.wikipedia.org/wiki/위키백과:대문"
|
url = "https://ko.wikipedia.org/wiki/위키백과:대문"
|
||||||
|
|
||||||
encoded = url(url)
|
encoded = MediaProxy.url(url)
|
||||||
assert decode_result(encoded) == url
|
assert decode_result(encoded) == url
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "when disabled" do
|
describe "when disabled" do
|
||||||
setup do
|
setup do: clear_config([:media_proxy, :enabled], false)
|
||||||
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
|
|
||||||
|
|
||||||
test "does not encode remote urls" do
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
defp decode_result(encoded) do
|
defp decode_result(encoded) do
|
||||||
[_, "proxy", sig, base64 | _] = URI.parse(encoded).path |> String.split("/")
|
[_, "proxy", sig, base64 | _] = URI.parse(encoded).path |> String.split("/")
|
||||||
{:ok, decoded} = decode_url(sig, base64)
|
{:ok, decoded} = MediaProxy.decode_url(sig, base64)
|
||||||
decoded
|
decoded
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "whitelist" do
|
describe "whitelist" do
|
||||||
setup do
|
setup do: clear_config([:media_proxy, :enabled], true)
|
||||||
Pleroma.Config.put([:media_proxy, :enabled], true)
|
|
||||||
:ok
|
|
||||||
end
|
|
||||||
|
|
||||||
test "mediaproxy whitelist" do
|
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"
|
url = "https://feld.me/foo.png"
|
||||||
|
|
||||||
unencoded = url(url)
|
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 = MediaProxy.url(url)
|
||||||
assert unencoded == url
|
assert unencoded == url
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -211,17 +157,17 @@ test "does not change whitelisted urls" do
|
||||||
media_url = "https://mycdn.akamai.com"
|
media_url = "https://mycdn.akamai.com"
|
||||||
|
|
||||||
url = "#{media_url}/static/logo.png"
|
url = "#{media_url}/static/logo.png"
|
||||||
encoded = url(url)
|
encoded = MediaProxy.url(url)
|
||||||
|
|
||||||
assert String.starts_with?(encoded, media_url)
|
assert String.starts_with?(encoded, media_url)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "ensure Pleroma.Upload base_url is always whitelisted" do
|
test "ensure Pleroma.Upload base_url is always whitelisted" do
|
||||||
media_url = "https://media.pleroma.social"
|
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"
|
url = "#{media_url}/static/logo.png"
|
||||||
encoded = url(url)
|
encoded = MediaProxy.url(url)
|
||||||
|
|
||||||
assert String.starts_with?(encoded, media_url)
|
assert String.starts_with?(encoded, media_url)
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue