- Mix tasks for controlling user account confirmation status in bulk (`mix pleroma.user confirm_all` and `mix pleroma.user unconfirm_all`)
- Mix task for sending confirmation emails to all unconfirmed users (`mix pleroma.email send_confirmation_mails`)
- Mix task option for force-unfollowing relays
+- Account backup
### Changed
+ - **Breaking** Requires `libmagic` (or `file`) to guess file types.
- **Breaking:** Pleroma Admin API: emoji packs and files routes changed.
- **Breaking:** Sensitive/NSFW statuses no longer disable link previews.
- Search: Users are now findable by their urls.
queues: [
activity_expiration: 10,
token_expiration: 5,
+ backup: 1,
federator_incoming: 50,
federator_outgoing: 50,
ingestion_queue: 50,
config :pleroma, Pleroma.Workers.PurgeExpiredActivity, enabled: true, min_lifetime: 600
- config :pleroma, Pleroma.Plugs.RemoteIp, enabled: true
+ config :pleroma, Pleroma.Web.Plugs.RemoteIp,
+ enabled: true,
+ headers: ["x-forwarded-for"],
+ proxies: [],
+ reserved: [
+ "127.0.0.0/8",
+ "::1/128",
+ "fc00::/7",
+ "10.0.0.0/8",
+ "172.16.0.0/12",
+ "192.168.0.0/16"
+ ]
config :pleroma, :static_fe, enabled: false
timeout: 300_000
]
+ config :pleroma, :majic_pool, size: 2
+
private_instance? = :if_instance_is_private
config :pleroma, :restrict_unauthenticated,
config :pleroma, Pleroma.Web.Auth.Authenticator, Pleroma.Web.Auth.PleromaAuthenticator
+config :pleroma, Pleroma.Backup,
+ purge_after_days: 30,
+ limit_days: 7,
+ dir: nil
+
# Import environment specific config. This must remain at the bottom
# of this file so it overrides the configuration defined above.
import_config "#{Mix.env()}.exs"
description: "Activity expiration queue",
suggestions: [10]
},
+ %{
+ key: :backup,
+ type: :integer,
+ description: "Backup queue",
+ suggestions: [1]
+ },
%{
key: :attachments_cleanup,
type: :integer,
},
%{
group: :pleroma,
- key: Pleroma.Plugs.RemoteIp,
+ key: Pleroma.Web.Plugs.RemoteIp,
type: :group,
description: """
- `Pleroma.Plugs.RemoteIp` is a shim to call [`RemoteIp`](https://git.pleroma.social/pleroma/remote_ip) but with runtime configuration.
+ `Pleroma.Web.Plugs.RemoteIp` is a shim to call [`RemoteIp`](https://git.pleroma.social/pleroma/remote_ip) but with runtime configuration.
**If your instance is not behind at least one reverse proxy, you should not enable this plug.**
""",
children: [
%{
key: :headers,
type: {:list, :string},
- description:
- "A list of strings naming the `req_headers` to use when deriving the `remote_ip`. Order does not matter. Default: `~w[forwarded x-forwarded-for x-client-ip x-real-ip]`."
+ description: """
+ A list of strings naming the HTTP headers to use when deriving the true client IP. Default: `["x-forwarded-for"]`.
+ """
},
%{
key: :proxies,
type: {:list, :string},
description:
- "A list of strings in [CIDR](https://en.wikipedia.org/wiki/CIDR) notation specifying the IPs of known proxies. Default: `[]`."
+ "A list of upstream proxy IP subnets in CIDR notation from which we will parse the content of `headers`. Defaults to `[]`. IPv4 entries without a bitmask will be assumed to be /32 and IPv6 /128."
},
%{
key: :reserved,
type: {:list, :string},
- description:
- "Defaults to [localhost](https://en.wikipedia.org/wiki/Localhost) and [private network](https://en.wikipedia.org/wiki/Private_network)."
+ description: """
+ A list of reserved IP subnets in CIDR notation which should be ignored if found in `headers`. Defaults to `["127.0.0.0/8", "::1/128", "fc00::/7", "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"]`
+ """
}
]
},
}
]
},
+ %{
+ group: :pleroma,
+ key: :majic_pool,
+ type: :group,
+ description: "Majic/libmagic configuration",
+ children: [
+ %{
+ key: :size,
+ type: :integer,
+ description: "Number of majic workers to start.",
+ suggestions: [2]
+ }
+ ]
++ },
+ %{
+ group: :pleroma,
+ key: Pleroma.Backup,
+ type: :group,
+ description: "Account Backup",
+ children: [
+ %{
+ key: :purge_after_days,
+ type: :integer,
+ description: "Remove backup achives after N days",
+ suggestions: [30]
+ },
+ %{
+ key: :limit_days,
+ type: :integer,
+ description: "Limit user to export not more often than once per N days",
+ suggestions: [7]
+ }
+ ]
}
]
* `Pleroma.Web.ActivityPub.MRF.MentionPolicy`: Drops posts mentioning configurable users. (See [`:mrf_mention`](#mrf_mention)).
* `Pleroma.Web.ActivityPub.MRF.VocabularyPolicy`: Restricts activities to a configured set of vocabulary. (See [`:mrf_vocabulary`](#mrf_vocabulary)).
* `Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy`: Rejects or delists posts based on their age when received. (See [`:mrf_object_age`](#mrf_object_age)).
- * `Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy`: Sets a default expiration on all posts made by users of the local instance. Requires `Pleroma.ActivityExpiration` to be enabled for processing the scheduled delections.
+ * `Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy`: Sets a default expiration on all posts made by users of the local instance. Requires `Pleroma.Workers.PurgeExpiredActivity` to be enabled for processing the scheduled delections.
* `Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicy`: Makes all bot posts to disappear from public timelines.
* `transparency`: Make the content of your Message Rewrite Facility settings public (via nodeinfo).
* `transparency_exclusions`: Exclude specific instance names from MRF transparency. The use of the exclusions feature will be disclosed in nodeinfo as a boolean value.
* `total_user_limit`: the number of scheduled activities a user is allowed to create in total (Default: `300`)
* `enabled`: whether scheduled activities are sent to the job queue to be executed
- ## Pleroma.ActivityExpiration
-
- Enables the worker which processes posts scheduled for deletion. Pinned posts are exempt from expiration.
-
- * `enabled`: whether expired activities will be sent to the job queue to be deleted
-
## FedSockets
FedSockets is an experimental feature allowing for Pleroma backends to federate using a persistant websocket connection as opposed to making each federation a seperate http connection. This feature is currently off by default. It is configurable throught he following options.
* ``referrer_policy``: The referrer policy to use, either `"same-origin"` or `"no-referrer"`.
* ``report_uri``: Adds the specified url to `report-uri` and `report-to` group in CSP header.
- ### Pleroma.Plugs.RemoteIp
+ ### Pleroma.Web.Plugs.RemoteIp
!!! warning
If your instance is not behind at least one reverse proxy, you should not enable this plug.
- `Pleroma.Plugs.RemoteIp` is a shim to call [`RemoteIp`](https://git.pleroma.social/pleroma/remote_ip) but with runtime configuration.
+ `Pleroma.Web.Plugs.RemoteIp` is a shim to call [`RemoteIp`](https://git.pleroma.social/pleroma/remote_ip) but with runtime configuration.
Available options:
* `enabled` - Enable/disable the plug. Defaults to `false`.
- * `headers` - A list of strings naming the `req_headers` to use when deriving the `remote_ip`. Order does not matter. Defaults to `["x-forwarded-for"]`.
- * `proxies` - A list of strings in [CIDR](https://en.wikipedia.org/wiki/CIDR) notation specifying the IPs of known proxies. Defaults to `[]`.
- * `reserved` - Defaults to [localhost](https://en.wikipedia.org/wiki/Localhost) and [private network](https://en.wikipedia.org/wiki/Private_network).
+ * `headers` - A list of strings naming the HTTP headers to use when deriving the true client IP address. Defaults to `["x-forwarded-for"]`.
+ * `proxies` - A list of upstream proxy IP subnets in CIDR notation from which we will parse the content of `headers`. Defaults to `[]`. IPv4 entries without a bitmask will be assumed to be /32 and IPv6 /128.
+ * `reserved` - A list of reserved IP subnets in CIDR notation which should be ignored if found in `headers`. Defaults to `["127.0.0.0/8", "::1/128", "fc00::/7", "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"]`.
### :rate_limit
!!! note
- If your instance is behind a reverse proxy ensure [`Pleroma.Plugs.RemoteIp`](#pleroma-plugs-remoteip) is enabled (it is enabled by default).
+ If your instance is behind a reverse proxy ensure [`Pleroma.Web.Plugs.RemoteIp`](#pleroma-plugs-remoteip) is enabled (it is enabled by default).
A keyword list of rate limiters where a key is a limiter name and value is the limiter configuration. The basic configuration is a tuple where:
* `enabled`: Allow/disallow displaying and getting instances favicons
+## Account Backup
+
+!!! note
+ Requires enabled email
+
+* `:purge_after_days` an integer, remove backup achives after N days.
+* `:limit_days` an integer, limit user to export not more often than once per N days.
+* `:dir` a string with a path to backup temporary directory or `nil` to let Pleroma choose temporary directory in the following order:
+ 1. the directory named by the TMPDIR environment variable
+ 2. the directory named by the TEMP environment variable
+ 3. the directory named by the TMP environment variable
+ 4. C:\TMP on Windows or /tmp on Unix-like operating systems
+ 5. as a last resort, the current working directory
+
## Frontend management
Frontends in Pleroma are swappable - you can specify which one to use here.
+ # Pleroma: A lightweight social networking server
+ # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+ # SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.ModerationLog do
use Ecto.Schema
"@#{actor_nickname} deleted chat message ##{subject_id}"
end
+ def get_log_entry_message(%ModerationLog{
+ data: %{
+ "actor" => %{"nickname" => actor_nickname},
+ "action" => "create_backup",
+ "subject" => %{"nickname" => user_nickname}
+ }
+ }) do
+ "@#{actor_nickname} requested account backup for @#{user_nickname}"
+ end
+
defp nicknames_to_string(nicknames) do
nicknames
|> Enum.map(&"@#{&1}")
alias Pleroma.Config
alias Pleroma.MFA
alias Pleroma.ModerationLog
- alias Pleroma.Plugs.OAuthScopesPlug
alias Pleroma.Stats
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.AdminAPI.ModerationLogView
alias Pleroma.Web.AdminAPI.Search
alias Pleroma.Web.Endpoint
+ alias Pleroma.Web.Plugs.OAuthScopesPlug
alias Pleroma.Web.Router
+ require Logger
+
@users_page_size 50
plug(
OAuthScopesPlug,
%{scopes: ["read:accounts"], admin: true}
- when action in [:list_users, :user_show, :right_get, :show_user_credentials]
+ when action in [:list_users, :user_show, :right_get, :show_user_credentials, :create_backup]
)
plug(
json(conn, %{"status_visibility" => counters})
end
+ def create_backup(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do
+ with %User{} = user <- User.get_by_nickname(nickname),
+ {:ok, _} <- Pleroma.Backup.create(user, admin.id) do
+ ModerationLog.insert_log(%{actor: admin, subject: user, action: "create_backup"})
+
+ json(conn, "")
+ end
+ end
+
defp page_params(params) do
{get_page(params["page"]), get_page_size(params["page_size"])}
end
pipeline :oauth do
plug(:fetch_session)
- plug(Pleroma.Plugs.OAuthPlug)
- plug(Pleroma.Plugs.UserEnabledPlug)
+ plug(Pleroma.Web.Plugs.OAuthPlug)
+ plug(Pleroma.Web.Plugs.UserEnabledPlug)
end
pipeline :expect_authentication do
- plug(Pleroma.Plugs.ExpectAuthenticatedCheckPlug)
+ plug(Pleroma.Web.Plugs.ExpectAuthenticatedCheckPlug)
end
pipeline :expect_public_instance_or_authentication do
- plug(Pleroma.Plugs.ExpectPublicOrAuthenticatedCheckPlug)
+ plug(Pleroma.Web.Plugs.ExpectPublicOrAuthenticatedCheckPlug)
end
pipeline :authenticate do
- plug(Pleroma.Plugs.OAuthPlug)
- plug(Pleroma.Plugs.BasicAuthDecoderPlug)
- plug(Pleroma.Plugs.UserFetcherPlug)
- plug(Pleroma.Plugs.SessionAuthenticationPlug)
- plug(Pleroma.Plugs.LegacyAuthenticationPlug)
- plug(Pleroma.Plugs.AuthenticationPlug)
+ plug(Pleroma.Web.Plugs.OAuthPlug)
+ plug(Pleroma.Web.Plugs.BasicAuthDecoderPlug)
+ plug(Pleroma.Web.Plugs.UserFetcherPlug)
+ plug(Pleroma.Web.Plugs.SessionAuthenticationPlug)
+ plug(Pleroma.Web.Plugs.LegacyAuthenticationPlug)
+ plug(Pleroma.Web.Plugs.AuthenticationPlug)
end
pipeline :after_auth do
- plug(Pleroma.Plugs.UserEnabledPlug)
- plug(Pleroma.Plugs.SetUserSessionIdPlug)
- plug(Pleroma.Plugs.EnsureUserKeyPlug)
+ plug(Pleroma.Web.Plugs.UserEnabledPlug)
+ plug(Pleroma.Web.Plugs.SetUserSessionIdPlug)
+ plug(Pleroma.Web.Plugs.EnsureUserKeyPlug)
end
pipeline :base_api do
plug(:expect_public_instance_or_authentication)
plug(:base_api)
plug(:after_auth)
- plug(Pleroma.Plugs.IdempotencyPlug)
+ plug(Pleroma.Web.Plugs.IdempotencyPlug)
end
pipeline :authenticated_api do
plug(:expect_authentication)
plug(:base_api)
plug(:after_auth)
- plug(Pleroma.Plugs.EnsureAuthenticatedPlug)
- plug(Pleroma.Plugs.IdempotencyPlug)
+ plug(Pleroma.Web.Plugs.EnsureAuthenticatedPlug)
+ plug(Pleroma.Web.Plugs.IdempotencyPlug)
end
pipeline :admin_api do
plug(:expect_authentication)
plug(:base_api)
- plug(Pleroma.Plugs.AdminSecretAuthenticationPlug)
+ plug(Pleroma.Web.Plugs.AdminSecretAuthenticationPlug)
plug(:after_auth)
- plug(Pleroma.Plugs.EnsureAuthenticatedPlug)
- plug(Pleroma.Plugs.UserIsAdminPlug)
- plug(Pleroma.Plugs.IdempotencyPlug)
+ plug(Pleroma.Web.Plugs.EnsureAuthenticatedPlug)
+ plug(Pleroma.Web.Plugs.UserIsAdminPlug)
+ plug(Pleroma.Web.Plugs.IdempotencyPlug)
end
pipeline :mastodon_html do
pipeline :pleroma_html do
plug(:browser)
plug(:authenticate)
- plug(Pleroma.Plugs.EnsureUserKeyPlug)
+ plug(Pleroma.Web.Plugs.EnsureUserKeyPlug)
end
pipeline :well_known do
scope "/api/pleroma/admin", Pleroma.Web.AdminAPI do
pipe_through(:admin_api)
+ post("/backups", AdminAPIController, :create_backup)
+
post("/users/follow", AdminAPIController, :user_follow)
post("/users/unfollow", AdminAPIController, :user_unfollow)
put("/mascot", MascotController, :update)
post("/scrobble", ScrobbleController, :create)
+
+ get("/backups", BackupController, :index)
+ post("/backups", BackupController, :create)
end
scope [] do
pipeline :ostatus do
plug(:accepts, ["html", "xml", "rss", "atom", "activity+json", "json"])
- plug(Pleroma.Plugs.StaticFEPlug)
+ plug(Pleroma.Web.Plugs.StaticFEPlug)
end
pipeline :oembed do
get("/check_password", MongooseIMController, :check_password)
end
- scope "/", Fallback do
+ scope "/", Pleroma.Web.Fallback do
get("/registration/:token", RedirectController, :registration_page)
get("/:maybe_nickname_or_id", RedirectController, :redirector_with_meta)
get("/api*path", RedirectController, :api_not_implemented)
response["status_visibility"]
end
end
+
+ describe "/api/pleroma/backups" do
+ test "it creates a backup", %{conn: conn} do
+ admin = %{id: admin_id, nickname: admin_nickname} = insert(:user, is_admin: true)
+ token = insert(:oauth_admin_token, user: admin)
+ user = %{id: user_id, nickname: user_nickname} = insert(:user)
+
+ assert "" ==
+ conn
+ |> assign(:user, admin)
+ |> assign(:token, token)
+ |> post("/api/pleroma/admin/backups", %{nickname: user.nickname})
+ |> json_response(200)
+
+ assert [backup] = Repo.all(Pleroma.Backup)
+
+ ObanHelpers.perform_all()
+
+ email = Pleroma.Emails.UserEmail.backup_is_ready_email(backup, admin.id)
+
+ assert String.contains?(email.html_body, "Admin @#{admin.nickname} requested a full backup")
+ assert_email_sent(to: {user.name, user.email}, html_body: email.html_body)
+
+ log_message = "@#{admin_nickname} requested account backup for @#{user_nickname}"
+
+ assert [
+ %{
+ data: %{
+ "action" => "create_backup",
+ "actor" => %{
+ "id" => ^admin_id,
+ "nickname" => ^admin_nickname
+ },
+ "message" => ^log_message,
+ "subject" => %{
+ "id" => ^user_id,
+ "nickname" => ^user_nickname
+ }
+ }
+ }
+ ] = Pleroma.ModerationLog |> Repo.all()
+ end
+
+ test "it doesn't limit admins", %{conn: conn} do
+ admin = insert(:user, is_admin: true)
+ token = insert(:oauth_admin_token, user: admin)
+ user = insert(:user)
+
+ assert "" ==
+ conn
+ |> assign(:user, admin)
+ |> assign(:token, token)
+ |> post("/api/pleroma/admin/backups", %{nickname: user.nickname})
+ |> json_response(200)
+
+ assert [_backup] = Repo.all(Pleroma.Backup)
+
+ assert "" ==
+ conn
+ |> assign(:user, admin)
+ |> assign(:token, token)
+ |> post("/api/pleroma/admin/backups", %{nickname: user.nickname})
+ |> json_response(200)
+
+ assert Repo.aggregate(Pleroma.Backup, :count) == 2
+ end
+ end
end
# Needed for testing