Merge branch 'develop' into gun

This commit is contained in:
Alexander Strizhakov 2020-03-12 18:31:10 +03:00
commit 39ed608b13
No known key found for this signature in database
GPG key ID: 022896A53AEF1381
34 changed files with 457 additions and 90 deletions

View file

@ -38,6 +38,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Rate limiter is now disabled for localhost/socket (unless remoteip plug is enabled) - Rate limiter is now disabled for localhost/socket (unless remoteip plug is enabled)
- Logger: default log level changed from `warn` to `info`. - Logger: default log level changed from `warn` to `info`.
- Config mix task `migrate_to_db` truncates `config` table before migrating the config file. - Config mix task `migrate_to_db` truncates `config` table before migrating the config file.
- Allow account registration without an email
- Default to `prepare: :unnamed` in the database configuration. - Default to `prepare: :unnamed` in the database configuration.
- Instance stats are now loaded on startup instead of being empty until next hourly job. - Instance stats are now loaded on startup instead of being empty until next hourly job.
<details> <details>

View file

@ -10,11 +10,11 @@
Replaces embedded objects with references to them in the `objects` table. Only needs to be ran once if the instance was created before Pleroma 1.0.5. The reason why this is not a migration is because it could significantly increase the database size after being ran, however after this `VACUUM FULL` will be able to reclaim about 20% (really depends on what is in the database, your mileage may vary) of the db size before the migration. Replaces embedded objects with references to them in the `objects` table. Only needs to be ran once if the instance was created before Pleroma 1.0.5. The reason why this is not a migration is because it could significantly increase the database size after being ran, however after this `VACUUM FULL` will be able to reclaim about 20% (really depends on what is in the database, your mileage may vary) of the db size before the migration.
```sh tab="OTP" ```sh tab="OTP"
./bin/pleroma_ctl database remove_embedded_objects [<options>] ./bin/pleroma_ctl database remove_embedded_objects [option ...]
``` ```
```sh tab="From Source" ```sh tab="From Source"
mix pleroma.database remove_embedded_objects [<options>] mix pleroma.database remove_embedded_objects [option ...]
``` ```
### Options ### Options
@ -28,11 +28,11 @@ This will prune remote posts older than 90 days (configurable with [`config :ple
The disk space will only be reclaimed after `VACUUM FULL`. You may run out of disk space during the execution of the task or vacuuming if you don't have about 1/3rds of the database size free. The disk space will only be reclaimed after `VACUUM FULL`. You may run out of disk space during the execution of the task or vacuuming if you don't have about 1/3rds of the database size free.
```sh tab="OTP" ```sh tab="OTP"
./bin/pleroma_ctl database prune_objects [<options>] ./bin/pleroma_ctl database prune_objects [option ...]
``` ```
```sh tab="From Source" ```sh tab="From Source"
mix pleroma.database prune_objects [<options>] mix pleroma.database prune_objects [option ...]
``` ```
### Options ### Options

View file

@ -5,11 +5,11 @@
## Send digest email since given date (user registration date by default) ignoring user activity status. ## Send digest email since given date (user registration date by default) ignoring user activity status.
```sh tab="OTP" ```sh tab="OTP"
./bin/pleroma_ctl digest test <nickname> [<since_date>] ./bin/pleroma_ctl digest test <nickname> [since_date]
``` ```
```sh tab="From Source" ```sh tab="From Source"
mix pleroma.digest test <nickname> [<since_date>] mix pleroma.digest test <nickname> [since_date]
``` ```

View file

@ -5,11 +5,11 @@
## Lists emoji packs and metadata specified in the manifest ## Lists emoji packs and metadata specified in the manifest
```sh tab="OTP" ```sh tab="OTP"
./bin/pleroma_ctl emoji ls-packs [<options>] ./bin/pleroma_ctl emoji ls-packs [option ...]
``` ```
```sh tab="From Source" ```sh tab="From Source"
mix pleroma.emoji ls-packs [<options>] mix pleroma.emoji ls-packs [option ...]
``` ```
@ -19,11 +19,11 @@ mix pleroma.emoji ls-packs [<options>]
## Fetch, verify and install the specified packs from the manifest into `STATIC-DIR/emoji/PACK-NAME` ## Fetch, verify and install the specified packs from the manifest into `STATIC-DIR/emoji/PACK-NAME`
```sh tab="OTP" ```sh tab="OTP"
./bin/pleroma_ctl emoji get-packs [<options>] <packs> ./bin/pleroma_ctl emoji get-packs [option ...] <pack ...>
``` ```
```sh tab="From Source" ```sh tab="From Source"
mix pleroma.emoji get-packs [<options>] <packs> mix pleroma.emoji get-packs [option ...] <pack ...>
``` ```
### Options ### Options

View file

@ -4,11 +4,11 @@
## Generate a new configuration file ## Generate a new configuration file
```sh tab="OTP" ```sh tab="OTP"
./bin/pleroma_ctl instance gen [<options>] ./bin/pleroma_ctl instance gen [option ...]
``` ```
```sh tab="From Source" ```sh tab="From Source"
mix pleroma.instance gen [<options>] mix pleroma.instance gen [option ...]
``` ```

View file

@ -4,11 +4,11 @@
## Migrate uploads from local to remote storage ## Migrate uploads from local to remote storage
```sh tab="OTP" ```sh tab="OTP"
./bin/pleroma_ctl uploads migrate_local <target_uploader> [<options>] ./bin/pleroma_ctl uploads migrate_local <target_uploader> [option ...]
``` ```
```sh tab="From Source" ```sh tab="From Source"
mix pleroma.uploads migrate_local <target_uploader> [<options>] mix pleroma.uploads migrate_local <target_uploader> [option ...]
``` ```
### Options ### Options

View file

@ -5,11 +5,11 @@
## Create a user ## Create a user
```sh tab="OTP" ```sh tab="OTP"
./bin/pleroma_ctl user new <email> [<options>] ./bin/pleroma_ctl user new <nickname> <email> [option ...]
``` ```
```sh tab="From Source" ```sh tab="From Source"
mix pleroma.user new <email> [<options>] mix pleroma.user new <nickname> <email> [option ...]
``` ```
@ -33,11 +33,11 @@ mix pleroma.user list
## Generate an invite link ## Generate an invite link
```sh tab="OTP" ```sh tab="OTP"
./bin/pleroma_ctl user invite [<options>] ./bin/pleroma_ctl user invite [option ...]
``` ```
```sh tab="From Source" ```sh tab="From Source"
mix pleroma.user invite [<options>] mix pleroma.user invite [option ...]
``` ```
@ -137,11 +137,11 @@ mix pleroma.user reset_password <nickname>
## Set the value of the given user's settings ## Set the value of the given user's settings
```sh tab="OTP" ```sh tab="OTP"
./bin/pleroma_ctl user set <nickname> [<options>] ./bin/pleroma_ctl user set <nickname> [option ...]
``` ```
```sh tab="From Source" ```sh tab="From Source"
mix pleroma.user set <nickname> [<options>] mix pleroma.user set <nickname> [option ...]
``` ```
### Options ### Options

View file

@ -18,9 +18,8 @@
6. Run `sudo -Hu postgres pg_restore -d <pleroma_db> -v -1 </path/to/backup_location/pleroma.pgdump>` 6. Run `sudo -Hu postgres pg_restore -d <pleroma_db> -v -1 </path/to/backup_location/pleroma.pgdump>`
7. If you installed a newer Pleroma version, you should run `mix ecto.migrate`[^1]. This task performs database migrations, if there were any. 7. If you installed a newer Pleroma version, you should run `mix ecto.migrate`[^1]. This task performs database migrations, if there were any.
8. Restart the Pleroma service. 8. Restart the Pleroma service.
9. After you've restarted Pleroma, you will notice that postgres will take up more cpu resources than usual. A lot in fact. To fix this you must do a VACUUM ANLAYZE. This can also be done while the instance is still running like so: 9. Run `sudo -Hu postgres vacuumdb --all --analyze-in-stages`. This will quickly generate the statistics so that postgres can properly plan queries.
$ sudo -u postgres psql pleroma_database_name
pleroma=# VACUUM ANALYZE;
[^1]: Prefix with `MIX_ENV=prod` to run it using the production config file. [^1]: Prefix with `MIX_ENV=prod` to run it using the production config file.
## Remove ## Remove

View file

@ -156,8 +156,8 @@ cp /opt/pleroma/installation/pleroma.nginx /etc/nginx/conf.d/pleroma.conf
``` ```
```sh tab="Debian/Ubuntu" ```sh tab="Debian/Ubuntu"
cp /opt/pleroma/installation/pleroma.nginx /etc/nginx/sites-available/pleroma.nginx cp /opt/pleroma/installation/pleroma.nginx /etc/nginx/sites-available/pleroma.conf
ln -s /etc/nginx/sites-available/pleroma.nginx /etc/nginx/sites-enabled/pleroma.nginx ln -s /etc/nginx/sites-available/pleroma.conf /etc/nginx/sites-enabled/pleroma.conf
``` ```
If your distro does not have either of those you can append `include /etc/nginx/pleroma.conf` to the end of the http section in /etc/nginx/nginx.conf and If your distro does not have either of those you can append `include /etc/nginx/pleroma.conf` to the end of the http section in /etc/nginx/nginx.conf and

View file

@ -28,7 +28,7 @@ def run(_) do
defp do_run(implementation) do defp do_run(implementation) do
start_pleroma() start_pleroma()
with descriptions <- Pleroma.Config.Loader.load("config/description.exs"), with descriptions <- Pleroma.Config.Loader.read("config/description.exs"),
{:ok, file_path} <- {:ok, file_path} <-
Pleroma.Docs.Generator.process( Pleroma.Docs.Generator.process(
implementation, implementation,

View file

@ -35,7 +35,7 @@ def run(["unfollow", target]) do
def run(["list"]) do def run(["list"]) do
start_pleroma() start_pleroma()
with {:ok, list} <- Relay.list() do with {:ok, list} <- Relay.list(true) do
list |> Enum.each(&shell_info(&1)) list |> Enum.each(&shell_info(&1))
else else
{:error, e} -> shell_error("Error while fetching relay subscription list: #{inspect(e)}") {:error, e} -> shell_error("Error while fetching relay subscription list: #{inspect(e)}")

View file

@ -308,6 +308,13 @@ def follow_requests_for_actor(%Pleroma.User{ap_id: ap_id}) do
|> where([a], fragment("? ->> 'state' = 'pending'", a.data)) |> where([a], fragment("? ->> 'state' = 'pending'", a.data))
end end
def following_requests_for_actor(%Pleroma.User{ap_id: ap_id}) do
Queries.by_type("Follow")
|> where([a], fragment("?->>'state' = 'pending'", a.data))
|> where([a], a.actor == ^ap_id)
|> Repo.all()
end
def restrict_deactivated_users(query) do def restrict_deactivated_users(query) do
deactivated_users = deactivated_users =
from(u in User.Query.build(%{deactivated: true}), select: u.ap_id) from(u in User.Query.build(%{deactivated: true}), select: u.ap_id)

View file

@ -35,6 +35,7 @@ def user_agent do
# See http://elixir-lang.org/docs/stable/elixir/Application.html # See http://elixir-lang.org/docs/stable/elixir/Application.html
# for more information on OTP Applications # for more information on OTP Applications
def start(_type, _args) do def start(_type, _args) do
Pleroma.Config.Holder.save_default()
Pleroma.HTML.compile_scrubbers() Pleroma.HTML.compile_scrubbers()
Config.DeprecationWarnings.warn() Config.DeprecationWarnings.warn()
Pleroma.Plugs.HTTPSecurityPlug.warn_if_disabled() Pleroma.Plugs.HTTPSecurityPlug.warn_if_disabled()

View file

@ -3,14 +3,33 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Config.Holder do defmodule Pleroma.Config.Holder do
@config Pleroma.Config.Loader.load_and_merge() @config Pleroma.Config.Loader.default_config()
@spec config() :: keyword() @spec save_default() :: :ok
def config, do: @config def save_default do
default_config =
if System.get_env("RELEASE_NAME") do
release_config =
[:code.root_dir(), "releases", System.get_env("RELEASE_VSN"), "releases.exs"]
|> Path.join()
|> Pleroma.Config.Loader.read()
@spec config(atom()) :: any() Pleroma.Config.Loader.merge(@config, release_config)
def config(group), do: @config[group] else
@config
end
@spec config(atom(), atom()) :: any() Pleroma.Config.put(:default_config, default_config)
def config(group, key), do: @config[group][key] end
@spec default_config() :: keyword()
def default_config, do: get_default()
@spec default_config(atom()) :: keyword()
def default_config(group), do: Keyword.get(get_default(), group)
@spec default_config(atom(), atom()) :: keyword()
def default_config(group, key), do: get_in(get_default(), [group, key])
defp get_default, do: Pleroma.Config.get(:default_config)
end end

View file

@ -13,32 +13,28 @@ defmodule Pleroma.Config.Loader do
] ]
if Code.ensure_loaded?(Config.Reader) do if Code.ensure_loaded?(Config.Reader) do
@spec load(Path.t()) :: keyword() @reader Config.Reader
def load(path), do: Config.Reader.read!(path)
defp do_merge(conf1, conf2), do: Config.Reader.merge(conf1, conf2) def read(path), do: @reader.read!(path)
else else
# support for Elixir less than 1.9 # support for Elixir less than 1.9
@spec load(Path.t()) :: keyword() @reader Mix.Config
def load(path) do def read(path) do
path path
|> Mix.Config.eval!() |> @reader.eval!()
|> elem(0) |> elem(0)
end end
defp do_merge(conf1, conf2), do: Mix.Config.merge(conf1, conf2)
end end
@spec load_and_merge() :: keyword() @spec read(Path.t()) :: keyword()
def load_and_merge do
all_paths =
if Pleroma.Config.get(:release),
do: ["config/config.exs", "config/releases.exs"],
else: ["config/config.exs"]
all_paths @spec merge(keyword(), keyword()) :: keyword()
|> Enum.map(&load(&1)) def merge(c1, c2), do: @reader.merge(c1, c2)
|> Enum.reduce([], &do_merge(&2, &1))
@spec default_config() :: keyword()
def default_config do
"config/config.exs"
|> read()
|> filter() |> filter()
end end

View file

@ -104,7 +104,7 @@ defp merge_and_update(setting) do
key = ConfigDB.from_string(setting.key) key = ConfigDB.from_string(setting.key)
group = ConfigDB.from_string(setting.group) group = ConfigDB.from_string(setting.group)
default = Config.Holder.config(group, key) default = Config.Holder.default_config(group, key)
value = ConfigDB.from_binary(setting.value) value = ConfigDB.from_binary(setting.value)
merged_value = merged_value =

View file

@ -15,7 +15,7 @@ def process(descriptions) do
end end
def compile do def compile do
with config <- Pleroma.Config.Loader.load("config/description.exs") do with config <- Pleroma.Config.Loader.read("config/description.exs") do
config[:pleroma][:config_description] config[:pleroma][:config_description]
|> Pleroma.Docs.Generator.convert_to_strings() |> Pleroma.Docs.Generator.convert_to_strings()
|> Jason.encode!() |> Jason.encode!()

View file

@ -530,7 +530,14 @@ def register_changeset(struct, params \\ %{}, opts \\ []) do
end end
def maybe_validate_required_email(changeset, true), do: changeset def maybe_validate_required_email(changeset, true), do: changeset
def maybe_validate_required_email(changeset, _), do: validate_required(changeset, [:email])
def maybe_validate_required_email(changeset, _) do
if Pleroma.Config.get([:instance, :account_activation_required]) do
validate_required(changeset, [:email])
else
changeset
end
end
defp put_ap_id(changeset) do defp put_ap_id(changeset) do
ap_id = ap_id(%User{nickname: get_field(changeset, :nickname)}) ap_id = ap_id(%User{nickname: get_field(changeset, :nickname)})

View file

@ -60,15 +60,28 @@ def publish(%Activity{data: %{"type" => "Create"}} = activity) do
def publish(_), do: {:error, "Not implemented"} def publish(_), do: {:error, "Not implemented"}
@spec list() :: {:ok, [String.t()]} | {:error, any()} @spec list(boolean()) :: {:ok, [String.t()]} | {:error, any()}
def list do def list(with_not_accepted \\ false) do
with %User{} = user <- get_actor() do with %User{} = user <- get_actor() do
list = accepted =
user user
|> User.following() |> User.following()
|> Enum.map(fn entry -> URI.parse(entry).host end) |> Enum.map(fn entry -> URI.parse(entry).host end)
|> Enum.uniq() |> Enum.uniq()
list =
if with_not_accepted do
without_accept =
user
|> Pleroma.Activity.following_requests_for_actor()
|> Enum.map(fn a -> URI.parse(a.data["object"]).host <> " (no Accept received)" end)
|> Enum.uniq()
accepted ++ without_accept
else
accepted
end
{:ok, list} {:ok, list}
else else
error -> format_error(error) error -> format_error(error)

View file

@ -834,7 +834,7 @@ def config_show(conn, _params) do
configs = ConfigDB.get_all_as_keyword() configs = ConfigDB.get_all_as_keyword()
merged = merged =
Config.Holder.config() Config.Holder.default_config()
|> ConfigDB.merge(configs) |> ConfigDB.merge(configs)
|> Enum.map(fn {group, values} -> |> Enum.map(fn {group, values} ->
Enum.map(values, fn {key, value} -> Enum.map(values, fn {key, value} ->

View file

@ -591,7 +591,7 @@ def validate_character_limit(full_payload, _attachments) do
limit = Pleroma.Config.get([:instance, :limit]) limit = Pleroma.Config.get([:instance, :limit])
length = String.length(full_payload) length = String.length(full_payload)
if length < limit do if length <= limit do
:ok :ok
else else
{:error, dgettext("errors", "The status is over the character limit")} {:error, dgettext("errors", "The status is over the character limit")}

View file

@ -76,7 +76,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
@doc "POST /api/v1/accounts" @doc "POST /api/v1/accounts"
def create( def create(
%{assigns: %{app: app}} = conn, %{assigns: %{app: app}} = conn,
%{"username" => nickname, "email" => _, "password" => _, "agreement" => true} = params %{"username" => nickname, "password" => _, "agreement" => true} = params
) do ) do
params = params =
params params
@ -93,7 +93,8 @@ def create(
|> Map.put("bio", params["bio"] || "") |> Map.put("bio", params["bio"] || "")
|> Map.put("confirm", params["password"]) |> Map.put("confirm", params["password"])
with {:ok, user} <- TwitterAPI.register_user(params, need_confirmation: true), with :ok <- validate_email_param(params),
{:ok, user} <- TwitterAPI.register_user(params, need_confirmation: true),
{:ok, token} <- Token.create_token(app, user, %{scopes: app.scopes}) do {:ok, token} <- Token.create_token(app, user, %{scopes: app.scopes}) do
json(conn, %{ json(conn, %{
token_type: "Bearer", token_type: "Bearer",
@ -114,6 +115,15 @@ def create(conn, _) do
render_error(conn, :forbidden, "Invalid credentials") render_error(conn, :forbidden, "Invalid credentials")
end end
defp validate_email_param(%{"email" => _}), do: :ok
defp validate_email_param(_) do
case Pleroma.Config.get([:instance, :account_activation_required]) do
true -> {:error, %{"error" => "Missing parameters"}}
_ -> :ok
end
end
@doc "GET /api/v1/accounts/verify_credentials" @doc "GET /api/v1/accounts/verify_credentials"
def verify_credentials(%{assigns: %{user: user}} = conn, _) do def verify_credentials(%{assigns: %{user: user}} = conn, _) do
chat_token = Phoenix.Token.sign(conn, "user socket", user.id) chat_token = Phoenix.Token.sign(conn, "user socket", user.id)

View file

@ -0,0 +1,176 @@
body {
background-color: #282c37;
font-family: sans-serif;
color: white;
}
main {
margin: 50px auto;
max-width: 960px;
padding: 40px;
background-color: #313543;
border-radius: 4px;
}
header {
margin: 50px auto;
max-width: 960px;
padding: 40px;
background-color: #313543;
border-radius: 4px;
}
.activity {
border-radius: 4px;
padding: 1em;
padding-bottom: 2em;
margin-bottom: 1em;
}
.avatar {
cursor: pointer;
}
.avatar img {
float: left;
border-radius: 4px;
margin-right: 4px;
}
.activity-content img, video, audio {
padding: 1em;
max-width: 800px;
max-height: 800px;
}
#selected {
background-color: #1b2735;
}
.counts dt, .counts dd {
float: left;
margin-left: 1em;
}
a {
color: white;
}
.h-card {
min-height: 48px;
margin-bottom: 8px;
}
header a, .h-card a {
text-decoration: none;
}
header a:hover, .h-card a:hover {
text-decoration: underline;
}
.display-name {
padding-top: 4px;
display: block;
text-overflow: ellipsis;
overflow: hidden;
color: white;
}
/* keep emoji from being hilariously huge */
.display-name img {
max-height: 1em;
}
.display-name .nickname {
padding-top: 4px;
display: block;
}
.nickname:hover {
text-decoration: none;
}
.pull-right {
float: right;
}
.collapse {
margin: 0;
width: auto;
}
h1 {
margin: 0;
}
h2 {
color: #9baec8;
font-weight: normal;
font-size: 20px;
margin-bottom: 40px;
}
form {
width: 100%;
}
input {
box-sizing: border-box;
width: 100%;
padding: 10px;
margin-top: 20px;
background-color: rgba(0,0,0,.1);
color: white;
border: 0;
border-bottom: 2px solid #9baec8;
font-size: 14px;
}
input:focus {
border-bottom: 2px solid #4b8ed8;
}
input[type="checkbox"] {
width: auto;
}
button {
box-sizing: border-box;
width: 100%;
color: white;
background-color: #419bdd;
border-radius: 4px;
border: none;
padding: 10px;
margin-top: 30px;
text-transform: uppercase;
font-weight: 500;
font-size: 16px;
}
.alert-danger {
box-sizing: border-box;
width: 100%;
color: #D8000C;
background-color: #FFD2D2;
border-radius: 4px;
border: none;
padding: 10px;
margin-top: 20px;
font-weight: 500;
font-size: 16px;
}
.alert-info {
box-sizing: border-box;
width: 100%;
color: #00529B;
background-color: #BDE5F8;
border-radius: 4px;
border: none;
padding: 10px;
margin-top: 20px;
font-weight: 500;
font-size: 16px;
}

View file

@ -7,8 +7,8 @@ defmodule Pleroma.Config.HolderTest do
alias Pleroma.Config.Holder alias Pleroma.Config.Holder
test "config/0" do test "default_config/0" do
config = Holder.config() config = Holder.default_config()
assert config[:pleroma][Pleroma.Uploaders.Local][:uploads] == "test/uploads" assert config[:pleroma][Pleroma.Uploaders.Local][:uploads] == "test/uploads"
assert config[:tesla][:adapter] == Tesla.Mock assert config[:tesla][:adapter] == Tesla.Mock
@ -20,15 +20,15 @@ test "config/0" do
refute config[:phoenix][:serve_endpoints] refute config[:phoenix][:serve_endpoints]
end end
test "config/1" do test "default_config/1" do
pleroma_config = Holder.config(:pleroma) pleroma_config = Holder.default_config(:pleroma)
assert pleroma_config[Pleroma.Uploaders.Local][:uploads] == "test/uploads" assert pleroma_config[Pleroma.Uploaders.Local][:uploads] == "test/uploads"
tesla_config = Holder.config(:tesla) tesla_config = Holder.default_config(:tesla)
assert tesla_config[:adapter] == Tesla.Mock assert tesla_config[:adapter] == Tesla.Mock
end end
test "config/2" do test "default_config/2" do
assert Holder.config(:pleroma, Pleroma.Uploaders.Local) == [uploads: "test/uploads"] assert Holder.default_config(:pleroma, Pleroma.Uploaders.Local) == [uploads: "test/uploads"]
assert Holder.config(:tesla, :adapter) == Tesla.Mock assert Holder.default_config(:tesla, :adapter) == Tesla.Mock
end end
end end

View file

@ -7,28 +7,13 @@ defmodule Pleroma.Config.LoaderTest do
alias Pleroma.Config.Loader alias Pleroma.Config.Loader
test "load/1" do test "read/1" do
config = Loader.load("test/fixtures/config/temp.secret.exs") config = Loader.read("test/fixtures/config/temp.secret.exs")
assert config[:pleroma][:first_setting][:key] == "value" assert config[:pleroma][:first_setting][:key] == "value"
assert config[:pleroma][:first_setting][:key2] == [Pleroma.Repo] assert config[:pleroma][:first_setting][:key2] == [Pleroma.Repo]
assert config[:quack][:level] == :info assert config[:quack][:level] == :info
end end
test "load_and_merge/0" do
config = Loader.load_and_merge()
refute config[:pleroma][Pleroma.Repo]
refute config[:pleroma][Pleroma.Web.Endpoint]
refute config[:pleroma][:env]
refute config[:pleroma][:configurable_from_database]
refute config[:pleroma][:database]
refute config[:phoenix][:serve_endpoints]
assert config[:pleroma][:ecto_repos] == [Pleroma.Repo]
assert config[:pleroma][Pleroma.Uploaders.Local][:uploads] == "test/uploads"
assert config[:tesla][:adapter] == Tesla.Mock
end
test "filter_group/2" do test "filter_group/2" do
assert Loader.filter_group(:pleroma, assert Loader.filter_group(:pleroma,
pleroma: [ pleroma: [

View file

@ -70,7 +70,7 @@ test "transfer config values for 1 group and some keys" do
assert Application.get_env(:quack, :level) == :info assert Application.get_env(:quack, :level) == :info
assert Application.get_env(:quack, :meta) == [:none] assert Application.get_env(:quack, :meta) == [:none]
default = Pleroma.Config.Holder.config(:quack, :webhook_url) default = Pleroma.Config.Holder.default_config(:quack, :webhook_url)
assert Application.get_env(:quack, :webhook_url) == default assert Application.get_env(:quack, :webhook_url) == default
on_exit(fn -> on_exit(fn ->

15
test/fixtures/relay/accept-follow.json vendored Normal file
View file

@ -0,0 +1,15 @@
{
"@context": "https://www.w3.org/ns/activitystreams",
"actor": "https://relay.mastodon.host/actor",
"id": "https://relay.mastodon.host/activities/ec477b69-db26-4019-923e-cf809de516ab",
"object": {
"actor": "{{ap_id}}",
"id": "{{activity_id}}",
"object": "https://relay.mastodon.host/actor",
"type": "Follow"
},
"to": [
"{{ap_id}}"
],
"type": "Accept"
}

20
test/fixtures/relay/relay.json vendored Normal file
View file

@ -0,0 +1,20 @@
{
"@context": "https://www.w3.org/ns/activitystreams",
"endpoints": {
"sharedInbox": "https://relay.mastodon.host/inbox"
},
"followers": "https://relay.mastodon.host/followers",
"following": "https://relay.mastodon.host/following",
"inbox": "https://relay.mastodon.host/inbox",
"name": "ActivityRelay",
"type": "Application",
"id": "https://relay.mastodon.host/actor",
"publicKey": {
"id": "https://relay.mastodon.host/actor#main-key",
"owner": "https://relay.mastodon.host/actor",
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuNYHNYETdsZFsdcTTEQo\nlsTP9yz4ZjOGrQ1EjoBA7NkjBUxxUAPxZbBjWPT9F+L3IbCX1IwI2OrBM/KwDlug\nV41xnjNmxSCUNpxX5IMZtFaAz9/hWu6xkRTs9Bh6XWZxi+db905aOqszb9Mo3H2g\nQJiAYemXwTh2kBO7XlBDbsMhO11Tu8FxcWTMdR54vlGv4RoiVh8dJRa06yyiTs+m\njbj/OJwR06mHHwlKYTVT/587NUb+e9QtCK6t/dqpyZ1o7vKSK5PSldZVjwHt292E\nXVxFOQVXi7JazTwpdPww79ECSe8ThCykOYCNkm3RjsKuLuokp7Vzq1hXIoeBJ7z2\ndU8vbgg/JyazsOsTxkVs2nd2i9/QW2SH+sX9X3357+XLSCh/A8p8fv/GeoN7UCXe\n4DWHFJZDlItNFfymiPbQH+omuju8qrfW9ngk1gFeI2mahXFQVu7x0qsaZYioCIrZ\nwq0zPnUGl9u0tLUXQz+ZkInRrEz+JepDVauy5/3QdzMLG420zCj/ygDrFzpBQIrc\n62Z6URueUBJox0UK71K+usxqOrepgw8haFGMvg3STFo34pNYjoK4oKO+h5qZEDFD\nb1n57t6JWUaBocZbJns9RGASq5gih+iMk2+zPLWp1x64yvuLsYVLPLBHxjCxS6lA\ndWcopZHi7R/OsRz+vTT7420CAwEAAQ==\n-----END PUBLIC KEY-----"
},
"summary": "ActivityRelay bot",
"preferredUsername": "relay",
"url": "https://relay.mastodon.host/actor"
}

View file

@ -1287,6 +1287,10 @@ def get("http://example.com/rel_me/error", _, _, _) do
{:ok, %Tesla.Env{status: 404, body: ""}} {:ok, %Tesla.Env{status: 404, body: ""}}
end end
def get("https://relay.mastodon.host/actor", _, _, _) do
{:ok, %Tesla.Env{status: 200, body: File.read!("test/fixtures/relay/relay.json")}}
end
def get(url, query, body, headers) do def get(url, query, body, headers) do
{:error, {:error,
"Mock response not implemented for GET #{inspect(url)}, #{query}, #{inspect(body)}, #{ "Mock response not implemented for GET #{inspect(url)}, #{query}, #{inspect(body)}, #{
@ -1299,6 +1303,10 @@ def get(url, query, body, headers) do
def post(url, query \\ [], body \\ [], headers \\ []) def post(url, query \\ [], body \\ [], headers \\ [])
def post("https://relay.mastodon.host/inbox", _, _, _) do
{:ok, %Tesla.Env{status: 200, body: ""}}
end
def post("http://example.org/needs_refresh", _, _, _) do def post("http://example.org/needs_refresh", _, _, _) do
{:ok, {:ok,
%Tesla.Env{ %Tesla.Env{

View file

@ -38,6 +38,9 @@ test "relay is followed" do
assert activity.data["type"] == "Follow" assert activity.data["type"] == "Follow"
assert activity.data["actor"] == local_user.ap_id assert activity.data["actor"] == local_user.ap_id
assert activity.data["object"] == target_user.ap_id assert activity.data["object"] == target_user.ap_id
:ok = Mix.Tasks.Pleroma.Relay.run(["list"])
assert_receive {:mix_shell, :info, ["mastodon.example.org (no Accept received)"]}
end end
end end

View file

@ -412,7 +412,11 @@ test "it sends a welcome message if it is set" do
assert activity.actor == welcome_user.ap_id assert activity.actor == welcome_user.ap_id
end end
test "it requires an email, name, nickname and password, bio is optional" do clear_config([:instance, :account_activation_required])
test "it requires an email, name, nickname and password, bio is optional when account_activation_required is enabled" do
Pleroma.Config.put([:instance, :account_activation_required], true)
@full_user_data @full_user_data
|> Map.keys() |> Map.keys()
|> Enum.each(fn key -> |> Enum.each(fn key ->
@ -423,6 +427,19 @@ test "it requires an email, name, nickname and password, bio is optional" do
end) end)
end end
test "it requires an name, nickname and password, bio and email are optional when account_activation_required is disabled" do
Pleroma.Config.put([:instance, :account_activation_required], false)
@full_user_data
|> Map.keys()
|> Enum.each(fn key ->
params = Map.delete(@full_user_data, key)
changeset = User.register_changeset(%User{}, params)
assert if key in [:bio, :email], do: changeset.valid?, else: not changeset.valid?
end)
end
test "it restricts certain nicknames" do test "it restricts certain nicknames" do
[restricted_name | _] = Pleroma.Config.get([User, :restricted_nicknames]) [restricted_name | _] = Pleroma.Config.get([User, :restricted_nicknames])

View file

@ -341,6 +341,44 @@ test "it clears `unreachable` federation status of the sender", %{conn: conn} do
assert "ok" == json_response(conn, 200) assert "ok" == json_response(conn, 200)
assert Instances.reachable?(sender_url) assert Instances.reachable?(sender_url)
end end
test "accept follow activity", %{conn: conn} do
Pleroma.Config.put([:instance, :federating], true)
relay = Relay.get_actor()
assert {:ok, %Activity{} = activity} = Relay.follow("https://relay.mastodon.host/actor")
followed_relay = Pleroma.User.get_by_ap_id("https://relay.mastodon.host/actor")
relay = refresh_record(relay)
accept =
File.read!("test/fixtures/relay/accept-follow.json")
|> String.replace("{{ap_id}}", relay.ap_id)
|> String.replace("{{activity_id}}", activity.data["id"])
assert "ok" ==
conn
|> assign(:valid_signature, true)
|> put_req_header("content-type", "application/activity+json")
|> post("/inbox", accept)
|> json_response(200)
ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
assert Pleroma.FollowingRelationship.following?(
relay,
followed_relay
)
Mix.shell(Mix.Shell.Process)
on_exit(fn ->
Mix.shell(Mix.Shell.IO)
end)
:ok = Mix.Tasks.Pleroma.Relay.run(["list"])
assert_receive {:mix_shell, :info, ["relay.mastodon.host"]}
end
end end
describe "/users/:nickname/inbox" do describe "/users/:nickname/inbox" do

View file

@ -202,13 +202,15 @@ test "it returns error when status is empty and no attachments" do
CommonAPI.post(user, %{"status" => ""}) CommonAPI.post(user, %{"status" => ""})
end end
test "it returns error when character limit is exceeded" do test "it validates character limits are correctly enforced" do
Pleroma.Config.put([:instance, :limit], 5) Pleroma.Config.put([:instance, :limit], 5)
user = insert(:user) user = insert(:user)
assert {:error, "The status is over the character limit"} = assert {:error, "The status is over the character limit"} =
CommonAPI.post(user, %{"status" => "foobar"}) CommonAPI.post(user, %{"status" => "foobar"})
assert {:ok, activity} = CommonAPI.post(user, %{"status" => "12345"})
end end
test "it can handle activities that expire" do test "it can handle activities that expire" do

View file

@ -601,6 +601,8 @@ test "blocking / unblocking a user" do
[valid_params: valid_params] [valid_params: valid_params]
end end
clear_config([:instance, :account_activation_required])
test "Account registration via Application", %{conn: conn} do test "Account registration via Application", %{conn: conn} do
conn = conn =
post(conn, "/api/v1/apps", %{ post(conn, "/api/v1/apps", %{
@ -685,7 +687,7 @@ test "returns bad_request if missing required params", %{
assert json_response(res, 200) assert json_response(res, 200)
[{127, 0, 0, 1}, {127, 0, 0, 2}, {127, 0, 0, 3}, {127, 0, 0, 4}] [{127, 0, 0, 1}, {127, 0, 0, 2}, {127, 0, 0, 3}, {127, 0, 0, 4}]
|> Stream.zip(valid_params) |> Stream.zip(Map.delete(valid_params, :email))
|> Enum.each(fn {ip, {attr, _}} -> |> Enum.each(fn {ip, {attr, _}} ->
res = res =
conn conn
@ -697,6 +699,54 @@ test "returns bad_request if missing required params", %{
end) end)
end end
clear_config([:instance, :account_activation_required])
test "returns bad_request if missing email params when :account_activation_required is enabled",
%{conn: conn, valid_params: valid_params} do
Pleroma.Config.put([:instance, :account_activation_required], true)
app_token = insert(:oauth_token, user: nil)
conn = put_req_header(conn, "authorization", "Bearer " <> app_token.token)
res =
conn
|> Map.put(:remote_ip, {127, 0, 0, 5})
|> post("/api/v1/accounts", Map.delete(valid_params, :email))
assert json_response(res, 400) == %{"error" => "Missing parameters"}
res =
conn
|> Map.put(:remote_ip, {127, 0, 0, 6})
|> post("/api/v1/accounts", Map.put(valid_params, :email, ""))
assert json_response(res, 400) == %{"error" => "{\"email\":[\"can't be blank\"]}"}
end
test "allow registration without an email", %{conn: conn, valid_params: valid_params} do
app_token = insert(:oauth_token, user: nil)
conn = put_req_header(conn, "authorization", "Bearer " <> app_token.token)
res =
conn
|> Map.put(:remote_ip, {127, 0, 0, 7})
|> post("/api/v1/accounts", Map.delete(valid_params, :email))
assert json_response(res, 200)
end
test "allow registration with an empty email", %{conn: conn, valid_params: valid_params} do
app_token = insert(:oauth_token, user: nil)
conn = put_req_header(conn, "authorization", "Bearer " <> app_token.token)
res =
conn
|> Map.put(:remote_ip, {127, 0, 0, 8})
|> post("/api/v1/accounts", Map.put(valid_params, :email, ""))
assert json_response(res, 200)
end
test "returns forbidden if token is invalid", %{conn: conn, valid_params: valid_params} do test "returns forbidden if token is invalid", %{conn: conn, valid_params: valid_params} do
conn = put_req_header(conn, "authorization", "Bearer " <> "invalid-token") conn = put_req_header(conn, "authorization", "Bearer " <> "invalid-token")