Merge branch 'develop' into feature/update-welcome-setting-in-description

This commit is contained in:
Maksim Pechnikov 2020-08-07 09:48:34 +03:00
commit 8e90cc58e7
38 changed files with 435 additions and 70 deletions

View file

@ -49,6 +49,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Added ### Added
- Configuration: Added a blacklist for email servers.
- Chats: Added `accepts_chat_messages` field to user, exposed in APIs and federation. - Chats: Added `accepts_chat_messages` field to user, exposed in APIs and federation.
- Chats: Added support for federated chats. For details, see the docs. - Chats: Added support for federated chats. For details, see the docs.
- ActivityPub: Added support for existing AP ids for instances migrated from Mastodon. - ActivityPub: Added support for existing AP ids for instances migrated from Mastodon.
@ -101,6 +102,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Fix CSP policy generation to include remote Captcha services - Fix CSP policy generation to include remote Captcha services
- Fix edge case where MediaProxy truncates media, usually caused when Caddy is serving content for the other Federated instance. - Fix edge case where MediaProxy truncates media, usually caused when Caddy is serving content for the other Federated instance.
- Emoji Packs could not be listed when instance was set to `public: false` - Emoji Packs could not be listed when instance was set to `public: false`
- Fix whole_word always returning false on filter get requests
## [Unreleased (patch)] ## [Unreleased (patch)]

View file

@ -516,7 +516,8 @@
"user_exists", "user_exists",
"users", "users",
"web" "web"
] ],
email_blacklist: []
config :pleroma, Oban, config :pleroma, Oban,
repo: Pleroma.Repo, repo: Pleroma.Repo,

View file

@ -3056,6 +3056,7 @@
%{ %{
key: :restricted_nicknames, key: :restricted_nicknames,
type: {:list, :string}, type: {:list, :string},
description: "List of nicknames users may not register with.",
suggestions: [ suggestions: [
".well-known", ".well-known",
"~", "~",
@ -3088,6 +3089,12 @@
"users", "users",
"web" "web"
] ]
},
%{
key: :email_blacklist,
type: {:list, :string},
description: "List of email domains users may not register with.",
suggestions: ["mailinator.com", "maildrop.cc"]
} }
] ]
}, },

View file

@ -120,6 +120,8 @@
config :tzdata, :autoupdate, :disabled config :tzdata, :autoupdate, :disabled
config :pleroma, :mrf, policies: []
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

