Verified Commit 5bb88fd1 authored by href's avatar href
Browse files

Runtime configuration

Related to #85

Everything should now be configured at runtime, with the exception of
the `Pleroma.HTML` scrubbers (the scrubbers used can be
changed at runtime, but their configuration is compile-time) because
it's building a module with a macro.
parent 25512aa2
defmodule Pleroma.Config do
def get([key]), do: get(key)
defmodule Error do
defexception [:message]
end
def get(key), do: get(key, nil)
def get([key], default), do: get(key, default)
def get([parent_key | keys]) do
def get([parent_key | keys], default) do
Application.get_env(:pleroma, parent_key)
|> get_in(keys)
|> get_in(keys) || default
end
def get(key) do
Application.get_env(:pleroma, key)
def get(key, default) do
Application.get_env(:pleroma, key, default)
end
def get!(key) do
value = get(key, nil)
if value == nil do
raise(Error, message: "Missing configuration value: #{inspect(key)}")
else
value
end
end
def put([key], value), do: put(key, value)
......
......@@ -29,8 +29,6 @@ def parse_mentions(text) do
|> Enum.filter(fn {_match, user} -> user end)
end
@instance Application.get_env(:pleroma, :instance)
def emojify(text) do
emojify(text, Emoji.get_all())
end
......
defmodule Pleroma.Gopher.Server do
use GenServer
require Logger
@gopher Application.get_env(:pleroma, :gopher)
def start_link() do
ip = Keyword.get(@gopher, :ip, {0, 0, 0, 0})
port = Keyword.get(@gopher, :port, 1234)
config = Pleroma.Config.get(:gopher, [])
ip = Keyword.get(config, :ip, {0, 0, 0, 0})
port = Keyword.get(config, :port, 1234)
GenServer.start_link(__MODULE__, [ip, port], [])
end
def init([ip, port]) do
if Keyword.get(@gopher, :enabled, false) do
if Pleroma.Config.get([:gopher, :enabled], false) do
Logger.info("Starting gopher server on #{port}")
:ranch.start_listener(
......@@ -37,9 +37,6 @@ defmodule Pleroma.Gopher.Server.ProtocolHandler do
alias Pleroma.Repo
alias Pleroma.HTML
@instance Application.get_env(:pleroma, :instance)
@gopher Application.get_env(:pleroma, :gopher)
def start_link(ref, socket, transport, opts) do
pid = spawn_link(__MODULE__, :init, [ref, socket, transport, opts])
{:ok, pid}
......@@ -62,7 +59,7 @@ def info(text) do
def link(name, selector, type \\ 1) do
address = Pleroma.Web.Endpoint.host()
port = Keyword.get(@gopher, :port, 1234)
port = Pleroma.Config.get([:gopher, :port], 1234)
"#{type}#{name}\t#{selector}\t#{address}\t#{port}\r\n"
end
......@@ -85,7 +82,7 @@ def render_activities(activities) do
end
def response("") do
info("Welcome to #{Keyword.get(@instance, :name, "Pleroma")}!") <>
info("Welcome to #{Pleroma.Config.get([:instance, :name], "Pleroma")}!") <>
link("Public Timeline", "/main/public") <>
link("Federated Timeline", "/main/all") <> ".\r\n"
end
......
defmodule Pleroma.HTML do
alias HtmlSanitizeEx.Scrubber
@markup Application.get_env(:pleroma, :markup)
defp get_scrubbers(scrubber) when is_atom(scrubber), do: [scrubber]
defp get_scrubbers(scrubbers) when is_list(scrubbers), do: scrubbers
defp get_scrubbers(_), do: [Pleroma.HTML.Scrubber.Default]
def get_scrubbers() do
Keyword.get(@markup, :scrub_policy)
Pleroma.Config.get([:markup, :scrub_policy])
|> get_scrubbers
end
......
defmodule Pleroma.Upload do
alias Ecto.UUID
@storage_backend Application.get_env(:pleroma, Pleroma.Upload)
|> Keyword.fetch!(:uploader)
def check_file_size(path, nil), do: true
def check_file_size(path, size_limit) do
......@@ -21,8 +18,7 @@ def store(%Plug.Upload{} = file, should_dedupe, size_limit) do
true <- check_file_size(file.path, size_limit) do
strip_exif_data(content_type, file.path)
{:ok, url_path} =
@storage_backend.put_file(name, uuid, file.path, content_type, should_dedupe)
{:ok, url_path} = uploader().put_file(name, uuid, file.path, content_type, should_dedupe)
%{
"type" => "Document",
......@@ -57,8 +53,7 @@ def store(%{"img" => "data:image/" <> image_data}, should_dedupe, size_limit) do
content_type
)
{:ok, url_path} =
@storage_backend.put_file(name, uuid, tmp_path, content_type, should_dedupe)
{:ok, url_path} = uploader().put_file(name, uuid, tmp_path, content_type, should_dedupe)
%{
"type" => "Image",
......@@ -182,4 +177,8 @@ def get_content_type(file) do
_e -> "application/octet-stream"
end
end
defp uploader() do
Pleroma.Config.get!([Pleroma.Upload, :uploader])
end
end
defmodule Pleroma.Uploaders.Swift.Keystone do
use HTTPoison.Base
@settings Application.get_env(:pleroma, Pleroma.Uploaders.Swift)
def process_url(url) do
Enum.join(
[Keyword.fetch!(@settings, :auth_url), url],
[Pleroma.Config.get!([Pleroma.Uploaders.Swift, :auth_url]), url],
"/"
)
end
......@@ -16,9 +14,10 @@ def process_response_body(body) do
end
def get_token() do
username = Keyword.fetch!(@settings, :username)
password = Keyword.fetch!(@settings, :password)
tenant_id = Keyword.fetch!(@settings, :tenant_id)
settings = Pleroma.Config.get(Pleroma.Uploaders.Swift)
username = Keyword.fetch!(settings, :username)
password = Keyword.fetch!(settings, :password)
tenant_id = Keyword.fetch!(settings, :tenant_id)
case post(
"/tokens",
......
defmodule Pleroma.Uploaders.Swift.Client do
use HTTPoison.Base
@settings Application.get_env(:pleroma, Pleroma.Uploaders.Swift)
def process_url(url) do
Enum.join(
[Keyword.fetch!(@settings, :storage_url), url],
[Pleroma.Config.get!([Pleroma.Uploaders.Swift, :storage_url]), url],
"/"
)
end
def upload_file(filename, body, content_type) do
object_url = Keyword.fetch!(@settings, :object_url)
object_url = Pleroma.Config.get!([Pleroma.Uploaders.Swift, :object_url])
token = Pleroma.Uploaders.Swift.Keystone.get_token()
case put("#{filename}", body, "X-Auth-Token": token, "Content-Type": content_type) do
......
......@@ -10,8 +10,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
@httpoison Application.get_env(:pleroma, :httpoison)
@instance Application.get_env(:pleroma, :instance)
# For Announce activities, we filter the recipients based on following status for any actors
# that match actual users. See issue #164 for more information about why this is necessary.
defp get_recipients(%{"type" => "Announce"} = data) do
......@@ -659,14 +657,12 @@ def make_user_from_nickname(nickname) do
end
end
@quarantined_instances Keyword.get(@instance, :quarantined_instances, [])
def should_federate?(inbox, public) do
if public do
true
else
inbox_info = URI.parse(inbox)
inbox_info.host not in @quarantined_instances
!Enum.member?(Pleroma.Config.get([:instance, :quarantined_instances], []), inbox_info.host)
end
end
......
......@@ -3,10 +3,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.NormalizeMarkup do
@behaviour Pleroma.Web.ActivityPub.MRF
@mrf_normalize_markup Application.get_env(:pleroma, :mrf_normalize_markup)
def filter(%{"type" => activity_type} = object) when activity_type == "Create" do
scrub_policy = Keyword.get(@mrf_normalize_markup, :scrub_policy)
scrub_policy = Pleroma.Config.get([:mrf_normalize_markup, :scrub_policy])
child = object["object"]
......
......@@ -2,10 +2,6 @@ defmodule Pleroma.Web.ActivityPub.MRF.RejectNonPublic do
alias Pleroma.User
@behaviour Pleroma.Web.ActivityPub.MRF
@mrf_rejectnonpublic Application.get_env(:pleroma, :mrf_rejectnonpublic)
@allow_followersonly Keyword.get(@mrf_rejectnonpublic, :allow_followersonly)
@allow_direct Keyword.get(@mrf_rejectnonpublic, :allow_direct)
@impl true
def filter(%{"type" => "Create"} = object) do
user = User.get_cached_by_ap_id(object["actor"])
......@@ -20,6 +16,8 @@ def filter(%{"type" => "Create"} = object) do
true -> "direct"
end
policy = Pleroma.Config.get(:mrf_rejectnonpublic)
case visibility do
"public" ->
{:ok, object}
......@@ -28,14 +26,14 @@ def filter(%{"type" => "Create"} = object) do
{:ok, object}
"followers" ->
with true <- @allow_followersonly do
with true <- Keyword.get(policy, :allow_followersonly) do
{:ok, object}
else
_e -> {:reject, nil}
end
"direct" ->
with true <- @allow_direct do
with true <- Keyword.get(policy, :allow_direct) do
{:ok, object}
else
_e -> {:reject, nil}
......
......@@ -2,60 +2,75 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
alias Pleroma.User
@behaviour Pleroma.Web.ActivityPub.MRF
@mrf_policy Application.get_env(:pleroma, :mrf_simple)
defp check_accept(%{host: actor_host} = _actor_info, object) do
accepts = Pleroma.Config.get([:mrf_simple, :accept])
@accept Keyword.get(@mrf_policy, :accept)
defp check_accept(%{host: actor_host} = actor_info, object)
when length(@accept) > 0 and not (actor_host in @accept) do
{:reject, nil}
cond do
accepts == [] -> {:ok, object}
Enum.member?(accepts, actor_host) -> {:ok, object}
true -> {:reject, nil}
end
end
defp check_accept(actor_info, object), do: {:ok, object}
@reject Keyword.get(@mrf_policy, :reject)
defp check_reject(%{host: actor_host} = actor_info, object) when actor_host in @reject do
{:reject, nil}
defp check_reject(%{host: actor_host} = _actor_info, object) do
if Enum.member?(Pleroma.Config.get([:mrf_simple, :reject]), actor_host) do
{:reject, nil}
else
{:ok, object}
end
end
defp check_reject(actor_info, object), do: {:ok, object}
defp check_media_removal(
%{host: actor_host} = _actor_info,
%{"type" => "Create", "object" => %{"attachement" => child_attachement}} = object
)
when length(child_attachement) > 0 do
object =
if Enum.member?(Pleroma.Config.get([:mrf_simple, :media_removal]), actor_host) do
child_object = Map.delete(object["object"], "attachment")
Map.put(object, "object", child_object)
else
object
end
@media_removal Keyword.get(@mrf_policy, :media_removal)
defp check_media_removal(%{host: actor_host} = actor_info, %{"type" => "Create"} = object)
when actor_host in @media_removal do
child_object = Map.delete(object["object"], "attachment")
object = Map.put(object, "object", child_object)
{:ok, object}
end
defp check_media_removal(actor_info, object), do: {:ok, object}
defp check_media_removal(_actor_info, object), do: {:ok, object}
@media_nsfw Keyword.get(@mrf_policy, :media_nsfw)
defp check_media_nsfw(
%{host: actor_host} = actor_info,
%{host: actor_host} = _actor_info,
%{
"type" => "Create",
"object" => %{"attachment" => child_attachment} = child_object
} = object
)
when actor_host in @media_nsfw and length(child_attachment) > 0 do
tags = (child_object["tag"] || []) ++ ["nsfw"]
child_object = Map.put(child_object, "tags", tags)
child_object = Map.put(child_object, "sensitive", true)
object = Map.put(object, "object", child_object)
when length(child_attachment) > 0 do
object =
if Enum.member?(Pleroma.Config.get([:mrf_simple, :media_nsfw]), actor_host) do
tags = (child_object["tag"] || []) ++ ["nsfw"]
child_object = Map.put(child_object, "tags", tags)
child_object = Map.put(child_object, "sensitive", true)
Map.put(object, "object", child_object)
else
object
end
{:ok, object}
end
defp check_media_nsfw(actor_info, object), do: {:ok, object}
@ftl_removal Keyword.get(@mrf_policy, :federated_timeline_removal)
defp check_ftl_removal(%{host: actor_host} = actor_info, object)
when actor_host in @ftl_removal do
user = User.get_by_ap_id(object["actor"])
defp check_media_nsfw(_actor_info, object), do: {:ok, object}
# flip to/cc relationship to make the post unlisted
defp check_ftl_removal(%{host: actor_host} = _actor_info, object) do
object =
if "https://www.w3.org/ns/activitystreams#Public" in object["to"] and
user.follower_address in object["cc"] do
with true <-
Enum.member?(
Pleroma.Config.get([:mrf_simple, :federated_timeline_removal]),
actor_host
),
user <- User.get_by_ap_id(object["actor"]),
true <- "https://www.w3.org/ns/activitystreams#Public" in object["to"],
true <- user.follower_address in object["cc"] do
to =
List.delete(object["to"], "https://www.w3.org/ns/activitystreams#Public") ++
[user.follower_address]
......@@ -68,14 +83,12 @@ defp check_ftl_removal(%{host: actor_host} = actor_info, object)
|> Map.put("to", to)
|> Map.put("cc", cc)
else
object
_ -> object
end
{:ok, object}
end
defp check_ftl_removal(actor_info, object), do: {:ok, object}
@impl true
def filter(object) do
actor_info = URI.parse(object["actor"])
......
......@@ -506,9 +506,6 @@ def handle_incoming(
end
end
@ap_config Application.get_env(:pleroma, :activitypub)
@accept_blocks Keyword.get(@ap_config, :accept_blocks)
def handle_incoming(
%{
"type" => "Undo",
......@@ -517,7 +514,7 @@ def handle_incoming(
"id" => id
} = _data
) do
with true <- @accept_blocks,
with true <- Pleroma.Config.get([:activitypub, :accept_blocks]),
%User{local: true} = blocked <- User.get_cached_by_ap_id(blocked),
%User{} = blocker <- User.get_or_fetch_by_ap_id(blocker),
{:ok, activity} <- ActivityPub.unblock(blocker, blocked, id, false) do
......@@ -531,7 +528,7 @@ def handle_incoming(
def handle_incoming(
%{"type" => "Block", "object" => blocked, "actor" => blocker, "id" => id} = data
) do
with true <- @accept_blocks,
with true <- Pleroma.Config.get([:activitypub, :accept_blocks]),
%User{local: true} = blocked = User.get_cached_by_ap_id(blocked),
%User{} = blocker = User.get_or_fetch_by_ap_id(blocker),
{:ok, activity} <- ActivityPub.block(blocker, blocked, id, false) do
......
......@@ -70,15 +70,17 @@ def get_visibility(%{"in_reply_to_status_id" => status_id}) when not is_nil(stat
def get_visibility(_), do: "public"
@instance Application.get_env(:pleroma, :instance)
@allowed_post_formats Keyword.get(@instance, :allowed_post_formats)
defp get_content_type(content_type) when content_type in @allowed_post_formats, do: content_type
defp get_content_type(_), do: "text/plain"
defp get_content_type(content_type) do
if Enum.member?(Pleroma.Config.get([:instance, :allowed_post_formats]), content_type) do
content_type
else
"text/plain"
end
end
@limit Keyword.get(@instance, :limit)
def post(user, %{"status" => status} = data) do
visibility = get_visibility(data)
limit = Pleroma.Config.get([:instance, :limit])
with status <- String.trim(status),
attachments <- attachments_from_ids(data["media_ids"]),
......@@ -98,7 +100,7 @@ def post(user, %{"status" => status} = data) do
context <- make_context(inReplyTo),
cw <- data["spoiler_text"],
full_payload <- String.trim(status <> (data["spoiler_text"] || "")),
length when length in 1..@limit <- String.length(full_payload),
length when length in 1..limit <- String.length(full_payload),
object <-
make_note_data(
user.ap_id,
......
......@@ -12,8 +12,6 @@ defmodule Pleroma.Web.Federator do
@websub Application.get_env(:pleroma, :websub)
@ostatus Application.get_env(:pleroma, :ostatus)
@httpoison Application.get_env(:pleroma, :httpoison)
@instance Application.get_env(:pleroma, :instance)
@federating Keyword.get(@instance, :federating)
@max_jobs 20
def init(args) do
......@@ -147,7 +145,7 @@ def handle(type, _) do
end
def enqueue(type, payload, priority \\ 1) do
if @federating do
if Pleroma.Config.get([:instance, :federating]) do
if Mix.env() == :test do
handle(type, payload)
else
......
......@@ -132,22 +132,23 @@ def user(%{assigns: %{user: for_user}} = conn, %{"id" => id}) do
end
end
@instance Application.get_env(:pleroma, :instance)
@mastodon_api_level "2.5.0"
def masto_instance(conn, _params) do
instance = Pleroma.Config.get(:instance)
response = %{
uri: Web.base_url(),
title: Keyword.get(@instance, :name),
description: Keyword.get(@instance, :description),
version: "#{@mastodon_api_level} (compatible; #{Keyword.get(@instance, :version)})",
email: Keyword.get(@instance, :email),
title: Keyword.get(instance, :name),
description: Keyword.get(instance, :description),
version: "#{@mastodon_api_level} (compatible; #{Keyword.get(instance, :version)})",
email: Keyword.get(instance, :email),
urls: %{
streaming_api: String.replace(Pleroma.Web.Endpoint.static_url(), "http", "ws")
},
stats: Stats.get_stats(),
thumbnail: Web.base_url() <> "/instance/thumbnail.jpeg",
max_toot_chars: Keyword.get(@instance, :limit)
max_toot_chars: Keyword.get(instance, :limit)
}
json(conn, response)
......@@ -581,15 +582,16 @@ def reject_follow_request(%{assigns: %{user: followed}} = conn, %{"id" => id}) d
end
end
@activitypub Application.get_env(:pleroma, :activitypub)
@follow_handshake_timeout Keyword.get(@activitypub, :follow_handshake_timeout)
def follow(%{assigns: %{user: follower}} = conn, %{"id" => id}) do
with %User{} = followed <- Repo.get(User, id),
{:ok, follower} <- User.maybe_direct_follow(follower, followed),
{:ok, _activity} <- ActivityPub.follow(follower, followed),
{:ok, follower, followed} <-
User.wait_and_refresh(@follow_handshake_timeout, follower, followed) do
User.wait_and_refresh(
Pleroma.Config.get([:activitypub, :follow_handshake_timeout]),
follower,
followed
) do
render(conn, AccountView, "relationship.json", %{user: follower, target: followed})
else
{:error, message} ->
......@@ -880,6 +882,8 @@ def index(%{assigns: %{user: user}} = conn, _params) do
if user && token do
mastodon_emoji = mastodonized_emoji()
limit = Pleroma.Config.get([:instance, :limit])
accounts =
Map.put(%{}, user.id, AccountView.render("account.json", %{user: user, for: user}))
......@@ -899,7 +903,7 @@ def index(%{assigns: %{user: user}} = conn, _params) do
auto_play_gif: false,
display_sensitive_media: false,
reduce_motion: false,
max_toot_chars: Keyword.get(@instance, :limit)
max_toot_chars: limit
},
rights: %{
delete_others_notice: !!user.info["is_moderator"]
......@@ -959,7 +963,7 @@ def index(%{assigns: %{user: user}} = conn, _params) do
push_subscription: nil,
accounts: accounts,
custom_emojis: mastodon_emoji,
char_limit: Keyword.get(@instance, :limit)
char_limit: limit
}
|> Jason.encode!()
......@@ -1165,18 +1169,15 @@ def errors(conn, _) do
|> json("Something went wrong")
end
@suggestions Application.get_env(:pleroma, :suggestions)
def suggestions(%{assigns: %{user: user}} = conn, _) do
if Keyword.get(@suggestions, :enabled, false) do
api = Keyword.get(@suggestions, :third_party_engine, "")
timeout = Keyword.get(@suggestions, :timeout, 5000)
limit = Keyword.get(@suggestions, :limit, 23)
host =
Application.get_env(:pleroma, Pleroma.Web.Endpoint)
|> Keyword.get(:url)
|> Keyword.get(:host)
suggestions = Pleroma.Config.get(:suggestions)
if Keyword.get(suggestions, :enabled, false) do
api = Keyword.get(suggestions, :third_party_engine, "")
timeout = Keyword.get(suggestions, :timeout, 5000)
limit = Keyword.get(suggestions, :limit, 23)
host = Pleroma.Config.get([Pleroma.Web.Endpoint, :url, :host])
user = user.nickname
url = String.replace(api, "{{host}}", host) |> String.replace("{{user}}", user)
......
......@@ -134,19 +134,20 @@ def do_remote_follow(%{assigns: %{user: user}} = conn, %{"user" => %{"id" => id}
end
end
@instance Application.get_env(:pleroma, :instance)
@instance_fe Application.get_env(:pleroma, :fe)