summaryrefslogtreecommitdiff
path: root/lib/pleroma/web/media_proxy
diff options
context:
space:
mode:
Diffstat (limited to 'lib/pleroma/web/media_proxy')
-rw-r--r--lib/pleroma/web/media_proxy/invalidation.ex38
-rw-r--r--lib/pleroma/web/media_proxy/invalidations/http.ex40
-rw-r--r--lib/pleroma/web/media_proxy/invalidations/script.ex43
-rw-r--r--lib/pleroma/web/media_proxy/media_proxy.ex64
-rw-r--r--lib/pleroma/web/media_proxy/media_proxy_controller.ex4
5 files changed, 177 insertions, 12 deletions
diff --git a/lib/pleroma/web/media_proxy/invalidation.ex b/lib/pleroma/web/media_proxy/invalidation.ex
new file mode 100644
index 000000000..5808861e6
--- /dev/null
+++ b/lib/pleroma/web/media_proxy/invalidation.ex
@@ -0,0 +1,38 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.MediaProxy.Invalidation do
+ @moduledoc false
+
+ @callback purge(list(String.t()), Keyword.t()) :: {:ok, list(String.t())} | {:error, String.t()}
+
+ alias Pleroma.Config
+ alias Pleroma.Web.MediaProxy
+
+ @spec enabled?() :: boolean()
+ def enabled?, do: Config.get([:media_proxy, :invalidation, :enabled])
+
+ @spec purge(list(String.t()) | String.t()) :: {:ok, list(String.t())} | {:error, String.t()}
+ def purge(urls) do
+ prepared_urls = prepare_urls(urls)
+
+ if enabled?() do
+ do_purge(prepared_urls)
+ else
+ {:ok, prepared_urls}
+ end
+ end
+
+ defp do_purge(urls) do
+ provider = Config.get([:media_proxy, :invalidation, :provider])
+ options = Config.get(provider)
+ provider.purge(urls, options)
+ end
+
+ def prepare_urls(urls) do
+ urls
+ |> List.wrap()
+ |> Enum.map(&MediaProxy.url/1)
+ end
+end
diff --git a/lib/pleroma/web/media_proxy/invalidations/http.ex b/lib/pleroma/web/media_proxy/invalidations/http.ex
new file mode 100644
index 000000000..bb81d8888
--- /dev/null
+++ b/lib/pleroma/web/media_proxy/invalidations/http.ex
@@ -0,0 +1,40 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.MediaProxy.Invalidation.Http do
+ @moduledoc false
+ @behaviour Pleroma.Web.MediaProxy.Invalidation
+
+ require Logger
+
+ @impl Pleroma.Web.MediaProxy.Invalidation
+ def purge(urls, opts \\ []) do
+ method = Keyword.get(opts, :method, :purge)
+ headers = Keyword.get(opts, :headers, [])
+ options = Keyword.get(opts, :options, [])
+
+ Logger.debug("Running cache purge: #{inspect(urls)}")
+
+ Enum.each(urls, fn url ->
+ with {:error, error} <- do_purge(method, url, headers, options) do
+ Logger.error("Error while cache purge: url - #{url}, error: #{inspect(error)}")
+ end
+ end)
+
+ {:ok, urls}
+ end
+
+ defp do_purge(method, url, headers, options) do
+ case Pleroma.HTTP.request(method, url, "", headers, options) do
+ {:ok, %{status: status} = env} when 400 <= status and status < 500 ->
+ {:error, env}
+
+ {:error, error} = error ->
+ error
+
+ _ ->
+ {:ok, "success"}
+ end
+ end
+end
diff --git a/lib/pleroma/web/media_proxy/invalidations/script.ex b/lib/pleroma/web/media_proxy/invalidations/script.ex
new file mode 100644
index 000000000..d32ffc50b
--- /dev/null
+++ b/lib/pleroma/web/media_proxy/invalidations/script.ex
@@ -0,0 +1,43 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.MediaProxy.Invalidation.Script do
+ @moduledoc false
+
+ @behaviour Pleroma.Web.MediaProxy.Invalidation
+
+ require Logger
+
+ @impl Pleroma.Web.MediaProxy.Invalidation
+ def purge(urls, opts \\ []) do
+ args =
+ urls
+ |> List.wrap()
+ |> Enum.uniq()
+ |> Enum.join(" ")
+
+ opts
+ |> Keyword.get(:script_path)
+ |> do_purge([args])
+ |> handle_result(urls)
+ end
+
+ defp do_purge(script_path, args) when is_binary(script_path) do
+ path = Path.expand(script_path)
+ Logger.debug("Running cache purge: #{inspect(args)}, #{inspect(path)}")
+ System.cmd(path, args)
+ rescue
+ error -> error
+ end
+
+ defp do_purge(_, _), do: {:error, "not found script path"}
+
+ defp handle_result({_result, 0}, urls), do: {:ok, urls}
+ defp handle_result({:error, error}, urls), do: handle_result(error, urls)
+
+ defp handle_result(error, _) do
+ Logger.error("Error while cache purge: #{inspect(error)}")
+ {:error, inspect(error)}
+ end
+end
diff --git a/lib/pleroma/web/media_proxy/media_proxy.ex b/lib/pleroma/web/media_proxy/media_proxy.ex
index b2b524524..e18dd8224 100644
--- a/lib/pleroma/web/media_proxy/media_proxy.ex
+++ b/lib/pleroma/web/media_proxy/media_proxy.ex
@@ -6,20 +6,56 @@ defmodule Pleroma.Web.MediaProxy do
alias Pleroma.Config
alias Pleroma.Upload
alias Pleroma.Web
+ alias Pleroma.Web.MediaProxy.Invalidation
@base64_opts [padding: false]
+ @cache_table :banned_urls_cache
+
+ def cache_table, do: @cache_table
+
+ @spec in_banned_urls(String.t()) :: boolean()
+ def in_banned_urls(url), do: elem(Cachex.exists?(@cache_table, url(url)), 1)
+
+ def remove_from_banned_urls(urls) when is_list(urls) do
+ Cachex.execute!(@cache_table, fn cache ->
+ Enum.each(Invalidation.prepare_urls(urls), &Cachex.del(cache, &1))
+ end)
+ end
+
+ def remove_from_banned_urls(url) when is_binary(url) do
+ Cachex.del(@cache_table, url(url))
+ end
+
+ def put_in_banned_urls(urls) when is_list(urls) do
+ Cachex.execute!(@cache_table, fn cache ->
+ Enum.each(Invalidation.prepare_urls(urls), &Cachex.put(cache, &1, true))
+ end)
+ end
+
+ def put_in_banned_urls(url) when is_binary(url) do
+ Cachex.put(@cache_table, url(url), true)
+ end
def url(url) when is_nil(url) or url == "", do: nil
def url("/" <> _ = url), do: url
def url(url) do
- if disabled?() or local?(url) or whitelisted?(url) do
+ if disabled?() or not url_proxiable?(url) do
url
else
encode_url(url)
end
end
+ @spec url_proxiable?(String.t()) :: boolean()
+ def url_proxiable?(url) do
+ if local?(url) or whitelisted?(url) do
+ false
+ else
+ true
+ end
+ end
+
defp disabled?, do: !Config.get([:media_proxy, :enabled], false)
defp local?(url), do: String.starts_with?(url, Pleroma.Web.base_url())
@@ -27,22 +63,28 @@ defmodule Pleroma.Web.MediaProxy do
defp whitelisted?(url) do
%{host: domain} = URI.parse(url)
- mediaproxy_whitelist = Config.get([:media_proxy, :whitelist])
+ mediaproxy_whitelist_domains =
+ [:media_proxy, :whitelist]
+ |> Config.get()
+ |> Enum.map(&maybe_get_domain_from_url/1)
- upload_base_url_domain =
- if !is_nil(Config.get([Upload, :base_url])) do
- [URI.parse(Config.get([Upload, :base_url])).host]
+ whitelist_domains =
+ if base_url = Config.get([Upload, :base_url]) do
+ %{host: base_domain} = URI.parse(base_url)
+ [base_domain | mediaproxy_whitelist_domains]
else
- []
+ mediaproxy_whitelist_domains
end
- whitelist = mediaproxy_whitelist ++ upload_base_url_domain
+ domain in whitelist_domains
+ end
- Enum.any?(whitelist, fn pattern ->
- String.equivalent?(domain, pattern)
- end)
+ defp maybe_get_domain_from_url("http" <> _ = url) do
+ URI.parse(url).host
end
+ defp maybe_get_domain_from_url(domain), do: domain
+
def encode_url(url) do
base64 = Base.url_encode64(url, @base64_opts)
@@ -73,7 +115,7 @@ defmodule Pleroma.Web.MediaProxy do
def build_url(sig_base64, url_base64, filename \\ nil) do
[
- Pleroma.Config.get([:media_proxy, :base_url], Web.base_url()),
+ Config.get([:media_proxy, :base_url], Web.base_url()),
"proxy",
sig_base64,
url_base64,
diff --git a/lib/pleroma/web/media_proxy/media_proxy_controller.ex b/lib/pleroma/web/media_proxy/media_proxy_controller.ex
index 1a09ac62a..9a64b0ef3 100644
--- a/lib/pleroma/web/media_proxy/media_proxy_controller.ex
+++ b/lib/pleroma/web/media_proxy/media_proxy_controller.ex
@@ -4,6 +4,7 @@
defmodule Pleroma.Web.MediaProxy.MediaProxyController do
use Pleroma.Web, :controller
+
alias Pleroma.ReverseProxy
alias Pleroma.Web.MediaProxy
@@ -13,10 +14,11 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyController do
with config <- Pleroma.Config.get([:media_proxy], []),
true <- Keyword.get(config, :enabled, false),
{:ok, url} <- MediaProxy.decode_url(sig64, url64),
+ {_, false} <- {:in_banned_urls, MediaProxy.in_banned_urls(url)},
:ok <- filename_matches(params, conn.request_path, url) do
ReverseProxy.call(conn, url, Keyword.get(config, :proxy_opts, @default_proxy_opts))
else
- false ->
+ error when error in [false, {:in_banned_urls, true}] ->
send_resp(conn, 404, Plug.Conn.Status.reason_phrase(404))
{:error, :invalid_signature} ->