[#1478] OAuth admin tweaks: enforced OAuth admin scopes usage by default, migrated...
[akkoma] / test / web / pleroma_api / controllers / emoji_api_controller_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.PleromaAPI.EmojiAPIControllerTest do
6 use Pleroma.Web.ConnCase
7
8 import Tesla.Mock
9
10 import Pleroma.Factory
11
12 @emoji_dir_path Path.join(
13 Pleroma.Config.get!([:instance, :static_dir]),
14 "emoji"
15 )
16
17 clear_config([:auth, :enforce_oauth_admin_scope_usage]) do
18 Pleroma.Config.put([:auth, :enforce_oauth_admin_scope_usage], false)
19 end
20
21 test "shared & non-shared pack information in list_packs is ok" do
22 conn = build_conn()
23 resp = conn |> get(emoji_api_path(conn, :list_packs)) |> json_response(200)
24
25 assert Map.has_key?(resp, "test_pack")
26
27 pack = resp["test_pack"]
28
29 assert Map.has_key?(pack["pack"], "download-sha256")
30 assert pack["pack"]["can-download"]
31
32 assert pack["files"] == %{"blank" => "blank.png"}
33
34 # Non-shared pack
35
36 assert Map.has_key?(resp, "test_pack_nonshared")
37
38 pack = resp["test_pack_nonshared"]
39
40 refute pack["pack"]["shared"]
41 refute pack["pack"]["can-download"]
42 end
43
44 test "listing remote packs" do
45 admin = insert(:user, is_admin: true)
46 conn = build_conn() |> assign(:user, admin)
47
48 resp = conn |> get(emoji_api_path(conn, :list_packs)) |> json_response(200)
49
50 mock(fn
51 %{method: :get, url: "https://example.com/.well-known/nodeinfo"} ->
52 json(%{links: [%{href: "https://example.com/nodeinfo/2.1.json"}]})
53
54 %{method: :get, url: "https://example.com/nodeinfo/2.1.json"} ->
55 json(%{metadata: %{features: ["shareable_emoji_packs"]}})
56
57 %{method: :get, url: "https://example.com/api/pleroma/emoji/packs"} ->
58 json(resp)
59 end)
60
61 assert conn
62 |> post(emoji_api_path(conn, :list_from), %{instance_address: "https://example.com"})
63 |> json_response(200) == resp
64 end
65
66 test "downloading a shared pack from download_shared" do
67 conn = build_conn()
68
69 resp =
70 conn
71 |> get(emoji_api_path(conn, :download_shared, "test_pack"))
72 |> response(200)
73
74 {:ok, arch} = :zip.unzip(resp, [:memory])
75
76 assert Enum.find(arch, fn {n, _} -> n == 'pack.json' end)
77 assert Enum.find(arch, fn {n, _} -> n == 'blank.png' end)
78 end
79
80 test "downloading shared & unshared packs from another instance via download_from, deleting them" do
81 on_exit(fn ->
82 File.rm_rf!("#{@emoji_dir_path}/test_pack2")
83 File.rm_rf!("#{@emoji_dir_path}/test_pack_nonshared2")
84 end)
85
86 mock(fn
87 %{method: :get, url: "https://old-instance/.well-known/nodeinfo"} ->
88 json(%{links: [%{href: "https://old-instance/nodeinfo/2.1.json"}]})
89
90 %{method: :get, url: "https://old-instance/nodeinfo/2.1.json"} ->
91 json(%{metadata: %{features: []}})
92
93 %{method: :get, url: "https://example.com/.well-known/nodeinfo"} ->
94 json(%{links: [%{href: "https://example.com/nodeinfo/2.1.json"}]})
95
96 %{method: :get, url: "https://example.com/nodeinfo/2.1.json"} ->
97 json(%{metadata: %{features: ["shareable_emoji_packs"]}})
98
99 %{
100 method: :get,
101 url: "https://example.com/api/pleroma/emoji/packs/list"
102 } ->
103 conn = build_conn()
104
105 conn
106 |> get(emoji_api_path(conn, :list_packs))
107 |> json_response(200)
108 |> json()
109
110 %{
111 method: :get,
112 url: "https://example.com/api/pleroma/emoji/packs/download_shared/test_pack"
113 } ->
114 conn = build_conn()
115
116 conn
117 |> get(emoji_api_path(conn, :download_shared, "test_pack"))
118 |> response(200)
119 |> text()
120
121 %{
122 method: :get,
123 url: "https://nonshared-pack"
124 } ->
125 text(File.read!("#{@emoji_dir_path}/test_pack_nonshared/nonshared.zip"))
126 end)
127
128 admin = insert(:user, is_admin: true)
129
130 conn = build_conn() |> assign(:user, admin)
131
132 assert (conn
133 |> put_req_header("content-type", "application/json")
134 |> post(
135 emoji_api_path(
136 conn,
137 :download_from
138 ),
139 %{
140 instance_address: "https://old-instance",
141 pack_name: "test_pack",
142 as: "test_pack2"
143 }
144 |> Jason.encode!()
145 )
146 |> json_response(500))["error"] =~ "does not support"
147
148 assert conn
149 |> put_req_header("content-type", "application/json")
150 |> post(
151 emoji_api_path(
152 conn,
153 :download_from
154 ),
155 %{
156 instance_address: "https://example.com",
157 pack_name: "test_pack",
158 as: "test_pack2"
159 }
160 |> Jason.encode!()
161 )
162 |> json_response(200) == "ok"
163
164 assert File.exists?("#{@emoji_dir_path}/test_pack2/pack.json")
165 assert File.exists?("#{@emoji_dir_path}/test_pack2/blank.png")
166
167 assert conn
168 |> delete(emoji_api_path(conn, :delete, "test_pack2"))
169 |> json_response(200) == "ok"
170
171 refute File.exists?("#{@emoji_dir_path}/test_pack2")
172
173 # non-shared, downloaded from the fallback URL
174
175 conn = build_conn() |> assign(:user, admin)
176
177 assert conn
178 |> put_req_header("content-type", "application/json")
179 |> post(
180 emoji_api_path(
181 conn,
182 :download_from
183 ),
184 %{
185 instance_address: "https://example.com",
186 pack_name: "test_pack_nonshared",
187 as: "test_pack_nonshared2"
188 }
189 |> Jason.encode!()
190 )
191 |> json_response(200) == "ok"
192
193 assert File.exists?("#{@emoji_dir_path}/test_pack_nonshared2/pack.json")
194 assert File.exists?("#{@emoji_dir_path}/test_pack_nonshared2/blank.png")
195
196 assert conn
197 |> delete(emoji_api_path(conn, :delete, "test_pack_nonshared2"))
198 |> json_response(200) == "ok"
199
200 refute File.exists?("#{@emoji_dir_path}/test_pack_nonshared2")
201 end
202
203 describe "updating pack metadata" do
204 setup do
205 pack_file = "#{@emoji_dir_path}/test_pack/pack.json"
206 original_content = File.read!(pack_file)
207
208 on_exit(fn ->
209 File.write!(pack_file, original_content)
210 end)
211
212 {:ok,
213 admin: insert(:user, is_admin: true),
214 pack_file: pack_file,
215 new_data: %{
216 "license" => "Test license changed",
217 "homepage" => "https://pleroma.social",
218 "description" => "Test description",
219 "share-files" => false
220 }}
221 end
222
223 test "for a pack without a fallback source", ctx do
224 conn = build_conn()
225
226 assert conn
227 |> assign(:user, ctx[:admin])
228 |> post(
229 emoji_api_path(conn, :update_metadata, "test_pack"),
230 %{
231 "new_data" => ctx[:new_data]
232 }
233 )
234 |> json_response(200) == ctx[:new_data]
235
236 assert Jason.decode!(File.read!(ctx[:pack_file]))["pack"] == ctx[:new_data]
237 end
238
239 test "for a pack with a fallback source", ctx do
240 mock(fn
241 %{
242 method: :get,
243 url: "https://nonshared-pack"
244 } ->
245 text(File.read!("#{@emoji_dir_path}/test_pack_nonshared/nonshared.zip"))
246 end)
247
248 new_data = Map.put(ctx[:new_data], "fallback-src", "https://nonshared-pack")
249
250 new_data_with_sha =
251 Map.put(
252 new_data,
253 "fallback-src-sha256",
254 "74409E2674DAA06C072729C6C8426C4CB3B7E0B85ED77792DB7A436E11D76DAF"
255 )
256
257 conn = build_conn()
258
259 assert conn
260 |> assign(:user, ctx[:admin])
261 |> post(
262 emoji_api_path(conn, :update_metadata, "test_pack"),
263 %{
264 "new_data" => new_data
265 }
266 )
267 |> json_response(200) == new_data_with_sha
268
269 assert Jason.decode!(File.read!(ctx[:pack_file]))["pack"] == new_data_with_sha
270 end
271
272 test "when the fallback source doesn't have all the files", ctx do
273 mock(fn
274 %{
275 method: :get,
276 url: "https://nonshared-pack"
277 } ->
278 {:ok, {'empty.zip', empty_arch}} = :zip.zip('empty.zip', [], [:memory])
279 text(empty_arch)
280 end)
281
282 new_data = Map.put(ctx[:new_data], "fallback-src", "https://nonshared-pack")
283
284 conn = build_conn()
285
286 assert (conn
287 |> assign(:user, ctx[:admin])
288 |> post(
289 emoji_api_path(conn, :update_metadata, "test_pack"),
290 %{
291 "new_data" => new_data
292 }
293 )
294 |> json_response(:bad_request))["error"] =~ "does not have all"
295 end
296 end
297
298 test "updating pack files" do
299 pack_file = "#{@emoji_dir_path}/test_pack/pack.json"
300 original_content = File.read!(pack_file)
301
302 on_exit(fn ->
303 File.write!(pack_file, original_content)
304
305 File.rm_rf!("#{@emoji_dir_path}/test_pack/blank_url.png")
306 File.rm_rf!("#{@emoji_dir_path}/test_pack/dir")
307 File.rm_rf!("#{@emoji_dir_path}/test_pack/dir_2")
308 end)
309
310 admin = insert(:user, is_admin: true)
311
312 conn = build_conn()
313
314 same_name = %{
315 "action" => "add",
316 "shortcode" => "blank",
317 "filename" => "dir/blank.png",
318 "file" => %Plug.Upload{
319 filename: "blank.png",
320 path: "#{@emoji_dir_path}/test_pack/blank.png"
321 }
322 }
323
324 different_name = %{same_name | "shortcode" => "blank_2"}
325
326 conn = conn |> assign(:user, admin)
327
328 assert (conn
329 |> post(emoji_api_path(conn, :update_file, "test_pack"), same_name)
330 |> json_response(:conflict))["error"] =~ "already exists"
331
332 assert conn
333 |> post(emoji_api_path(conn, :update_file, "test_pack"), different_name)
334 |> json_response(200) == %{"blank" => "blank.png", "blank_2" => "dir/blank.png"}
335
336 assert File.exists?("#{@emoji_dir_path}/test_pack/dir/blank.png")
337
338 assert conn
339 |> post(emoji_api_path(conn, :update_file, "test_pack"), %{
340 "action" => "update",
341 "shortcode" => "blank_2",
342 "new_shortcode" => "blank_3",
343 "new_filename" => "dir_2/blank_3.png"
344 })
345 |> json_response(200) == %{"blank" => "blank.png", "blank_3" => "dir_2/blank_3.png"}
346
347 refute File.exists?("#{@emoji_dir_path}/test_pack/dir/")
348 assert File.exists?("#{@emoji_dir_path}/test_pack/dir_2/blank_3.png")
349
350 assert conn
351 |> post(emoji_api_path(conn, :update_file, "test_pack"), %{
352 "action" => "remove",
353 "shortcode" => "blank_3"
354 })
355 |> json_response(200) == %{"blank" => "blank.png"}
356
357 refute File.exists?("#{@emoji_dir_path}/test_pack/dir_2/")
358
359 mock(fn
360 %{
361 method: :get,
362 url: "https://test-blank/blank_url.png"
363 } ->
364 text(File.read!("#{@emoji_dir_path}/test_pack/blank.png"))
365 end)
366
367 # The name should be inferred from the URL ending
368 from_url = %{
369 "action" => "add",
370 "shortcode" => "blank_url",
371 "file" => "https://test-blank/blank_url.png"
372 }
373
374 assert conn
375 |> post(emoji_api_path(conn, :update_file, "test_pack"), from_url)
376 |> json_response(200) == %{
377 "blank" => "blank.png",
378 "blank_url" => "blank_url.png"
379 }
380
381 assert File.exists?("#{@emoji_dir_path}/test_pack/blank_url.png")
382
383 assert conn
384 |> post(emoji_api_path(conn, :update_file, "test_pack"), %{
385 "action" => "remove",
386 "shortcode" => "blank_url"
387 })
388 |> json_response(200) == %{"blank" => "blank.png"}
389
390 refute File.exists?("#{@emoji_dir_path}/test_pack/blank_url.png")
391 end
392
393 test "creating and deleting a pack" do
394 on_exit(fn ->
395 File.rm_rf!("#{@emoji_dir_path}/test_created")
396 end)
397
398 admin = insert(:user, is_admin: true)
399
400 conn = build_conn() |> assign(:user, admin)
401
402 assert conn
403 |> put_req_header("content-type", "application/json")
404 |> put(
405 emoji_api_path(
406 conn,
407 :create,
408 "test_created"
409 )
410 )
411 |> json_response(200) == "ok"
412
413 assert File.exists?("#{@emoji_dir_path}/test_created/pack.json")
414
415 assert Jason.decode!(File.read!("#{@emoji_dir_path}/test_created/pack.json")) == %{
416 "pack" => %{},
417 "files" => %{}
418 }
419
420 assert conn
421 |> delete(emoji_api_path(conn, :delete, "test_created"))
422 |> json_response(200) == "ok"
423
424 refute File.exists?("#{@emoji_dir_path}/test_created/pack.json")
425 end
426
427 test "filesystem import" do
428 on_exit(fn ->
429 File.rm!("#{@emoji_dir_path}/test_pack_for_import/emoji.txt")
430 File.rm!("#{@emoji_dir_path}/test_pack_for_import/pack.json")
431 end)
432
433 conn = build_conn()
434 resp = conn |> get(emoji_api_path(conn, :list_packs)) |> json_response(200)
435
436 refute Map.has_key?(resp, "test_pack_for_import")
437
438 admin = insert(:user, is_admin: true)
439
440 assert conn
441 |> assign(:user, admin)
442 |> post(emoji_api_path(conn, :import_from_fs))
443 |> json_response(200) == ["test_pack_for_import"]
444
445 resp = conn |> get(emoji_api_path(conn, :list_packs)) |> json_response(200)
446 assert resp["test_pack_for_import"]["files"] == %{"blank" => "blank.png"}
447
448 File.rm!("#{@emoji_dir_path}/test_pack_for_import/pack.json")
449 refute File.exists?("#{@emoji_dir_path}/test_pack_for_import/pack.json")
450
451 emoji_txt_content = "blank, blank.png, Fun\n\nblank2, blank.png"
452
453 File.write!("#{@emoji_dir_path}/test_pack_for_import/emoji.txt", emoji_txt_content)
454
455 assert conn
456 |> assign(:user, admin)
457 |> post(emoji_api_path(conn, :import_from_fs))
458 |> json_response(200) == ["test_pack_for_import"]
459
460 resp = conn |> get(emoji_api_path(conn, :list_packs)) |> json_response(200)
461
462 assert resp["test_pack_for_import"]["files"] == %{
463 "blank" => "blank.png",
464 "blank2" => "blank.png"
465 }
466 end
467 end