Simplified HTTP signature processing
[akkoma] / test / pleroma / web / plugs / http_signature_plug_test.exs
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.Plugs.HTTPSignaturePlugTest do
6 use Pleroma.Web.ConnCase, async: false
7 import Pleroma.Factory
8 alias Pleroma.Web.Plugs.HTTPSignaturePlug
9 alias Pleroma.Instances.Instance
10 alias Pleroma.Repo
11
12 import Plug.Conn
13 import Phoenix.Controller, only: [put_format: 2]
14 import Mock
15
16 setup_with_mocks([
17 {HTTPSignatures, [],
18 [
19 signature_for_conn: fn _ ->
20 %{"keyId" => "http://mastodon.example.org/users/admin#main-key"}
21 end,
22 validate_conn: fn conn ->
23 Map.get(conn.assigns, :valid_signature, true)
24 end
25 ]}
26 ]) do
27 :ok
28 end
29
30 defp submit_to_plug(host), do: submit_to_plug(host, :get, "/doesntmattter")
31
32 defp submit_to_plug(host, method, path) do
33 params = %{"actor" => "http://#{host}/users/admin"}
34
35 build_conn(method, path, params)
36 |> put_req_header(
37 "signature",
38 "keyId=\"http://#{host}/users/admin#main-key"
39 )
40 |> put_format("activity+json")
41 |> HTTPSignaturePlug.call(%{})
42 end
43
44 test "it call HTTPSignatures to check validity if the actor signed it" do
45 params = %{"actor" => "http://mastodon.example.org/users/admin"}
46 conn = build_conn(:get, "/doesntmattter", params)
47
48 conn =
49 conn
50 |> put_req_header(
51 "signature",
52 "keyId=\"http://mastodon.example.org/users/admin#main-key"
53 )
54 |> put_format("activity+json")
55 |> HTTPSignaturePlug.call(%{})
56
57 assert conn.assigns.valid_signature == true
58 assert conn.assigns.signature_actor_id == params["actor"]
59 assert conn.halted == false
60 assert called(HTTPSignatures.validate_conn(:_))
61 end
62
63 test "it sets request signatures property on the instance" do
64 host = "mastodon.example.org"
65 conn = submit_to_plug(host)
66 assert conn.assigns.valid_signature == true
67 instance = Repo.get_by(Instance, %{host: host})
68 assert instance.has_request_signatures
69 end
70
71 test "it does not set request signatures property on the instance when using inbox" do
72 host = "mastodon.example.org"
73 conn = submit_to_plug(host, :post, "/inbox")
74 assert conn.assigns.valid_signature == true
75
76 # we don't even create the instance entry if its just POST /inbox
77 refute Repo.get_by(Instance, %{host: host})
78 end
79
80 test "it does not set request signatures property on the instance when its cached" do
81 host = "mastodon.example.org"
82 Cachex.put(:request_signatures_cache, host, true)
83 conn = submit_to_plug(host)
84 assert conn.assigns.valid_signature == true
85
86 # we don't even create the instance entry if it was already done
87 refute Repo.get_by(Instance, %{host: host})
88 end
89
90 describe "requires a signature when `authorized_fetch_mode` is enabled" do
91 setup do
92 clear_config([:activitypub, :authorized_fetch_mode], true)
93
94 params = %{"actor" => "http://mastodon.example.org/users/admin"}
95 conn = build_conn(:get, "/doesntmattter", params) |> put_format("activity+json")
96
97 [conn: conn]
98 end
99
100 test "and signature is present and incorrect", %{conn: conn} do
101 conn =
102 conn
103 |> assign(:valid_signature, false)
104 |> put_req_header(
105 "signature",
106 "keyId=\"http://mastodon.example.org/users/admin#main-key"
107 )
108 |> HTTPSignaturePlug.call(%{})
109
110 assert conn.assigns.valid_signature == false
111 assert called(HTTPSignatures.validate_conn(:_))
112 end
113
114 test "and signature is correct", %{conn: conn} do
115 conn =
116 conn
117 |> put_req_header(
118 "signature",
119 "keyId=\"http://mastodon.example.org/users/admin#main-key"
120 )
121 |> HTTPSignaturePlug.call(%{})
122
123 assert conn.assigns.valid_signature == true
124 assert called(HTTPSignatures.validate_conn(:_))
125 end
126
127 test "and halts the connection when `signature` header is not present", %{conn: conn} do
128 conn = HTTPSignaturePlug.call(conn, %{})
129 assert conn.assigns[:valid_signature] == nil
130 end
131 end
132
133 test "aliases redirected /object endpoints", _ do
134 obj = insert(:note)
135 act = insert(:note_activity, note: obj)
136 params = %{"actor" => "someparam"}
137 path = URI.parse(obj.data["id"]).path
138 conn = build_conn(:get, path, params)
139
140 assert ["/notice/#{act.id}", "/notice/#{act.id}?actor=someparam"] ==
141 HTTPSignaturePlug.route_aliases(conn)
142 end
143 end