Merge branch 'secure-mode' into 'develop'
Reject requests from specified instances if `authorized_fetch_mode` is enabled See merge request pleroma/pleroma!3711
This commit is contained in:
commit
bef15cde61
|
@ -0,0 +1 @@
|
||||||
|
Add an option to reject certain domains when authorized fetch is enabled.
|
|
@ -192,6 +192,7 @@
|
||||||
allow_relay: true,
|
allow_relay: true,
|
||||||
public: true,
|
public: true,
|
||||||
quarantined_instances: [],
|
quarantined_instances: [],
|
||||||
|
rejected_instances: [],
|
||||||
static_dir: "instance/static/",
|
static_dir: "instance/static/",
|
||||||
allowed_post_formats: [
|
allowed_post_formats: [
|
||||||
"text/plain",
|
"text/plain",
|
||||||
|
|
|
@ -774,6 +774,18 @@
|
||||||
{"*.quarantined.com", "Reason"}
|
{"*.quarantined.com", "Reason"}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
%{
|
||||||
|
key: :rejected_instances,
|
||||||
|
type: {:list, :tuple},
|
||||||
|
key_placeholder: "instance",
|
||||||
|
value_placeholder: "reason",
|
||||||
|
description:
|
||||||
|
"List of ActivityPub instances to reject requests from if authorized_fetch_mode is enabled",
|
||||||
|
suggestions: [
|
||||||
|
{"rejected.com", "Reason"},
|
||||||
|
{"*.rejected.com", "Reason"}
|
||||||
|
]
|
||||||
|
},
|
||||||
%{
|
%{
|
||||||
key: :static_dir,
|
key: :static_dir,
|
||||||
type: :string,
|
type: :string,
|
||||||
|
|
|
@ -155,6 +155,10 @@
|
||||||
config :pleroma, Pleroma.Web.RichMedia.Helpers, config_impl: Pleroma.StaticStubbedConfigMock
|
config :pleroma, Pleroma.Web.RichMedia.Helpers, config_impl: Pleroma.StaticStubbedConfigMock
|
||||||
config :pleroma, Pleroma.Uploaders.IPFS, config_impl: Pleroma.UnstubbedConfigMock
|
config :pleroma, Pleroma.Uploaders.IPFS, config_impl: Pleroma.UnstubbedConfigMock
|
||||||
config :pleroma, Pleroma.Web.Plugs.HTTPSecurityPlug, config_impl: Pleroma.StaticStubbedConfigMock
|
config :pleroma, Pleroma.Web.Plugs.HTTPSecurityPlug, config_impl: Pleroma.StaticStubbedConfigMock
|
||||||
|
config :pleroma, Pleroma.Web.Plugs.HTTPSignaturePlug, config_impl: Pleroma.StaticStubbedConfigMock
|
||||||
|
|
||||||
|
config :pleroma, Pleroma.Web.Plugs.HTTPSignaturePlug,
|
||||||
|
http_signatures_impl: Pleroma.StubbedHTTPSignaturesMock
|
||||||
|
|
||||||
peer_module =
|
peer_module =
|
||||||
if String.to_integer(System.otp_release()) >= 25 do
|
if String.to_integer(System.otp_release()) >= 25 do
|
||||||
|
|
|
@ -41,6 +41,7 @@ To add configuration to your config file, you can copy it from the base config.
|
||||||
* `allow_relay`: Permits remote instances to subscribe to all public posts of your instance. This may increase the visibility of your instance.
|
* `allow_relay`: Permits remote instances to subscribe to all public posts of your instance. This may increase the visibility of your instance.
|
||||||
* `public`: Makes the client API in authenticated mode-only except for user-profiles. Useful for disabling the Local Timeline and The Whole Known Network. Note that there is a dependent setting restricting or allowing unauthenticated access to specific resources, see `restrict_unauthenticated` for more details.
|
* `public`: Makes the client API in authenticated mode-only except for user-profiles. Useful for disabling the Local Timeline and The Whole Known Network. Note that there is a dependent setting restricting or allowing unauthenticated access to specific resources, see `restrict_unauthenticated` for more details.
|
||||||
* `quarantined_instances`: ActivityPub instances where private (DMs, followers-only) activities will not be send.
|
* `quarantined_instances`: ActivityPub instances where private (DMs, followers-only) activities will not be send.
|
||||||
|
* `rejected_instances`: ActivityPub instances to reject requests from if authorized_fetch_mode is enabled.
|
||||||
* `allowed_post_formats`: MIME-type list of formats allowed to be posted (transformed into HTML).
|
* `allowed_post_formats`: MIME-type list of formats allowed to be posted (transformed into HTML).
|
||||||
* `extended_nickname_format`: Set to `true` to use extended local nicknames format (allows underscores/dashes). This will break federation with
|
* `extended_nickname_format`: Set to `true` to use extended local nicknames format (allows underscores/dashes). This will break federation with
|
||||||
older software for theses nicknames.
|
older software for theses nicknames.
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
defmodule Pleroma.HTTPSignaturesAPI do
|
||||||
|
@callback validate_conn(conn :: Plug.Conn.t()) :: boolean
|
||||||
|
@callback signature_for_conn(conn :: Plug.Conn.t()) :: map
|
||||||
|
end
|
|
@ -44,8 +44,7 @@ defp remove_suffix(uri, [test | rest]) do
|
||||||
defp remove_suffix(uri, []), do: uri
|
defp remove_suffix(uri, []), do: uri
|
||||||
|
|
||||||
def fetch_public_key(conn) do
|
def fetch_public_key(conn) do
|
||||||
with %{"keyId" => kid} <- HTTPSignatures.signature_for_conn(conn),
|
with {:ok, actor_id} <- get_actor_id(conn),
|
||||||
{:ok, actor_id} <- key_id_to_actor_id(kid),
|
|
||||||
{:ok, public_key} <- User.get_public_key_for_ap_id(actor_id) do
|
{:ok, public_key} <- User.get_public_key_for_ap_id(actor_id) do
|
||||||
{:ok, public_key}
|
{:ok, public_key}
|
||||||
else
|
else
|
||||||
|
@ -55,8 +54,7 @@ def fetch_public_key(conn) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def refetch_public_key(conn) do
|
def refetch_public_key(conn) do
|
||||||
with %{"keyId" => kid} <- HTTPSignatures.signature_for_conn(conn),
|
with {:ok, actor_id} <- get_actor_id(conn),
|
||||||
{:ok, actor_id} <- key_id_to_actor_id(kid),
|
|
||||||
{:ok, _user} <- ActivityPub.make_user_from_ap_id(actor_id),
|
{:ok, _user} <- ActivityPub.make_user_from_ap_id(actor_id),
|
||||||
{:ok, public_key} <- User.get_public_key_for_ap_id(actor_id) do
|
{:ok, public_key} <- User.get_public_key_for_ap_id(actor_id) do
|
||||||
{:ok, public_key}
|
{:ok, public_key}
|
||||||
|
@ -66,6 +64,16 @@ def refetch_public_key(conn) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def get_actor_id(conn) do
|
||||||
|
with %{"keyId" => kid} <- HTTPSignatures.signature_for_conn(conn),
|
||||||
|
{:ok, actor_id} <- key_id_to_actor_id(kid) do
|
||||||
|
{:ok, actor_id}
|
||||||
|
else
|
||||||
|
e ->
|
||||||
|
{:error, e}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def sign(%User{keys: keys} = user, headers) do
|
def sign(%User{keys: keys} = user, headers) do
|
||||||
with {:ok, private_key, _} <- Keys.keys_from_pem(keys) do
|
with {:ok, private_key, _} <- Keys.keys_from_pem(keys) do
|
||||||
HTTPSignatures.sign(private_key, user.ap_id <> "#main-key", headers)
|
HTTPSignatures.sign(private_key, user.ap_id <> "#main-key", headers)
|
||||||
|
|
|
@ -152,6 +152,7 @@ def features do
|
||||||
|
|
||||||
def federation do
|
def federation do
|
||||||
quarantined = Config.get([:instance, :quarantined_instances], [])
|
quarantined = Config.get([:instance, :quarantined_instances], [])
|
||||||
|
rejected = Config.get([:instance, :rejected_instances], [])
|
||||||
|
|
||||||
if Config.get([:mrf, :transparency]) do
|
if Config.get([:mrf, :transparency]) do
|
||||||
{:ok, data} = MRF.describe()
|
{:ok, data} = MRF.describe()
|
||||||
|
@ -171,6 +172,12 @@ def federation do
|
||||||
|> Enum.map(fn {instance, reason} -> {instance, %{"reason" => reason}} end)
|
|> Enum.map(fn {instance, reason} -> {instance, %{"reason" => reason}} end)
|
||||||
|> Map.new()
|
|> Map.new()
|
||||||
})
|
})
|
||||||
|
|> Map.put(
|
||||||
|
:rejected_instances,
|
||||||
|
rejected
|
||||||
|
|> Enum.map(fn {instance, reason} -> {instance, %{"reason" => reason}} end)
|
||||||
|
|> Map.new()
|
||||||
|
)
|
||||||
else
|
else
|
||||||
%{}
|
%{}
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,8 +7,18 @@ defmodule Pleroma.Web.Plugs.HTTPSignaturePlug do
|
||||||
|
|
||||||
import Plug.Conn
|
import Plug.Conn
|
||||||
import Phoenix.Controller, only: [get_format: 1, text: 2]
|
import Phoenix.Controller, only: [get_format: 1, text: 2]
|
||||||
|
|
||||||
|
alias Pleroma.Web.ActivityPub.MRF
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
|
@config_impl Application.compile_env(:pleroma, [__MODULE__, :config_impl], Pleroma.Config)
|
||||||
|
@http_signatures_impl Application.compile_env(
|
||||||
|
:pleroma,
|
||||||
|
[__MODULE__, :http_signatures_impl],
|
||||||
|
HTTPSignatures
|
||||||
|
)
|
||||||
|
|
||||||
def init(options) do
|
def init(options) do
|
||||||
options
|
options
|
||||||
end
|
end
|
||||||
|
@ -21,7 +31,9 @@ def call(conn, _opts) do
|
||||||
if get_format(conn) in ["json", "activity+json"] do
|
if get_format(conn) in ["json", "activity+json"] do
|
||||||
conn
|
conn
|
||||||
|> maybe_assign_valid_signature()
|
|> maybe_assign_valid_signature()
|
||||||
|
|> maybe_assign_actor_id()
|
||||||
|> maybe_require_signature()
|
|> maybe_require_signature()
|
||||||
|
|> maybe_filter_requests()
|
||||||
else
|
else
|
||||||
conn
|
conn
|
||||||
end
|
end
|
||||||
|
@ -35,7 +47,7 @@ defp validate_signature(conn, request_target) do
|
||||||
|> put_req_header("(request-target)", request_target)
|
|> put_req_header("(request-target)", request_target)
|
||||||
|> put_req_header("@request-target", request_target)
|
|> put_req_header("@request-target", request_target)
|
||||||
|
|
||||||
HTTPSignatures.validate_conn(conn)
|
@http_signatures_impl.validate_conn(conn)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp validate_signature(conn) do
|
defp validate_signature(conn) do
|
||||||
|
@ -85,6 +97,16 @@ defp maybe_assign_valid_signature(conn) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp maybe_assign_actor_id(%{assigns: %{valid_signature: true}} = conn) do
|
||||||
|
adapter = Application.get_env(:http_signatures, :adapter)
|
||||||
|
|
||||||
|
{:ok, actor_id} = adapter.get_actor_id(conn)
|
||||||
|
|
||||||
|
assign(conn, :actor_id, actor_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp maybe_assign_actor_id(conn), do: conn
|
||||||
|
|
||||||
defp has_signature_header?(conn) do
|
defp has_signature_header?(conn) do
|
||||||
conn |> get_req_header("signature") |> Enum.at(0, false)
|
conn |> get_req_header("signature") |> Enum.at(0, false)
|
||||||
end
|
end
|
||||||
|
@ -92,9 +114,9 @@ defp has_signature_header?(conn) do
|
||||||
defp maybe_require_signature(%{assigns: %{valid_signature: true}} = conn), do: conn
|
defp maybe_require_signature(%{assigns: %{valid_signature: true}} = conn), do: conn
|
||||||
|
|
||||||
defp maybe_require_signature(%{remote_ip: remote_ip} = conn) do
|
defp maybe_require_signature(%{remote_ip: remote_ip} = conn) do
|
||||||
if Pleroma.Config.get([:activitypub, :authorized_fetch_mode], false) do
|
if @config_impl.get([:activitypub, :authorized_fetch_mode], false) do
|
||||||
exceptions =
|
exceptions =
|
||||||
Pleroma.Config.get([:activitypub, :authorized_fetch_mode_exceptions], [])
|
@config_impl.get([:activitypub, :authorized_fetch_mode_exceptions], [])
|
||||||
|> Enum.map(&InetHelper.parse_cidr/1)
|
|> Enum.map(&InetHelper.parse_cidr/1)
|
||||||
|
|
||||||
if Enum.any?(exceptions, fn x -> InetCidr.contains?(x, remote_ip) end) do
|
if Enum.any?(exceptions, fn x -> InetCidr.contains?(x, remote_ip) end) do
|
||||||
|
@ -109,4 +131,29 @@ defp maybe_require_signature(%{remote_ip: remote_ip} = conn) do
|
||||||
conn
|
conn
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp maybe_filter_requests(%{halted: true} = conn), do: conn
|
||||||
|
|
||||||
|
defp maybe_filter_requests(conn) do
|
||||||
|
if @config_impl.get([:activitypub, :authorized_fetch_mode], false) and
|
||||||
|
conn.assigns[:actor_id] do
|
||||||
|
%{host: host} = URI.parse(conn.assigns.actor_id)
|
||||||
|
|
||||||
|
if MRF.subdomain_match?(rejected_domains(), host) do
|
||||||
|
conn
|
||||||
|
|> put_status(:unauthorized)
|
||||||
|
|> halt()
|
||||||
|
else
|
||||||
|
conn
|
||||||
|
end
|
||||||
|
else
|
||||||
|
conn
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp rejected_domains do
|
||||||
|
@config_impl.get([:instance, :rejected_instances])
|
||||||
|
|> Pleroma.Web.ActivityPub.MRF.instance_list_from_tuples()
|
||||||
|
|> Pleroma.Web.ActivityPub.MRF.subdomains_regex()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -67,6 +67,14 @@ test "it returns error when not found user" do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "get_actor_id/1" do
|
||||||
|
test "it returns actor id" do
|
||||||
|
ap_id = "https://mastodon.social/users/lambadalambda"
|
||||||
|
|
||||||
|
assert Signature.get_actor_id(make_fake_conn(ap_id)) == {:ok, ap_id}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "sign/2" do
|
describe "sign/2" do
|
||||||
test "it returns signature headers" do
|
test "it returns signature headers" do
|
||||||
user =
|
user =
|
||||||
|
|
|
@ -3,77 +3,89 @@
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.Plugs.HTTPSignaturePlugTest do
|
defmodule Pleroma.Web.Plugs.HTTPSignaturePlugTest do
|
||||||
use Pleroma.Web.ConnCase
|
use Pleroma.Web.ConnCase, async: true
|
||||||
|
|
||||||
|
alias Pleroma.StaticStubbedConfigMock, as: ConfigMock
|
||||||
|
alias Pleroma.StubbedHTTPSignaturesMock, as: HTTPSignaturesMock
|
||||||
alias Pleroma.Web.Plugs.HTTPSignaturePlug
|
alias Pleroma.Web.Plugs.HTTPSignaturePlug
|
||||||
|
|
||||||
import Plug.Conn
|
import Mox
|
||||||
import Phoenix.Controller, only: [put_format: 2]
|
import Phoenix.Controller, only: [put_format: 2]
|
||||||
import Mock
|
import Plug.Conn
|
||||||
|
|
||||||
test "it call HTTPSignatures to check validity if the actor sighed it" do
|
test "it calls HTTPSignatures to check validity if the actor signed it" do
|
||||||
params = %{"actor" => "http://mastodon.example.org/users/admin"}
|
params = %{"actor" => "http://mastodon.example.org/users/admin"}
|
||||||
conn = build_conn(:get, "/doesntmattter", params)
|
conn = build_conn(:get, "/doesntmattter", params)
|
||||||
|
|
||||||
with_mock HTTPSignatures, validate_conn: fn _ -> true end do
|
HTTPSignaturesMock
|
||||||
conn =
|
|> expect(:validate_conn, fn _ -> true end)
|
||||||
conn
|
|
||||||
|> put_req_header(
|
|
||||||
"signature",
|
|
||||||
"keyId=\"http://mastodon.example.org/users/admin#main-key"
|
|
||||||
)
|
|
||||||
|> put_format("activity+json")
|
|
||||||
|> HTTPSignaturePlug.call(%{})
|
|
||||||
|
|
||||||
assert conn.assigns.valid_signature == true
|
conn =
|
||||||
assert conn.halted == false
|
conn
|
||||||
assert called(HTTPSignatures.validate_conn(:_))
|
|> put_req_header(
|
||||||
end
|
"signature",
|
||||||
|
"keyId=\"http://mastodon.example.org/users/admin#main-key"
|
||||||
|
)
|
||||||
|
|> put_format("activity+json")
|
||||||
|
|> HTTPSignaturePlug.call(%{})
|
||||||
|
|
||||||
|
assert conn.assigns.valid_signature == true
|
||||||
|
assert conn.halted == false
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "requires a signature when `authorized_fetch_mode` is enabled" do
|
describe "requires a signature when `authorized_fetch_mode` is enabled" do
|
||||||
setup do
|
setup do
|
||||||
clear_config([:activitypub, :authorized_fetch_mode], true)
|
|
||||||
|
|
||||||
params = %{"actor" => "http://mastodon.example.org/users/admin"}
|
params = %{"actor" => "http://mastodon.example.org/users/admin"}
|
||||||
conn = build_conn(:get, "/doesntmattter", params) |> put_format("activity+json")
|
conn = build_conn(:get, "/doesntmattter", params) |> put_format("activity+json")
|
||||||
|
|
||||||
[conn: conn]
|
[conn: conn]
|
||||||
end
|
end
|
||||||
|
|
||||||
test "when signature header is present", %{conn: conn} do
|
test "when signature header is present", %{conn: orig_conn} do
|
||||||
with_mock HTTPSignatures, validate_conn: fn _ -> false end do
|
ConfigMock
|
||||||
conn =
|
|> expect(:get, fn [:activitypub, :authorized_fetch_mode], false -> true end)
|
||||||
conn
|
|> expect(:get, fn [:activitypub, :authorized_fetch_mode_exceptions], [] -> [] end)
|
||||||
|> put_req_header(
|
|
||||||
"signature",
|
|
||||||
"keyId=\"http://mastodon.example.org/users/admin#main-key"
|
|
||||||
)
|
|
||||||
|> HTTPSignaturePlug.call(%{})
|
|
||||||
|
|
||||||
assert conn.assigns.valid_signature == false
|
HTTPSignaturesMock
|
||||||
assert conn.halted == true
|
|> expect(:validate_conn, 2, fn _ -> false end)
|
||||||
assert conn.status == 401
|
|
||||||
assert conn.state == :sent
|
|
||||||
assert conn.resp_body == "Request not signed"
|
|
||||||
assert called(HTTPSignatures.validate_conn(:_))
|
|
||||||
end
|
|
||||||
|
|
||||||
with_mock HTTPSignatures, validate_conn: fn _ -> true end do
|
conn =
|
||||||
conn =
|
orig_conn
|
||||||
conn
|
|> put_req_header(
|
||||||
|> put_req_header(
|
"signature",
|
||||||
"signature",
|
"keyId=\"http://mastodon.example.org/users/admin#main-key"
|
||||||
"keyId=\"http://mastodon.example.org/users/admin#main-key"
|
)
|
||||||
)
|
|> HTTPSignaturePlug.call(%{})
|
||||||
|> HTTPSignaturePlug.call(%{})
|
|
||||||
|
|
||||||
assert conn.assigns.valid_signature == true
|
assert conn.assigns.valid_signature == false
|
||||||
assert conn.halted == false
|
assert conn.halted == true
|
||||||
assert called(HTTPSignatures.validate_conn(:_))
|
assert conn.status == 401
|
||||||
end
|
assert conn.state == :sent
|
||||||
|
assert conn.resp_body == "Request not signed"
|
||||||
|
|
||||||
|
ConfigMock
|
||||||
|
|> expect(:get, fn [:activitypub, :authorized_fetch_mode], false -> true end)
|
||||||
|
|
||||||
|
HTTPSignaturesMock
|
||||||
|
|> expect(:validate_conn, fn _ -> true end)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
orig_conn
|
||||||
|
|> put_req_header(
|
||||||
|
"signature",
|
||||||
|
"keyId=\"http://mastodon.example.org/users/admin#main-key"
|
||||||
|
)
|
||||||
|
|> HTTPSignaturePlug.call(%{})
|
||||||
|
|
||||||
|
assert conn.assigns.valid_signature == true
|
||||||
|
assert conn.halted == false
|
||||||
end
|
end
|
||||||
|
|
||||||
test "halts the connection when `signature` header is not present", %{conn: conn} do
|
test "halts the connection when `signature` header is not present", %{conn: conn} do
|
||||||
|
ConfigMock
|
||||||
|
|> expect(:get, fn [:activitypub, :authorized_fetch_mode], false -> true end)
|
||||||
|
|> expect(:get, fn [:activitypub, :authorized_fetch_mode_exceptions], [] -> [] end)
|
||||||
|
|
||||||
conn = HTTPSignaturePlug.call(conn, %{})
|
conn = HTTPSignaturePlug.call(conn, %{})
|
||||||
assert conn.assigns[:valid_signature] == nil
|
assert conn.assigns[:valid_signature] == nil
|
||||||
assert conn.halted == true
|
assert conn.halted == true
|
||||||
|
@ -83,22 +95,71 @@ test "halts the connection when `signature` header is not present", %{conn: conn
|
||||||
end
|
end
|
||||||
|
|
||||||
test "exempts specific IPs from `authorized_fetch_mode_exceptions`", %{conn: conn} do
|
test "exempts specific IPs from `authorized_fetch_mode_exceptions`", %{conn: conn} do
|
||||||
clear_config([:activitypub, :authorized_fetch_mode_exceptions], ["192.168.0.0/24"])
|
ConfigMock
|
||||||
|
|> expect(:get, fn [:activitypub, :authorized_fetch_mode], false -> true end)
|
||||||
|
|> expect(:get, fn [:activitypub, :authorized_fetch_mode_exceptions], [] ->
|
||||||
|
["192.168.0.0/24"]
|
||||||
|
end)
|
||||||
|
|> expect(:get, fn [:activitypub, :authorized_fetch_mode], false -> true end)
|
||||||
|
|
||||||
with_mock HTTPSignatures, validate_conn: fn _ -> false end do
|
HTTPSignaturesMock
|
||||||
conn =
|
|> expect(:validate_conn, 2, fn _ -> false end)
|
||||||
conn
|
|
||||||
|> Map.put(:remote_ip, {192, 168, 0, 1})
|
|
||||||
|> put_req_header(
|
|
||||||
"signature",
|
|
||||||
"keyId=\"http://mastodon.example.org/users/admin#main-key"
|
|
||||||
)
|
|
||||||
|> HTTPSignaturePlug.call(%{})
|
|
||||||
|
|
||||||
assert conn.remote_ip == {192, 168, 0, 1}
|
conn =
|
||||||
assert conn.halted == false
|
conn
|
||||||
assert called(HTTPSignatures.validate_conn(:_))
|
|> Map.put(:remote_ip, {192, 168, 0, 1})
|
||||||
end
|
|> put_req_header(
|
||||||
|
"signature",
|
||||||
|
"keyId=\"http://mastodon.example.org/users/admin#main-key"
|
||||||
|
)
|
||||||
|
|> HTTPSignaturePlug.call(%{})
|
||||||
|
|
||||||
|
assert conn.remote_ip == {192, 168, 0, 1}
|
||||||
|
assert conn.halted == false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "rejects requests from `rejected_instances` when `authorized_fetch_mode` is enabled" do
|
||||||
|
ConfigMock
|
||||||
|
|> expect(:get, fn [:activitypub, :authorized_fetch_mode], false -> true end)
|
||||||
|
|> expect(:get, fn [:instance, :rejected_instances] ->
|
||||||
|
[{"mastodon.example.org", "no reason"}]
|
||||||
|
end)
|
||||||
|
|
||||||
|
HTTPSignaturesMock
|
||||||
|
|> expect(:validate_conn, fn _ -> true end)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn(:get, "/doesntmattter", %{"actor" => "http://mastodon.example.org/users/admin"})
|
||||||
|
|> put_req_header(
|
||||||
|
"signature",
|
||||||
|
"keyId=\"http://mastodon.example.org/users/admin#main-key"
|
||||||
|
)
|
||||||
|
|> put_format("activity+json")
|
||||||
|
|> HTTPSignaturePlug.call(%{})
|
||||||
|
|
||||||
|
assert conn.assigns.valid_signature == true
|
||||||
|
assert conn.halted == true
|
||||||
|
|
||||||
|
ConfigMock
|
||||||
|
|> expect(:get, fn [:activitypub, :authorized_fetch_mode], false -> true end)
|
||||||
|
|> expect(:get, fn [:instance, :rejected_instances] ->
|
||||||
|
[{"mastodon.example.org", "no reason"}]
|
||||||
|
end)
|
||||||
|
|
||||||
|
HTTPSignaturesMock
|
||||||
|
|> expect(:validate_conn, fn _ -> true end)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn(:get, "/doesntmattter", %{"actor" => "http://allowed.example.org/users/admin"})
|
||||||
|
|> put_req_header(
|
||||||
|
"signature",
|
||||||
|
"keyId=\"http://allowed.example.org/users/admin#main-key"
|
||||||
|
)
|
||||||
|
|> put_format("activity+json")
|
||||||
|
|> HTTPSignaturePlug.call(%{})
|
||||||
|
|
||||||
|
assert conn.assigns.valid_signature == true
|
||||||
|
assert conn.halted == false
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -116,6 +116,7 @@ def stub_pipeline do
|
||||||
Mox.stub_with(Pleroma.Web.FederatorMock, Pleroma.Web.Federator)
|
Mox.stub_with(Pleroma.Web.FederatorMock, Pleroma.Web.Federator)
|
||||||
Mox.stub_with(Pleroma.ConfigMock, Pleroma.Config)
|
Mox.stub_with(Pleroma.ConfigMock, Pleroma.Config)
|
||||||
Mox.stub_with(Pleroma.StaticStubbedConfigMock, Pleroma.Test.StaticConfig)
|
Mox.stub_with(Pleroma.StaticStubbedConfigMock, Pleroma.Test.StaticConfig)
|
||||||
|
Mox.stub_with(Pleroma.StubbedHTTPSignaturesMock, Pleroma.Test.HTTPSignaturesProxy)
|
||||||
end
|
end
|
||||||
|
|
||||||
def ensure_local_uploader(context) do
|
def ensure_local_uploader(context) do
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
defmodule Pleroma.Test.HTTPSignaturesProxy do
|
||||||
|
@behaviour Pleroma.HTTPSignaturesAPI
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
defdelegate validate_conn(conn), to: HTTPSignatures
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
defdelegate signature_for_conn(conn), to: HTTPSignatures
|
||||||
|
end
|
|
@ -28,6 +28,7 @@
|
||||||
Mox.defmock(Pleroma.ConfigMock, for: Pleroma.Config.Getting)
|
Mox.defmock(Pleroma.ConfigMock, for: Pleroma.Config.Getting)
|
||||||
Mox.defmock(Pleroma.UnstubbedConfigMock, for: Pleroma.Config.Getting)
|
Mox.defmock(Pleroma.UnstubbedConfigMock, for: Pleroma.Config.Getting)
|
||||||
Mox.defmock(Pleroma.StaticStubbedConfigMock, for: Pleroma.Config.Getting)
|
Mox.defmock(Pleroma.StaticStubbedConfigMock, for: Pleroma.Config.Getting)
|
||||||
|
Mox.defmock(Pleroma.StubbedHTTPSignaturesMock, for: Pleroma.HTTPSignaturesAPI)
|
||||||
|
|
||||||
Mox.defmock(Pleroma.LoggerMock, for: Pleroma.Logging)
|
Mox.defmock(Pleroma.LoggerMock, for: Pleroma.Logging)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue