Merge branch 'develop' into gun

This commit is contained in:
Alexander Strizhakov 2020-02-21 10:02:37 +03:00
commit 13918cb545
No known key found for this signature in database
GPG key ID: 022896A53AEF1381
56 changed files with 860 additions and 223 deletions

View file

@ -72,8 +72,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- User notification settings: Add `privacy_option` option. - User notification settings: Add `privacy_option` option.
- Support for custom Elixir modules (such as MRF policies) - Support for custom Elixir modules (such as MRF policies)
- User settings: Add _This account is a_ option. - User settings: Add _This account is a_ option.
- A new users admin digest email
- OAuth: admin scopes support (relevant setting: `[:auth, :enforce_oauth_admin_scope_usage]`). - OAuth: admin scopes support (relevant setting: `[:auth, :enforce_oauth_admin_scope_usage]`).
- New HTTP adapter [gun](https://github.com/ninenines/gun). Gun adapter requires minimum OTP version of 22.2 otherwise Pleroma wont start. For hackney OTP update is not required. - New HTTP adapter [gun](https://github.com/ninenines/gun). Gun adapter requires minimum OTP version of 22.2 otherwise Pleroma wont start. For hackney OTP update is not required.
- Add an option `authorized_fetch_mode` to require HTTP signatures for AP fetches.
<details> <details>
<summary>API Changes</summary> <summary>API Changes</summary>
@ -115,6 +117,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Configuration: `feed.logo` option for tag feed. - Configuration: `feed.logo` option for tag feed.
- Tag feed: `/tags/:tag.rss` - list public statuses by hashtag. - Tag feed: `/tags/:tag.rss` - list public statuses by hashtag.
- Mastodon API: Add `reacted` property to `emoji_reactions` - Mastodon API: Add `reacted` property to `emoji_reactions`
- Pleroma API: Add reactions for a single emoji.
</details> </details>
### Fixed ### Fixed

View file

@ -49,7 +49,8 @@
config :pleroma, Pleroma.Repo, config :pleroma, Pleroma.Repo,
types: Pleroma.PostgresTypes, types: Pleroma.PostgresTypes,
telemetry_event: [Pleroma.Repo.Instrumenter], telemetry_event: [Pleroma.Repo.Instrumenter],
migration_lock: nil migration_lock: nil,
parameters: [gin_fuzzy_search_limit: "500"]
config :pleroma, Pleroma.Captcha, config :pleroma, Pleroma.Captcha,
enabled: true, enabled: true,
@ -304,7 +305,8 @@
unfollow_blocked: true, unfollow_blocked: true,
outgoing_blocks: true, outgoing_blocks: true,
follow_handshake_timeout: 500, follow_handshake_timeout: 500,
sign_object_fetches: true sign_object_fetches: true,
authorized_fetch_mode: false
config :pleroma, :streamer, config :pleroma, :streamer,
workers: 3, workers: 3,
@ -458,13 +460,15 @@
transmogrifier: 20, transmogrifier: 20,
scheduled_activities: 10, scheduled_activities: 10,
background: 5, background: 5,
attachments_cleanup: 5 attachments_cleanup: 5,
new_users_digest: 1
], ],
crontab: [ crontab: [
{"0 0 * * *", Pleroma.Workers.Cron.ClearOauthTokenWorker}, {"0 0 * * *", Pleroma.Workers.Cron.ClearOauthTokenWorker},
{"0 * * * *", Pleroma.Workers.Cron.StatsWorker}, {"0 * * * *", Pleroma.Workers.Cron.StatsWorker},
{"* * * * *", Pleroma.Workers.Cron.PurgeExpiredActivitiesWorker}, {"* * * * *", Pleroma.Workers.Cron.PurgeExpiredActivitiesWorker},
{"0 0 * * 0", Pleroma.Workers.Cron.DigestEmailsWorker} {"0 0 * * 0", Pleroma.Workers.Cron.DigestEmailsWorker},
{"0 0 * * *", Pleroma.Workers.Cron.NewUsersDigestWorker}
] ]
config :pleroma, :workers, config :pleroma, :workers,
@ -538,6 +542,8 @@
text_muted_color: "#b9b9ba" text_muted_color: "#b9b9ba"
} }
config :pleroma, Pleroma.Emails.NewUsersDigestEmail, enabled: false
config :prometheus, Pleroma.Web.Endpoint.MetricsExporter, path: "/api/pleroma/app_metrics" config :prometheus, Pleroma.Web.Endpoint.MetricsExporter, path: "/api/pleroma/app_metrics"
config :pleroma, Pleroma.ScheduledActivity, config :pleroma, Pleroma.ScheduledActivity,

View file

@ -101,7 +101,7 @@
%{ %{
key: :versions, key: :versions,
type: {:list, :atom}, type: {:list, :atom},
description: "List of TLS version to use", description: "List of TLS versions to use",
suggestions: [:tlsv1, ":tlsv1.1", ":tlsv1.2"] suggestions: [:tlsv1, ":tlsv1.1", ":tlsv1.2"]
} }
] ]
@ -534,7 +534,8 @@
%{ %{
key: :description, key: :description,
type: :string, type: :string,
description: "The instance's description, can be seen in nodeinfo and /api/v1/instance", description:
"The instance's description. It can be seen in nodeinfo and `/api/v1/instance`",
suggestions: [ suggestions: [
"Very cool instance" "Very cool instance"
] ]
@ -770,7 +771,7 @@
key: :cleanup_attachments, key: :cleanup_attachments,
type: :boolean, type: :boolean,
description: """ description: """
"Enable to remove associated attachments when status is removed. Enable to remove associated attachments when status is removed.
This will not affect duplicates and attachments without status. This will not affect duplicates and attachments without status.
Enabling this will increase load to database when deleting statuses on larger instances. Enabling this will increase load to database when deleting statuses on larger instances.
""" """
@ -838,7 +839,7 @@
%{ %{
key: :healthcheck, key: :healthcheck,
type: :boolean, type: :boolean,
description: "If enabled, system data will be shown on /api/pleroma/healthcheck" description: "If enabled, system data will be shown on `/api/pleroma/healthcheck`"
}, },
%{ %{
key: :remote_post_retention_days, key: :remote_post_retention_days,
@ -1296,14 +1297,14 @@
%{ %{
key: :media_removal, key: :media_removal,
type: {:list, :string}, type: {:list, :string},
description: "List of instances to remove medias from", description: "List of instances to strip media attachments from",
suggestions: ["example.com", "*.example.com"] suggestions: ["example.com", "*.example.com"]
}, },
%{ %{
key: :media_nsfw, key: :media_nsfw,
label: "Media NSFW", label: "Media NSFW",
type: {:list, :string}, type: {:list, :string},
description: "List of instances to put medias as NSFW (sensitive) from", description: "List of instances to tag all media as NSFW (sensitive) from",
suggestions: ["example.com", "*.example.com"] suggestions: ["example.com", "*.example.com"]
}, },
%{ %{
@ -1422,21 +1423,21 @@
key: :reject, key: :reject,
type: [:string, :regex], type: [:string, :regex],
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: [:string, :regex],
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: [{:tuple, :string, :string}, {:tuple, :regex, :string}],
description: description:
"A list of tuples containing {pattern, replacement}, 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"}]
} }
] ]
@ -1451,7 +1452,7 @@
%{ %{
key: :actors, key: :actors,
type: {:list, :string}, type: {:list, :string},
description: "A list of actors, for which to drop any posts mentioning", description: "A list of actors for which any post mentioning them will be dropped.",
suggestions: ["actor1", "actor2"] suggestions: ["actor1", "actor2"]
} }
] ]
@ -1855,9 +1856,8 @@
type: :string, type: :string,
description: description:
"A mailto link for the administrative contact." <> "A mailto link for the administrative contact." <>
" It's best if this email is not a personal email address, but rather a group email so that if a person leaves an organization," <> " It's best if this email is not a personal email address, but rather a group email to the instance moderation team.",
" is unavailable for an extended period, or otherwise can't respond, someone else on the list can.", suggestions: ["mailto:moderators@pleroma.com"]
suggestions: ["Subject"]
}, },
%{ %{
key: :public_key, key: :public_key,
@ -1924,7 +1924,7 @@
key: :admin_token, key: :admin_token,
type: :string, type: :string,
description: "Token", description: "Token",
suggestions: ["some_random_token"] suggestions: ["We recommend a secure random string or UUID"]
} }
] ]
}, },
@ -1986,6 +1986,7 @@
"Background jobs queues (keys: queues, values: max numbers of concurrent jobs)", "Background jobs queues (keys: queues, values: max numbers of concurrent jobs)",
suggestions: [ suggestions: [
activity_expiration: 10, activity_expiration: 10,
attachments_cleanup: 5,
background: 5, background: 5,
federator_incoming: 50, federator_incoming: 50,
federator_outgoing: 50, federator_outgoing: 50,
@ -2001,6 +2002,12 @@
description: "Activity expiration queue", description: "Activity expiration queue",
suggestions: [10] suggestions: [10]
}, },
%{
key: :attachments_cleanup,
type: :integer,
description: "Attachment deletion queue",
suggestions: [5]
},
%{ %{
key: :background, key: :background,
type: :integer, type: :integer,
@ -2099,7 +2106,7 @@
%{ %{
key: :enabled, key: :enabled,
type: :boolean, type: :boolean,
description: "Enables/disables RichMedia." description: "Enables RichMedia parsing of URLs."
}, },
%{ %{
key: :ignore_hosts, key: :ignore_hosts,
@ -2145,8 +2152,7 @@
%{ %{
key: :enabled, key: :enabled,
type: :boolean, type: :boolean,
description: description: "Fetch posts when a new user is federated with"
"If enabled, when a new user is federated with, fetch some of their latest posts"
}, },
%{ %{
key: :pages, key: :pages,
@ -2165,13 +2171,13 @@
%{ %{
key: :class, key: :class,
type: [:string, false], type: [:string, false],
description: "Specify the class to be added to the generated link. `False` to clear", description: "Specify the class to be added to the generated link. Disable to clear",
suggestions: ["auto-linker", false] suggestions: ["auto-linker", false]
}, },
%{ %{
key: :rel, key: :rel,
type: [:string, false], type: [:string, false],
description: "Override the rel attribute. `False` to clear", description: "Override the rel attribute. Disable to clear",
suggestions: ["ugc", "noopener noreferrer", false] suggestions: ["ugc", "noopener noreferrer", false]
}, },
%{ %{
@ -2281,7 +2287,7 @@
key: :ssl, key: :ssl,
label: "SSL", label: "SSL",
type: :boolean, type: :boolean,
description: "`True` to use SSL, usually implies the port 636" description: "Enable to use SSL, usually implies the port 636"
}, },
%{ %{
key: :sslopts, key: :sslopts,
@ -2308,7 +2314,7 @@
key: :tls, key: :tls,
label: "TLS", label: "TLS",
type: :boolean, type: :boolean,
description: "`True` to start TLS, usually implies the port 389" description: "Enable to use STARTTLS, usually implies the port 389"
}, },
%{ %{
key: :tlsopts, key: :tlsopts,
@ -2358,7 +2364,7 @@
description: description:
"OAuth admin scope requirement toggle. " <> "OAuth admin scope requirement toggle. " <>
"If enabled, admin actions explicitly demand admin OAuth scope(s) presence in OAuth token " <> "If enabled, admin actions explicitly demand admin OAuth scope(s) presence in OAuth token " <>
"(client app must support admin scopes). If `false` and token doesn't have admin scope(s)," <> "(client app must support admin scopes). If disabled and token doesn't have admin scope(s)," <>
"`is_admin` user flag grants access to admin-specific actions." "`is_admin` user flag grants access to admin-specific actions."
}, },
%{ %{
@ -2380,7 +2386,7 @@
key: :oauth_consumer_strategies, key: :oauth_consumer_strategies,
type: {:list, :string}, type: {:list, :string},
description: description:
"The list of enabled OAuth consumer strategies; by default it's set by OAUTH_CONSUMER_STRATEGIES environment variable." <> "The list of enabled OAuth consumer strategies. By default it's set by OAUTH_CONSUMER_STRATEGIES environment variable." <>
" Each entry in this space-delimited string should be of format \"strategy\" or \"strategy:dependency\"" <> " Each entry in this space-delimited string should be of format \"strategy\" or \"strategy:dependency\"" <>
" (e.g. twitter or keycloak:ueberauth_keycloak_strategy in case dependency is named differently than ueberauth_<strategy>).", " (e.g. twitter or keycloak:ueberauth_keycloak_strategy in case dependency is named differently than ueberauth_<strategy>).",
suggestions: ["twitter", "keycloak:ueberauth_keycloak_strategy"] suggestions: ["twitter", "keycloak:ueberauth_keycloak_strategy"]
@ -2496,6 +2502,20 @@
} }
] ]
}, },
%{
group: :pleroma,
key: Pleroma.Emails.NewUsersDigestEmail,
type: :group,
description: "New users admin email digest",
children: [
%{
key: :enabled,
type: :boolean,
description: "enables new users admin digest email when `true`",
suggestions: [false]
}
]
},
%{ %{
group: :pleroma, group: :pleroma,
key: :oauth2, key: :oauth2,
@ -2517,7 +2537,7 @@
%{ %{
key: :clean_expired_tokens, key: :clean_expired_tokens,
type: :boolean, type: :boolean,
description: "Enable a background job to clean expired oauth tokens. Default: `false`." description: "Enable a background job to clean expired oauth tokens. Default: disabled."
} }
] ]
}, },
@ -2577,7 +2597,7 @@
%{ %{
key: :rum_enabled, key: :rum_enabled,
type: :boolean, type: :boolean,
description: "If RUM indexes should be used. Default: `false`" description: "If RUM indexes should be used. Default: disabled"
} }
] ]
}, },
@ -2963,7 +2983,7 @@
%{ %{
key: :enabled, key: :enabled,
type: :boolean, type: :boolean,
description: "Enable/disable the plug. Default: `false`." description: "Enable/disable the plug. Default: disabled."
}, },
%{ %{
key: :headers, key: :headers,
@ -3017,7 +3037,7 @@
%{ %{
key: :enabled, key: :enabled,
type: :boolean, type: :boolean,
description: "Enables the rendering of static HTML. Defaults to `false`." description: "Enables the rendering of static HTML. Default: disabled."
} }
] ]
}, },
@ -3093,7 +3113,7 @@
key: :configurable_from_database, key: :configurable_from_database,
type: :boolean, type: :boolean,
description: description:
"Allow transferring configuration to DB with the subsequent customization from Admin api. Defaults to `false`" "Allow transferring configuration to DB with the subsequent customization from Admin api. Default: disabled"
} }
] ]
} }