@ -207,6 +207,11 @@ config :pleroma, :mrf_user_allowlist, %{
* `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 * `authorized_fetch_mode`: Require HTTP signatures for AP fetches
## Pleroma.User
* `restricted_nicknames`: List of nicknames users may not register with.
* `email_blacklist`: List of email domains users may not register with.
## Pleroma.ScheduledActivity ## Pleroma.ScheduledActivity
* `daily_user_limit`: the number of scheduled activities a user is allowed to create in a single day (Default: `25`) * `daily_user_limit`: the number of scheduled activities a user is allowed to create in a single day (Default: `25`)

View file

@ -47,6 +47,7 @@ def start(_type, _args) do
Pleroma.ApplicationRequirements.verify!() Pleroma.ApplicationRequirements.verify!()
setup_instrumenters() setup_instrumenters()
load_custom_modules() load_custom_modules()
check_system_commands()
Pleroma.Docs.JSON.compile() Pleroma.Docs.JSON.compile()
adapter = Application.get_env(:tesla, :adapter) adapter = Application.get_env(:tesla, :adapter)
@ -249,4 +250,21 @@ defp http_children(Tesla.Adapter.Gun, _) do
end end
defp http_children(_, _), do: [] defp http_children(_, _), do: []
defp check_system_commands do
filters = Config.get([Pleroma.Upload, :filters])
check_filter = fn filter, command_required ->
with true <- filter in filters,
false <- Pleroma.Utils.command_available?(command_required) do
Logger.error(
"#{filter} is specified in list of Pleroma.Upload filters, but the #{command_required} command is not found"
)
end
end
check_filter.(Pleroma.Upload.Filters.Exiftool, "exiftool")
check_filter.(Pleroma.Upload.Filters.Mogrify, "mogrify")
check_filter.(Pleroma.Upload.Filters.Mogrifun, "mogrify")
end
end end

View file

@ -11,12 +11,10 @@ def get(key), do: get(key, nil)
def get([key], default), do: get(key, default) def get([key], default), do: get(key, default)
def get([parent_key | keys], default) do def get([_ | _] = path, default) do
case :pleroma case fetch(path) do
|> Application.get_env(parent_key) {:ok, value} -> value
|> get_in(keys) do :error -> default
nil -> default
any -> any
end end
end end
@ -34,6 +32,24 @@ def get!(key) do
end end
end end
def fetch(key) when is_atom(key), do: fetch([key])
def fetch([root_key | keys]) do
Enum.reduce_while(keys, Application.fetch_env(:pleroma, root_key), fn
key, {:ok, config} when is_map(config) or is_list(config) ->
case Access.fetch(config, key) do
:error ->
{:halt, :error}
value ->
{:cont, value}
end
_key, _config ->
{:halt, :error}
end)
end
def put([key], value), do: put(key, value) def put([key], value), do: put(key, value)
def put([parent_key | keys], value) do def put([parent_key | keys], value) do
@ -50,13 +66,16 @@ def put(key, value) do
def delete([key]), do: delete(key) def delete([key]), do: delete(key)
def delete([parent_key | keys]) do def delete([parent_key | keys] = path) do
with {:ok, _} <- fetch(path) do
{_, parent} = {_, parent} =
Application.get_env(:pleroma, parent_key) parent_key
|> get()
|> get_and_update_in(keys, fn _ -> :pop end) |> get_and_update_in(keys, fn _ -> :pop end)
Application.put_env(:pleroma, parent_key, parent) Application.put_env(:pleroma, parent_key, parent)
end end
end
def delete(key) do def delete(key) do
Application.delete_env(:pleroma, key) Application.delete_env(:pleroma, key)

View file

@ -9,9 +9,17 @@ defmodule Pleroma.Upload.Filter.Exiftool do
""" """
@behaviour Pleroma.Upload.Filter @behaviour Pleroma.Upload.Filter
@spec filter(Pleroma.Upload.t()) :: :ok | {:error, String.t()}
def filter(%Pleroma.Upload{tempfile: file, content_type: "image" <> _}) do def filter(%Pleroma.Upload{tempfile: file, content_type: "image" <> _}) do
System.cmd("exiftool", ["-overwrite_original", "-gps:all=", file], parallelism: true) try do
:ok case System.cmd("exiftool", ["-overwrite_original", "-gps:all=", file], parallelism: true) do
{_response, 0} -> :ok
{error, 1} -> {:error, error}
end
rescue
_e in ErlangError ->
{:error, "exiftool command not found"}
end
end end
def filter(_), do: :ok def filter(_), do: :ok

View file

@ -34,10 +34,15 @@ defmodule Pleroma.Upload.Filter.Mogrifun do
[{"fill", "yellow"}, {"tint", "40"}] [{"fill", "yellow"}, {"tint", "40"}]
] ]
@spec filter(Pleroma.Upload.t()) :: :ok | {:error, String.t()}
def filter(%Pleroma.Upload{tempfile: file, content_type: "image" <> _}) do def filter(%Pleroma.Upload{tempfile: file, content_type: "image" <> _}) do
try do
Filter.Mogrify.do_filter(file, [Enum.random(@filters)]) Filter.Mogrify.do_filter(file, [Enum.random(@filters)])
:ok :ok
rescue
_e in ErlangError ->
{:error, "mogrify command not found"}
end
end end
def filter(_), do: :ok def filter(_), do: :ok

View file

@ -8,11 +8,15 @@ defmodule Pleroma.Upload.Filter.Mogrify do
@type conversion :: action :: String.t() | {action :: String.t(), opts :: String.t()} @type conversion :: action :: String.t() | {action :: String.t(), opts :: String.t()}
@type conversions :: conversion() | [conversion()] @type conversions :: conversion() | [conversion()]
@spec filter(Pleroma.Upload.t()) :: :ok | {:error, String.t()}
def filter(%Pleroma.Upload{tempfile: file, content_type: "image" <> _}) do def filter(%Pleroma.Upload{tempfile: file, content_type: "image" <> _}) do
filters = Pleroma.Config.get!([__MODULE__, :args]) try do
do_filter(file, Pleroma.Config.get!([__MODULE__, :args]))
do_filter(file, filters)
:ok :ok
rescue
_e in ErlangError ->
{:error, "mogrify command not found"}
end
end end
def filter(_), do: :ok def filter(_), do: :ok

View file

@ -676,10 +676,19 @@ def register_changeset(struct, params \\ %{}, opts \\ []) do
|> validate_required([:name, :nickname, :password, :password_confirmation]) |> validate_required([:name, :nickname, :password, :password_confirmation])
|> validate_confirmation(:password) |> validate_confirmation(:password)
|> unique_constraint(:email) |> unique_constraint(:email)
|> validate_format(:email, @email_regex)
|> validate_change(:email, fn :email, email ->
valid? =
Config.get([User, :email_blacklist])
|> Enum.all?(fn blacklisted_domain ->
!String.ends_with?(email, ["@" <> blacklisted_domain, "." <> blacklisted_domain])
end)
if valid?, do: [], else: [email: "Invalid email"]
end)
|> unique_constraint(:nickname) |> unique_constraint(:nickname)
|> validate_exclusion(:nickname, Config.get([User, :restricted_nicknames])) |> validate_exclusion(:nickname, Config.get([User, :restricted_nicknames]))
|> validate_format(:nickname, local_nickname_regex()) |> validate_format(:nickname, local_nickname_regex())
|> validate_format(:email, @email_regex)
|> validate_length(:bio, max: bio_limit) |> validate_length(:bio, max: bio_limit)
|> validate_length(:name, min: 1, max: name_limit) |> validate_length(:name, min: 1, max: name_limit)
|> validate_length(:registration_reason, max: reason_limit) |> validate_length(:registration_reason, max: reason_limit)

View file

@ -9,4 +9,19 @@ def compile_dir(dir) when is_binary(dir) do
|> Enum.map(&Path.join(dir, &1)) |> Enum.map(&Path.join(dir, &1))
|> Kernel.ParallelCompiler.compile() |> Kernel.ParallelCompiler.compile()
end end
@doc """
POSIX-compliant check if command is available in the system
## Examples
iex> command_available?("git")
true
iex> command_available?("wrongcmd")
false
"""
@spec command_available?(String.t()) :: boolean()
def command_available?(command) do
match?({_output, 0}, System.cmd("sh", ["-c", "command -v #{command}"]))
end
end end

View file

@ -21,8 +21,8 @@ def filter(activity) do
@impl true @impl true
def describe, do: {:ok, %{}} def describe, do: {:ok, %{}}
defp local?(%{"id" => id}) do defp local?(%{"actor" => actor}) do
String.starts_with?(id, Pleroma.Web.Endpoint.url()) String.starts_with?(actor, Pleroma.Web.Endpoint.url())
end end
defp note?(activity) do defp note?(activity) do

View file

@ -34,9 +34,14 @@ def validate_actor_presence(cng, options \\ []) do
cng cng
|> validate_change(field_name, fn field_name, actor -> |> validate_change(field_name, fn field_name, actor ->
if User.get_cached_by_ap_id(actor) do case User.get_cached_by_ap_id(actor) do
%User{deactivated: true} ->
[{field_name, "user is deactivated"}]
%User{} ->
[] []
else
_ ->
[{field_name, "can't find user"}] [{field_name, "can't find user"}]
end end
end) end)

View file

@ -25,7 +25,7 @@ def render("show.json", %{filter: filter}) do
context: filter.context, context: filter.context,
expires_at: expires_at, expires_at: expires_at,
irreversible: filter.hide, irreversible: filter.hide,
whole_word: false whole_word: filter.whole_word
} }
end end
end end

View file

@ -76,6 +76,13 @@ defp do_authorize(%Plug.Conn{} = conn, params) do
available_scopes = (app && app.scopes) || [] available_scopes = (app && app.scopes) || []
scopes = Scopes.fetch_scopes(params, available_scopes) scopes = Scopes.fetch_scopes(params, available_scopes)
scopes =
if scopes == [] do
available_scopes
else
scopes
end
# Note: `params` might differ from `conn.params`; use `@params` not `@conn.params` in template # Note: `params` might differ from `conn.params`; use `@params` not `@conn.params` in template
render(conn, Authenticator.auth_template(), %{ render(conn, Authenticator.auth_template(), %{
response_type: params["response_type"], response_type: params["response_type"],

View file

@ -9,6 +9,11 @@ defmodule Pleroma.Web.RichMedia.Helpers do
alias Pleroma.Object alias Pleroma.Object
alias Pleroma.Web.RichMedia.Parser alias Pleroma.Web.RichMedia.Parser
@rich_media_options [
pool: :media,
max_body: 2_000_000
]
@spec validate_page_url(URI.t() | binary()) :: :ok | :error @spec validate_page_url(URI.t() | binary()) :: :ok | :error
defp validate_page_url(page_url) when is_binary(page_url) do defp validate_page_url(page_url) when is_binary(page_url) do
validate_tld = Pleroma.Config.get([Pleroma.Formatter, :validate_tld]) validate_tld = Pleroma.Config.get([Pleroma.Formatter, :validate_tld])
@ -77,4 +82,20 @@ def perform(:fetch, %Activity{} = activity) do
fetch_data_for_activity(activity) fetch_data_for_activity(activity)
:ok :ok
end end
def rich_media_get(url) do
headers = [{"user-agent", Pleroma.Application.user_agent() <> "; Bot"}]
options =
if Application.get_env(:tesla, :adapter) == Tesla.Adapter.Hackney do
Keyword.merge(@rich_media_options,
recv_timeout: 2_000,
with_body: true
)
else
@rich_media_options
end
Pleroma.HTTP.get(url, headers, options)
end
end end

View file

@ -3,11 +3,6 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.RichMedia.Parser do defmodule Pleroma.Web.RichMedia.Parser do
@options [
pool: :media,
max_body: 2_000_000
]
defp parsers do defp parsers do
Pleroma.Config.get([:rich_media, :parsers]) Pleroma.Config.get([:rich_media, :parsers])
end end
@ -75,21 +70,8 @@ defp get_ttl_from_image(data, url) do
end end
defp parse_url(url) do defp parse_url(url) do
opts =
if Application.get_env(:tesla, :adapter) == Tesla.Adapter.Hackney do
Keyword.merge(@options,
recv_timeout: 2_000,
with_body: true
)
else
@options
end
try do try do
rich_media_agent = Pleroma.Application.user_agent() <> "; Bot" {:ok, %Tesla.Env{body: html}} = Pleroma.Web.RichMedia.Helpers.rich_media_get(url)
{:ok, %Tesla.Env{body: html}} =
Pleroma.HTTP.get(url, [{"user-agent", rich_media_agent}], adapter: opts)
html html
|> parse_html() |> parse_html()

View file

@ -22,7 +22,7 @@ defp get_oembed_url([{"link", attributes, _children} | _]) do
end end
defp get_oembed_data(url) do defp get_oembed_data(url) do
with {:ok, %Tesla.Env{body: json}} <- Pleroma.HTTP.get(url, [], adapter: [pool: :media]) do with {:ok, %Tesla.Env{body: json}} <- Pleroma.Web.RichMedia.Helpers.rich_media_get(url) do
Jason.decode(json) Jason.decode(json)
end end
end end

View file

@ -37,7 +37,7 @@
} }
a { a {
color: color: #d8a070; color: #d8a070;
text-decoration: none; text-decoration: none;
} }

View file

@ -214,7 +214,8 @@ defp aliases do
"ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"], "ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"],
"ecto.reset": ["ecto.drop", "ecto.setup"], "ecto.reset": ["ecto.drop", "ecto.setup"],
test: ["ecto.create --quiet", "ecto.migrate", "test"], test: ["ecto.create --quiet", "ecto.migrate", "test"],
docs: ["pleroma.docs", "docs"] docs: ["pleroma.docs", "docs"],
analyze: ["credo --strict --only=warnings,todo,fixme,consistency,readability"]
] ]
end end
@ -228,10 +229,10 @@ defp aliases do
defp version(version) do defp version(version) do
identifier_filter = ~r/[^0-9a-z\-]+/i identifier_filter = ~r/[^0-9a-z\-]+/i
{_cmdgit, cmdgit_err} = System.cmd("sh", ["-c", "command -v git"]) git_available? = match?({_output, 0}, System.cmd("sh", ["-c", "command -v git"]))
git_pre_release = git_pre_release =
if cmdgit_err == 0 do if git_available? do
{tag, tag_err} = {tag, tag_err} =
System.cmd("git", ["describe", "--tags", "--abbrev=0"], stderr_to_stdout: true) System.cmd("git", ["describe", "--tags", "--abbrev=0"], stderr_to_stdout: true)
@ -257,7 +258,7 @@ defp version(version) do
# Branch name as pre-release version component, denoted with a dot # Branch name as pre-release version component, denoted with a dot
branch_name = branch_name =
with 0 <- cmdgit_err, with true <- git_available?,
{branch_name, 0} <- System.cmd("git", ["rev-parse", "--abbrev-ref", "HEAD"]), {branch_name, 0} <- System.cmd("git", ["rev-parse", "--abbrev-ref", "HEAD"]),
branch_name <- String.trim(branch_name), branch_name <- String.trim(branch_name),
branch_name <- System.get_env("PLEROMA_BUILD_BRANCH") || branch_name, branch_name <- System.get_env("PLEROMA_BUILD_BRANCH") || branch_name,

View file

@ -0,0 +1,37 @@
# Fix legacy tags set by AdminFE that don't align with TagPolicy MRF
defmodule Pleroma.Repo.Migrations.FixLegacyTags do
use Ecto.Migration
alias Pleroma.Repo
alias Pleroma.User
import Ecto.Query
@old_new_map %{
"force_nsfw" => "mrf_tag:media-force-nsfw",
"strip_media" => "mrf_tag:media-strip",
"force_unlisted" => "mrf_tag:force-unlisted",
"sandbox" => "mrf_tag:sandbox",
"disable_remote_subscription" => "mrf_tag:disable-remote-subscription",
"disable_any_subscription" => "mrf_tag:disable-any-subscription"
}
def change do
legacy_tags = Map.keys(@old_new_map)
from(u in User, where: fragment("? && ?", u.tags, ^legacy_tags))
|> Repo.all()
|> Enum.each(fn user ->
fix_tags_changeset(user)
|> Repo.update()
end)
end
defp fix_tags_changeset(%User{tags: tags} = user) do
new_tags =
Enum.map(tags, fn tag ->
Map.get(@old_new_map, tag, tag)
end)
Ecto.Changeset.change(user, tags: new_tags)
end
end

View file

@ -0,0 +1,19 @@
defmodule Pleroma.Repo.Migrations.RemoveNonlocalExpirations do
use Ecto.Migration
def up do
statement = """
DELETE FROM
activity_expirations A USING activities B
WHERE
A.activity_id = B.id
AND B.local = false;
"""
execute(statement)
end
def down do
:ok
end
end

View file

@ -0,0 +1,7 @@
defmodule Pleroma.Repo.Migrations.AddUniqueIndexToAppClientId do
use Ecto.Migration
def change do
create(unique_index(:apps, [:client_id]))
end
end

View file

@ -28,6 +28,34 @@ test "get/1 with a list of keys" do
assert Pleroma.Config.get([:azerty, :uiop], true) == true assert Pleroma.Config.get([:azerty, :uiop], true) == true
end end
describe "nil values" do
setup do
Pleroma.Config.put(:lorem, nil)
Pleroma.Config.put(:ipsum, %{dolor: [sit: nil]})
Pleroma.Config.put(:dolor, sit: %{amet: nil})
on_exit(fn -> Enum.each(~w(lorem ipsum dolor)a, &Pleroma.Config.delete/1) end)
end
test "get/1 with an atom for nil value" do
assert Pleroma.Config.get(:lorem) == nil
end
test "get/2 with an atom for nil value" do
assert Pleroma.Config.get(:lorem, true) == nil
end
test "get/1 with a list of keys for nil value" do
assert Pleroma.Config.get([:ipsum, :dolor, :sit]) == nil
assert Pleroma.Config.get([:dolor, :sit, :amet]) == nil
end
test "get/2 with a list of keys for nil value" do
assert Pleroma.Config.get([:ipsum, :dolor, :sit], true) == nil
assert Pleroma.Config.get([:dolor, :sit, :amet], true) == nil
end
end
test "get/1 when value is false" do test "get/1 when value is false" do
Pleroma.Config.put([:instance, :false_test], false) Pleroma.Config.put([:instance, :false_test], false)
Pleroma.Config.put([:instance, :nested], []) Pleroma.Config.put([:instance, :nested], [])
@ -89,5 +117,23 @@ test "delete/2 with a list of keys" do
Pleroma.Config.put([:delete_me, :delete_me], hello: "world", world: "Hello") Pleroma.Config.put([:delete_me, :delete_me], hello: "world", world: "Hello")
Pleroma.Config.delete([:delete_me, :delete_me, :world]) Pleroma.Config.delete([:delete_me, :delete_me, :world])
assert Pleroma.Config.get([:delete_me, :delete_me]) == [hello: "world"] assert Pleroma.Config.get([:delete_me, :delete_me]) == [hello: "world"]
assert Pleroma.Config.delete([:this_key_does_not_exist])
assert Pleroma.Config.delete([:non, :existing, :key])
end
test "fetch/1" do
Pleroma.Config.put([:lorem], :ipsum)
Pleroma.Config.put([:ipsum], dolor: :sit)
assert Pleroma.Config.fetch([:lorem]) == {:ok, :ipsum}
assert Pleroma.Config.fetch(:lorem) == {:ok, :ipsum}
assert Pleroma.Config.fetch([:ipsum, :dolor]) == {:ok, :sit}
assert Pleroma.Config.fetch([:lorem, :ipsum]) == :error
assert Pleroma.Config.fetch([:loremipsum]) == :error
assert Pleroma.Config.fetch(:loremipsum) == :error
Pleroma.Config.delete([:lorem])
Pleroma.Config.delete([:ipsum])
end end
end end

View file

@ -0,0 +1,24 @@
defmodule Pleroma.Repo.Migrations.FixLegacyTagsTest do
alias Pleroma.User
use Pleroma.DataCase
import Pleroma.Factory
import Pleroma.Tests.Helpers
setup_all do: require_migration("20200802170532_fix_legacy_tags")
test "change/0 converts legacy user tags into correct values", %{migration: migration} do
user = insert(:user, tags: ["force_nsfw", "force_unlisted", "verified"])
user2 = insert(:user)
assert :ok == migration.change()
fixed_user = User.get_by_id(user.id)
fixed_user2 = User.get_by_id(user2.id)
assert fixed_user.tags == ["mrf_tag:media-force-nsfw", "mrf_tag:force-unlisted", "verified"]
assert fixed_user2.tags == []
# user2 should not have been updated
assert fixed_user2.updated_at == fixed_user2.inserted_at
end
end

View file

@ -17,9 +17,19 @@ defmacro clear_config(config_path) do
defmacro clear_config(config_path, do: yield) do defmacro clear_config(config_path, do: yield) do
quote do quote do
initial_setting = Config.get(unquote(config_path)) initial_setting = Config.fetch(unquote(config_path))
unquote(yield) unquote(yield)
on_exit(fn -> Config.put(unquote(config_path), initial_setting) end)
on_exit(fn ->
case initial_setting do
:error ->
Config.delete(unquote(config_path))
{:ok, value} ->
Config.put(unquote(config_path), value)
end
end)
:ok :ok
end end
end end

View file

@ -50,13 +50,13 @@ test "with errors" do
defp assert_app(name, redirect, scopes) do defp assert_app(name, redirect, scopes) do
app = Repo.get_by(Pleroma.Web.OAuth.App, client_name: name) app = Repo.get_by(Pleroma.Web.OAuth.App, client_name: name)
assert_received {:mix_shell, :info, [message]} assert_receive {:mix_shell, :info, [message]}
assert message == "#{name} successfully created:" assert message == "#{name} successfully created:"
assert_received {:mix_shell, :info, [message]} assert_receive {:mix_shell, :info, [message]}
assert message == "App client_id: #{app.client_id}" assert message == "App client_id: #{app.client_id}"
assert_received {:mix_shell, :info, [message]} assert_receive {:mix_shell, :info, [message]}
assert message == "App client_secret: #{app.client_secret}" assert message == "App client_secret: #{app.client_secret}"
assert app.scopes == scopes assert app.scopes == scopes

View file

@ -7,6 +7,8 @@ defmodule Pleroma.Upload.Filter.ExiftoolTest do
alias Pleroma.Upload.Filter alias Pleroma.Upload.Filter
test "apply exiftool filter" do test "apply exiftool filter" do
assert Pleroma.Utils.command_available?("exiftool")
File.cp!( File.cp!(
"test/fixtures/DSCN0010.jpg", "test/fixtures/DSCN0010.jpg",
"test/fixtures/DSCN0010_tmp.jpg" "test/fixtures/DSCN0010_tmp.jpg"

View file

@ -513,6 +513,29 @@ test "it restricts certain nicknames" do
refute changeset.valid? refute changeset.valid?
end end
test "it blocks blacklisted email domains" do
clear_config([User, :email_blacklist], ["trolling.world"])
# Block with match
params = Map.put(@full_user_data, :email, "troll@trolling.world")
changeset = User.register_changeset(%User{}, params)
refute changeset.valid?
# Block with subdomain match
params = Map.put(@full_user_data, :email, "troll@gnomes.trolling.world")
changeset = User.register_changeset(%User{}, params)
refute changeset.valid?
# Pass with different domains that are similar
params = Map.put(@full_user_data, :email, "troll@gnomestrolling.world")
changeset = User.register_changeset(%User{}, params)
assert changeset.valid?
params = Map.put(@full_user_data, :email, "troll@trolling.world.us")
changeset = User.register_changeset(%User{}, params)
assert changeset.valid?
end
test "it sets the password_hash and ap_id" do test "it sets the password_hash and ap_id" do
changeset = User.register_changeset(%User{}, @full_user_data) changeset = User.register_changeset(%User{}, @full_user_data)

View file

@ -7,11 +7,13 @@ defmodule Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicyTest do
alias Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy alias Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy
@id Pleroma.Web.Endpoint.url() <> "/activities/cofe" @id Pleroma.Web.Endpoint.url() <> "/activities/cofe"
@local_actor Pleroma.Web.Endpoint.url() <> "/users/cofe"
test "adds `expires_at` property" do test "adds `expires_at` property" do
assert {:ok, %{"type" => "Create", "expires_at" => expires_at}} = assert {:ok, %{"type" => "Create", "expires_at" => expires_at}} =
ActivityExpirationPolicy.filter(%{ ActivityExpirationPolicy.filter(%{
"id" => @id, "id" => @id,
"actor" => @local_actor,
"type" => "Create", "type" => "Create",
"object" => %{"type" => "Note"} "object" => %{"type" => "Note"}
}) })
@ -25,6 +27,7 @@ test "keeps existing `expires_at` if it less than the config setting" do
assert {:ok, %{"type" => "Create", "expires_at" => ^expires_at}} = assert {:ok, %{"type" => "Create", "expires_at" => ^expires_at}} =
ActivityExpirationPolicy.filter(%{ ActivityExpirationPolicy.filter(%{
"id" => @id, "id" => @id,
"actor" => @local_actor,
"type" => "Create", "type" => "Create",
"expires_at" => expires_at, "expires_at" => expires_at,
"object" => %{"type" => "Note"} "object" => %{"type" => "Note"}
@ -37,6 +40,7 @@ test "overwrites existing `expires_at` if it greater than the config setting" do
assert {:ok, %{"type" => "Create", "expires_at" => expires_at}} = assert {:ok, %{"type" => "Create", "expires_at" => expires_at}} =
ActivityExpirationPolicy.filter(%{ ActivityExpirationPolicy.filter(%{
"id" => @id, "id" => @id,
"actor" => @local_actor,
"type" => "Create", "type" => "Create",
"expires_at" => too_distant_future, "expires_at" => too_distant_future,
"object" => %{"type" => "Note"} "object" => %{"type" => "Note"}
@ -49,6 +53,7 @@ test "ignores remote activities" do
assert {:ok, activity} = assert {:ok, activity} =
ActivityExpirationPolicy.filter(%{ ActivityExpirationPolicy.filter(%{
"id" => "https://example.com/123", "id" => "https://example.com/123",
"actor" => "https://example.com/users/cofe",
"type" => "Create", "type" => "Create",
"object" => %{"type" => "Note"} "object" => %{"type" => "Note"}
}) })
@ -60,6 +65,7 @@ test "ignores non-Create/Note activities" do
assert {:ok, activity} = assert {:ok, activity} =
ActivityExpirationPolicy.filter(%{ ActivityExpirationPolicy.filter(%{
"id" => "https://example.com/123", "id" => "https://example.com/123",
"actor" => "https://example.com/users/cofe",
"type" => "Follow" "type" => "Follow"
}) })
@ -68,6 +74,7 @@ test "ignores non-Create/Note activities" do
assert {:ok, activity} = assert {:ok, activity} =
ActivityExpirationPolicy.filter(%{ ActivityExpirationPolicy.filter(%{
"id" => "https://example.com/123", "id" => "https://example.com/123",
"actor" => "https://example.com/users/cofe",
"type" => "Create", "type" => "Create",
"object" => %{"type" => "Cofe"} "object" => %{"type" => "Cofe"}
}) })

View file

@ -124,6 +124,24 @@ test "it fetches the actor if they aren't in our system" do
{:ok, %Activity{} = _activity} = Transmogrifier.handle_incoming(data) {:ok, %Activity{} = _activity} = Transmogrifier.handle_incoming(data)
end end
test "it doesn't work for deactivated users" do
data =
File.read!("test/fixtures/create-chat-message.json")
|> Poison.decode!()
_author =
insert(:user,
ap_id: data["actor"],
local: false,
last_refreshed_at: DateTime.utc_now(),
deactivated: true
)
_recipient = insert(:user, ap_id: List.first(data["to"]), local: true)
assert {:error, _} = Transmogrifier.handle_incoming(data)
end
test "it inserts it and creates a chat" do test "it inserts it and creates a chat" do
data = data =
File.read!("test/fixtures/create-chat-message.json") File.read!("test/fixtures/create-chat-message.json")

View file

@ -163,6 +163,14 @@ test "it does not crash if the object in inReplyTo can't be fetched" do
end) =~ "[warn] Couldn't fetch \"https://404.site/whatever\", error: nil" end) =~ "[warn] Couldn't fetch \"https://404.site/whatever\", error: nil"
end end
test "it does not work for deactivated users" do
data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()
insert(:user, ap_id: data["actor"], deactivated: true)
assert {:error, _} = Transmogrifier.handle_incoming(data)
end
test "it works for incoming notices" do test "it works for incoming notices" do
data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!() data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()

View file

@ -458,6 +458,11 @@ test "it adds emoji in the object" do
end end
describe "posting" do describe "posting" do
test "deactivated users can't post" do
user = insert(:user, deactivated: true)
assert {:error, _} = CommonAPI.post(user, %{status: "ye"})
end
test "it supports explicit addressing" do test "it supports explicit addressing" do
user = insert(:user) user = insert(:user)
user_two = insert(:user) user_two = insert(:user)

View file

@ -940,17 +940,32 @@ test "registers and logs in without :account_activation_required / :account_appr
assert refresh assert refresh
assert scope == "read write follow" assert scope == "read write follow"
conn = clear_config([User, :email_blacklist], ["example.org"])
build_conn()
|> put_req_header("content-type", "multipart/form-data") params = %{
|> put_req_header("authorization", "Bearer " <> token)
|> post("/api/v1/accounts", %{
username: "lain", username: "lain",
email: "lain@example.org", email: "lain@example.org",
password: "PlzDontHackLain", password: "PlzDontHackLain",
bio: "Test Bio", bio: "Test Bio",
agreement: true agreement: true
}) }
conn =
build_conn()
|> put_req_header("content-type", "multipart/form-data")
|> put_req_header("authorization", "Bearer " <> token)
|> post("/api/v1/accounts", params)
assert %{"error" => "{\"email\":[\"Invalid email\"]}"} =
json_response_and_validate_schema(conn, 400)
Pleroma.Config.put([User, :email_blacklist], [])
conn =
build_conn()
|> put_req_header("content-type", "multipart/form-data")
|> put_req_header("authorization", "Bearer " <> token)
|> post("/api/v1/accounts", params)
%{ %{
"access_token" => token, "access_token" => token,

View file

@ -64,11 +64,13 @@ test "fetching a list of filters" do
test "get a filter" do test "get a filter" do
%{user: user, conn: conn} = oauth_access(["read:filters"]) %{user: user, conn: conn} = oauth_access(["read:filters"])
# check whole_word false
query = %Pleroma.Filter{ query = %Pleroma.Filter{
user_id: user.id, user_id: user.id,
filter_id: 2, filter_id: 2,
phrase: "knight", phrase: "knight",
context: ["home"] context: ["home"],
whole_word: false
} }
{:ok, filter} = Pleroma.Filter.create(query) {:ok, filter} = Pleroma.Filter.create(query)
@ -76,6 +78,25 @@ test "get a filter" do
conn = get(conn, "/api/v1/filters/#{filter.filter_id}") conn = get(conn, "/api/v1/filters/#{filter.filter_id}")
assert response = json_response_and_validate_schema(conn, 200) assert response = json_response_and_validate_schema(conn, 200)
assert response["whole_word"] == false
# check whole_word true
%{user: user, conn: conn} = oauth_access(["read:filters"])
query = %Pleroma.Filter{
user_id: user.id,
filter_id: 3,
phrase: "knight",
context: ["home"],
whole_word: true
}
{:ok, filter} = Pleroma.Filter.create(query)
conn = get(conn, "/api/v1/filters/#{filter.filter_id}")
assert response = json_response_and_validate_schema(conn, 200)
assert response["whole_word"] == true
end end
test "update a filter" do test "update a filter" do
@ -86,7 +107,8 @@ test "update a filter" do
filter_id: 2, filter_id: 2,
phrase: "knight", phrase: "knight",
context: ["home"], context: ["home"],
hide: true hide: true,
whole_word: true
} }
{:ok, _filter} = Pleroma.Filter.create(query) {:ok, _filter} = Pleroma.Filter.create(query)
@ -108,6 +130,7 @@ test "update a filter" do
assert response["phrase"] == new.phrase assert response["phrase"] == new.phrase
assert response["context"] == new.context assert response["context"] == new.context
assert response["irreversible"] == true assert response["irreversible"] == true
assert response["whole_word"] == true
end end
test "delete a filter" do test "delete a filter" do

View file

@ -17,8 +17,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPITest do
test "returns error when followed user is deactivated" do test "returns error when followed user is deactivated" do
follower = insert(:user) follower = insert(:user)
user = insert(:user, local: true, deactivated: true) user = insert(:user, local: true, deactivated: true)
{:error, error} = MastodonAPI.follow(follower, user) assert {:error, _error} = MastodonAPI.follow(follower, user)
assert error == :rejected
end end
test "following for user" do test "following for user" do

View file

@ -29,5 +29,16 @@ test "gets exist app and updates scopes" do
assert exist_app.id == app.id assert exist_app.id == app.id
assert exist_app.scopes == ["read", "write", "follow", "push"] assert exist_app.scopes == ["read", "write", "follow", "push"]
end end
test "has unique client_id" do
insert(:oauth_app, client_name: "", redirect_uris: "", client_id: "boop")
error =
catch_error(insert(:oauth_app, client_name: "", redirect_uris: "", client_id: "boop"))
assert %Ecto.ConstraintError{} = error
assert error.constraint == "apps_client_id_index"
assert error.type == :unique
end
end end
end end