Merge branch 'develop' into feature/bulk-confirmation
[akkoma] / test / 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 setup do
85 clear_config([:media_proxy, :enabled], true)
86 clear_config([:media_preview_proxy, :enabled], true)
87 clear_config([Pleroma.Web.Endpoint, :secret_key_base], "00000000000")
88
89 original_url = "https://google.fn/test.png"
90
91 [
92 url: MediaProxy.encode_preview_url(original_url),
93 media_proxy_url: MediaProxy.encode_url(original_url)
94 ]
95 end
96
97 test "returns 404 when media proxy is disabled", %{conn: conn} do
98 clear_config([:media_proxy, :enabled], false)
99
100 assert %Conn{
101 status: 404,
102 resp_body: "Not Found"
103 } = get(conn, "/proxy/preview/hhgfh/eeeee")
104
105 assert %Conn{
106 status: 404,
107 resp_body: "Not Found"
108 } = get(conn, "/proxy/preview/hhgfh/fff")
109 end
110
111 test "returns 404 when disabled", %{conn: conn} do
112 clear_config([:media_preview_proxy, :enabled], false)
113
114 assert %Conn{
115 status: 404,
116 resp_body: "Not Found"
117 } = get(conn, "/proxy/preview/hhgfh/eeeee")
118
119 assert %Conn{
120 status: 404,
121 resp_body: "Not Found"
122 } = get(conn, "/proxy/preview/hhgfh/fff")
123 end
124
125 test "it returns 403 for invalid signature", %{conn: conn, url: url} do
126 Pleroma.Config.put([Pleroma.Web.Endpoint, :secret_key_base], "000")
127 %{path: path} = URI.parse(url)
128
129 assert %Conn{
130 status: 403,
131 resp_body: "Forbidden"
132 } = get(conn, path)
133
134 assert %Conn{
135 status: 403,
136 resp_body: "Forbidden"
137 } = get(conn, "/proxy/preview/hhgfh/eeee")
138
139 assert %Conn{
140 status: 403,
141 resp_body: "Forbidden"
142 } = get(conn, "/proxy/preview/hhgfh/eeee/fff")
143 end
144
145 test "redirects to valid url when filename is invalidated", %{conn: conn, url: url} do
146 invalid_url = String.replace(url, "test.png", "test-file.png")
147 response = get(conn, invalid_url)
148 assert response.status == 302
149 assert redirected_to(response) == url
150 end
151
152 test "responds with 424 Failed Dependency if HEAD request to media proxy fails", %{
153 conn: conn,
154 url: url,
155 media_proxy_url: media_proxy_url
156 } do
157 Tesla.Mock.mock(fn
158 %{method: "head", url: ^media_proxy_url} ->
159 %Tesla.Env{status: 500, body: ""}
160 end)
161
162 response = get(conn, url)
163 assert response.status == 424
164 assert response.resp_body == "Can't fetch HTTP headers (HTTP 500)."
165 end
166
167 test "redirects to media proxy URI on unsupported content type", %{
168 conn: conn,
169 url: url,
170 media_proxy_url: media_proxy_url
171 } do
172 Tesla.Mock.mock(fn
173 %{method: "head", url: ^media_proxy_url} ->
174 %Tesla.Env{status: 200, body: "", headers: [{"content-type", "application/pdf"}]}
175 end)
176
177 response = get(conn, url)
178 assert response.status == 302
179 assert redirected_to(response) == media_proxy_url
180 end
181
182 test "with `static=true` and GIF image preview requested, responds with JPEG image", %{
183 conn: conn,
184 url: url,
185 media_proxy_url: media_proxy_url
186 } do
187 # Setting a high :min_content_length to ensure this scenario is not affected by its logic
188 clear_config([:media_preview_proxy, :min_content_length], 1_000_000_000)
189
190 Tesla.Mock.mock(fn
191 %{method: "head", url: ^media_proxy_url} ->
192 %Tesla.Env{
193 status: 200,
194 body: "",
195 headers: [{"content-type", "image/gif"}, {"content-length", "1001718"}]
196 }
197
198 %{method: :get, url: ^media_proxy_url} ->
199 %Tesla.Env{status: 200, body: File.read!("test/fixtures/image.gif")}
200 end)
201
202 response = get(conn, url <> "?static=true")
203
204 assert response.status == 200
205 assert Conn.get_resp_header(response, "content-type") == ["image/jpeg"]
206 assert response.resp_body != ""
207 end
208
209 test "with GIF image preview requested and no `static` param, redirects to media proxy URI",
210 %{
211 conn: conn,
212 url: url,
213 media_proxy_url: media_proxy_url
214 } do
215 Tesla.Mock.mock(fn
216 %{method: "head", url: ^media_proxy_url} ->
217 %Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/gif"}]}
218 end)
219
220 response = get(conn, url)
221
222 assert response.status == 302
223 assert redirected_to(response) == media_proxy_url
224 end
225
226 test "with `static` param and non-GIF image preview requested, " <>
227 "redirects to media preview proxy URI without `static` param",
228 %{
229 conn: conn,
230 url: url,
231 media_proxy_url: media_proxy_url
232 } do
233 Tesla.Mock.mock(fn
234 %{method: "head", url: ^media_proxy_url} ->
235 %Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/jpeg"}]}
236 end)
237
238 response = get(conn, url <> "?static=true")
239
240 assert response.status == 302
241 assert redirected_to(response) == url
242 end
243
244 test "with :min_content_length setting not matched by Content-Length header, " <>
245 "redirects to media proxy URI",
246 %{
247 conn: conn,
248 url: url,
249 media_proxy_url: media_proxy_url
250 } do
251 clear_config([:media_preview_proxy, :min_content_length], 100_000)
252
253 Tesla.Mock.mock(fn
254 %{method: "head", url: ^media_proxy_url} ->
255 %Tesla.Env{
256 status: 200,
257 body: "",
258 headers: [{"content-type", "image/gif"}, {"content-length", "5000"}]
259 }
260 end)
261
262 response = get(conn, url)
263
264 assert response.status == 302
265 assert redirected_to(response) == media_proxy_url
266 end
267
268 test "thumbnails PNG images into PNG", %{
269 conn: conn,
270 url: url,
271 media_proxy_url: media_proxy_url
272 } do
273 Tesla.Mock.mock(fn
274 %{method: "head", url: ^media_proxy_url} ->
275 %Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/png"}]}
276
277 %{method: :get, url: ^media_proxy_url} ->
278 %Tesla.Env{status: 200, body: File.read!("test/fixtures/image.png")}
279 end)
280
281 response = get(conn, url)
282
283 assert response.status == 200
284 assert Conn.get_resp_header(response, "content-type") == ["image/png"]
285 assert response.resp_body != ""
286 end
287
288 test "thumbnails JPEG images into JPEG", %{
289 conn: conn,
290 url: url,
291 media_proxy_url: media_proxy_url
292 } do
293 Tesla.Mock.mock(fn
294 %{method: "head", url: ^media_proxy_url} ->
295 %Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/jpeg"}]}
296
297 %{method: :get, url: ^media_proxy_url} ->
298 %Tesla.Env{status: 200, body: File.read!("test/fixtures/image.jpg")}
299 end)
300
301 response = get(conn, url)
302
303 assert response.status == 200
304 assert Conn.get_resp_header(response, "content-type") == ["image/jpeg"]
305 assert response.resp_body != ""
306 end
307
308 test "redirects to media proxy URI in case of thumbnailing error", %{
309 conn: conn,
310 url: url,
311 media_proxy_url: media_proxy_url
312 } do
313 Tesla.Mock.mock(fn
314 %{method: "head", url: ^media_proxy_url} ->
315 %Tesla.Env{status: 200, body: "", headers: [{"content-type", "image/jpeg"}]}
316
317 %{method: :get, url: ^media_proxy_url} ->
318 %Tesla.Env{status: 200, body: "<html><body>error</body></html>"}
319 end)
320
321 response = get(conn, url)
322
323 assert response.status == 302
324 assert redirected_to(response) == media_proxy_url
325 end
326 end
327 end