View file

@ -96,6 +96,8 @@
config :pleroma, Pleroma.Gun.API, Pleroma.Gun.API.Mock config :pleroma, Pleroma.Gun.API, Pleroma.Gun.API.Mock
config :pleroma, Pleroma.Emails.NewUsersDigestEmail, enabled: true
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

View file

@ -459,3 +459,16 @@ Emoji reactions work a lot like favourites do. They make it possible to react to
{"name": "☕", "count": 1, "me": false, "accounts": [{"id" => "abc..."}]} {"name": "☕", "count": 1, "me": false, "accounts": [{"id" => "abc..."}]}
] ]
``` ```
## `GET /api/v1/pleroma/statuses/:id/reactions/:emoji`
### Get an object of emoji to account mappings with accounts that reacted to the post for a specific emoji`
* Method: `GET`
* Authentication: optional
* Params: None
* Response: JSON, a list of emoji/account list tuples
* Example Response:
```json
[
{"name": "😀", "count": 2, "me": true, "accounts": [{"id" => "xyz.."...}, {"id" => "zyx..."}]}
]
```

View file

@ -1,4 +1,21 @@
# Updating your instance # Updating your instance
You should **always check the release notes/changelog** in case there are config deprecations, special update special update steps, etc.
Besides that, doing the following is generally enough:
## For OTP installations
```sh
# Download the new release
su pleroma -s $SHELL -lc "./bin/pleroma_ctl update"
# Migrate the database, you are advised to stop the instance before doing that
su pleroma -s $SHELL -lc "./bin/pleroma_ctl migrate"
```
## For from source installations (using git)
1. Go to the working directory of Pleroma (default is `/opt/pleroma`) 1. Go to the working directory of Pleroma (default is `/opt/pleroma`)
2. Run `git pull`. This pulls the latest changes from upstream. 2. Run `git pull`. This pulls the latest changes from upstream.
3. Run `mix deps.get`. This pulls in any new dependencies. 3. Run `mix deps.get`. This pulls in any new dependencies.

View file

@ -143,10 +143,11 @@ config :pleroma, :mrf_user_allowlist,
* `:reject` rejects the message entirely * `:reject` rejects the message entirely
### :activitypub ### :activitypub
* ``unfollow_blocked``: Whether blocks result in people getting unfollowed * `unfollow_blocked`: Whether blocks result in people getting unfollowed
* ``outgoing_blocks``: Whether to federate blocks to other instances * `outgoing_blocks`: Whether to federate blocks to other instances
* ``deny_follow_blocked``: Whether to disallow following an account that has blocked the user in question * `deny_follow_blocked`: Whether to disallow following an account that has blocked the user in question
* ``sign_object_fetches``: Sign object fetches with HTTP signatures * `sign_object_fetches`: Sign object fetches with HTTP signatures
* `authorized_fetch_mode`: Require HTTP signatures for AP fetches
### :fetch_initial_posts ### :fetch_initial_posts
* `enabled`: if enabled, when a new user is federated with, fetch some of their latest posts * `enabled`: if enabled, when a new user is federated with, fetch some of their latest posts
@ -533,6 +534,10 @@ Email notifications settings.
- `:logo` - a path to a custom logo. Set it to `nil` to use the default Pleroma logo. - `:logo` - a path to a custom logo. Set it to `nil` to use the default Pleroma logo.
- `:styling` - a map with color settings for email templates. - `:styling` - a map with color settings for email templates.
### Pleroma.Emails.NewUsersDigestEmail
- `:enabled` - a boolean, enables new users admin digest email when `true`. Defaults to `false`.
## Background jobs ## Background jobs
### Oban ### Oban

View file

