Merge remote-tracking branch 'upstream/develop' into registration-workflow
[akkoma] / test / pleroma / web / media_proxy / media_proxy_controller_test.exs
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.MediaProxy.MediaProxyControllerTest do
6 use Pleroma.Web.ConnCase
7
8 import Mock
9
10 alias Pleroma.Web.MediaProxy
11 alias Plug.Conn
12
13 setup do
14 on_exit(fn -> Cachex.clear(:banned_urls_cache) end)
15 end
16
17 describe "Media Proxy" do
18 setup do
19 clear_config([:media_proxy, :enabled], true)
20 clear_config([Pleroma.Web.Endpoint, :secret_key_base], "00000000000")
21
22 [url: MediaProxy.encode_url("https://google.fn/test.png")]
23 end
24
25 test "it returns 404 when disabled", %{conn: conn} do
26 clear_config([:media_proxy, :enabled], false)
27
28 assert %Conn{
29 status: 404,
30 resp_body: "Not Found"
31 } = get(conn, "/proxy/hhgfh/eeeee")
32
33 assert %Conn{
34 status: 404,
35 resp_body: "Not Found"
36 } = get(conn, "/proxy/hhgfh/eeee/fff")
37 end
38
39 test "it returns 403 for invalid signature", %{conn: conn, url: url} do
40 Pleroma.Config.put([Pleroma.Web.Endpoint, :secret_key_base], "000")
41 %{path: path} = URI.parse(url)
42
43 assert %Conn{
44 status: 403,
45 resp_body: "Forbidden"
46 } = get(conn, path)
47
48 assert %Conn{
49 status: 403,
50 resp_body: "Forbidden"
51 } = get(conn, "/proxy/hhgfh/eeee")
52
53 assert %Conn{
54 status: 403,
55 resp_body: "Forbidden"
56 } = get(conn, "/proxy/hhgfh/eeee/fff")
57 end
58
59 test "redirects to valid url when filename is invalidated", %{conn: conn, url: url} do
60 invalid_url = String.replace(url, "test.png", "test-file.png")
61 response = get(conn, invalid_url)
62 assert response.status == 302
63 assert redirected_to(response) == url
64 end
65
66 test "it performs ReverseProxy.call with valid signature", %{conn: conn, url: url} do
67 with_mock Pleroma.ReverseProxy,
68 call: fn _conn, _url, _opts -> %Conn{status: :success} end do
69 assert %Conn{status: :success} = get(conn, url)
70 end
71 end
72
73 test "it returns 404 when url is in banned_urls cache", %{conn: conn, url: url} do
74 MediaProxy.put_in_banned_urls("https://google.fn/test.png")
75
76 with_mock Pleroma.ReverseProxy,
77 call: fn _conn, _url, _opts -> %Conn{status: :success} end do
78 assert %Conn{status: 404, resp_body: "Not Found"} = get(conn, url)
79 end
80 end
81 end
82
83 describe "Media Preview Proxy" do
84 def assert_dependencies_installed do
85 missing_dependencies = Pleroma.Helpers.MediaHelper.missing_dependencies()
86
87 assert missing_dependencies == [],
88 "Error: missing dependencies (please refer to `docs/installation`): #{
89 inspect(missing_dependencies)
90 }"
91 end
92
93 setup do
94 clear_config([:media_proxy, :enabled], true)
95 clear_config([:media_preview_proxy, :enabled], true)
96 clear_config([Pleroma.Web.Endpoint, :secret_key_base], "00000000000")
97
98 original_url = "https://google.fn/test.png"
99
100 [
101 url: MediaProxy.encode_preview_url(original_url),
102 media_proxy_url: MediaProxy.encode_url(original_url)
103 ]
104 end
105
106 test "returns 404 when media proxy is disabled", %{conn: conn} do
107 clear_config([:media_proxy, :enabled], false)
108
109 assert %Conn{
110 status: 404,
111 resp_body: "Not Found"
112 } = get(conn, "/proxy/preview/hhgfh/eeeee")
113
114 assert %Conn{
115 status: 404,
116 resp_body: "Not Found"
117 } = get(conn, "/proxy/preview/hhgfh/fff")
118 end
119
120 test "returns 404 when disabled", %{conn: conn} do
121 clear_config([:media_preview_proxy, :enabled], false)
122
123 assert %Conn{
124 status: 404,
125 resp_body: "Not Found"
126 } = get(conn, "/proxy/preview/hhgfh/eeeee")
127
128 assert %Conn{
129 status: 404,
130 resp_body: "Not Found"
131 } = get(conn, "/proxy/preview/hhgfh/fff")
132 end
133
134 test "it returns 403 for invalid signature", %{conn: conn, url: url} do
135 Pleroma.Config.put([Pleroma.Web.Endpoint, :secret_key_base], "000")
136 %{path: path} = URI.parse(url)
137
138 assert %Conn{
139 status: 403,
140 resp_body: "Forbidden"
141 } = get(conn, path)
142
143 assert %Conn{
144 status: 403,
145 resp_body: "Forbidden"
146 } = get(conn, "/proxy/preview/hhgfh/eeee")
147
148 assert %Conn{
149 status: 403,
150 resp_body: "Forbidden"
151 } = get(conn, "/proxy/preview/hhgfh/eeee/fff")
152 end
153
154 test "redirects to valid url when filename is invalidated", %{conn: conn, url: url} do
155 invalid_url = String.replace(url, "test.png", "test-file.png")
156 response = get(conn, invalid_url)
157 assert response.status == 302
158 assert redirected_to(response) == url
159 end
160
161 test "responds with 424 Failed Dependency if HEAD request to media proxy fails", %{
162 conn: conn,
163 url: url,
164 media_proxy_url: media_proxy_url
165 } do
166 Tesla.Mock.mock(fn
167 %{method: "head", url: ^media_proxy_url} ->
168 %Tesla.Env{status: 500, body: ""}
169 end)
170
171 response = get(conn, url)
172 assert response.status == 424
173 assert response.resp_body == "Can't fetch HTTP headers (HTTP 500)."
174 end
175
176 test "redirects to media proxy URI on unsupported content type", %{
177 conn: conn,
178 url: url,
179 media_proxy_url: media_proxy_url
180 } do
181 Tesla.Mock.mock(fn
182 %{method: "head", url: ^media_proxy_url} ->
183 %Tesla.Env{status: 200, body: "", headers: [{"content-type", "application/pdf"}]}
184 end)
185
186 response = get(conn, url)
187 assert response.status == 302
188 assert redirected_to(response) == media_proxy_url
189 end
190
191 test "with `static=true` and GIF image preview requested, responds with JPEG image", %{
192 conn: conn,
193 url: url,
194 media_proxy_url: media_proxy_url
195 } do
196 assert_dependencies_installed()
197
198 # Setting a high :min_content_length to ensure this scenario is not affected by its logic
199 clear_config([:media_preview_proxy, :min_content_length], 1_000_000_000)
200
201 Tesla.Mock.mock(fn
202 %{method: "head", url: ^media_proxy_url} ->
203 %Tesla.Env{
204 status: 200,
205 body: "",
206 headers: [{"content-type", "image/gif"}, {"content-length", "1001718"}]
207 }
208
209 %{method: :get, url: ^media_proxy_url} ->
210 %Tesla.Env{status: 200, body: File.read!("test/fixtures/image.gif")}
211 end)
212
213 response = get(conn, url <> "?static=true")
214
215 assert response.status == 200
216 assert Conn.get_resp_header(response, "content-type") == ["image/jpeg"]
217 assert response.resp_body != ""
218 end
219
220 test "with GIF image preview requested and no `static` param, redirects to media proxy URI",
221 %{
222 conn: conn,
223 url: url,
224 media_proxy_url: media_proxy_url
225 } do
226 Tesla.Mock.mock(fn
227 %{method: "head", url: ^media_proxy_url} ->
228 %Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/gif"}]}
229 end)
230
231 response = get(conn, url)
232
233 assert response.status == 302
234 assert redirected_to(response) == media_proxy_url
235 end
236
237 test "with `static` param and non-GIF image preview requested, " <>
238 "redirects to media preview proxy URI without `static` param",
239 %{
240 conn: conn,
241 url: url,
242 media_proxy_url: media_proxy_url
243 } do
244 Tesla.Mock.mock(fn
245 %{method: "head", url: ^media_proxy_url} ->
246 %Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/jpeg"}]}
247 end)
248
249 response = get(conn, url <> "?static=true")
250
251 assert response.status == 302
252 assert redirected_to(response) == url
253 end
254
255 test "with :min_content_length setting not matched by Content-Length header, " <>
256 "redirects to media proxy URI",
257 %{
258 conn: conn,
259 url: url,
260 media_proxy_url: media_proxy_url
261 } do
262 clear_config([:media_preview_proxy, :min_content_length], 100_000)
263
264 Tesla.Mock.mock(fn
265 %{method: "head", url: ^media_proxy_url} ->
266 %Tesla.Env{
267 status: 200,
268 body: "",
269 headers: [{"content-type", "image/gif"}, {"content-length", "5000"}]
270 }
271 end)
272
273 response = get(conn, url)
274
275 assert response.status == 302
276 assert redirected_to(response) == media_proxy_url
277 end
278
279 test "thumbnails PNG images into PNG", %{
280 conn: conn,
281 url: url,
282 media_proxy_url: media_proxy_url
283 } do
284 assert_dependencies_installed()
285
286 Tesla.Mock.mock(fn
287 %{method: "head", url: ^media_proxy_url} ->
288 %Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/png"}]}
289
290 %{method: :get, url: ^media_proxy_url} ->
291 %Tesla.Env{status: 200, body: File.read!("test/fixtures/image.png")}
292 end)
293
294 response = get(conn, url)
295
296 assert response.status == 200
297 assert Conn.get_resp_header(response, "content-type") == ["image/png"]
298 assert response.resp_body != ""
299 end
300
301 test "thumbnails JPEG images into JPEG", %{
302 conn: conn,
303 url: url,
304 media_proxy_url: media_proxy_url
305 } do
306 assert_dependencies_installed()
307
308 Tesla.Mock.mock(fn
309 %{method: "head", url: ^media_proxy_url} ->
310 %Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/jpeg"}]}
311
312 %{method: :get, url: ^media_proxy_url} ->
313 %Tesla.Env{status: 200, body: File.read!("test/fixtures/image.jpg")}
314 end)
315
316 response = get(conn, url)
317
318 assert response.status == 200
319 assert Conn.get_resp_header(response, "content-type") == ["image/jpeg"]
320 assert response.resp_body != ""
321 end
322
323 test "redirects to media proxy URI in case of thumbnailing error", %{
324 conn: conn,
325 url: url,
326 media_proxy_url: media_proxy_url
327 } do
328 Tesla.Mock.mock(fn
329 %{method: "head", url: ^media_proxy_url} ->
330 %Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/jpeg"}]}
331
332 %{method: :get, url: ^media_proxy_url} ->
333 %Tesla.Env{status: 200, body: "<html><body>error</body></html>"}
334 end)
335
336 response = get(conn, url)
337
338 assert response.status == 302
339 assert redirected_to(response) == media_proxy_url
340 end
341 end
342 end