46ca545de7be258db102542fae6d2def3d2ce63e
[akkoma] / test / web / websub / websub_test.exs
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.WebsubTest do
6 use Pleroma.DataCase
7 use Oban.Testing, repo: Pleroma.Repo
8
9 alias Pleroma.Tests.ObanHelpers
10 alias Pleroma.Web.Router.Helpers
11 alias Pleroma.Web.Websub
12 alias Pleroma.Web.Websub.WebsubClientSubscription
13 alias Pleroma.Web.Websub.WebsubServerSubscription
14 alias Pleroma.Workers.SubscriberWorker
15
16 import Pleroma.Factory
17 import Tesla.Mock
18
19 setup do
20 mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
21 :ok
22 end
23
24 test "a verification of a request that is accepted" do
25 sub = insert(:websub_subscription)
26 topic = sub.topic
27
28 getter = fn _path, _headers, options ->
29 %{
30 "hub.challenge": challenge,
31 "hub.lease_seconds": seconds,
32 "hub.topic": ^topic,
33 "hub.mode": "subscribe"
34 } = Keyword.get(options, :params)
35
36 assert String.to_integer(seconds) > 0
37
38 {:ok,
39 %Tesla.Env{
40 status: 200,
41 body: challenge
42 }}
43 end
44
45 {:ok, sub} = Websub.verify(sub, getter)
46 assert sub.state == "active"
47 end
48
49 test "a verification of a request that doesn't return 200" do
50 sub = insert(:websub_subscription)
51
52 getter = fn _path, _headers, _options ->
53 {:ok,
54 %Tesla.Env{
55 status: 500,
56 body: ""
57 }}
58 end
59
60 {:error, sub} = Websub.verify(sub, getter)
61 # Keep the current state.
62 assert sub.state == "requested"
63 end
64
65 test "an incoming subscription request" do
66 user = insert(:user)
67
68 data = %{
69 "hub.callback" => "http://example.org/sub",
70 "hub.mode" => "subscribe",
71 "hub.topic" => Pleroma.Web.OStatus.feed_path(user),
72 "hub.secret" => "a random secret",
73 "hub.lease_seconds" => "100"
74 }
75
76 {:ok, subscription} = Websub.incoming_subscription_request(user, data)
77 assert subscription.topic == Pleroma.Web.OStatus.feed_path(user)
78 assert subscription.state == "requested"
79 assert subscription.secret == "a random secret"
80 assert subscription.callback == "http://example.org/sub"
81 end
82
83 test "an incoming subscription request for an existing subscription" do
84 user = insert(:user)
85
86 sub =
87 insert(:websub_subscription, state: "accepted", topic: Pleroma.Web.OStatus.feed_path(user))
88
89 data = %{
90 "hub.callback" => sub.callback,
91 "hub.mode" => "subscribe",
92 "hub.topic" => Pleroma.Web.OStatus.feed_path(user),
93 "hub.secret" => "a random secret",
94 "hub.lease_seconds" => "100"
95 }
96
97 {:ok, subscription} = Websub.incoming_subscription_request(user, data)
98 assert subscription.topic == Pleroma.Web.OStatus.feed_path(user)
99 assert subscription.state == sub.state
100 assert subscription.secret == "a random secret"
101 assert subscription.callback == sub.callback
102 assert length(Repo.all(WebsubServerSubscription)) == 1
103 assert subscription.id == sub.id
104 end
105
106 def accepting_verifier(subscription) do
107 {:ok, %{subscription | state: "accepted"}}
108 end
109
110 test "initiate a subscription for a given user and topic" do
111 subscriber = insert(:user)
112 user = insert(:user, %{info: %Pleroma.User.Info{topic: "some_topic", hub: "some_hub"}})
113
114 {:ok, websub} = Websub.subscribe(subscriber, user, &accepting_verifier/1)
115 assert websub.subscribers == [subscriber.ap_id]
116 assert websub.topic == "some_topic"
117 assert websub.hub == "some_hub"
118 assert is_binary(websub.secret)
119 assert websub.user == user
120 assert websub.state == "accepted"
121 end
122
123 test "discovers the hub and canonical url" do
124 topic = "https://mastodon.social/users/lambadalambda.atom"
125
126 {:ok, discovered} = Websub.gather_feed_data(topic)
127
128 expected = %{
129 "hub" => "https://mastodon.social/api/push",
130 "uri" => "https://mastodon.social/users/lambadalambda",
131 "nickname" => "lambadalambda",
132 "name" => "Critical Value",
133 "host" => "mastodon.social",
134 "bio" => "a cool dude.",
135 "avatar" => %{
136 "type" => "Image",
137 "url" => [
138 %{
139 "href" =>
140 "https://files.mastodon.social/accounts/avatars/000/000/264/original/1429214160519.gif?1492379244",
141 "mediaType" => "image/gif",
142 "type" => "Link"
143 }
144 ]
145 }
146 }
147
148 assert expected == discovered
149 end
150
151 test "calls the hub, requests topic" do
152 hub = "https://social.heldscal.la/main/push/hub"
153 topic = "https://social.heldscal.la/api/statuses/user_timeline/23211.atom"
154 websub = insert(:websub_client_subscription, %{hub: hub, topic: topic})
155
156 poster = fn ^hub, {:form, data}, _headers ->
157 assert Keyword.get(data, :"hub.mode") == "subscribe"
158
159 assert Keyword.get(data, :"hub.callback") ==
160 Helpers.websub_url(
161 Pleroma.Web.Endpoint,
162 :websub_subscription_confirmation,
163 websub.id
164 )
165
166 {:ok, %{status: 202}}
167 end
168
169 task = Task.async(fn -> Websub.request_subscription(websub, poster) end)
170
171 change = Ecto.Changeset.change(websub, %{state: "accepted"})
172 {:ok, _} = Repo.update(change)
173
174 {:ok, websub} = Task.await(task)
175
176 assert websub.state == "accepted"
177 end
178
179 test "rejects the subscription if it can't be accepted" do
180 hub = "https://social.heldscal.la/main/push/hub"
181 topic = "https://social.heldscal.la/api/statuses/user_timeline/23211.atom"
182 websub = insert(:websub_client_subscription, %{hub: hub, topic: topic})
183
184 poster = fn ^hub, {:form, _data}, _headers ->
185 {:ok, %{status: 202}}
186 end
187
188 {:error, websub} = Websub.request_subscription(websub, poster, 1000)
189 assert websub.state == "rejected"
190
191 websub = insert(:websub_client_subscription, %{hub: hub, topic: topic})
192
193 poster = fn ^hub, {:form, _data}, _headers ->
194 {:ok, %{status: 400}}
195 end
196
197 {:error, websub} = Websub.request_subscription(websub, poster, 1000)
198 assert websub.state == "rejected"
199 end
200
201 test "sign a text" do
202 signed = Websub.sign("secret", "text")
203 assert signed == "B8392C23690CCF871F37EC270BE1582DEC57A503" |> String.downcase()
204
205 _signed = Websub.sign("secret", [["て"], ['す']])
206 end
207
208 describe "renewing subscriptions" do
209 test "it renews subscriptions that have less than a day of time left" do
210 day = 60 * 60 * 24
211 now = NaiveDateTime.utc_now()
212
213 still_good =
214 insert(:websub_client_subscription, %{
215 valid_until: NaiveDateTime.add(now, 2 * day),
216 topic: "http://example.org/still_good",
217 hub: "http://example.org/still_good",
218 state: "accepted"
219 })
220
221 needs_refresh =
222 insert(:websub_client_subscription, %{
223 valid_until: NaiveDateTime.add(now, day - 100),
224 topic: "http://example.org/needs_refresh",
225 hub: "http://example.org/needs_refresh",
226 state: "accepted"
227 })
228
229 _refresh = Websub.refresh_subscriptions()
230 ObanHelpers.perform(all_enqueued(worker: SubscriberWorker))
231
232 assert still_good == Repo.get(WebsubClientSubscription, still_good.id)
233 refute needs_refresh == Repo.get(WebsubClientSubscription, needs_refresh.id)
234 end
235 end
236 end