@ -1,4 +1,5 @@
# Message Rewrite Facility # Message Rewrite Facility
The Message Rewrite Facility (MRF) is a subsystem that is implemented as a series of hooks that allows the administrator to rewrite or discard messages. The Message Rewrite Facility (MRF) is a subsystem that is implemented as a series of hooks that allows the administrator to rewrite or discard messages.
Possible uses include: Possible uses include:
@ -11,6 +12,7 @@ Possible uses include:
* sending only public messages to a specific instance * sending only public messages to a specific instance
The MRF provides user-configurable policies. The default policy is `NoOpPolicy`, which disables the MRF functionality. Pleroma also includes an easy to use policy called `SimplePolicy` which maps messages matching certain pre-defined criterion to actions built into the policy module. The MRF provides user-configurable policies. The default policy is `NoOpPolicy`, which disables the MRF functionality. Pleroma also includes an easy to use policy called `SimplePolicy` which maps messages matching certain pre-defined criterion to actions built into the policy module.
It is possible to use multiple, active MRF policies at the same time. It is possible to use multiple, active MRF policies at the same time.
## Quarantine Instances ## Quarantine Instances
@ -18,7 +20,8 @@ It is possible to use multiple, active MRF policies at the same time.
You have the ability to prevent from private / followers-only messages from federating with specific instances. Which means they will only get the public or unlisted messages from your instance. You have the ability to prevent from private / followers-only messages from federating with specific instances. Which means they will only get the public or unlisted messages from your instance.
If, for example, you're using `MIX_ENV=prod` aka using production mode, you would open your configuration file located in `config/prod.secret.exs` and edit or add the option under your `:instance` config object. Then you would specify the instance within quotes. If, for example, you're using `MIX_ENV=prod` aka using production mode, you would open your configuration file located in `config/prod.secret.exs` and edit or add the option under your `:instance` config object. Then you would specify the instance within quotes.
```
```elixir
config :pleroma, :instance, config :pleroma, :instance,
[...] [...]
quarantined_instances: ["instance.example", "other.example"] quarantined_instances: ["instance.example", "other.example"]
@ -30,7 +33,7 @@ config :pleroma, :instance,
To use `SimplePolicy`, you must enable it. Do so by adding the following to your `:instance` config object, so that it looks like this: To use `SimplePolicy`, you must enable it. Do so by adding the following to your `:instance` config object, so that it looks like this:
``` ```elixir
config :pleroma, :instance, config :pleroma, :instance,
[...] [...]
rewrite_policy: Pleroma.Web.ActivityPub.MRF.SimplePolicy rewrite_policy: Pleroma.Web.ActivityPub.MRF.SimplePolicy
@ -50,7 +53,7 @@ Servers should be configured as lists.
This example will enable `SimplePolicy`, block media from `illegalporn.biz`, mark media as NSFW from `porn.biz` and `porn.business`, reject messages from `spam.com`, remove messages from `spam.university` from the federated timeline and block reports (flags) from `whiny.whiner`: This example will enable `SimplePolicy`, block media from `illegalporn.biz`, mark media as NSFW from `porn.biz` and `porn.business`, reject messages from `spam.com`, remove messages from `spam.university` from the federated timeline and block reports (flags) from `whiny.whiner`:
``` ```elixir
config :pleroma, :instance, config :pleroma, :instance,
rewrite_policy: [Pleroma.Web.ActivityPub.MRF.SimplePolicy] rewrite_policy: [Pleroma.Web.ActivityPub.MRF.SimplePolicy]
@ -60,7 +63,6 @@ config :pleroma, :mrf_simple,
reject: ["spam.com"], reject: ["spam.com"],
federated_timeline_removal: ["spam.university"], federated_timeline_removal: ["spam.university"],
report_removal: ["whiny.whiner"] report_removal: ["whiny.whiner"]
``` ```
### Use with Care ### Use with Care
@ -74,16 +76,18 @@ As discussed above, the MRF system is a modular system that supports pluggable p
For example, here is a sample policy module which rewrites all messages to "new message content": For example, here is a sample policy module which rewrites all messages to "new message content":
```elixir ```elixir
# This is a sample MRF policy which rewrites all Notes to have "new message defmodule Pleroma.Web.ActivityPub.MRF.RewritePolicy do
# content." @moduledoc "MRF policy which rewrites all Notes to have 'new message content'."
defmodule Site.RewritePolicy do @behaviour Pleroma.Web.ActivityPub.MRF
@behavior Pleroma.Web.ActivityPub.MRF
# Catch messages which contain Note objects with actual data to filter. # Catch messages which contain Note objects with actual data to filter.
# Capture the object as `object`, the message content as `content` and the # Capture the object as `object`, the message content as `content` and the
# message itself as `message`. # message itself as `message`.
@impl true @impl true
def filter(%{"type" => Create", "object" => {"type" => "Note", "content" => content} = object} = message) def filter(
%{"type" => "Create", "object" => %{"type" => "Note", "content" => content} = object} =
message
)
when is_binary(content) do when is_binary(content) do
# Subject / CW is stored as summary instead of `name` like other AS2 objects # Subject / CW is stored as summary instead of `name` like other AS2 objects
# because of Mastodon doing it that way. # because of Mastodon doing it that way.
@ -106,16 +110,21 @@ defmodule Site.RewritePolicy do
# Let all other messages through without modifying them. # Let all other messages through without modifying them.
@impl true @impl true
def filter(message), do: {:ok, message} def filter(message), do: {:ok, message}
@impl true
def describe do
{:ok, %{mrf_sample: %{content: "new message content"}}}`
end
end end
``` ```
If you save this file as `lib/site/mrf/rewrite_policy.ex`, it will be included when you next rebuild Pleroma. You can enable it in the configuration like so: If you save this file as `lib/pleroma/web/activity_pub/mrf/rewrite_policy.ex`, it will be included when you next rebuild Pleroma. You can enable it in the configuration like so:
``` ```elixir
config :pleroma, :instance, config :pleroma, :instance,
rewrite_policy: [ rewrite_policy: [
Pleroma.Web.ActivityPub.MRF.SimplePolicy, Pleroma.Web.ActivityPub.MRF.SimplePolicy,
Site.RewritePolicy Pleroma.Web.ActivityPub.MRF.RewritePolicy
] ]
``` ```

View file

@ -259,19 +259,14 @@ su pleroma -s $SHELL -lc "./bin/pleroma_ctl user new joeuser joeuser@sld.tld --a
``` ```
This will create an account withe the username of 'joeuser' with the email address of joeuser@sld.tld, and set that user's account as an admin. This will result in a link that you can paste into the browser, which logs you in and enables you to set the password. This will create an account withe the username of 'joeuser' with the email address of joeuser@sld.tld, and set that user's account as an admin. This will result in a link that you can paste into the browser, which logs you in and enables you to set the password.
### Updating
Generally, doing the following is enough:
```sh
# Download the new release
su pleroma -s $SHELL -lc "./bin/pleroma_ctl update"
# Migrate the database, you are advised to stop the instance before doing that
su pleroma -s $SHELL -lc "./bin/pleroma_ctl migrate"
```
But you should **always check the release notes/changelog** in case there are config deprecations, special update steps, etc.
## Further reading ## Further reading
* [Backup your instance](../administration/backup.md) * [Backup your instance](../administration/backup.md)
* [Hardening your instance](../configuration/hardening.md) * [Hardening your instance](../configuration/hardening.md)
* [How to activate mediaproxy](../configuration/howto_mediaproxy.md) * [How to activate mediaproxy](../configuration/howto_mediaproxy.md)
* [Updating your instance](../administration/updating.md)
## Questions
Questions about the installation or didnt it work as it should be, ask in [#pleroma:matrix.org](https://matrix.heldscal.la/#/room/#freenode_#pleroma:matrix.org) or IRC Channel **#pleroma** on **Freenode**.

View file

@ -0,0 +1,32 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Emails.NewUsersDigestEmail do
use Phoenix.Swoosh, view: Pleroma.Web.EmailView, layout: {Pleroma.Web.LayoutView, :email_styled}
defp instance_notify_email do
Pleroma.Config.get([:instance, :notify_email]) || Pleroma.Config.get([:instance, :email])
end
def new_users(to, users_and_statuses) do
instance_name = Pleroma.Config.get([:instance, :name])
styling = Pleroma.Config.get([Pleroma.Emails.UserEmail, :styling])
logo_url =
Pleroma.Web.Endpoint.url() <>
Pleroma.Config.get([:frontend_configurations, :pleroma_fe, :logo])
new()
|> to({to.name, to.email})
|> from({instance_name, instance_notify_email()})
|> subject("#{instance_name} New Users")
|> render_body("new_users_digest.html", %{
title: "New Users",
users_and_statuses: users_and_statuses,
instance: instance_name,
styling: styling,
logo_url: logo_url
})
end
end

View file

@ -4,6 +4,7 @@
defmodule Pleroma.Web.Plugs.HTTPSignaturePlug do defmodule Pleroma.Web.Plugs.HTTPSignaturePlug do
import Plug.Conn import Plug.Conn
import Phoenix.Controller, only: [get_format: 1, text: 2]
require Logger require Logger
def init(options) do def init(options) do
@ -15,26 +16,28 @@ def call(%{assigns: %{valid_signature: true}} = conn, _opts) do
end end
def call(conn, _opts) do def call(conn, _opts) do
headers = get_req_header(conn, "signature") if get_format(conn) == "activity+json" do
signature = Enum.at(headers, 0)
if signature do
# set (request-target) header to the appropriate value
# we also replace the digest header with the one we computed
conn =
conn conn
|> put_req_header( |> maybe_assign_valid_signature()
"(request-target)", |> maybe_require_signature()
String.downcase("#{conn.method}") <> " #{conn.request_path}"
)
conn =
if conn.assigns[:digest] do
conn
|> put_req_header("digest", conn.assigns[:digest])
else else
conn conn
end end
end
defp maybe_assign_valid_signature(conn) do
if has_signature_header?(conn) do
# set (request-target) header to the appropriate value
# we also replace the digest header with the one we computed
request_target = String.downcase("#{conn.method}") <> " #{conn.request_path}"
conn =
conn
|> put_req_header("(request-target)", request_target)
|> case do
%{assigns: %{digest: digest}} = conn -> put_req_header(conn, "digest", digest)
conn -> conn
end
assign(conn, :valid_signature, HTTPSignatures.validate_conn(conn)) assign(conn, :valid_signature, HTTPSignatures.validate_conn(conn))
else else
@ -42,4 +45,21 @@ def call(conn, _opts) do
conn conn
end end
end end
defp has_signature_header?(conn) do
conn |> get_req_header("signature") |> Enum.at(0, false)
end
defp maybe_require_signature(%{assigns: %{valid_signature: true}} = conn), do: conn
defp maybe_require_signature(conn) do
if Pleroma.Config.get([:activitypub, :authorized_fetch_mode], false) do
conn
|> put_status(:unauthorized)
|> text("Request not signed")
|> halt()
else
conn
end
end
end end

View file

@ -41,13 +41,16 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug) plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
def emoji_reactions_by(%{assigns: %{user: user}} = conn, %{"id" => activity_id}) do def emoji_reactions_by(%{assigns: %{user: user}} = conn, %{"id" => activity_id} = params) do
with %Activity{} = activity <- Activity.get_by_id_with_object(activity_id), with %Activity{} = activity <- Activity.get_by_id_with_object(activity_id),
%Object{data: %{"reactions" => emoji_reactions}} when is_list(emoji_reactions) <- %Object{data: %{"reactions" => emoji_reactions}} when is_list(emoji_reactions) <-
Object.normalize(activity) do Object.normalize(activity) do
reactions = reactions =
emoji_reactions emoji_reactions
|> Enum.map(fn [emoji, user_ap_ids] -> |> Enum.map(fn [emoji, user_ap_ids] ->
if params["emoji"] && params["emoji"] != emoji do
nil
else
users = users =
Enum.map(user_ap_ids, &User.get_cached_by_ap_id/1) Enum.map(user_ap_ids, &User.get_cached_by_ap_id/1)
|> Enum.filter(& &1) |> Enum.filter(& &1)
@ -58,7 +61,9 @@ def emoji_reactions_by(%{assigns: %{user: user}} = conn, %{"id" => activity_id})
accounts: AccountView.render("index.json", %{users: users, for: user, as: :user}), accounts: AccountView.render("index.json", %{users: users, for: user, as: :user}),
me: !!(user && user.ap_id in user_ap_ids) me: !!(user && user.ap_id in user_ap_ids)
} }
end
end) end)
|> Enum.filter(& &1)
conn conn
|> json(reactions) |> json(reactions)

View file

@ -271,6 +271,7 @@ defmodule Pleroma.Web.Router do
scope "/api/v1/pleroma", Pleroma.Web.PleromaAPI do scope "/api/v1/pleroma", Pleroma.Web.PleromaAPI do
pipe_through(:api) pipe_through(:api)
get("/statuses/:id/reactions/:emoji", PleromaAPIController, :emoji_reactions_by)
get("/statuses/:id/reactions", PleromaAPIController, :emoji_reactions_by) get("/statuses/:id/reactions", PleromaAPIController, :emoji_reactions_by)
end end

View file

@ -0,0 +1,158 @@
<%= for {user, total_statuses, latest_status} <- @users_and_statuses do %>
<%# user card START %>
<div style="background-color:transparent;">
<div class="block-grid mixed-two-up no-stack"
style="Margin: 0 auto; min-width: 320px; max-width: 590px; overflow-wrap: break-word; word-wrap: break-word; word-break: break-word; background-color: <%= @styling.content_background_color%>;">
<div style="border-collapse: collapse;display: table;width: 100%;background-color:<%= @styling.content_background_color%>;">
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0" style="background-color:transparent;"><tr><td align="center"><table cellpadding="0" cellspacing="0" border="0" style="width:590px"><tr class="layout-full-width" style="background-color:<%= @styling.content_background_color%>"><![endif]-->
<!--[if (mso)|(IE)]><td align="center" width="147" style="background-color:<%= @styling.content_background_color%>;width:76px; border-top: 0px solid transparent; border-left: 0px solid transparent; border-bottom: 0px solid transparent; border-right: 0px solid transparent;" valign="top"><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 0px; padding-left: 20px; padding-top:5px; padding-bottom:5px;"><![endif]-->
<div class="col num3"
style="display: table-cell; vertical-align: top; max-width: 320px; min-width: 76px; width: 76px;">
<div style="width:100% !important;">
<!--[if (!mso)&(!IE)]><!-->
<div
style="border-top:0px solid transparent; border-left:0px solid transparent; border-bottom:0px solid transparent; border-right:0px solid transparent; padding-top:5px; padding-bottom:5px; padding-right: 0px; padding-left: 20px;">
<!--<![endif]-->
<div align="left" class="img-container left "
style="padding-right: 0px;padding-left: 0px;">
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr style="line-height:0px"><td style="padding-right: 0px;padding-left: 0px;" align="left"><![endif]--><img
alt="<%= user.name %>" border="0" class="left " src="<%= avatar_url(user) %>"
style="text-decoration: none; -ms-interpolation-mode: bicubic; border: 0; height: auto; width: 100%; max-width: 76px; display: block;"
title="<%= user.name %>" width="76" />
<!--[if mso]></td></tr></table><![endif]-->
</div>
<!--[if (!mso)&(!IE)]><!-->
</div>
<!--<![endif]-->
</div>
</div>
<!--[if (mso)|(IE)]></td></tr></table><![endif]-->
<!--[if (mso)|(IE)]></td><td align="center" width="442" style="background-color:<%= @styling.content_background_color%>;width:442px; border-top: 0px solid transparent; border-left: 0px solid transparent; border-bottom: 0px solid transparent; border-right: 0px solid transparent;" valign="top"><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 0px; padding-left: 0px; padding-top:5px; padding-bottom:5px;"><![endif]-->
<div class="col num9"
style="display: table-cell; vertical-align: top; min-width: 320px; max-width: 441px; width: 442px;">
<div style="width:100% !important;">
<!--[if (!mso)&(!IE)]><!-->
<div
style="border-top:0px solid transparent; border-left:0px solid transparent; border-bottom:0px solid transparent; border-right:0px solid transparent; padding-top:5px; padding-bottom:5px; padding-right: 0px; padding-left: 0px;">
<!--<![endif]-->
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 10px; padding-left: 10px; padding-top: 10px; padding-bottom: 10px; font-family: Arial, sans-serif"><![endif]-->
<div
style="color:<%= @styling.text_color %>;font-family:Arial, 'Helvetica Neue', Helvetica, sans-serif;line-height:120%;padding-top:10px;padding-right:10px;padding-bottom:10px;padding-left:10px;">
<div
style="font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; font-size: 12px; line-height: 14px; color: <%= @styling.text_color %>;">
<p style="font-size: 14px; line-height: 19px; margin: 0;"><span
style="font-size: 16px; color: <%= @styling.text_color %>;"><%= user.name %></span></p>
<p style="font-size: 14px; line-height: 19px; margin: 0;"><span
style="font-size: 16px;"><%= link "@" <> user.nickname, style: "color: #{@styling.link_color};text-decoration: none;", to: admin_user_url(user) %></span></p>
<p style="font-size: 14px; line-height: 19px; margin: 0;"><span
style="font-size: 16px;">Total: <%= total_statuses %></span></p>
</div>
</div>
<!--[if mso]></td></tr></table><![endif]-->
<!--[if (!mso)&(!IE)]><!-->
</div>
<!--<![endif]-->
</div>
</div>
<!--[if (mso)|(IE)]></td></tr></table><![endif]-->
<!--[if (mso)|(IE)]></td></tr></table></td></tr></table><![endif]-->
</div>
</div>
</div>
<%# user card END %>
<%= if latest_status do %>
<div style="background-color:transparent;">
<div class="block-grid"
style="Margin: 0 auto; min-width: 320px; max-width: 590px; overflow-wrap: break-word; word-wrap: break-word; word-break: break-word; background-color: <%= @styling.content_background_color%>;">
<div style="border-collapse: collapse;display: table;width: 100%;background-color:<%= @styling.content_background_color%>;">
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0" style="background-color:transparent;"><tr><td align="center"><table cellpadding="0" cellspacing="0" border="0" style="width:590px"><tr class="layout-full-width" style="background-color:<%= @styling.content_background_color%>"><![endif]-->
<!--[if (mso)|(IE)]><td align="center" width="590" style="background-color:<%= @styling.content_background_color%>;width:590px; border-top: 0px solid transparent; border-left: 0px solid transparent; border-bottom: 0px solid transparent; border-right: 0px solid transparent;" valign="top"><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 15px; padding-left: 15px; padding-top:5px; padding-bottom:5px;"><![endif]-->
<div class="col num12"
style="min-width: 320px; max-width: 590px; display: table-cell; vertical-align: top; width: 590px;">
<div style="width:100% !important;">
<!--[if (!mso)&(!IE)]><!-->
<div
style="border-top:0px solid transparent; border-left:0px solid transparent; border-bottom:0px solid transparent; border-right:0px solid transparent; padding-top:5px; padding-bottom:5px; padding-right: 15px; padding-left: 15px;">
<!--<![endif]-->
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 10px; padding-left: 10px; padding-top: 10px; padding-bottom: 10px; font-family: Arial, sans-serif"><![endif]-->
<div
style="color:<%= @styling.text_color %>;font-family:Arial, 'Helvetica Neue', Helvetica, sans-serif;line-height:120%;padding-top:10px;padding-right:10px;padding-bottom:10px;padding-left:10px;">
<div
style="font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; font-size: 12px; line-height: 14px; color: <%= @styling.text_color %>;">
<span style="font-size: 16px; line-height: 19px;"><%= raw latest_status.object.data["content"] %></span></div>
</div>
<!--[if mso]></td></tr></table><![endif]-->
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 10px; padding-left: 15px; padding-top: 10px; padding-bottom: 10px; font-family: Arial, sans-serif"><![endif]-->
<div
style="color:<%= @styling.text_muted_color %>;font-family:Arial, 'Helvetica Neue', Helvetica, sans-serif;line-height:120%;padding-top:10px;padding-right:10px;padding-bottom:10px;padding-left:15px;">
<div
style="font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; font-size: 12px; line-height: 14px; color: <%= @styling.text_muted_color %>;">
<p style="font-size: 14px; line-height: 16px; margin: 0;"><%= format_date latest_status.object.data["published"] %></p>
</div>
</div>
<!--[if mso]></td></tr></table><![endif]-->
<!--[if (!mso)&(!IE)]><!-->
</div>
<!--<![endif]-->
</div>
</div>
<!--[if (mso)|(IE)]></td></tr></table><![endif]-->
<!--[if (mso)|(IE)]></td></tr></table></td></tr></table><![endif]-->
</div>
</div>
</div>
<% end %>
<%# divider start %>
<div style="background-color:transparent;">
<div class="block-grid"
style="Margin: 0 auto; min-width: 320px; max-width: 590px; overflow-wrap: break-word; word-wrap: break-word; word-break: break-word; background-color: <%= @styling.content_background_color%>;">
<div style="border-collapse: collapse;display: table;width: 100%;background-color:<%= @styling.content_background_color%>;">
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0" style="background-color:transparent;"><tr><td align="center"><table cellpadding="0" cellspacing="0" border="0" style="width:590px"><tr class="layout-full-width" style="background-color:<%= @styling.content_background_color%>"><![endif]-->
<!--[if (mso)|(IE)]><td align="center" width="590" style="background-color:<%= @styling.content_background_color%>;width:590px; border-top: 0px solid transparent; border-left: 0px solid transparent; border-bottom: 0px solid transparent; border-right: 0px solid transparent;" valign="top"><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 0px; padding-left: 0px; padding-top:5px; padding-bottom:5px;"><![endif]-->
<div class="col num12"
style="min-width: 320px; max-width: 590px; display: table-cell; vertical-align: top; width: 590px;">
<div style="width:100% !important;">
<!--[if (!mso)&(!IE)]><!-->
<div
style="border-top:0px solid transparent; border-left:0px solid transparent; border-bottom:0px solid transparent; border-right:0px solid transparent; padding-top:5px; padding-bottom:5px; padding-right: 0px; padding-left: 0px;">
<!--<![endif]-->
<table border="0" cellpadding="0" cellspacing="0" class="divider" role="presentation"
style="table-layout: fixed; vertical-align: top; border-spacing: 0; border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt; min-width: 100%; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;"
valign="top" width="100%">
<tbody>
<tr style="vertical-align: top;" valign="top">
<td class="divider_inner"
style="word-break: break-word; vertical-align: top; min-width: 100%; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; padding-top: 10px; padding-right: 10px; padding-bottom: 10px; padding-left: 10px;"
valign="top">
<table align="center" border="0" cellpadding="0" cellspacing="0" class="divider_content"
height="0" role="presentation"
style="table-layout: fixed; vertical-align: top; border-spacing: 0; border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; border-top: 1px solid <%= @styling.text_color %>; height: 0px;"
valign="top" width="100%">
<tbody>
<tr style="vertical-align: top;" valign="top">
<td height="0"
style="word-break: break-word; vertical-align: top; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;"
valign="top"><span></span></td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<!--[if (!mso)&(!IE)]><!-->
</div>
<!--<![endif]-->
</div>
</div>
<!--[if (mso)|(IE)]></td></tr></table><![endif]-->
<!--[if (mso)|(IE)]></td></tr></table></td></tr></table><![endif]-->
</div>
</div>
</div>
<%# divider end %>
<%# user card END %>
<% end %>

View file

@ -0,0 +1,193 @@
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional //EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:o="urn:schemas-microsoft-com:office:office"
xmlns:v="urn:schemas-microsoft-com:vml">
<head>
<!--[if gte mso 9]><xml><o:OfficeDocumentSettings><o:AllowPNG/><o:PixelsPerInch>96</o:PixelsPerInch></o:OfficeDocumentSettings></xml><![endif]-->
<meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
<meta content="width=device-width" name="viewport" />
<!--[if !mso]><!-->
<meta content="IE=edge" http-equiv="X-UA-Compatible" />
<!--<![endif]-->
<title><%= @email.subject %></title>
<!--[if !mso]><!-->
<!--<![endif]-->
<style type="text/css">
body {
margin: 0;
padding: 0;
}
a {
color: <%= @styling.link_color %>;
text-decoration: none;
}
table,
td,
tr {
vertical-align: top;
border-collapse: collapse;
}
* {
line-height: inherit;
}
a[x-apple-data-detectors=true] {
color: inherit !important;
text-decoration: none !important;
}
</style>
<style id="media-query" type="text/css">
@media (max-width: 610px) {
.block-grid,
.col {
min-width: 320px !important;
max-width: 100% !important;
display: block !important;
}
.block-grid {
width: 100% !important;
}
.col {
width: 100% !important;
}
.col>div {
margin: 0 auto;
}
.no-stack .col {
min-width: 0 !important;
display: table-cell !important;
}
.no-stack.two-up .col {
width: 50% !important;
}
.no-stack .col.num4 {
width: 33% !important;
}
.no-stack .col.num8 {
width: 66% !important;
}
.no-stack .col.num4 {
width: 33% !important;
}
.no-stack .col.num3 {
width: 25% !important;
}
.no-stack .col.num6 {
width: 50% !important;
}
.no-stack .col.num9 {
width: 75% !important;
}
}
</style>
</head>
<body class="clean-body" style="margin: 0; padding: 0; -webkit-text-size-adjust: 100%; background-color: <%= @styling.background_color %>;">
<!--[if IE]><div class="ie-browser"><![endif]-->
<table bgcolor="<%= @styling.background_color %>" cellpadding="0" cellspacing="0" class="nl-container" role="presentation"
style="table-layout: fixed; vertical-align: top; min-width: 320px; Margin: 0 auto; border-spacing: 0; border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt; background-color: <%= @styling.background_color %>; width: 100%;"
valign="top" width="100%">
<tbody>
<tr style="vertical-align: top;" valign="top">
<td style="word-break: break-word; vertical-align: top;" valign="top">
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td align="center" style="background-color:<%= @styling.background_color %>"><![endif]-->
<%# header %>
<div style="background-color:transparent;">
<div class="block-grid"
style="Margin: 0 auto; min-width: 320px; max-width: 590px; overflow-wrap: break-word; word-wrap: break-word; word-break: break-word; background-color: <%= @styling.content_background_color%>;">
<div style="border-collapse: collapse;display: table;width: 100%;background-color:<%= @styling.content_background_color%>;">
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0" style="background-color:transparent;"><tr><td align="center"><table cellpadding="0" cellspacing="0" border="0" style="width:590px"><tr class="layout-full-width" style="background-color:<%= @styling.content_background_color%>"><![endif]-->
<!--[if (mso)|(IE)]><td align="center" width="590" style="background-color:<%= @styling.content_background_color%>;width:590px; border-top: 0px solid transparent; border-left: 0px solid transparent; border-bottom: 0px solid transparent; border-right: 0px solid transparent;" valign="top"><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 0px; padding-left: 0px; padding-top:5px; padding-bottom:5px;"><![endif]-->
<div class="col num12"
style="min-width: 320px; max-width: 590px; display: table-cell; vertical-align: top; width: 590px;">
<div style="width:100% !important;">
<!--[if (!mso)&(!IE)]><!-->
<div
style="border-top:0px solid transparent; border-left:0px solid transparent; border-bottom:0px solid transparent; border-right:0px solid transparent; padding-top:5px; padding-bottom:5px; padding-right: 0px; padding-left: 0px;">
<!--<![endif]-->
<div align="center" class="img-container center"
style="padding-right: 0px;padding-left: 0px;">
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr style="line-height:0px"><td style="padding-right: 0px;padding-left: 0px;" align="center"><![endif]--><img
align="center" alt="Image" border="0" class="center" src="<%= @logo_url %>"
style="text-decoration: none; -ms-interpolation-mode: bicubic; border: 0; height: 80px; width: auto; max-height: 80px; display: block;"
title="Image" height="80" />
<!--[if mso]></td></tr></table><![endif]-->
</div>
<!--[if (!mso)&(!IE)]><!-->
</div>
<!--<![endif]-->
</div>
</div>
<!--[if (mso)|(IE)]></td></tr></table><![endif]-->
<!--[if (mso)|(IE)]></td></tr></table></td></tr></table><![endif]-->
</div>
</div>
</div>
<%# title %>
<%= if @title do %>
<div style="background-color:transparent;">
<div class="block-grid"
style="Margin: 0 auto; min-width: 320px; max-width: 590px; overflow-wrap: break-word; word-wrap: break-word; word-break: break-word; background-color: <%= @styling.content_background_color%>;">
<div style="border-collapse: collapse;display: table;width: 100%;background-color:<%= @styling.content_background_color%>;">
<!--[if (mso)|(IE)]><table width="100%" cellpadding="0" cellspacing="0" border="0" style="background-color:transparent;"><tr><td align="center"><table cellpadding="0" cellspacing="0" border="0" style="width:590px"><tr class="layout-full-width" style="background-color:<%= @styling.content_background_color%>"><![endif]-->
<!--[if (mso)|(IE)]><td align="center" width="590" style="background-color:<%= @styling.content_background_color%>;width:590px; border-top: 0px solid transparent; border-left: 0px solid transparent; border-bottom: 0px solid transparent; border-right: 0px solid transparent;" valign="top"><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 0px; padding-left: 0px; padding-top:5px; padding-bottom:5px;"><![endif]-->
<div class="col num12"
style="min-width: 320px; max-width: 590px; display: table-cell; vertical-align: top; width: 590px;">
<div style="width:100% !important;">
<!--[if (!mso)&(!IE)]><!-->
<div
style="border-top:0px solid transparent; border-left:0px solid transparent; border-bottom:0px solid transparent; border-right:0px solid transparent; padding-top:5px; padding-bottom:5px; padding-right: 0px; padding-left: 0px;">
<!--<![endif]-->
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td style="padding-right: 10px; padding-left: 10px; padding-top: 10px; padding-bottom: 10px; font-family: Arial, sans-serif"><![endif]-->
<div
style="line-height:120%;padding-top:10px;padding-right:10px;padding-bottom:10px;padding-left:10px;">
<div
style="font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif;line-height: 14px; color: <%= @styling.header_color %>;">
<p style="line-height: 36px; text-align: center; margin: 0;"><span
style="font-size: 30px; color: <%= @styling.header_color %>;"><%= @title %></span></p>
</div>
</div>
<!--[if mso]></td></tr></table><![endif]-->
<!--[if (!mso)&(!IE)]><!-->
</div>
<!--<![endif]-->
</div>
</div>
<!--[if (mso)|(IE)]></td></tr></table><![endif]-->
<!--[if (mso)|(IE)]></td></tr></table></td></tr></table><![endif]-->
</div>
</div>
</div>
<% end %>
<%= render @view_module, @view_template, assigns %>
</td>
</tr>
</tbody>
</table>
<!--[if (IE)]></div><![endif]-->
</body>
</html>

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.TwitterAPI.RemoteFollowController do defmodule Pleroma.Web.TwitterAPI.RemoteFollowController do
@ -69,7 +69,7 @@ defp is_status?(acct) do
def do_follow(%{assigns: %{user: %User{} = user}} = conn, %{"user" => %{"id" => id}}) do def do_follow(%{assigns: %{user: %User{} = user}} = conn, %{"user" => %{"id" => id}}) do
with {:fetch_user, %User{} = followee} <- {:fetch_user, User.get_cached_by_id(id)}, with {:fetch_user, %User{} = followee} <- {:fetch_user, User.get_cached_by_id(id)},
{:ok, _, _, _} <- CommonAPI.follow(user, followee) do {:ok, _, _, _} <- CommonAPI.follow(user, followee) do
render(conn, "followed.html", %{error: false}) redirect(conn, to: "/users/#{followee.id}")
else else
error -> error ->
handle_follow_error(conn, error) handle_follow_error(conn, error)
@ -80,7 +80,7 @@ def do_follow(conn, %{"authorization" => %{"name" => _, "password" => _, "id" =>
with {:fetch_user, %User{} = followee} <- {:fetch_user, User.get_cached_by_id(id)}, with {:fetch_user, %User{} = followee} <- {:fetch_user, User.get_cached_by_id(id)},
{_, {:ok, user}, _} <- {:auth, Authenticator.get_user(conn), followee}, {_, {:ok, user}, _} <- {:auth, Authenticator.get_user(conn), followee},
{:ok, _, _, _} <- CommonAPI.follow(user, followee) do {:ok, _, _, _} <- CommonAPI.follow(user, followee) do
render(conn, "followed.html", %{error: false}) redirect(conn, to: "/users/#{followee.id}")
else else
error -> error ->
handle_follow_error(conn, error) handle_follow_error(conn, error)

View file

@ -12,4 +12,8 @@ def format_date(date) when is_binary(date) do
|> Timex.parse!("{ISO:Extended:Z}") |> Timex.parse!("{ISO:Extended:Z}")
|> Timex.format!("{Mshort} {D}, {YYYY} {h24}:{m}") |> Timex.format!("{Mshort} {D}, {YYYY} {h24}:{m}")
end end
def admin_user_url(%{id: id}) do
Pleroma.Web.Endpoint.url() <> "/pleroma/admin/#/users/" <> id
end
end end

View file

@ -0,0 +1,60 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Workers.Cron.NewUsersDigestWorker do
alias Pleroma.Activity
alias Pleroma.Repo
alias Pleroma.User
import Ecto.Query
use Pleroma.Workers.WorkerHelper, queue: "new_users_digest"
@impl Oban.Worker
def perform(_args, _job) do
if Pleroma.Config.get([Pleroma.Emails.NewUsersDigestEmail, :enabled]) do
today = NaiveDateTime.utc_now() |> Timex.beginning_of_day()
a_day_ago =
today
|> Timex.shift(days: -1)
|> Timex.beginning_of_day()
users_and_statuses =
%{
local: true,
order_by: :inserted_at
}
|> User.Query.build()
|> where([u], u.inserted_at >= ^a_day_ago and u.inserted_at < ^today)
|> Repo.all()
|> Enum.map(fn user ->
latest_status =
Activity
|> Activity.Queries.by_actor(user.ap_id)
|> Activity.Queries.by_type("Create")
|> Activity.with_preloaded_object()
|> order_by(desc: :inserted_at)
|> limit(1)
|> Repo.one()
total_statuses =
Activity
|> Activity.Queries.by_actor(user.ap_id)
|> Activity.Queries.by_type("Create")
|> Repo.aggregate(:count, :id)
{user, total_statuses, latest_status}
end)
if users_and_statuses != [] do
%{is_admin: true}
|> User.Query.build()
|> Repo.all()
|> Enum.map(&Pleroma.Emails.NewUsersDigestEmail.new_users(&1, users_and_statuses))
|> Enum.each(&Pleroma.Emails.Mailer.deliver/1)
end
end
end
end

View file

@ -17,7 +17,11 @@ def up do
Repo.stream(query) Repo.stream(query)
|> Enum.each(fn %{id: user_id, bookmarks: bookmarks} -> |> Enum.each(fn %{id: user_id, bookmarks: bookmarks} ->
Enum.each(bookmarks, fn ap_id -> Enum.each(bookmarks, fn ap_id ->
activity = Activity.get_create_by_object_ap_id(ap_id) activity =
ap_id
|> Activity.create_by_object_ap_id()
|> Repo.one()
unless is_nil(activity), do: {:ok, _} = Bookmark.create(user_id, activity.id) unless is_nil(activity), do: {:ok, _} = Bookmark.create(user_id, activity.id)
end) end)
end) end)

View file

@ -1,7 +1,8 @@
defmodule Pleroma.Repo.Migrations.AddFollowingAddressFromSourceData do defmodule Pleroma.Repo.Migrations.AddFollowingAddressFromSourceData do
use Ecto.Migration
import Ecto.Query
alias Pleroma.User alias Pleroma.User
import Ecto.Query
require Logger
use Ecto.Migration
def change do def change do
query = query =
@ -19,6 +20,9 @@ def change do
:following_address :following_address
]) ])
|> Pleroma.Repo.update() |> Pleroma.Repo.update()
user ->
Logger.warn("User #{user.id} / #{user.nickname} does not seem to have source_data")
end) end)
end end
end end

View file

@ -2,6 +2,8 @@ defmodule Pleroma.Repo.Migrations.CopyMutedToMutedNotifications do
use Ecto.Migration use Ecto.Migration
def change do def change do
execute("update users set info = '{}' where info is null")
execute( execute(
"update users set info = safe_jsonb_set(info, '{muted_notifications}', info->'mutes', true) where local = true" "update users set info = safe_jsonb_set(info, '{muted_notifications}', info->'mutes', true) where local = true"
) )

View file

@ -138,6 +138,8 @@ test "when association is not loaded" do
} }
end end
clear_config([:instance, :limit_to_local_content])
test "finds utf8 text in statuses", %{ test "finds utf8 text in statuses", %{
japanese_activity: japanese_activity, japanese_activity: japanese_activity,
user: user user: user
@ -165,7 +167,6 @@ test "find only local statuses for unauthenticated users when `limit_to_local_c
%{local_activity: local_activity} do %{local_activity: local_activity} do
Pleroma.Config.put([:instance, :limit_to_local_content], :all) Pleroma.Config.put([:instance, :limit_to_local_content], :all)
assert [^local_activity] = Activity.search(nil, "find me") assert [^local_activity] = Activity.search(nil, "find me")
Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
end end
test "find all statuses for unauthenticated users when `limit_to_local_content` is `false`", test "find all statuses for unauthenticated users when `limit_to_local_content` is `false`",
@ -178,8 +179,6 @@ test "find all statuses for unauthenticated users when `limit_to_local_content`
activities = Enum.sort_by(Activity.search(nil, "find me"), & &1.id) activities = Enum.sort_by(Activity.search(nil, "find me"), & &1.id)
assert [^local_activity, ^remote_activity] = activities assert [^local_activity, ^remote_activity] = activities
Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
end end
end end

View file

@ -11,6 +11,7 @@ defmodule Pleroma.HTTP.RequestBuilderTest do
describe "headers/2" do describe "headers/2" do
clear_config([:http, :send_user_agent]) clear_config([:http, :send_user_agent])
clear_config([:http, :user_agent])
test "don't send pleroma user agent" do test "don't send pleroma user agent" do
assert RequestBuilder.headers(%Request{}, []) == %Request{headers: []} assert RequestBuilder.headers(%Request{}, []) == %Request{headers: []}

View file

@ -75,6 +75,7 @@ test "ensures cache is cleared for the object" do
describe "delete attachments" do describe "delete attachments" do
clear_config([Pleroma.Upload]) clear_config([Pleroma.Upload])
clear_config([:instance, :cleanup_attachments])
test "Disabled via config" do test "Disabled via config" do
Pleroma.Config.put([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local) Pleroma.Config.put([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local)

View file

@ -23,6 +23,8 @@ test "does nothing if a user is assigned", %{conn: conn} do
end end
describe "when secret set it assigns an admin user" do describe "when secret set it assigns an admin user" do
clear_config([:admin_token])
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")

View file

@ -9,6 +9,7 @@ defmodule Pleroma.Web.Plugs.HTTPSecurityPlugTest do
clear_config([:http_securiy, :enabled]) clear_config([:http_securiy, :enabled])
clear_config([:http_security, :sts]) clear_config([:http_security, :sts])
clear_config([:http_security, :referrer_policy])
describe "http security enabled" do describe "http security enabled" do
setup do setup do

View file

@ -7,6 +7,7 @@ defmodule Pleroma.Web.Plugs.HTTPSignaturePlugTest do
alias Pleroma.Web.Plugs.HTTPSignaturePlug alias Pleroma.Web.Plugs.HTTPSignaturePlug
import Plug.Conn import Plug.Conn
import Phoenix.Controller, only: [put_format: 2]
import Mock import Mock
test "it call HTTPSignatures to check validity if the actor sighed it" do test "it call HTTPSignatures to check validity if the actor sighed it" do
@ -20,10 +21,69 @@ test "it call HTTPSignatures to check validity if the actor sighed it" do
"signature", "signature",
"keyId=\"http://mastodon.example.org/users/admin#main-key" "keyId=\"http://mastodon.example.org/users/admin#main-key"
) )
|> put_format("activity+json")
|> HTTPSignaturePlug.call(%{}) |> HTTPSignaturePlug.call(%{})
assert conn.assigns.valid_signature == true assert conn.assigns.valid_signature == true
assert conn.halted == false
assert called(HTTPSignatures.validate_conn(:_)) assert called(HTTPSignatures.validate_conn(:_))
end end
end end
describe "requires a signature when `authorized_fetch_mode` is enabled" do
setup do
Pleroma.Config.put([:activitypub, :authorized_fetch_mode], true)
on_exit(fn ->
Pleroma.Config.put([:activitypub, :authorized_fetch_mode], false)
end)
params = %{"actor" => "http://mastodon.example.org/users/admin"}
conn = build_conn(:get, "/doesntmattter", params) |> put_format("activity+json")
[conn: conn]
end
test "when signature header is present", %{conn: conn} do
with_mock HTTPSignatures, validate_conn: fn _ -> false end do
conn =
conn
|> put_req_header(
"signature",
"keyId=\"http://mastodon.example.org/users/admin#main-key"
)
|> HTTPSignaturePlug.call(%{})
assert conn.assigns.valid_signature == false
assert conn.halted == true
assert conn.status == 401
assert conn.state == :sent
assert conn.resp_body == "Request not signed"
assert called(HTTPSignatures.validate_conn(:_))
end
with_mock HTTPSignatures, validate_conn: fn _ -> true end do
conn =
conn
|> put_req_header(
"signature",
"keyId=\"http://mastodon.example.org/users/admin#main-key"
)
|> HTTPSignaturePlug.call(%{})
assert conn.assigns.valid_signature == true
assert conn.halted == false
assert called(HTTPSignatures.validate_conn(:_))
end
end
test "halts the connection when `signature` header is not present", %{conn: conn} do
conn = HTTPSignaturePlug.call(conn, %{})
assert conn.assigns[:valid_signature] == nil
assert conn.halted == true
assert conn.status == 401
assert conn.state == :sent
assert conn.resp_body == "Request not signed"
end
end
end end

View file

@ -8,6 +8,10 @@ defmodule Pleroma.Plugs.RemoteIpTest do
alias Pleroma.Plugs.RemoteIp alias Pleroma.Plugs.RemoteIp
import Pleroma.Tests.Helpers, only: [clear_config: 1, clear_config: 2]
clear_config(RemoteIp)
test "disabled" do test "disabled" do
Pleroma.Config.put(RemoteIp, enabled: false) Pleroma.Config.put(RemoteIp, enabled: false)

View file

@ -8,6 +8,8 @@ defmodule Pleroma.Plugs.UserEnabledPlugTest do
alias Pleroma.Plugs.UserEnabledPlug alias Pleroma.Plugs.UserEnabledPlug
import Pleroma.Factory import Pleroma.Factory
clear_config([:instance, :account_activation_required])
test "doesn't do anything if the user isn't set", %{conn: conn} do test "doesn't do anything if the user isn't set", %{conn: conn} do
ret_conn = ret_conn =
conn conn
@ -18,7 +20,6 @@ test "doesn't do anything if the user isn't set", %{conn: conn} do
test "with a user that's not confirmed and a config requiring confirmation, it removes that user", test "with a user that's not confirmed and a config requiring confirmation, it removes that user",
%{conn: conn} do %{conn: conn} do
old = Pleroma.Config.get([:instance, :account_activation_required])
Pleroma.Config.put([:instance, :account_activation_required], true) Pleroma.Config.put([:instance, :account_activation_required], true)
user = insert(:user, confirmation_pending: true) user = insert(:user, confirmation_pending: true)
@ -29,8 +30,6 @@ test "with a user that's not confirmed and a config requiring confirmation, it r
|> UserEnabledPlug.call(%{}) |> UserEnabledPlug.call(%{})
assert conn.assigns.user == nil assert conn.assigns.user == nil
Pleroma.Config.put([:instance, :account_activation_required], old)
end end
test "with a user that is deactivated, it removes that user", %{conn: conn} do test "with a user that is deactivated, it removes that user", %{conn: conn} do

View file

@ -67,6 +67,8 @@ test "return error if has not assoc " do
:ok :ok
end end
clear_config([:i_am_aware_this_may_cause_data_loss, :disable_migration_check])
test "raises if it detects unapplied migrations" do test "raises if it detects unapplied migrations" do
assert_raise Pleroma.Repo.UnappliedMigrationsError, fn -> assert_raise Pleroma.Repo.UnappliedMigrationsError, fn ->
capture_log(&Repo.check_migrations_applied!/0) capture_log(&Repo.check_migrations_applied!/0)
@ -74,18 +76,8 @@ test "raises if it detects unapplied migrations" do
end end
test "doesn't do anything if disabled" do test "doesn't do anything if disabled" do
disable_migration_check =
Pleroma.Config.get([:i_am_aware_this_may_cause_data_loss, :disable_migration_check])
Pleroma.Config.put([:i_am_aware_this_may_cause_data_loss, :disable_migration_check], true) Pleroma.Config.put([:i_am_aware_this_may_cause_data_loss, :disable_migration_check], true)
on_exit(fn ->
Pleroma.Config.put(
[:i_am_aware_this_may_cause_data_loss, :disable_migration_check],
disable_migration_check
)
end)
assert :ok == Repo.check_migrations_applied!() assert :ok == Repo.check_migrations_applied!()
end end
end end

View file

@ -26,6 +26,7 @@ defmacro clear_config(config_path, do: yield) do
end end
end end
@doc "Stores initial config value and restores it after *all* test examples are executed."
defmacro clear_config_all(config_path) do defmacro clear_config_all(config_path) do
quote do quote do
clear_config_all(unquote(config_path)) do clear_config_all(unquote(config_path)) do
@ -33,6 +34,11 @@ defmacro clear_config_all(config_path) do
end end
end end
@doc """
Stores initial config value and restores it after *all* test examples are executed.
Only use if *all* test examples should work with the same stubbed value
(*no* examples set a different value).
"""
defmacro clear_config_all(config_path, do: yield) do defmacro clear_config_all(config_path, do: yield) do
quote do quote do
setup_all do setup_all do

View file

@ -15,6 +15,8 @@ defmodule Pleroma.UserSearchTest do
end end
describe "User.search" do describe "User.search" do
clear_config([:instance, :limit_to_local_content])
test "excluded invisible users from results" do test "excluded invisible users from results" do
user = insert(:user, %{nickname: "john t1000"}) user = insert(:user, %{nickname: "john t1000"})
insert(:user, %{invisible: true, nickname: "john t800"}) insert(:user, %{invisible: true, nickname: "john t800"})
@ -127,8 +129,6 @@ test "find only local users for authenticated users when `limit_to_local_content
insert(:user, %{nickname: "lain@pleroma.soykaf.com", local: false}) insert(:user, %{nickname: "lain@pleroma.soykaf.com", local: false})
assert [%{id: ^id}] = User.search("lain") assert [%{id: ^id}] = User.search("lain")
Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
end end
test "find all users for unauthenticated users when `limit_to_local_content` is `false`" do test "find all users for unauthenticated users when `limit_to_local_content` is `false`" do
@ -145,8 +145,6 @@ test "find all users for unauthenticated users when `limit_to_local_content` is
|> Enum.sort() |> Enum.sort()
assert [u1.id, u2.id, u3.id] == results assert [u1.id, u2.id, u3.id] == results
Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
end end
test "does not yield false-positive matches" do test "does not yield false-positive matches" do

View file

@ -297,15 +297,7 @@ test "local users do not automatically follow local locked accounts" do
end end
describe "unfollow/2" do describe "unfollow/2" do
setup do clear_config([:instance, :external_user_synchronization])
setting = Pleroma.Config.get([:instance, :external_user_synchronization])
on_exit(fn ->
Pleroma.Config.put([:instance, :external_user_synchronization], setting)
end)
:ok
end
test "unfollow with syncronizes external user" do test "unfollow with syncronizes external user" do
Pleroma.Config.put([:instance, :external_user_synchronization], true) Pleroma.Config.put([:instance, :external_user_synchronization], true)
@ -383,6 +375,7 @@ test "fetches correct profile for nickname beginning with number" do
password_confirmation: "test", password_confirmation: "test",
email: "email@example.com" email: "email@example.com"
} }
clear_config([:instance, :autofollowed_nicknames]) clear_config([:instance, :autofollowed_nicknames])
clear_config([:instance, :welcome_message]) clear_config([:instance, :welcome_message])
clear_config([:instance, :welcome_user_nickname]) clear_config([:instance, :welcome_user_nickname])
@ -1754,17 +1747,14 @@ test "changes email", %{user: user} do
describe "get_cached_by_nickname_or_id" do describe "get_cached_by_nickname_or_id" do
setup do setup do
limit_to_local_content = Pleroma.Config.get([:instance, :limit_to_local_content])
local_user = insert(:user) local_user = insert(:user)
remote_user = insert(:user, nickname: "nickname@example.com", local: false) remote_user = insert(:user, nickname: "nickname@example.com", local: false)
on_exit(fn ->
Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local_content)
end)
[local_user: local_user, remote_user: remote_user] [local_user: local_user, remote_user: remote_user]
end end
clear_config([:instance, :limit_to_local_content])
test "allows getting remote users by id no matter what :limit_to_local_content is set to", %{ test "allows getting remote users by id no matter what :limit_to_local_content is set to", %{
remote_user: remote_user remote_user: remote_user
} do } do

View file

@ -1224,6 +1224,8 @@ test "creates an undo activity for the last block" do
end end
describe "deletion" do describe "deletion" do
clear_config([:instance, :rewrite_policy])
test "it creates a delete activity and deletes the original object" do test "it creates a delete activity and deletes the original object" do
note = insert(:note_activity) note = insert(:note_activity)
object = Object.normalize(note) object = Object.normalize(note)
@ -1327,11 +1329,8 @@ test "decreases reply count" do
end end
test "it passes delete activity through MRF before deleting the object" do test "it passes delete activity through MRF before deleting the object" do
rewrite_policy = Pleroma.Config.get([:instance, :rewrite_policy])
Pleroma.Config.put([:instance, :rewrite_policy], Pleroma.Web.ActivityPub.MRF.DropPolicy) Pleroma.Config.put([:instance, :rewrite_policy], Pleroma.Web.ActivityPub.MRF.DropPolicy)
on_exit(fn -> Pleroma.Config.put([:instance, :rewrite_policy], rewrite_policy) end)
note = insert(:note_activity) note = insert(:note_activity)
object = Object.normalize(note) object = Object.normalize(note)
@ -1396,6 +1395,8 @@ test "it filters broken threads" do
end end
describe "update" do describe "update" do
clear_config([:instance, :max_pinned_statuses])
test "it creates an update activity with the new user data" do test "it creates an update activity with the new user data" do
user = insert(:user) user = insert(:user)
{:ok, user} = User.ensure_keys_present(user) {:ok, user} = User.ensure_keys_present(user)

View file

@ -26,6 +26,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.HellthreadPolicyTest do
[user: user, message: message] [user: user, message: message]
end end
clear_config(:mrf_hellthread)
describe "reject" do describe "reject" do
test "rejects the message if the recipient count is above reject_threshold", %{ test "rejects the message if the recipient count is above reject_threshold", %{
message: message message: message

View file

@ -7,6 +7,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicyTest do
alias Pleroma.Web.ActivityPub.MRF.KeywordPolicy alias Pleroma.Web.ActivityPub.MRF.KeywordPolicy
clear_config(:mrf_keyword)
setup do setup do
Pleroma.Config.put([:mrf_keyword], %{reject: [], federated_timeline_removal: [], replace: []}) Pleroma.Config.put([:mrf_keyword], %{reject: [], federated_timeline_removal: [], replace: []})
end end

View file

@ -7,6 +7,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.MentionPolicyTest do
alias Pleroma.Web.ActivityPub.MRF.MentionPolicy alias Pleroma.Web.ActivityPub.MRF.MentionPolicy
clear_config(:mrf_mention)
test "pass filter if allow list is empty" do test "pass filter if allow list is empty" do
Pleroma.Config.delete([:mrf_mention]) Pleroma.Config.delete([:mrf_mention])

View file

@ -14,6 +14,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.SubchainPolicyTest do
"object" => %{"content" => "hi"} "object" => %{"content" => "hi"}
} }
clear_config([:mrf_subchain, :match_actor])
test "it matches and processes subchains when the actor matches a configured target" do test "it matches and processes subchains when the actor matches a configured target" do
Pleroma.Config.put([:mrf_subchain, :match_actor], %{ Pleroma.Config.put([:mrf_subchain, :match_actor], %{
~r/^https:\/\/banned.com/s => [DropPolicy] ~r/^https:\/\/banned.com/s => [DropPolicy]

View file

@ -19,6 +19,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.FollowHandlingTest do
end end
describe "handle_incoming" do describe "handle_incoming" do
clear_config([:user, :deny_follow_blocked])
test "it works for osada follow request" do test "it works for osada follow request" do
user = insert(:user) user = insert(:user)

View file

@ -1893,9 +1893,7 @@ test "returns error when status is not exist", %{conn: conn} do
end end
test "when configuration from database is off", %{conn: conn} do test "when configuration from database is off", %{conn: conn} do
initial = Config.get(:configurable_from_database)
Config.put(:configurable_from_database, false) Config.put(:configurable_from_database, false)
on_exit(fn -> Config.put(:configurable_from_database, initial) end)
conn = get(conn, "/api/pleroma/admin/config") conn = get(conn, "/api/pleroma/admin/config")
assert json_response(conn, 400) == assert json_response(conn, 400) ==

View file

@ -68,6 +68,7 @@ test "with the safe_dm_mention option set, it does not mention people beyond the
har = insert(:user) har = insert(:user)
jafnhar = insert(:user) jafnhar = insert(:user)
tridi = insert(:user) tridi = insert(:user)
Pleroma.Config.put([:instance, :safe_dm_mentions], true) Pleroma.Config.put([:instance, :safe_dm_mentions], true)
{:ok, activity} = {:ok, activity} =

View file

@ -15,6 +15,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
import Pleroma.Factory import Pleroma.Factory
describe "account fetching" do describe "account fetching" do
clear_config([:instance, :limit_to_local_content])
test "works by id" do test "works by id" do
user = insert(:user) user = insert(:user)
@ -44,7 +46,6 @@ test "works by nickname" do
end end
test "works by nickname for remote users" do test "works by nickname for remote users" do
limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
Pleroma.Config.put([:instance, :limit_to_local_content], false) Pleroma.Config.put([:instance, :limit_to_local_content], false)
user = insert(:user, nickname: "user@example.com", local: false) user = insert(:user, nickname: "user@example.com", local: false)
@ -52,13 +53,11 @@ test "works by nickname for remote users" do
build_conn() build_conn()
|> get("/api/v1/accounts/#{user.nickname}") |> get("/api/v1/accounts/#{user.nickname}")
Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
assert %{"id" => id} = json_response(conn, 200) assert %{"id" => id} = json_response(conn, 200)
assert id == user.id assert id == user.id
end end
test "respects limit_to_local_content == :all for remote user nicknames" do test "respects limit_to_local_content == :all for remote user nicknames" do
limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
Pleroma.Config.put([:instance, :limit_to_local_content], :all) Pleroma.Config.put([:instance, :limit_to_local_content], :all)
user = insert(:user, nickname: "user@example.com", local: false) user = insert(:user, nickname: "user@example.com", local: false)
@ -67,12 +66,10 @@ test "respects limit_to_local_content == :all for remote user nicknames" do
build_conn() build_conn()
|> get("/api/v1/accounts/#{user.nickname}") |> get("/api/v1/accounts/#{user.nickname}")
Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
assert json_response(conn, 404) assert json_response(conn, 404)
end end
test "respects limit_to_local_content == :unauthenticated for remote user nicknames" do test "respects limit_to_local_content == :unauthenticated for remote user nicknames" do
limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated) Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
user = insert(:user, nickname: "user@example.com", local: false) user = insert(:user, nickname: "user@example.com", local: false)
@ -90,7 +87,6 @@ test "respects limit_to_local_content == :unauthenticated for remote user nickna
|> assign(:token, insert(:oauth_token, user: reading_user, scopes: ["read:accounts"])) |> assign(:token, insert(:oauth_token, user: reading_user, scopes: ["read:accounts"]))
|> get("/api/v1/accounts/#{user.nickname}") |> get("/api/v1/accounts/#{user.nickname}")
Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
assert %{"id" => id} = json_response(conn, 200) assert %{"id" => id} = json_response(conn, 200)
assert id == user.id assert id == user.id
end end
@ -677,6 +673,8 @@ test "returns error when user already registred", %{conn: conn, valid_params: va
assert json_response(res, 400) == %{"error" => "{\"email\":[\"has already been taken\"]}"} assert json_response(res, 400) == %{"error" => "{\"email\":[\"has already been taken\"]}"}
end end
clear_config([Pleroma.Plugs.RemoteIp, :enabled])
test "rate limit", %{conn: conn} do test "rate limit", %{conn: conn} do
Pleroma.Config.put([Pleroma.Plugs.RemoteIp, :enabled], true) Pleroma.Config.put([Pleroma.Plugs.RemoteIp, :enabled], true)
app_token = insert(:oauth_token, user: nil) app_token = insert(:oauth_token, user: nil)

View file

@ -21,6 +21,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
clear_config([:instance, :federating]) clear_config([:instance, :federating])
clear_config([:instance, :allow_relay]) clear_config([:instance, :allow_relay])
clear_config([:rich_media, :enabled])
describe "posting statuses" do describe "posting statuses" do
setup do: oauth_access(["write:statuses"]) setup do: oauth_access(["write:statuses"])

View file

@ -7,11 +7,8 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyControllerTest do
import Mock import Mock
alias Pleroma.Config alias Pleroma.Config
setup do clear_config(:media_proxy)
media_proxy_config = Config.get([:media_proxy]) || [] clear_config([Pleroma.Web.Endpoint, :secret_key_base])
on_exit(fn -> Config.put([:media_proxy], media_proxy_config) end)
:ok
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) Config.put([:media_proxy, :enabled], false)

View file

@ -9,6 +9,7 @@ defmodule Pleroma.Web.MediaProxyTest do
alias Pleroma.Web.MediaProxy.MediaProxyController alias Pleroma.Web.MediaProxy.MediaProxyController
clear_config([:media_proxy, :enabled]) clear_config([:media_proxy, :enabled])
clear_config(Pleroma.Upload)
describe "when enabled" do describe "when enabled" do
setup do setup do
@ -224,7 +225,6 @@ test "does not change whitelisted urls" do
end end
test "ensure Pleroma.Upload base_url is always whitelisted" do test "ensure Pleroma.Upload base_url is always whitelisted" do
upload_config = Pleroma.Config.get([Pleroma.Upload])
media_url = "https://media.pleroma.social" media_url = "https://media.pleroma.social"
Pleroma.Config.put([Pleroma.Upload, :base_url], media_url) Pleroma.Config.put([Pleroma.Upload, :base_url], media_url)
@ -232,8 +232,6 @@ test "ensure Pleroma.Upload base_url is always whitelisted" do
encoded = url(url) encoded = url(url)
assert String.starts_with?(encoded, media_url) assert String.starts_with?(encoded, media_url)
Pleroma.Config.put([Pleroma.Upload], upload_config)
end end
end end
end end

View file

@ -7,6 +7,8 @@ defmodule Pleroma.Web.Metadata.Providers.OpenGraphTest do
import Pleroma.Factory import Pleroma.Factory
alias Pleroma.Web.Metadata.Providers.OpenGraph alias Pleroma.Web.Metadata.Providers.OpenGraph
clear_config([Pleroma.Web.Metadata, :unfurl_nsfw])
test "it renders all supported types of attachments and skips unknown types" do test "it renders all supported types of attachments and skips unknown types" do
user = insert(:user) user = insert(:user)

View file

@ -13,6 +13,8 @@ defmodule Pleroma.Web.Metadata.Providers.TwitterCardTest do
alias Pleroma.Web.Metadata.Utils alias Pleroma.Web.Metadata.Utils
alias Pleroma.Web.Router alias Pleroma.Web.Router
clear_config([Pleroma.Web.Metadata, :unfurl_nsfw])
test "it renders twitter card for user info" do test "it renders twitter card for user info" do
user = insert(:user, name: "Jimmy Hendriks", bio: "born 19 March 1994") user = insert(:user, name: "Jimmy Hendriks", bio: "born 19 March 1994")
avatar_url = Utils.attachment_url(User.avatar_url(user)) avatar_url = Utils.attachment_url(User.avatar_url(user))

View file

@ -6,7 +6,9 @@ defmodule Pleroma.Web.NodeInfoTest do
use Pleroma.Web.ConnCase use Pleroma.Web.ConnCase
import Pleroma.Factory import Pleroma.Factory
clear_config([:mrf_simple]) clear_config([:mrf_simple])
clear_config(:instance)
test "GET /.well-known/nodeinfo", %{conn: conn} do test "GET /.well-known/nodeinfo", %{conn: conn} do
links = links =
@ -63,11 +65,6 @@ test "returns software.repository field in nodeinfo 2.1", %{conn: conn} do
end end
test "returns fieldsLimits field", %{conn: conn} do test "returns fieldsLimits field", %{conn: conn} do
max_account_fields = Pleroma.Config.get([:instance, :max_account_fields])
max_remote_account_fields = Pleroma.Config.get([:instance, :max_remote_account_fields])
account_field_name_length = Pleroma.Config.get([:instance, :account_field_name_length])
account_field_value_length = Pleroma.Config.get([:instance, :account_field_value_length])
Pleroma.Config.put([:instance, :max_account_fields], 10) Pleroma.Config.put([:instance, :max_account_fields], 10)
Pleroma.Config.put([:instance, :max_remote_account_fields], 15) Pleroma.Config.put([:instance, :max_remote_account_fields], 15)
Pleroma.Config.put([:instance, :account_field_name_length], 255) Pleroma.Config.put([:instance, :account_field_name_length], 255)
@ -82,11 +79,6 @@ test "returns fieldsLimits field", %{conn: conn} do
assert response["metadata"]["fieldsLimits"]["maxRemoteFields"] == 15 assert response["metadata"]["fieldsLimits"]["maxRemoteFields"] == 15
assert response["metadata"]["fieldsLimits"]["nameLength"] == 255 assert response["metadata"]["fieldsLimits"]["nameLength"] == 255
assert response["metadata"]["fieldsLimits"]["valueLength"] == 2048 assert response["metadata"]["fieldsLimits"]["valueLength"] == 2048
Pleroma.Config.put([:instance, :max_account_fields], max_account_fields)
Pleroma.Config.put([:instance, :max_remote_account_fields], max_remote_account_fields)
Pleroma.Config.put([:instance, :account_field_name_length], account_field_name_length)
Pleroma.Config.put([:instance, :account_field_value_length], account_field_value_length)
end end
test "it returns the safe_dm_mentions feature if enabled", %{conn: conn} do test "it returns the safe_dm_mentions feature if enabled", %{conn: conn} do
@ -112,9 +104,10 @@ test "it returns the safe_dm_mentions feature if enabled", %{conn: conn} do
Pleroma.Config.put([:instance, :safe_dm_mentions], option) Pleroma.Config.put([:instance, :safe_dm_mentions], option)
end end
test "it shows if federation is enabled/disabled", %{conn: conn} do describe "`metadata/federation/enabled`" do
original = Pleroma.Config.get([:instance, :federating]) clear_config([:instance, :federating])
test "it shows if federation is enabled/disabled", %{conn: conn} do
Pleroma.Config.put([:instance, :federating], true) Pleroma.Config.put([:instance, :federating], true)
response = response =
@ -132,8 +125,7 @@ test "it shows if federation is enabled/disabled", %{conn: conn} do
|> json_response(:ok) |> json_response(:ok)
assert response["metadata"]["federation"]["enabled"] == false assert response["metadata"]["federation"]["enabled"] == false
end
Pleroma.Config.put([:instance, :federating], original)
end end
test "it shows MRF transparency data if enabled", %{conn: conn} do test "it shows MRF transparency data if enabled", %{conn: conn} do

View file

@ -17,7 +17,8 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do
key: "_test", key: "_test",
signing_salt: "cooldude" signing_salt: "cooldude"
] ]
clear_config_all([:instance, :account_activation_required])
clear_config([:instance, :account_activation_required])
describe "in OAuth consumer mode, " do describe "in OAuth consumer mode, " do
setup do setup do

View file

@ -96,6 +96,32 @@ test "GET /api/v1/pleroma/statuses/:id/reactions", %{conn: conn} do
result result
end end
test "GET /api/v1/pleroma/statuses/:id/reactions/:emoji", %{conn: conn} do
user = insert(:user)
other_user = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{"status" => "#cofe"})
result =
conn
|> get("/api/v1/pleroma/statuses/#{activity.id}/reactions/🎅")
|> json_response(200)
assert result == []
{:ok, _, _} = CommonAPI.react_with_emoji(activity.id, other_user, "🎅")
{:ok, _, _} = CommonAPI.react_with_emoji(activity.id, other_user, "")
result =
conn
|> get("/api/v1/pleroma/statuses/#{activity.id}/reactions/🎅")
|> json_response(200)
[%{"name" => "🎅", "count" => 1, "accounts" => [represented_user], "me" => false}] = result
assert represented_user["id"] == other_user.id
end
test "/api/v1/pleroma/conversations/:id" do test "/api/v1/pleroma/conversations/:id" do
user = insert(:user) user = insert(:user)
%{user: other_user, conn: conn} = oauth_access(["read:statuses"]) %{user: other_user, conn: conn} = oauth_access(["read:statuses"])

View file

@ -4,7 +4,8 @@
defmodule Pleroma.Web.FederatingPlugTest do defmodule Pleroma.Web.FederatingPlugTest do
use Pleroma.Web.ConnCase use Pleroma.Web.ConnCase
clear_config_all([:instance, :federating])
clear_config([:instance, :federating])
test "returns and halt the conn when federating is disabled" do test "returns and halt the conn when federating is disabled" do
Pleroma.Config.put([:instance, :federating], false) Pleroma.Config.put([:instance, :federating], false)

View file

@ -20,7 +20,7 @@ defmodule Pleroma.Web.StreamerTest do
@streamer_timeout 150 @streamer_timeout 150
@streamer_start_wait 10 @streamer_start_wait 10
clear_config_all([:instance, :skip_thread_containment]) clear_config([:instance, :skip_thread_containment])
describe "user streams" do describe "user streams" do
setup do setup do

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.TwitterAPI.RemoteFollowControllerTest do defmodule Pleroma.Web.TwitterAPI.RemoteFollowControllerTest do
@ -92,15 +92,13 @@ test "follows user", %{conn: conn} do
user = insert(:user) user = insert(:user)
user2 = insert(:user) user2 = insert(:user)
response = conn =
conn conn
|> assign(:user, user) |> assign(:user, user)
|> assign(:token, insert(:oauth_token, user: user, scopes: ["write:follows"])) |> assign(:token, insert(:oauth_token, user: user, scopes: ["write:follows"]))
|> post(remote_follow_path(conn, :do_follow), %{"user" => %{"id" => user2.id}}) |> post(remote_follow_path(conn, :do_follow), %{"user" => %{"id" => user2.id}})
|> response(200)
assert response =~ "Account followed!" assert redirected_to(conn) == "/users/#{user2.id}"
assert user2.follower_address in User.following(user)
end end
test "returns error when user is deactivated", %{conn: conn} do test "returns error when user is deactivated", %{conn: conn} do
@ -149,14 +147,13 @@ test "returns success result when user already in followers", %{conn: conn} do
user2 = insert(:user) user2 = insert(:user)
{:ok, _, _, _} = CommonAPI.follow(user, user2) {:ok, _, _, _} = CommonAPI.follow(user, user2)
response = conn =
conn conn
|> assign(:user, refresh_record(user)) |> assign(:user, refresh_record(user))
|> assign(:token, insert(:oauth_token, user: user, scopes: ["write:follows"])) |> assign(:token, insert(:oauth_token, user: user, scopes: ["write:follows"]))
|> post(remote_follow_path(conn, :do_follow), %{"user" => %{"id" => user2.id}}) |> post(remote_follow_path(conn, :do_follow), %{"user" => %{"id" => user2.id}})
|> response(200)
assert response =~ "Account followed!" assert redirected_to(conn) == "/users/#{user2.id}"
end end
end end
@ -165,14 +162,13 @@ test "follows", %{conn: conn} do
user = insert(:user) user = insert(:user)
user2 = insert(:user) user2 = insert(:user)
response = conn =
conn conn
|> post(remote_follow_path(conn, :do_follow), %{ |> post(remote_follow_path(conn, :do_follow), %{
"authorization" => %{"name" => user.nickname, "password" => "test", "id" => user2.id} "authorization" => %{"name" => user.nickname, "password" => "test", "id" => user2.id}
}) })
|> response(200)
assert response =~ "Account followed!" assert redirected_to(conn) == "/users/#{user2.id}"
assert user2.follower_address in User.following(user) assert user2.follower_address in User.following(user)
end end

View file

@ -117,15 +117,8 @@ test "it registers a new user and parses mentions in the bio" do
end end
describe "register with one time token" do describe "register with one time token" do
setup do clear_config([:instance, :registrations_open]) do
setting = Pleroma.Config.get([:instance, :registrations_open])
if setting do
Pleroma.Config.put([:instance, :registrations_open], false) Pleroma.Config.put([:instance, :registrations_open], false)
on_exit(fn -> Pleroma.Config.put([:instance, :registrations_open], setting) end)
end
:ok
end end
test "returns user on success" do test "returns user on success" do
@ -191,14 +184,11 @@ test "returns error on expired token" do
end end
describe "registers with date limited token" do describe "registers with date limited token" do
setup do clear_config([:instance, :registrations_open]) do
setting = Pleroma.Config.get([:instance, :registrations_open])
if setting do
Pleroma.Config.put([:instance, :registrations_open], false) Pleroma.Config.put([:instance, :registrations_open], false)
on_exit(fn -> Pleroma.Config.put([:instance, :registrations_open], setting) end)
end end
setup do
data = %{ data = %{
"nickname" => "vinny", "nickname" => "vinny",
"email" => "pasta@pizza.vs", "email" => "pasta@pizza.vs",
@ -256,15 +246,8 @@ test "returns an error on overdue date", %{data: data} do
end end
describe "registers with reusable token" do describe "registers with reusable token" do
setup do clear_config([:instance, :registrations_open]) do
setting = Pleroma.Config.get([:instance, :registrations_open])
if setting do
Pleroma.Config.put([:instance, :registrations_open], false) Pleroma.Config.put([:instance, :registrations_open], false)
on_exit(fn -> Pleroma.Config.put([:instance, :registrations_open], setting) end)
end
:ok
end end
test "returns user on success, after him registration fails" do test "returns user on success, after him registration fails" do
@ -309,15 +292,8 @@ test "returns user on success, after him registration fails" do
end end
describe "registers with reusable date limited token" do describe "registers with reusable date limited token" do
setup do clear_config([:instance, :registrations_open]) do
setting = Pleroma.Config.get([:instance, :registrations_open])
if setting do
Pleroma.Config.put([:instance, :registrations_open], false) Pleroma.Config.put([:instance, :registrations_open], false)
on_exit(fn -> Pleroma.Config.put([:instance, :registrations_open], setting) end)
end
:ok
end end
test "returns user on success" do test "returns user on success" do

View file

@ -19,7 +19,6 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do
clear_config([:instance]) clear_config([:instance])
clear_config([:frontend_configurations, :pleroma_fe]) clear_config([:frontend_configurations, :pleroma_fe])
clear_config([:user, :deny_follow_blocked])
describe "POST /api/pleroma/follow_import" do describe "POST /api/pleroma/follow_import" do
setup do: oauth_access(["follow"]) setup do: oauth_access(["follow"])

View file

@ -0,0 +1,32 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Workers.Cron.NewUsersDigestWorkerTest do
use Pleroma.DataCase
import Pleroma.Factory
alias Pleroma.Tests.ObanHelpers
alias Pleroma.Web.CommonAPI
alias Pleroma.Workers.Cron.NewUsersDigestWorker
test "it sends new users digest emails" do
yesterday = NaiveDateTime.utc_now() |> Timex.shift(days: -1)
admin = insert(:user, %{is_admin: true})
user = insert(:user, %{inserted_at: yesterday})
user2 = insert(:user, %{inserted_at: yesterday})
CommonAPI.post(user, %{"status" => "cofe"})
NewUsersDigestWorker.perform(nil, nil)
ObanHelpers.perform_all()
assert_received {:email, email}
assert email.to == [{admin.name, admin.email}]
assert email.subject == "#{Pleroma.Config.get([:instance, :name])} New Users"
refute email.html_body =~ admin.nickname
assert email.html_body =~ user.nickname
assert email.html_body =~ user2.nickname
assert email.html_body =~ "cofe"
end
end